abstract Solver

# A Newton Raphsoin solver
immutable NRSolver <: Solver
    rel_tol::Float64
    abs_tol::Float64
    alt_tol::Float64
    max_iters::Int
    err_on_nonconv::Bool
end

NRSolver(;rel_tol=0.0::Float64, abs_tol=0.0::Float64,
          max_iters=10::Int, err_on_nonconv=true::Bool, alt_tol = 0.0) =
    NRSolver(rel_tol, abs_tol, alt_tol, max_iters, err_on_nonconv)

# Solves a nonlinear problem by iterating until convergence
function solve(solver::NRSolver, fp::FEProblem, exporter::AbstractDataExporter)
    println("Starting Newton-Raphson solver..")
    load = extload(fp)
    force_imbalance = similar(load)
    iteration = 0
    tstep = 0
    n_print = 0

    K = create_sparse_structure(fp::FEProblem)
    colptrs = get_colptrs(K, fp::FEProblem)

    for t in 0.0 #TODO Implement proper time stepping
        println("Current time $t")
        iteration = 0
        tstep += 1
        updatebcs!(fp, t)
        while true
            iteration += 1
            if iteration > solver.max_iters
                warn("Max iterations hit.")
                if solver.err_on_nonconv
                    error("Did not converge")
                end
                break
            end

            int_f = assemble_intf(fp)
            @devec force_imbalance[:] = load .- int_f

            abs_res = norm(force_imbalance)
            rel_res = norm(force_imbalance) / norm(load)

            @printf("\tIteration %d, relative residual %1.4e, absolute residual %1.4e", iteration, rel_res, abs_res)

            print("\n\tDofType residuals:\n \t\t")
            for (dof_type, eqs) in fp.doftype_eqs
                @devec r = sum(sqr(int_f[eqs]))
                @printf("%s: %1.2e   \t", dof_type, sqrt(r))
            end

            if  abs_res < solver.abs_tol
                println("\nConverged with absolute tolerance!")
                break
            end

            if rel_res < solver.rel_tol
                println("\nConverged with relative tolerance!")
                break
            end

            assembleK!(K, fp, colptrs, fp.dof_vals)

            #du = cholfact(Symmetric(K, :L)) \ force_imbalance
            du = K \ force_imbalance

            print("\n\tDofType updates:\n \t\t")
            for (dof_type, eqs) in fp.doftype_eqs
                @devec r = sum(sqr(du[eqs]))
                @printf("%s: %1.2e   \t", dof_type, sqrt(r))
            end
            updatedofs!(fp, du)

            if norm(du) < solver.alt_tol
                     println("\n Converged with alternative tolerance!")
                break
            end

            print("\n\n")
        end
    update_feproblem(fp)

    n_print += 1
    write_data(fp, exporter, n_print)
    end

end


# ____________________________________________________________________
# SpecLineGauss
#
mutable struct SpecLineGauss <: AbstractComponent
    norm::Parameter
    center::Parameter
    fwhm::Parameter
    voff::Parameter
    spec_res_kms::Float64
    norm_integrated::Bool

    function SpecLineGauss(center::Number)
        out = new(Parameter(1),
                  Parameter(center),
                  Parameter(3000),
                  Parameter(0),
                  0., true)

        @assert center > 0
        out.norm.low = 0
        out.center.low = 0
        out.fwhm.low = 0
        out.voff.low = 0
        out.center.fixed = true
        return out
    end
end

function prepare!(comp::SpecLineGauss, domain::Domain{1})
    return fill(NaN, length(domain))
end

function evaluate!(buffer, comp::SpecLineGauss, x::Domain{1},
                   norm, center, fwhm, voff)
    x0 = center - (voff / 3.e5) * center
    sigma_line = fwhm / 2.355      / 3.e5 * center
    sigma_spec = comp.spec_res_kms / 3.e5 * center
    sigma = sqrt(sigma_line^2 + sigma_spec^2)

    map!(x -> begin
         (abs.(x - x0) .> 4sigma)  &&  (return 0.)
         ret = norm * exp(-((x - x0) / sigma)^2 / 2)
         comp.norm_integrated  &&  (ret /= (sqrt(2pi) * sigma))
         return ret
         end, buffer, x.coords[:,1])
end

#=
    x = Domain(500:1:1500.)
    comp = QSFit.SpecLineGauss(1000.)
    comp.fwhm.val = 3e4
    ceval = GFit.CompEval(comp, x)
    GFit.evaluate_cached(ceval)
    @gp x[:] ceval.buffer ./ maximum(ceval.buffer) "w l"
=#


genblas_dot{T<:Base.LinAlg.BlasFloat}(x::Vector{T}, y::Vector{T}) = BLAS.dot(x, y)
genblas_dot(x, y) = dot(x,y)
genblas_scal!{T<:Base.LinAlg.BlasFloat}(a::T, x::Vector{T}) = BLAS.scal!(length(x), a, x, 1)
genblas_scal!(a, x) = x .*= a
genblas_axpy!{T<:Base.LinAlg.BlasFloat}(a::T, x::Vector{T}, y::Vector{T}) = BLAS.axpy!(a, x, y)
genblas_axpy!(a, x, y) = y .+= a.*x
genblas_nrm2{T<:Base.LinAlg.BlasFloat}(x::Vector{T}) = BLAS.nrm2(length(x), x, 1)
genblas_nrm2(x) = norm(x)


using IterTools, DataStructures

function update_WF_params(wms::Array{Ty,1}, α::Array{Ty,1}, Λ::Array{Array{Int64,1},1}, y::Array{Int64,2}; debug = false) where Ty<:Number
    #y is a matrix of dimension J*K, with K the dimension of the process
    # and J the number of observations
    # Julia is in row major, so the first index indicates the row (index of observation)
    # and the second the column (index of the dimension) (as in matrix mathematical notation)
    @assert length(wms) == size(Λ, 1)

    nJ = sum(y, dims = 2) |> vec#sum_j=1^K n_ij
    nK = sum(y, dims = 1) |> vec#sum_i=1^J n_ij
    sy = sum(y)
    J = size(y,1)
    sα = sum(α)

    first_term = sum(SpecialFunctions.logfactorial.(nJ) - sum(SpecialFunctions.logfactorial.(y), dims = 2))

    function lpga(m::Array{Int64,1})
        sm = sum(m)
        second_term = lgamma_local(sα + sm)
        third_term = sum(lgamma_local.(α + m + nK))
        fourth_term = -lgamma_local(sα + sm + sy)
        fifth_term = -sum(lgamma_local.(α + m))
        return first_term + second_term + third_term + fourth_term + fifth_term
    end

    filter_ = wms .== 0

    lwms_hat = log.(wms) .+ map(lpga, Λ)

    wms_hat = exp.(lwms_hat)
    wms_hat[filter_] .= 0
    wms_hat = wms_hat |> normalise

    if debug&&any(isnan, wms_hat)
        println("NAs in update step")
        println("wms")
        println(wms)
        println("pga")
        println(map(pga, Λ))
        println("wms_hat")
        println(wms_hat)
    end

    return [m .+ nK for m in Λ], wms_hat
end

function get_next_WF_filtering_distribution_and_loglikelihood_precomputed(current_Λ, current_wms, current_time, next_time, α, sα, next_y, precomputed_log_ν, precomputed_log_Cmmi, precomputed_log_binomial_coeff)
    predicted_Λ, predicted_wms = predict_WF_params_precomputed(current_wms, sα, current_Λ, next_time-current_time, precomputed_log_ν, precomputed_log_Cmmi, precomputed_log_binomial_coeff)

    μν_prime_im1 = logsumexp(log(predicted_wms[k]) + logμπh_WF(α, predicted_Λ[k], vec(next_y)) for k in eachindex(predicted_Λ))# for the time being, because one only deal with single observations

    filtered_Λ, filtered_wms = update_WF_params(predicted_wms, α, predicted_Λ, next_y)

    return filtered_Λ, filtered_wms, μν_prime_im1
end

function predict_WF_params_precomputed(wms::Array{Ty,1}, sα::Ty, Λ::Array{Array{Int64,1},1}, t::Ty, precomputed_log_ν, precomputed_log_Cmmi, precomputed_log_binomial_coeff; wm = 1) where {Ty<:Number}

    res = Accumulator{Array{Int64,1}, Float64}()

    for k in eachindex(Λ)
        res = merge(res, WF_prediction_for_one_m_precomputed(Λ[k], sα, t, precomputed_log_ν, precomputed_log_Cmmi, precomputed_log_binomial_coeff; wm = wms[k]))
    end

    ks = keys(res) |> collect

    return ks, [res[k] for k in ks]

end

function WF_prediction_for_one_m_precomputed(m::Array{Int64,1}, sα::Ty, t::Ty, precomputed_log_ν, precomputed_log_Cmmi, precomputed_log_binomial_coeff; wm = 1) where {Ty<:Number}
    # gm = map(x -> 0:x, m) |> vec |> x -> Iterators.product(x...)
    gm = indices_of_tree_below(m)

    function fun_n(n)
        i = m.-n
        # println(i)
        si = sum(i)
        sm = sum(m)
        return wm*(logpmmi_precomputed(i, m, sm, si, t, sα, precomputed_log_ν, precomputed_log_Cmmi, precomputed_log_binomial_coeff) |> exp)
    end

    Dict( collect(n) => fun_n(n) for n in gm ) |> Accumulator

end

function filter_WF_adaptive_precomputation_ar(α, data, do_the_pruning::Function; silence = false, return_precomputed_terms = false, trim0 = false)
    # println("filter_WF_mem2")

    @assert length(α) == length(data[collect(keys(data))[1]])
    Δt = assert_constant_time_step_and_compute_it(data)

    smmax = values(data) |> sum |> sum
    log_ν_ar = Array{Float64}(undef, smmax, smmax)
    log_Cmmi_ar = Array{Float64}(undef, smmax, smmax)
    log_binomial_coeff_ar_offset = Array{Float64}(undef,    smmax+1, smmax+1)

    sα = sum(α)
    times = keys(data) |> collect |> sort
    Λ_of_t = Dict()
    wms_of_t = Dict()

    filtered_Λ, filtered_wms = update_WF_params([1.], α, [repeat([0], inner = length(α))], data[times[1]])
    Λ_pruned, wms_pruned = do_the_pruning(filtered_Λ, filtered_wms)

    if trim0
        Λ_pruned, wms_pruned = Λ_pruned[wms_pruned.>0], wms_pruned[wms_pruned.>0]
    end

    Λ_of_t[times[1]] = filtered_Λ
    wms_of_t[times[1]] = filtered_wms
    new_sm_max = maximum(sum.(Λ_pruned))
    precompute_next_terms_ar!(0, new_sm_max, log_ν_ar, log_Cmmi_ar, log_binomial_coeff_ar_offset, sα, Δt)
    sm_max_so_far = new_sm_max

    for k in 1:(length(times)-1)
        if (!silence)
            println("Step index: $k")
            println("Number of components: $(length(filtered_Λ))")
        end
        last_sm_max = maximum(sum.(Λ_pruned))
        new_sm_max = last_sm_max + sum(data[times[k+1]])

        if sm_max_so_far < new_sm_max
            precompute_next_terms_ar!(sm_max_so_far, new_sm_max, log_ν_ar, log_Cmmi_ar, log_binomial_coeff_ar_offset, sα, Δt)
            sm_max_so_far = max(sm_max_so_far,new_sm_max)
        end

        filtered_Λ, filtered_wms = get_next_filtering_distribution_precomputed(Λ_pruned, wms_pruned, times[k], times[k+1], α, sα, data[times[k+1]], log_ν_ar, log_Cmmi_ar, log_binomial_coeff_ar_offset)
        Λ_pruned, wms_pruned = do_the_pruning(filtered_Λ, filtered_wms)
        Λ_of_t[times[k+1]] = filtered_Λ
        wms_of_t[times[k+1]] = filtered_wms
    end

    if return_precomputed_terms
        return Λ_of_t, wms_of_t, log_ν_ar, log_Cmmi_ar, log_binomial_coeff_ar_offset, sm_max_so_far
    else
        return Λ_of_t, wms_of_t
    end

end

function get_next_filtering_distribution_precomputed(current_Λ, current_wms, current_time, next_time, α, sα, next_y, precomputed_log_ν, precomputed_log_Cmmi, precomputed_log_binomial_coeff)
    predicted_Λ, predicted_wms = predict_WF_params_precomputed(current_wms, sα, current_Λ, next_time-current_time, precomputed_log_ν, precomputed_log_Cmmi, precomputed_log_binomial_coeff)
    filtered_Λ, filtered_wms = update_WF_params(predicted_wms, α, predicted_Λ, next_y)

    return filtered_Λ, filtered_wms
end

filter_WF = filter_WF_adaptive_precomputation_ar


#= test/spfft1.jl
=#

println("spfft2.jl")
@time begin

@testset "spfft2" begin

n1 = 50
n2 = 50
k1 = 11
k2 = 10

# complex transforms

r1 = randperm(n1)[1:k1]
r2 = randperm(n2)[1:k2]

for fcn in (:fft, :bfft)
  sf = Symbol("sp", fcn)
  for T in (ComplexF32, ComplexF64)
    println("  $sf/$T")
    Tr = real(T)

    psf = Symbol("plan_", sf)
    @eval P = $psf($T, $n1, $n2, $r1, $r2)

    x  = Array{T }(undef, n1, n2)
    y  = Array{T }(undef, k1, k2)
    xr = Array{Tr}(undef, size(x))
    yr = Array{Tr}(undef, size(y))

    # f2s
    f2s = Symbol(sf, "_f2s!")
    rand!(x)

    ## (c2c, f2s)
    @eval f = $fcn($x)[$r1,$r2]
    @eval $f2s($y, P, $x)
    @test f ≈ y

    ## (c2r, f2s)
    @eval $f2s($yr, P, $x)
    @test real(f) ≈ yr

    ## (r2c, f2s)
    xr[:] = real(x)
    @eval f = $fcn($xr)[$r1,$r2]
    @eval $f2s($y, P, $xr)
    @test f ≈ y

    # s2f
    s2f = Symbol(sf, "_s2f!")
    rand!(y)

    ## (c2c, s2f)
    fill!(x, 0)
    x[r1,r2] = y
    @eval f = $fcn($x)
    @eval $s2f($x, P, $y)
    @test f ≈ x

    ## (c2r, s2f)
    @eval $s2f($xr, P, $y)
    @test real(f) ≈ xr

    ## (r2c, s2f)
    yr[:] = real(y)
    fill!(x, 0)
    x[r1,r2] = yr
    @eval f = $fcn($x)
    @eval $s2f($x, P, $yr)
    @test f ≈ x
  end
end

# real transforms

m1 = div(n1,2) + 1
m2 = div(n2,2) + 1
r1 = vcat([1,m1], 1 .+ randperm(m1-2)[1:k1-2])  # include edge cases
r2 = vcat([1,m2], 1 .+ randperm(m2-2)[1:k2-2])

for T in (Float32, Float64)
  Tc = Complex{T}

  x  = Array{T }(undef, n1, n2)
  y  = Array{Tc}(undef, k1, k2)
  xc = Array{Tc}(undef, m1, n2)

  # (r2c, f2s)
  println("  sprfft/$T")
  P = plan_sprfft(T, n1, n2, r1, r2)
  rand!(x)
  f = rfft(x)[r1,r2]
  sprfft_f2s!(y, P, x)
  @test f ≈ y

  # (c2r, s2f)
  println("  spbrfft/$T")
  P = plan_spbrfft(T, n1, n2, r1, r2)
  rand!(y)
  y[r1.==1,r2.==1] = real(y[r1.==1,r2.==1])
  even1 = n1 % 2 == 0
  even2 = n2 % 2 == 0
  even1 && (y[r1.==m1,1] = real(y[r1.==m1,1]))
  even2 && (y[1,r2.==m2] = real(y[1,r2.==m2]))
  (even1 && even2) && (y[r1.==m1,r2.==m2] = real(y[r1.==m1,r2.==m2]))
  fill!(xc, 0)
  xc[r1,r2] = y
  f = brfft(xc, n1)
  spbrfft_s2f!(x, P, y)
  @test f ≈ x
end

end
end


__precompile__
include("quadrature.jl")
include("Basis.jl")

using ProgressMeter

struct Solver
    # spatial grid of cell interfaces
    x;

    # quadrature
    q::Quadrature;

    # Solver settings
    settings::Settings;

    # spatial basis functions
    basis::Basis;

    # constructor
    function Solver(settings)
        x = settings.x;
        q = Quadrature(settings.Nq,"Gauss");
        basis = Basis(q,settings);

        new(x,q,settings,basis);
    end
end

# physical flux Scalar valued Burgers
function f(u::Float64)
    return 0.5*u^2.0;
end

# physical flux Vector valued Burgers
function f(u::Array{Float64,1})
    return 0.5*u.^2.0;
end

# Lax-Friedrichs (LF) flux for Burgers
function numFlux(beta::Float64,u::Float64,v::Float64)
    #beta = max(abs(u),abs(v)); # max of abs(f'(u))
    #beta = obj.dx/obj.dt/2;
    return 0.5*(f(u)+f(v))-beta.*(v-u);
end

function SetupIC(obj::Solver)
    u = zeros(obj.settings.N*obj.settings.N,obj.settings.Nx);
    uVals = zeros(obj.settings.Nq^2);
    for j = 1:obj.settings.Nx
        for q = 1:obj.settings.Nq
            for k = 1:obj.settings.Nq
                uVals[(k-1)*obj.settings.Nq+q] = obj.settings.IC(obj.x[j],obj.q.xi[k],obj.q.xi[q])[1];
            end
        end
        u[:,j] = ComputeMoments(obj.basis,uVals*0.25);
    end
    return u;
end

function minmod(a::Float64,b::Float64)
    y = 0;
    if ( abs(a) < abs(b)) && a*b > 0
        y = a;
    elseif ( abs(b) < abs(a)) && a*b > 0
        y = b;
    end
    return y;
end

function Slope(obj::Solver,u::Array{Float64,1},v::Array{Float64,1},w::Array{Float64,1})
    if obj.settings.limiterType == "Minmod"
        return minmod.(w.-v,v.-u)/obj.settings.dx;
    else
        return 0.0;
    end
end

function Solve(obj::Solver)
    t = 0.0;
    dt = obj.settings.dt;
    dx = obj.x[2]-obj.x[1];
    tEnd = obj.settings.tEnd;
    Nx = obj.settings.Nx;
    N = obj.settings.N; # number of moments

    # Set up initial condition
    u = SetupIC(obj);
    uNew = deepcopy(u);

    Nt = round(tEnd/dt);

    # time loop
    @showprogress 0.1 "Progress " for n = 1:Nt

        # Update time by dt
        for j = 3:(Nx-2)
            uQjMM = EvalAtQuad(obj.basis,u[:,j-2]);
            uQjM = EvalAtQuad(obj.basis,u[:,j-1]);
            uQj = EvalAtQuad(obj.basis,u[:,j]);
            uQjP = EvalAtQuad(obj.basis,u[:,j+1]);
            uQjPP = EvalAtQuad(obj.basis,u[:,j+2]);
            numFRight = numFlux.( dx/dt/2,uQj.+0.5*dx*Slope(obj,uQjM,uQj,uQjP),uQjP.-0.5*dx*Slope(obj,uQj,uQjP,uQjPP));
            numFLeft = numFlux.( dx/dt/2,uQjM.+0.5*dx*Slope(obj,uQjMM,uQjM,uQj),uQj.-0.5*dx*Slope(obj,uQjM,uQj,uQjP));
            for i = 1:N
                uNew[:,j] = u[:,j].-dt/dx*ComputeMoments(obj.basis,(numFRight.-numFLeft)*0.25);
            end
        end
        u .= uNew;

        # apply filter step
        Filter(obj,u);
        t = t+dt;
    end

    # return end time and solution
    return t, u;

end

function Filter(obj::Solver,u::Array{Float64,2})
    lambda = obj.settings.lambda
    N = obj.settings.N;
    for j = 1:size(u,2)
        for i = 1:N
            for l = 1:N
                #u[(l-1)*N+i,j] = u[(l-1)*N+i,j]/(1+lambda*(i-1)^2*i^2*(l-1)^2*l^2);
                u[(l-1)*N+i,j] = u[(l-1)*N+i,j]/(1+lambda*(i-1)^2*i^2+lambda*(l-1)^2*l^2);
            end
        end
    end
end


"""
    `getz(tply::AbstractArray{<:Real,1}, nply::AbstractArray{<:Integer,1})`

    `getz(lam::Laminate)`

Returns a laminate's z-coordinates (coordinates of top and bottom of laminas)
given the thickness of plies in each lamina and the number of plies in each
lamina.
"""
function getz(tply::AbstractArray{<:Real,1}, nply::AbstractArray{<:Integer,1})
    ttotal = 0.0
    tlam = zeros(eltype(tply), length(tply)+1)
    for i = 1:length(tply)
        ttotal += tply[i]*nply[i]
        tlam[i+1] = ttotal
    end
    tlam .-= ttotal/2.0
    return tlam
end
getz(lam::Laminate) = getz(lam.tply, lam.nply)

"""
    `getQ(e1::Real, e2::Real, g12::Real, nu12::Real, theta::Real=0.0)`

    `getQ(mat::Material, theta::Real)`

    `getQ(mat::Material, lam::Laminate)`

Returns Q matrix rotated `theta` degrees
"""
function getQ(e1::Real, e2::Real, g12::Real, nu12::Real, theta::Real=0.0)
    nud = 1.0-e2/e1*nu12^2.0
    q11 = e1/nud
    q22 = e2/nud
    q12 = nu12*e2/nud
    q66 = g12
    return rotQ(q11, q12, q22, q66, theta)
end
getQ(mat::Material, theta::Real=0.0) = getQ(mat.e1, mat.e2, mat.g12, mat.nu12, theta)
getQ(mat::Array{Material,1}, lam::Laminate) = getQ.(mat, lam.theta)

"""
    `rotQ(q::AbstractArray{<:Real,2}, theta::Real)`

    `rotQ(qxx::Real, q12::Real, q22::Real, q66::Real, theta::Real)`

Rotates Q matrix by theta degrees
"""
function rotQ!(q::AbstractArray{<:Real,2}, theta::Real)

    if theta != zero(eltype(theta))
        c = cosd(theta)
        s = sind(theta)
        c2 = c^2
        s2 = s^2
        cs = c*s

        # qbar = inv(tsigma)*q*teps

        # tsigma = [c^2.0 s^2.0 2*c*s; s^2.0 c^2.0 -2*c*s; -c*s c*s c^2-s^2]

        inv_tsig11 = c2
        inv_tsig12 = s2
        inv_tsig13 = -2*cs
        inv_tsig21 = s^2
        inv_tsig22 = c^2
        inv_tsig23 = 2*cs
        inv_tsig31 = cs
        inv_tsig32 = -cs
        inv_tsig33 = 2*c2-1

        # teps = LinearAlgebra.Diagonal([1.0,1.0,2.0])*tsigma*LinearAlgebra.Diagonal([1.0,1.0,0.5])

        teps11 = c2
        teps12 = s2
        teps13 = cs
        teps21 = s2
        teps22 = c2
        teps23 = -cs
        teps31 = -2*cs
        teps32 = 2*cs
        teps33 = c2-s2

        # inv(tsigma)*q

        inv_tsig_q11 = inv_tsig11*q[1,1] + inv_tsig12*q[2,1] + inv_tsig13*q[3,1]
        inv_tsig_q12 = inv_tsig11*q[1,2] + inv_tsig12*q[2,2] + inv_tsig13*q[3,2]
        inv_tsig_q13 = inv_tsig11*q[1,3] + inv_tsig12*q[2,3] + inv_tsig13*q[3,3]
        inv_tsig_q21 = inv_tsig21*q[1,1] + inv_tsig22*q[2,1] + inv_tsig23*q[3,1]
        inv_tsig_q22 = inv_tsig21*q[1,2] + inv_tsig22*q[2,2] + inv_tsig23*q[3,2]
        inv_tsig_q23 = inv_tsig21*q[1,3] + inv_tsig22*q[2,3] + inv_tsig23*q[3,3]
        inv_tsig_q31 = inv_tsig31*q[1,1] + inv_tsig32*q[2,1] + inv_tsig33*q[3,1]
        inv_tsig_q32 = inv_tsig31*q[1,2] + inv_tsig32*q[2,2] + inv_tsig33*q[3,2]
        inv_tsig_q33 = inv_tsig31*q[1,3] + inv_tsig32*q[2,3] + inv_tsig33*q[3,3]

        # inv(tsigma)*q*teps

        q[1,1] = inv_tsig_q11*teps11 + inv_tsig_q12*teps21 + inv_tsig_q13*teps31
        q[1,2] = inv_tsig_q11*teps12 + inv_tsig_q12*teps22 + inv_tsig_q13*teps32
        q[1,3] = inv_tsig_q11*teps13 + inv_tsig_q12*teps23 + inv_tsig_q13*teps33
        q[2,1] = inv_tsig_q21*teps11 + inv_tsig_q22*teps21 + inv_tsig_q23*teps31
        q[2,2] = inv_tsig_q21*teps12 + inv_tsig_q22*teps22 + inv_tsig_q23*teps32
        q[2,3] = inv_tsig_q21*teps13 + inv_tsig_q22*teps23 + inv_tsig_q23*teps33
        q[3,1] = inv_tsig_q31*teps11 + inv_tsig_q32*teps21 + inv_tsig_q33*teps31
        q[3,2] = inv_tsig_q31*teps12 + inv_tsig_q32*teps22 + inv_tsig_q33*teps32
        q[3,3] = inv_tsig_q31*teps13 + inv_tsig_q32*teps23 + inv_tsig_q33*teps33

    end

    return q
end

function rotQ(q::AbstractArray{<:Real,2}, theta::Real)
    qbar = copy(q)
    return rotQ!(qbar, theta)
end

function rotQ(q11::Real, q12::Real, q22::Real, q66::Real, theta::Real)
  q0 = zero(eltype(q11))
  q = [q11 q12 q0; q12 q22 q0; q0 q0 q66]
  return rotQ!(q, theta)
end

"""
    `getABD(matid::AbstractArray{<:Integer,1},
        nply::AbstractArray{<:Integer,1}, tply::AbstractArray{<:Real,1},
        theta::AbstractArray{<:Real,1},
        q::AbstractArray{<:AbstractArray{<:Real,2},1})`

    `getABD(lam::Laminate, q::AbstractArray{<:AbstractArray{<:Real,2},1})`

Returns A, B, and D matrices

# Arguments:
* `matid::AbstractArray{<:Integer,1}`: material id of each lamina
* `nply::AbstractArray{<:Integer,1}`: number of plies in each lamina
* `tply::AbstractArray{<:Real,1}`: thickness of a ply (m) in each lamina
* `theta::AbstractArray{<:Real,1}`: orientation (deg) of each lamina
* `q::AbstractArray{<:AbstractArray{<:Real,2}}`: Stiffness matrix of each lamina
"""
function getABD!(A::AbstractArray{<:Real,2}, B::AbstractArray{<:Real,2}, D::AbstractArray{<:Real,2},
    matid::AbstractArray{<:Integer,1}, nply::AbstractArray{<:Integer,1}, tply::AbstractArray{<:Real,1},
    theta::AbstractArray{<:Real,1}, q::AbstractArray{<:AbstractArray{<:Real,2},1})

    typeof(A)

    R = eltype(tply)

    nlam = length(nply)

    z = getz(tply, nply)

    qbar = zeros(R, 3, 3)

    # Loop through layers filling in ABD matrix
    for k = 1:nlam
        # Rotate material stiffness properties by specifed angle
        imat = matid[k]
        qbar .= q[imat]
        rotQ!(qbar, theta[k])

        # Find lumped stiffness properties for the laminate
        for i = 1:3
            for j = 1:3
                A[i,j] = A[i,j]+qbar[i,j]*(z[k+1]-z[k])
                B[i,j] = B[i,j]+qbar[i,j]/2.0*(z[k+1]^2.0-z[k]^2.0)
                D[i,j] = D[i,j]+qbar[i,j]/3.0*(z[k+1]^3.0-z[k]^3.0)
            end
        end
    end

    return A, B, D
end

function getABD(matid::AbstractArray{<:Integer,1}, nply::AbstractArray{<:Integer,1}, tply::AbstractArray{<:Real,1},
    theta::AbstractArray{<:Real,1}, q::AbstractArray{<:AbstractArray{<:Real,2},1})

    R = eltype(tply)

    A = zeros(R, 3, 3)
    B = zeros(R, 3, 3)
    D = zeros(R, 3, 3)

    return getABD!(A, B, D, matid, nply, tply, theta, q)
end

getABD(lam::Laminate, q::AbstractArray{<:AbstractArray{<:Real,2},1}) =
    getABD(lam.matid, lam.nply, lam.tply, lam.theta, q)

"""
    compliance(A::Array{<:Real,2}, B::Array{<:Real,2}, D::Array{<:Real,2})
Returns alpha, beta, and delta
"""
function compliance(A::AbstractArray{<:Real,2}, B::AbstractArray{<:Real,2},
    D::AbstractArray{<:Real,2})
    invA = inv(A)
    delta = inv(D-B*invA*B)
    beta = -invA*B*delta
    alfa = invA-beta*B*invA
    return alfa, beta, delta
end # compliance


# # Rate Constants and T-Dependency
#
# Here, we will just briefly summarize how temperature affects enzymatic rate constants and how much uncertainty there actually is in literature (in terms of how to best define them and how plastic some of the variables are)

## Add usual tools we use:
using BenchmarkTools
using PyPlot

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

## load Photosynthesis module:
using Land.Photosynthesis
#----------------------------------------------------------------------------

## Specify Field Type
const FT = Float32;
#----------------------------------------------------------------------------

# ## T-dependence
# In literature, there are different implementations of the temperature dependence of enzymatic reaction rates. Common among all of them is a typical Arrhenius formulation with Activation energy $E_a$, which leads to a temperature dependence of a quantity $V$ given the standard value defined at 25 °C as $T_{ref}$.
#
# $$V(T)= V(T_{ref})\, \underbrace{\exp\left(\frac{E_a}{RT_{ref}}-\frac{E_a}{RT}\right)}_{\text{Activation}}$$
#
# Other formulations add de-activation of proteins due to denaturalization at higher temperatures:
#
# $$V(T)= V(T_{ref})\, \exp\left(\frac{E_a}{RT_{ref}}-\frac{E_a}{RT}\right) \underbrace{\frac{1+\exp\left[(\Delta ST_{ref}-H_d)/RT_{ref}\right]}{1+\exp\left[(\Delta ST-H_d)/RT\right]}}_{\text{de-activation}}$$
#
# which includes an entropy term $\Delta S$ and the energy for deactivation $H_d$.
#
# To illustrate the differences, we show two different implementations, one only using the activation part based on Bernacchi et al 2001[^1] and one with the typical CLM5 implementation.
#
# The T-Dependency applies to $V_{max}$, $J_{max}$, $K_{c}$, $K_{o}$, $K_{pep}$, Respiration, and $\Gamma^{*}$, and there are multiple parameter sets for each of the parameters. Below, we show the differences of the sets for each of $V_{max}$, $J_{max}$, $K_{c}$, $K_{o}$, $K_{pep}$, Respiration, and $\Gamma^{*}$.
#
# [^1]: Bernacchi, C.J., Singsaas, E.L., Pimentel, C., Portis Jr, A.R. and Long, S.P., 2001. Improved temperature response functions for models of Rubisco‐limited photosynthesis. Plant, Cell & Environment, 24(2), pp.253-259.
#
# ### $V_{cmax}$
#

## Bernacchi set
vc_td_bernacchi = Photosynthesis.VcmaxTDBernacchi(FT)
## CLM set
vc_td_clm       = Photosynthesis.VcmaxTDCLM(FT)
## Leuning set
vc_td_leuning   = Photosynthesis.VcmaxTDLeuning(FT)

## an array of temperature from 10 to 50 degree C
t_array = collect(FT, 10:50) .+ FT(273.15)
vcm_bernacchi = photo_TD_from_val(vc_td_bernacchi, FT(60), t_array)
vcm_clm       = photo_TD_from_val(vc_td_clm      , FT(60), t_array)
vcm_leuning   = photo_TD_from_val(vc_td_leuning  , FT(60), t_array)

## plot the TD curves
figure(figsize=(8,6), dpi=100)
tight_layout(true)
plot(t_array, vcm_bernacchi, "r-", label="Bernacchi")
plot(t_array, vcm_clm      , "g-", label="CLM5"     )
plot(t_array, vcm_leuning  , "b-", label="Leuning"  )
xlabel("Temperature (K)"            , fontsize=16)
ylabel("\$V_{cmax}\$ (μmol m⁻² s⁻¹)", fontsize=16)
legend(loc="upper left")
gcf()
#----------------------------------------------------------------------------

# ### $V_{pmax}$
#

## Boyd set
vp_td_boyd = Photosynthesis.VpmaxTDBoyd(FT)

## an array of temperature from 10 to 50 degree C
vpm_boyd = photo_TD_from_val(vp_td_boyd, FT(60), t_array)

## plot the TD curves
figure(figsize=(8,6), dpi=100)
tight_layout(true)
plot(t_array, vpm_boyd, "r-", label="Boyd")
xlabel("Temperature (K)"            , fontsize=16)
ylabel("\$V_{pmax}\$ (μmol m⁻² s⁻¹)", fontsize=16)
legend(loc="upper left")
gcf()
#----------------------------------------------------------------------------

# ### $J_{max}$
#

## Bernacchi set
j_td_bernacchi = Photosynthesis.JmaxTDBernacchi(FT)
## CLM set
j_td_clm       = Photosynthesis.JmaxTDCLM(FT)
## Leuning set
j_td_leuning   = Photosynthesis.JmaxTDLeuning(FT)

## an array of temperature from 10 to 50 degree C
jm_bernacchi = photo_TD_from_val(j_td_bernacchi, FT(120), t_array)
jm_clm       = photo_TD_from_val(j_td_clm      , FT(120),  t_array)
jm_leuning   = photo_TD_from_val(j_td_leuning  , FT(120),  t_array)

## plot the TD curves
figure(figsize=(8,6), dpi=100)
tight_layout(true)
plot(t_array, jm_bernacchi, "r-", label="Bernacchi")
plot(t_array, jm_clm      , "g-", label="CLM5"     )
plot(t_array, jm_leuning  , "b-", label="Leuning"  )
xlabel("Temperature (K)"           , fontsize=16)
ylabel("\$J_{max}\$ (μmol m⁻² s⁻¹)", fontsize=16)
legend(loc="upper left")
gcf()
#----------------------------------------------------------------------------

# ### $K_{c}$
#

## Bernacchi set
kc_td_bernacchi = Photosynthesis.KcTDBernacchi(FT)
## CLM set
kc_td_clm       = Photosynthesis.KcTDCLM(FT)

## an array of temperature from 10 to 50 degree C
kc_bernacchi = photo_TD_from_set(kc_td_bernacchi, t_array)
kc_clm       = photo_TD_from_set(kc_td_clm      , t_array)

## plot the TD curves
figure(figsize=(8,6), dpi=100)
tight_layout(true)
plot(t_array, kc_bernacchi, "r-", label="Bernacchi")
plot(t_array, kc_clm      , "g-", label="CLM5"     )
xlabel("Temperature (K)", fontsize=16)
ylabel("\$K_{c}\$ (Pa)" , fontsize=16)
legend(loc="upper left")
gcf()
#----------------------------------------------------------------------------

# ### $K_{o}$
#

## Bernacchi set
ko_td_bernacchi = Photosynthesis.KoTDBernacchi(FT)
## CLM set
ko_td_clm       = Photosynthesis.KoTDCLM(FT)

## an array of temperature from 10 to 50 degree C
ko_bernacchi = photo_TD_from_set(ko_td_bernacchi, t_array)
ko_clm       = photo_TD_from_set(ko_td_clm      , t_array)

## plot the TD curves
figure(figsize=(8,6), dpi=100)
tight_layout(true)
plot(t_array, ko_bernacchi, "r-", label="Bernacchi")
plot(t_array, ko_clm      , "g-", label="CLM5"     )
xlabel("Temperature (K)", fontsize=16)
ylabel("\$K_{o}\$ (Pa)" , fontsize=16)
legend(loc="upper left")
gcf()
#----------------------------------------------------------------------------

# ### $K_{pep}$
#

## Boyd set
kpep_td_boyd = Photosynthesis.KpepTDBoyd(FT)
## CLM set
kpep_td_clm  = Photosynthesis.KpepTDCLM(FT)

## an array of temperature from 10 to 50 degree C
kpep_boyd = photo_TD_from_set(kpep_td_boyd, t_array)
kpep_clm  = photo_TD_from_set(kpep_td_clm , t_array)

## plot the TD curves
figure(figsize=(8,6), dpi=100)
tight_layout(true)
plot(t_array, kpep_boyd, "r-", label="Boyd")
plot(t_array, kpep_clm , "g-", label="CLM5")
xlabel("Temperature (K)" , fontsize=16)
ylabel("\$K_{pep}\$ (Pa)", fontsize=16)
legend(loc="upper left")
gcf()
#----------------------------------------------------------------------------

# ### Respiration
#

## Bernacchi set
re_td_bernacchi = Photosynthesis.RespirationTDBernacchi(FT)
## CLM set
re_td_clm       = Photosynthesis.RespirationTDCLM(FT)

## an array of temperature from 10 to 50 degree C
re_bernacchi = photo_TD_from_val(re_td_bernacchi, FT(1.0), t_array)
re_clm       = photo_TD_from_val(re_td_clm      , FT(1.0), t_array)

## plot the TD curves
figure(figsize=(8,6), dpi=100)
tight_layout(true)
plot(t_array, re_bernacchi, "r-", label="Bernacchi")
plot(t_array, re_clm      , "g-", label="CLM5"     )
xlabel("Temperature (K)"                , fontsize=16)
ylabel("Respiration Rate (μmol m⁻² s⁻¹)", fontsize=16)
legend(loc="upper left")
gcf()
#----------------------------------------------------------------------------

# ### $\Gamma^{*}$
#

## Bernacchi set
Γs_td_bernacchi = Photosynthesis.ΓStarTDBernacchi(FT)
## CLM set
Γs_td_clm       = Photosynthesis.ΓStarTDCLM(FT)

## an array of temperature from 10 to 50 degree C
Γs_bernacchi = photo_TD_from_set(Γs_td_bernacchi, t_array)
Γs_clm       = photo_TD_from_set(Γs_td_clm      , t_array)

## plot the TD curves
figure(figsize=(8,6), dpi=100)
tight_layout(true)
plot(t_array, Γs_bernacchi, "r-", label="Bernacchi")
plot(t_array, Γs_clm      , "g-", label="CLM5"     )
xlabel("Temperature (K)"     , fontsize=16)
ylabel("\$\\Gamma^{*}\$ (Pa)", fontsize=16)
legend(loc="upper left")
gcf()
#----------------------------------------------------------------------------

# ## Summary
# The way land surface models implement temperature variations in $V_{c,max}$ is still rather variable and there is no clear consensus in what model is best. In addition, gas exchange datasets performed at different temperatures might actually alias different confounding factors into the $V_{c,max}$ determination, so that the actually fitted $V_{c,max}$ strongly depends on the model formulation. E.g. mesophyll conductance is usually ignored in deriving $V_{c,max}$ but might be temperature dependent, which can cause T-dependent errors in $V_{c,max}$. Also, the T-dependencies may change with time, known as acclimation of optimal temperature.
#
# That said, having the option to use different model forumations (as well as parameter settings) and actually using the ones that are implemented in land surface models directly when fitting leaf level measurements seems to be a prudent way to improve models in the future. More leaf level data might help in the future, especially looking at temperature dependencies, mesophyll conductance, and fluorescence yields. So many chemical and phsyical aspects change with temperature that it is hard to isolate impacting factors. Now, at least, we can run the models directly in the REPL, which is great for prototyping anc validating the model on the individual module level without any effort in terms of additional coding.


"""
Implementation of local response normalization.
"""
module LRNs
using Flux
import Flux: params

export LRN

"""
    LRN{T,I}

Local response normalization layer. Input `i` is processed according to
out(x,y,f,b) = x / [c + α * sum( i(x-k÷2:x+k÷2, y-k÷2:y+k÷2, f-n÷2:f+n÷2, b)^2 )]^β

Todo: β is currently ignored (always set to 0.5)

# Arguments
- `c::T`: additive constant for normalization sum. Default 1.0
- `α::T`: multiplicative scaling constant for normalization sum. Default 1.0
- `β::T`: power scaling constant for normalization sum. Default 0.5
- `size::Tuple{I, I}`: size of normalization field in image space. Default (1, 1)
- `depth::I`: number of neighboring feature maps that go into normalization. Default 5.
"""
struct LRN{T,I,A}
    c::T
    α::T
    β::T
    size::Tuple{I, I}
    depth::I
    kernel::A
end

# convenience constructor that generates the appropriate summation kernel
function LRN(c::T, α::T, β::T, size::Tuple{I, I}, depth::I, features::I) where {T, I}
    # using convolution to do the sum, like https://github.com/FluxML/Flux.jl/pull/720
    # create kernel:
    kernel = zeros(T, size[1], size[2], features, features)
    for i in 1:features
        lower = max(i-depth÷2, 1)
        upper = min(i+depth÷2, features)
        kernel[:, :, lower:upper, i] .= 1
    end
    LRN{T, I, typeof(kernel)}(c, α, β, size, depth, kernel)
end

# convenience constructor with default arguments
function LRN(features; c=1.0, α=1.0, β=0.5, size=(1, 1), depth=5)
    return LRN(c, α, β, size, depth, features)
end # function LRN

"""
    (l::LRN)(i)

Applies a local response normalization layer according to:
out(x,y,f,b) = x ./ [c + α * sum( i(x, y, f-n÷2:f+n÷2, b)^2 )]^(β)

Todo: β is currently ignored (always set to 0.5)
"""
function (l::LRN)(x)
    norm = l.c .+ l.α .* conv(x .^ 2, l.kernel)
    norm = norm .^ l.β
    return x ./ norm
end # function (l::LRN)

Flux.params(l::LRN) = nothing
Flux.@functor LRN

end # module LRNs


using BeefBLUP
using DataFrames
using Test

@testset "BeefBLUP.jl" begin
    # Write your tests here.
    correctX = [1 1 0 1; 1 1 0 0; 1 0 1 1; 1 0 1 0; 1 0 1 1; 1 0 1 0; 1 0 0 1]
    fixedfx = DataFrame(year = [1990, 1990, 1991, 1991, 1991, 1991, 1992], sex = ["male", "female", "male", "female", "male", "female", "male"])
    @test BeefBLUP.fixedeffectmatrix(fixedfx)[1] == correctX
    correctA = [1   0   1/2 1/2 1/2 0   0;
                0   1   0   0   1/2 1/2 0;
                1/2 0   1   1/4 1/4 0   0;
                1/2 0   1/4 1   1/4 0   0;
                1/2 1/2 1/4 1/4 1   1/4 0;
                0 1/2   0   0   1/4 1   0;
                0 0     0   0   0   0   1]
    id = collect(1:7)
    dam_id = [missing, missing, missing, missing, 2, 2, missing]
    sire_id = [missing, missing, 1, 1, 1, missing, missing]
    @test BeefBLUP.additiverelationshipmatrix(id, dam_id, sire_id) == correctA
end


using SamplingHS3

#f_const = (x::Vector{Float64}) -> 1.0

# "inverted" egg crate function for defining likelihood:
#f_ec = (x::Vector{Float64}) -> ( cos(x[1])^2 + cos(x[2])^2 ) / ( x[1]*x[1] + x[2]*x[2] )^(1/3)
f_ec = (x::Vector{Float64}) -> ( sin(x[1])^2 + sin(x[2])^2 ) / ( 0.1 + x[1]*x[1] + x[2]*x[2] )^(1/2.5)
f_double_ec = (x::Vector{Float64}) -> max( f_ec( x+[pi/4;pi/4] ) , f_ec( x-[pi/4;pi/4] ) )

#ndgrid(xlim,ylim,xres,yres) = ( [ xlim[1]+ (0.5+i)*((xlim[2]-xlim[1])/(xres+1)) for i=1:xres, j=1:yres ] , [ ylim[1]+ (0.5+j)*((ylim[2]-ylim[1])/(yres+1)) for i=1:xres, j=1:yres ] )
xres = 100; yres = 100;
xygrids = SamplingHS3.xygrid([-10,10,],[-10,10],xres,yres)
zgrid   = NaN*ones(xres,yres)
for zi=1:length(zgrid); zgrid[zi] = f_ec([xygrids[1][zi];xygrids[2][zi]] ); end

using Plots
plotly()
Plots.heatmap(zgrid)

#scatter( xygrids[1][:] , xygrids[2][:] , ma = 0.75 , zcolor=zgrid[:] , m=2, msw=0 , c=:lightrainbow) )

xlim = [-20.,20.]; ylim = [-20.,20.]
bounding_box_G =[eye(2);-eye(2)]
bounding_box_h =[-xlim[1];xlim[2];-ylim[1];ylim[2]]

# and create likelihood from "inverted egg crate"
f_likelihood = (x::Vector{Float64}) -> f_ec(x)
f_likelihood_2p = (x,y) -> f_ec([x;y])
#f_likelihood = (x::Vector{Float64}) -> f_double_ec(x)
#f_likelihood_2p = (x,y) -> f_double_ec([x;y])

# starting point
x0 = [5.;5.]

# pure mcmc sampling using mh:
conf_only_mh = SamplingHS3.DecorrelationConfig( 100000 , 1.0*eye(2) , 0.0 , 1.0 )
f_density    = f_likelihood
# this is quite slow.. most time is spent for the generation of the multivar. normal random numbers..
tic()
mcmc_result = SamplingHS3.run_mcmc( x0 , f_density(x0) , f_density , (x) -> 0 , 1.0 , bounding_box_G  , bounding_box_h , conf_only_mh )
toc()

# pure mcmc sampling using hr:
conf_only_hr = SamplingHS3.DecorrelationConfig( 10000 , 1.0*eye(2) , 1.0 , 0.0 )
f_density    = f_likelihood
# this is quite slow.. most time is spent for the generation of the multivar. normal random numbers..
tic()
mcmc_result_hr = SamplingHS3.run_mcmc( x0 , f_density(x0) , f_density , (x) -> 0 , 1.0 , bounding_box_G  , bounding_box_h , conf_only_hr )
toc()

using Plots
#plotly()
if(false)
    gr()
    Plots.plot(linspace(xlim[1],xlim[2],40),linspace(ylim[1],ylim[2],40),f_likelihood_2p, st = [:contourf])
    Plots.scatter!( mcmc_result.dd.xx[1,:] , mcmc_result.dd.xx[2,:] , ma = 0.75 , m=2, msw=0 )
    Plots.plot!( mcmc_result.dd.xx[1,:] , mcmc_result.dd.xx[2,:] , la = 0.1)
end
using Plots, GR
Plots.gr()
Plots.plot(layout=(1,2))
z_sampled = SamplingHS3.bin2d( mcmc_result_hr.dd.xx , 40 , 40 )
Plots.plot!( linspace(xlim[1],xlim[2],40), linspace(ylim[1],ylim[2],40) , f_likelihood_2p , subplot=1 , st = [:contourf])
Plots.plot!( z_sampled[2], [z_sampled[3]] , z_sampled[1][:]  , subplot=2 , st = [:contourf] , xlim=[-20;20] , ylim=[-20;20])
#Plots.plot!( linspace(xlim[1],xlim[2],40), linspace(ylim[1],ylim[2],40) , z_sampled[:]    , subplot=2 , st = [:contourf])
abc = Plots.savefig("C:\\Temp\\two_ec_densities.png")

# Lets define a restriction to a torus around the center
f_torus = (x) -> (sqrt( sum(x.^2) ) - 5)^2
f_torus_threshold = 1.0
x0 = [5.0;0.0]
mcmc_result_torus = SamplingHS3.run_mcmc( x0 , f_density(x0) , f_density , f_torus , f_torus_threshold , bounding_box_G  , bounding_box_h , conf_only_mh )

# Plot result binned:
using Plots, GR
gr()
Plots.plot(layout=(1,2))
z_sampled_tor = SamplingHS3.bin2d( mcmc_result_torus.dd.xx , 40 , 40 )
Plots.plot!( linspace(xlim[1],xlim[2],40), linspace(ylim[1],ylim[2],40) , f_likelihood_2p , subplot=1 , st = [:contourf])
Plots.plot!( linspace(xlim[1],xlim[2],40), linspace(ylim[1],ylim[2],40) , (x,y) -> 0. , subplot=2 , st = [:contourf])
Plots.plot!( z_sampled_tor[2], z_sampled_tor[3] , z_sampled_tor[1][:]    , subplot=2 , st = [:contourf] , xlim=[-20;20] , ylim=[-20;20])
abc = Plots.savefig("C:\\Temp\\two_ec_densities_torus.png")


# This example is used to generate plots in the paper, "Reflection from a multi-species material and its transmitted effective wavenumber." Proc. R. Soc. (2018): 20170864.

# Everything related to ploting has been commented so that this example can run with requiring the Plots package. That way, this example can be checked everytime the package is modified.

using EffectiveWaves

# using LaTeXStrings
# using Plots
# height=500
 # pyplot(linewidth=3, size=(2.6*height,height))
 # Plots.scalefontsizes(1.8)

filename="fluid"
mediumname = "water"
## choose material

  # fluid
  medium     = WaterDistilled
  inclusion1 = Glycerol # surfactant ?
  inclusion2 = Hexadecane
  # inclusion2 = SodiumNitrate
  # ωs = 2.0*pi*LinRange(1.0e1,1.0e7,100)

  ωfactor = 4000;
  ω = ωfactor*real(medium.c)/2.0

  vols = 0.01:0.003:0.4
  vol_prop = 0.5
  r1 = 0.1/ωfactor; r2 = 1.0/ωfactor;

  # True wavenumber
  kTs = map(vols) do v
    sp1 = Specie(ρ=inclusion1.ρ ,r=r1, c=inclusion1.c, volfrac = v*vol_prop)
    sp2 = Specie(ρ=inclusion2.ρ ,r=r2, c=inclusion2.c, volfrac = v*(1.0 - vol_prop))
    wavenumber_low_volfrac(ω, medium, [sp1,sp2])
  end
  # low volfrac wavenumber
  kT2s = map(vols) do v
    sp1 = Specie(ρ=inclusion1.ρ ,r=r1, c=inclusion1.c, volfrac = v*vol_prop)
    sp2 = Specie(ρ=inclusion2.ρ ,r=r2, c=inclusion2.c, volfrac = v*(1.0 - vol_prop))
    wavenumber_very_low_volfrac(ω, medium, [sp1,sp2])
  end

  # Approximate wavenumber
  kTLSs = map(vols) do v
    sp1 = Specie(ρ=inclusion1.ρ ,r=r1, c=inclusion1.c, volfrac = v*vol_prop)
    sp2 = Specie(ρ=inclusion2.ρ ,r=r2, c=inclusion2.c, volfrac = v*(1.0 - vol_prop))
    two_species_approx_wavenumber(ω, medium, [sp1,sp2])
  end
  # Approximate challis
  kTCs = map(vols) do v
    sp1 = Specie(ρ=inclusion1.ρ ,r=r1, c=inclusion1.c, volfrac = v*vol_prop)
    sp2 = Specie(ρ=inclusion2.ρ ,r=r2, c=inclusion2.c, volfrac = v*(1.0 - vol_prop))
    wavenumber_challis(ω, medium, [sp1,sp2])
  end

  kTs_arr = [kTs,kT2s, kTLSs,kTCs];
  speed_arr = [ ω./real(ks) for ks in kTs_arr];
  push!(speed_arr, 0 .*real(kTs) .+ real(medium.c))
  atten_arr = imag.(kTs_arr)

  styles = [:solid :dash :dashdot :dashdot :dot]
  ys_arr = speed_arr;
  xs = vols;
  # labs = [L"k_{*}" L"k_{*0}" L"k_{*LS}" L"k_{*C}" mediumname]
  # xlabel = "Total volume fraction"
  # m =5;
  # p1 = plot(xs, ys_arr, xlabel=xlabel, ylabel="sound speed (m/s)", labels=labs
                #  , line = styles, ylims = (minimum(minimum.(ys_arr)), maximum(ys_arr[1])*1.001));

  ys_arr = atten_arr;
  styles = [:solid :dash :dashdot :dashdot]
  # labs = [L"k_{*}" L"k_{*0}" L"k_{*LS}" L"k_{*C}"]
  # p2 = plot(xs, ys_arr, labels=labs, xlabel=xlabel, ylabel="attenuation (1/m)"
                    #   , line = styles )
                #  , ylims = (minimum(ys_arr[1]), maximum(ys_arr[1])*1.04));
  # plot(p1,p2)
  # gui()
  # try mkdir("media") end
  # savefig("media/compare_$(filename)_volfrac.png")
  # savefig("media/compare_$(filename)_volfrac.pdf")

# Plots.scalefontsizes(1/1.8)


function approximation_term(
    ::MinimumBudget{SpanningTreeInstance{T, Maximise}, U},
    ::LagrangianAlgorithm,
) where {T, U}
    return NaN
end
function approximation_ratio(
    ::MinimumBudget{SpanningTreeInstance{T, Maximise}, U},
    ::LagrangianAlgorithm,
) where {T, U}
    return NaN
end

function _st_prim_budgeted_lagrangian(
    i::MinimumBudget{SpanningTreeInstance{T, Maximise}, U},
    λ::Float64,
) where {T, U}
    # Solve the subproblem for one value of the dual multiplier λ:
    #     l(λ) = \max_{x spanning tree} (rewards + λ weights) x - λ budget.
    sti_rewards = Dict{Edge{T}, Float64}(
        e => i.instance.rewards[e] + λ * i.weights[e] for
        e in keys(i.instance.rewards)
    )
    sti = SpanningTreeInstance(i.instance.graph, sti_rewards)
    sti_sol = solve(sti, PrimAlgorithm())
    return _budgeted_spanning_tree_compute_value(sti, sti_sol.variables) -
           λ * i.min_budget,
    sti_sol.variables
end

function solve(
    i::MinimumBudget{SpanningTreeInstance{T, Maximise}, U},
    ::LagrangianAlgorithm;
    ε::Float64,
) where {T, U}
    # Approximately solve the problem \min_{l ≥ 0} l(λ), where
    #     l(λ) = \max_{x spanning tree} (rewards + λ weights) x - λ budget.
    # This problem is the Lagrangian dual of the budgeted maximum spanning-tree problem:
    #     \max_{x spanning tree} rewards x  s.t.  weights x >= budget.
    # This algorithm provides no guarantee on the optimality of the solution.

    # Initial set of values for λ. The optimum is guaranteed to be contained in this interval.
    weights_norm_inf = maximum(values(i.weights)) # Maximum weight.
    m = nv(i.instance.graph) - 1 # Maximum number of items in a solution. Easy to compute for a spanning tree!
    λmax = Float64(weights_norm_inf * m + 1)

    λlow = 0.0
    λhigh = λmax

    # Perform a golden-ratio search.
    while (λhigh - λlow) > ε
        λmidlow = λhigh - (λhigh - λlow) / MathConstants.φ
        λmidhigh = λlow + (λhigh - λlow) / MathConstants.φ
        vmidlow, _ = _st_prim_budgeted_lagrangian(i, λmidlow)
        vmidhigh, _ = _st_prim_budgeted_lagrangian(i, λmidhigh)

        if vmidlow < vmidhigh
            λhigh = λmidhigh
            vhigh = vmidhigh
        else
            λlow = λmidlow
            vlow = vmidlow
        end
    end

    vlow, stlow = _st_prim_budgeted_lagrangian(i, λlow)
    vhigh, sthigh = _st_prim_budgeted_lagrangian(i, λhigh)
    if vlow < vhigh
        return BudgetedSpanningTreeLagrangianSolution(
            i,
            stlow,
            λlow,
            vlow,
            λmax,
        )
    else
        return BudgetedSpanningTreeLagrangianSolution(
            i,
            sthigh,
            λhigh,
            vhigh,
            λmax,
        )
    end
end


# uses the Fast Iterative Soft-Thresholding Algorithm (FISTA) to
# minimize f(x) + g(x) = ½(|Ax - y|² + β|x|²) + ½α|Ψx|₁

fista(A, y, α, β, iters, tol, reg::Regularizer) = fista(A, y, α, β, iters, tol, reg::Regularizer, zeros(size(A)[2]))

# TODO: structure similarly to IterativeSolvers
function fista(A, y, α, β, iters, tol, reg::Regularizer, xstart)
    n, p = size(A)

    maxeig = abs(powm(A' * A, maxiter=100)[1]) # TODO: handle number of eigiters
    L = maxeig + β # Lipschitz constant of f (strongly convex term)
    η = 1 / (L * lipschitz_scale(reg))

    x = xstart[:]
    z = x[:]
    xold = similar(x)
    res = similar(y)
    grad = similar(x)
    t = 1

    iters_done = iters
    xupdates = Float64[]
    convdists = Float64[]
    evals = Float64[]

    Ψ = transform_op(reg)
    Fold = Inf

    for i = 1:iters

        xold .= x

        res .= mul!(res, A, z) .- y # TODO: maybe use five-arg mul! instead. (but IterativeSolvers sticks to three-arg)
        grad .= mul!(grad, A', res) .+ β .* z

        x .= z .- η .* grad
        proximal!(x, 1/2 * η * α, reg)

        restart = dot(z .- x, x .- xold)
        restart > 0 && (t = 1)

        told = t
        t = 1/2 * (1 + √(1 + 4t^2))

        z .= x .+ (told - 1)/t .* (x .- xold)

        xupdate = norm(z .- xold) / norm(xold)
        append!(xupdates, xupdate)

        if xupdate < tol
            iters_done = i
            break
        end
    end

    x, (;iters=iters_done, final_tol=norm(x .- xold) / norm(x), xupdates)
end


# # Airplane
#
#md # [![](https://img.shields.io/badge/show-nbviewer-579ACA.svg)](@__NBVIEWER_ROOT_URL__/models/Airplane.ipynb)
#
# This example consists of a dynamical system that is a simple model of a flying
# airplane. There are 12 state variables:
#
# ```math
# \begin{aligned}
# [x, y, z, u, v, w, ϕ, θ, ψ, r, p, q]
# \end{aligned}
# ```
#
# where $(x, y, z)$ is the position of the center of gravity (C.G.),
# $(u, v, w)$ are the components of velocity in $(x, y, z)$ directions,
# $(p, q, r)$ are body rotation rates, and $(ϕ, θ, ψ)$ are the Euler angles.
#
# See also the file `airplane.png` for a visualization of the meaning of the
# coordinates with respect to the airplane.

module Airplane  #jl

using NeuralNetworkAnalysis
using NeuralNetworkAnalysis: SingleEntryVector

# The following option determines whether the falsification settings should be
# used or not. The falsification settings are sufficient to show that the safety
# property is violated. Concretely we start from an initial point and use a
# smaller time step.
const falsification = true;

# ## Model
#
# The equations of motion are reduced to:
#
# ```math
# \left\{ \begin{array}{lcl}
# u̇ &=& − g \sin(\theta) + \frac{F_x}{m} - qw + rv \\
# v̇ &=& g \cos(\theta) \sin(\phi) + \frac{F_y}{m} - ru + pw \\
# ẇ &=& g \cos(\theta) \cos(\phi) + \frac{F_z}{m} - pv + qu \\
# I_x \dot p + I_{xz} \dot r &=& M_x - (I_z - I_y) qr - I_{xz} pq \\
# I_y \dot q &=& M_y - I_{xz}(r^2 - p^2) - (I_x - I_z) pr \\
# I_{xz} \dot p + I_z \dot r &=& M_z - (I_y - I_x) qp - I_{xz} rq
# \end{array} \right.
# ```
#
# The mass of the airplane is denoted with m and $I_x$ , $I_y$ , $I_z$
# and $I_{xz}$ are the moment of inertia with respect to the indicated axis.
# The control parameters include three force components $F_x$ , $F_y$ and
# $F_z$ and three moment components $M_x$ , $M_y$ , $M_z$ . Notice that
# for simplicity we have assumed the aerodynamic forces are absorbed in the
# $F$'s. In addition to these six equations, we have six additional kinematic
# equations:
#
# ```math
# \begin{bmatrix}
# \dot x \\ \dot y \\ \dot z
# \end{bmatrix}
# =
# \begin{bmatrix}
# \cos(\psi) & -\sin(\psi) & 0 \\
# \sin(\psi) & \cos(\psi) & 0 \\
# 0 & 0 & 1
# \end{bmatrix}
# \begin{bmatrix}
# \cos(\theta) & 0 & \sin(\theta) \\
# 0 & 1 & 0 \\
# -\sin(\theta) & 0 & \cos(\theta)
# \end{bmatrix}
# \begin{bmatrix}
# 1 & 0 & 0 \\
# 0 & \cos(\phi) & -\sin(\phi) \\
# 0 & \sin(\phi) & \cos(\phi)
# \end{bmatrix}
# ```
#
# and
#
# ```math
# \begin{bmatrix}
# \phi \\ \theta \\ \psi
# \end{bmatrix}
# =
# \begin{bmatrix}
# 1 & \tan(\theta) \sin(\phi) & \tan(\theta) \cos(\phi) \\
# 0 & \cos(\phi) & -\sin(\phi) \\
# 0 & \sec(\theta) sin(\phi) & \sec(\theta) \cos(\phi)
# \end{bmatrix}
# \begin{bmatrix}
# p \\ q \\ r
# \end{bmatrix}
# ```
#
# For the simplicity of control design, the parameters have been chosen to
# have some nominal dimensionless values:
# $m = 1$, $I_x = I_y = I_z = 1$, $I_{xz} = 0$ and $g = 1$.

Tψ = ψ -> [cos(ψ)  -sin(ψ)  0;
           sin(ψ)   cos(ψ)  0;
                0        0  1]

Tθ = θ -> [ cos(θ)  0  sin(θ);
                 0  1       0;
           -sin(θ)  0  cos(θ)]

Tϕ = ϕ -> [1       0        0;
           0  cos(ϕ)  -sin(ϕ);
           0  sin(ϕ)   cos(ϕ)]

Rϕθ = (ϕ, θ) -> [1  tan(θ) * sin(ϕ)  tan(θ) * cos(ϕ);
                 0           cos(θ)          -sin(ϕ);
                 0  sec(θ) * sin(ϕ)  sec(θ) * cos(ϕ)]

## alternative matrix with only sin/cos but with postprocessing
## Rϕθ_ = (ϕ, θ) -> [cos(θ)  sin(θ) * sin(ϕ)   sin(θ) * cos(ϕ);
##                        0  cos(θ) * cos(ϕ)  -cos(θ) * sin(ϕ);
##                        0           sin(ϕ)            cos(ϕ)]

## model constants
const m = 1.0
const g = 1.0

## unused constants (terms are simplified instead)
const Ix = 1.0
const Iy = 1.0
const Iz = 1.0
const Ixz = 0.0

@taylorize function airplane!(dx, x, p, t)
    _x, y, z, u, v, w, ϕ, θ, ψ, r, _p, q, Fx, Fy, Fz, Mx, My, Mz = x

    T_ψ = Tψ(ψ)
    T_θ = Tθ(θ)
    T_ϕ = Tϕ(ϕ)
    mat_1 = T_ψ * T_θ * T_ϕ
    xyz = mat_1 * vcat(u, v, w)

    mat_2 = Rϕθ(ϕ, θ)
    ## mat_2 = 1 / cos(θ) * Rϕθ_(ϕ, θ)  # alternative matrix with postprocessing
    ϕθψ = mat_2 * vcat(_p, q, r)

    dx[1] = xyz[1]
    dx[2] = xyz[2]
    dx[3] = xyz[3]
    dx[4] = -g * sin(θ) + Fx / m - q * w + r * v
    dx[5] = g * cos(θ) * sin(ϕ) + Fy / m - r * u + _p * w
    dx[6] = g * cos(θ) * cos(ϕ) + Fz / m - _p * v + q * u
    dx[7] = ϕθψ[1]
    dx[8] = ϕθψ[2]
    dx[9] = ϕθψ[3]
    dx[10] = Mx  # simplified term
    dx[11] = My  # simplified term
    dx[12] = Mz  # simplified term
    dx[13] = zero(Fx)
    dx[14] = zero(Fy)
    dx[15] = zero(Fz)
    dx[16] = zero(Mx)
    dx[17] = zero(My)
    dx[18] = zero(Mz)
end;

# ## Specifications

controller = read_nnet(@modelpath("Airplane", "controller_airplane.nnet"))

X₀ = Hyperrectangle(low=[0.0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
                    high=[0.0, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0])
if falsification
    ## choose a single point in the initial states (here: the top-most one)
    X₀ = Singleton(high(X₀))
end
U₀ = ZeroSet(6)

vars_idx = Dict(:state_vars=>1:12, :control_vars=>13:18)
ivp = @ivp(x' = airplane!(x), dim: 18, x(0) ∈ X₀ × U₀)

period = 0.1  # control period

prob = ControlledPlant(ivp, controller, vars_idx, period);

# Safety specification: $x_2$ should be in $[−0.5, 0.5]$ and $x_7, x_8, x_9$
# should be in $[-1, 1]$.
if falsification
    k = 4  # falsification can run for a shorter time horizon
else
    k = 20
end
T = k * period  # time horizon

safe_states = HPolyhedron([HalfSpace(SingleEntryVector(2, 18, 1.0), 0.5),
                           HalfSpace(SingleEntryVector(2, 18, -1.0), 0.5),
                           HalfSpace(SingleEntryVector(7, 18, 1.0), 1.0),
                           HalfSpace(SingleEntryVector(7, 18, -1.0), 1.0),
                           HalfSpace(SingleEntryVector(8, 18, 1.0), 1.0),
                           HalfSpace(SingleEntryVector(8, 18, -1.0), 1.0),
                           HalfSpace(SingleEntryVector(9, 18, 1.0), 1.0),
                           HalfSpace(SingleEntryVector(9, 18, -1.0), 1.0)])

## property for guaranteed violation
predicate = X -> isdisjoint(overapproximate(X, Hyperrectangle), safe_states)
predicate_sol = sol -> any(predicate(R) for F in sol for R in F);

# ## Results

import DifferentialEquations

alg = TMJets(abstol=1e-10, orderT=7, orderQ=1)
alg_nn = Ai2()

function benchmark(; silent::Bool=false)
    ## We solve the controlled system:
    silent || println("flowpipe construction")
    res_sol = @timed solve(prob, T=T, alg_nn=alg_nn, alg=alg)
    sol = res_sol.value
    silent || print_timed(res_sol)

    ## Next we check the property for an overapproximated flowpipe:
    silent || println("property checking")
    solz = overapproximate(sol, Zonotope)
    res_pred = @timed predicate_sol(solz)
    silent || print_timed(res_pred)
    if res_pred.value
        silent || println("The property is violated.")
    else
        silent || println("The property may be satisfied.")
    end

    ## We also compute some simulations:
    silent || println("simulation")
    trajectories = falsification ? 1 : 50
    res_sim = @timed simulate(prob, T=T, trajectories=trajectories,
                              include_vertices=!falsification)
    sim = res_sim.value
    silent || print_timed(res_sim)

    return solz, sim
end

benchmark(silent=true)  # warm-up
res = @timed benchmark()  # benchmark
sol, sim = res.value
println("total analysis time")
print_timed(res);
print(Main.io, "JuliaReach, Airplane, -, falsified, $(res.time * 1e9)\n")

# Finally we plot the results:

using Plots

## set more precise tolerance for plotting small sets correctly
LazySets.set_ztol(Float64, 1e-9)

function plot_helper(fig, vars)
    if vars[1] == 0
        safe_states_projected = project(safe_states, [vars[2]])
        time = Interval(0, T)
        safe_states_projected = cartesian_product(time, safe_states_projected)
    else
        safe_states_projected = project(safe_states, vars)
    end
    plot!(fig, safe_states_projected, color=:lightgreen, lab="safe states")
    if !falsification && 0 ∉ vars
        plot!(fig, project(initial_state(prob), vars), lab="X₀")
    end
    plot!(fig, sol, vars=vars, color=:yellow, lab="")
    lab_sim = falsification ? "simulation" : ""
    plot_simulation!(fig, sim; vars=vars, color=:black, lab=lab_sim)
end

vars = (2, 7)
fig = plot(xlab="y", ylab="ϕ", leg=:bottomleft)
plot_helper(fig, vars)
if falsification
    xlims!(-0.01, 0.65)
    ylims!(0.9, 1.01)
else
    xlims!(-1.8, 22.5)
    ylims!(-1.05, 1.05)
end
savefig("Airplane-x2-x7.png")

#-

vars = (8, 9)
fig = plot(xlab="θ", ylab="ψ", leg=:bottom)
plot_helper(fig, vars)
if falsification
    xlims!(0.999, 1.03)
    ylims!(0.99, 1.001)
else
    xlims!(-1.05, 1.2)
    ylims!(-1.05, 1.2)
end
savefig("Airplane-x8-x9.png")

#-

end  #jl
nothing  #jl


abstract AbstractGrid{T,N} <: AbstractArray{T,N}

abstract AbstractGrid1d{T<:Number,N} <: AbstractGrid{T,N}
abstract AbstractGrid2d{T<:Number,N} <: AbstractGrid{Point2{T},N}
abstract AbstractGrid3d{T<:Number,N} <: AbstractGrid{Point3{T},N}

Base.length(g::AbstractGrid) = length(g.c)
Base.eltype{T}(g::AbstractGrid{T}) = T
Base.ndims(g::AbstractGrid) = ndims(g.c)
Base.size(g::AbstractGrid) = size(g.c)
Base.size(g::AbstractGrid, i::Integer) = size(g.c, i)
Base.getindex(g::AbstractGrid, i...) = g.c[i...]
function Base.setindex!(g::AbstractGrid, value, i...)
    g.c[i...] = value
end
Base.transpose(g::AbstractGrid) = transpose(g.c)
Base.ctranspose(g::AbstractGrid) = ctranspose(g.c)
Base.eachindex(g::AbstractGrid) = eachindex(g.c)
Base.linearindexing{T<:AbstractGrid}(::Type{T}) = Base.LinearFast()
# Base.convert(T::Type{Array},g::AbstractGrid) = convert(T,g.c)

integrate(g::AbstractGrid, f) = integrate(g, f(g))
integrate(g::AbstractGrid, f::AbstractVector) = vecdot(g.w, f)

function integrate(g::AbstractGrid, f::AbstractArray)
    n,m = length(f),length(g)
    shp = (m,Int(n/m))
    At_mul_B(reshape(f, shp), vec(g.w))
end

# integrate(g::AbstractGrid, f::AbstractArray) = sum(g.w .* f, 1:ndims(g.w)) |> squeezeall

# integrate{T}(g::AbstractGrid{T,1}, f::AbstractMatrix) = At_mul_B(f, g.w)

∫ = integrate

function Base.writemime{T}(io::IO, mime::MIME"text/plain", g::AbstractGrid{T,1})
    write(io, summary(g), "\n")
    write(io, "[coords weights] =\n")
    writemime(io, mime, [g.c g.w])
end

function Base.writemime{T}(io::IO, mime::MIME"text/plain", g::AbstractGrid{T,2})
    write(io, summary(g), "\n")
    write(io, "coords =\n")
    writemime(io, mime, g.c)
end

function setposition(g::AbstractGrid3d, R::Point3{Float64})
    for i in 1:length(g)
        g[i] += R - g.R
    end
end

type LogarithmicGrid{T,A<:AbstractVector} <: AbstractGrid1d{T,1}
    c::A  # coords
    w::A  # weights
    dx::T  # spacing in log space
    function LogarithmicGrid(c::AbstractVector{T},w::AbstractVector{T},dx::T)
        @assert size(c) == size(w)
        new(c,w,dx)
    end
end

LogarithmicGrid{T}(c::AbstractVector{T},w::AbstractVector{T},dx::T) =
    LogarithmicGrid{eltype(c),typeof(c)}(c,w,dx)

function loggrid(start::Real, stop::Real, n::Int)
    x = collect(linspace(start, stop, n))
    dx = x[2] - x[1]
    r = exp(x)
    w = r.*dx
    LogarithmicGrid(r, w, dx)
end

# returns a logarithmic grid over same range but with different number of points
loggrid(r::LogarithmicGrid, n::Int) = loggrid(log(r[1]),log(r[end]),n)

type UniformGrid{T,A<:AbstractVector} <: AbstractGrid1d{T,1}
    c::A  # coordinates
    w::A  # weights
    dx::T  # spacing
    function UniformGrid(c::AbstractVector{T},w::AbstractVector{T},dx::T)
        @assert size(c) == size(w)
        new(c,w,dx)
    end
end

UniformGrid{T}(c::AbstractVector{T},w::AbstractVector{T},dx::T) =
    UniformGrid{eltype(c),typeof(c)}(c,w,dx)

function lingrid(start::Real, stop::Real, n::Int)
    x = collect(linspace(start, stop, n))
    dx = x[2]-x[1]
    UniformGrid(x, fill(dx, length(x)), dx)
end

function simpsgrid(start::Real, stop::Real, n::Int)
    @assert isodd(n) "Simpson's rule requires an odd number of grid points"
    x = collect(linspace(start, stop, n))
    dx = x[2] - x[1]
    w = fill(dx/3, n)
    w[2:2:end] .*= 4
    w[3:2:end-1] .*= 2
    UniformGrid(x, w, dx)
end

type AtomicRadialGrid{T,A<:AbstractVector} <: AbstractGrid1d{T,1}
    c::A  # coords
    w::A  # weights
    rmax::T
    function AtomicRadialGrid(c::AbstractVector{T},w::AbstractVector{T},rmax::T)
        @assert size(c) == size(w)
        new(c,w,rmax)
    end
end

AtomicRadialGrid{T}(c::AbstractVector{T},w::AbstractVector{T},rmax::T) =
    AtomicRadialGrid{eltype(c),typeof(c)}(c,w,rmax)

function atomicradgrid(n::Int, rmax::Real)
    s = collect(1:n)
    r = rmax * log(1 - (s/(n+1.)).^2) ./ log(1 - (n/(n+1))^2)
    w = -2rmax*s ./ ( (1+n-s) .* (1+n+s) .* log(1 - (n/(n+1.))^2) )
    AtomicRadialGrid(r, w, rmax)
end

loggrid(r::AtomicRadialGrid, n::Int) = loggrid(log(r[1]),log(r[end]),n)

type AngularGrid{T,A<:AbstractVector,B<:AbstractVector} <: AbstractGrid3d{T,1}
    c::A  # coords
    w::B  # weights
    function AngularGrid(c::AbstractVector{Point3{T}},w::AbstractVector{T})
        @assert size(c) == size(w)
        new(c,w)
    end
end

AngularGrid{T}(c::AbstractVector{Point3{T}},w::AbstractVector{T}) =
    AngularGrid{eltype(w),typeof(c),typeof(w)}(c,w)

typealias AngularGridf AngularGrid{Float64,Vector{Tuple{Float64,Float64,Float64}},Vector{Float64}}

lebgrid(n::Int) = lebedev[n]::AngularGridf

# function expand(grid, basis, f)
#     S = basis.data' * (basis .* x.w)
#     (integrate(x, basis.*h)' / S)'
# end

# function expand(grid, basis, f)
#     S = Symmetric(LinAlg.BLAS.syrk('U', 'T', basis.data .* sqrt(grid.w)))
#     (integrate(grid, basis.*f)' / S)'
# end

# LinAlg.BLAS.herk('U', 'C', 1.0, mat(basis.data .* sqrt(g.w)))

function qrgramschmidt(grid, basis)
    Q,R = qr(basis .* sqrt(grid.w));
    Q ./= sqrt(grid.w)
    Q
end


###########################################################
### Proximal Gradient
###########################################################
@with_kw struct WholespaceProximalGradient <: WholespaceUpdate
    backtracking::Bool = true
    γ_init = 1e5
end
@with_kw mutable struct WholespaceProximalGradientState <: AbstractUpdateState
    γ::Float64
end

Base.summary(::WholespaceProximalGradient) = "ProxGrad"

initial_state(o::WholespaceProximalGradient, x, reg; manifold=nothing) = WholespaceProximalGradientState(γ = o.γ_init)

str_updatelog(o::WholespaceProximalGradient, t::WholespaceProximalGradientState) = @sprintf "γ: %.3e" t.γ

function update_iterate!(state::PartlySmoothOptimizerState, pb, m::WholespaceProximalGradient)
    γ = state.update_to_updatestate[m].γ

    @assert state.x.repr == ambiant_repr

    if m.backtracking
        ncalls_f, γ = backtrack_f_lipschitzgradient!(state, pb, γ)
    end

    state.temppoint_amb .= state.x.amb_repr .- γ .* state.∇f_x

    state.x.man_repr, M = prox_αg(pb, state.temppoint_amb, γ); state.ncalls_proxg += 1
    state.x.repr = manifold_repr
    state.x.M = M

    state.M = M
    state.update_to_updatestate[m].γ = γ

    return
end

function display_logs()
    # γ          %.3e  state.γ
end

function backtrack_f_lipschitzgradient!(state::PartlySmoothOptimizerState, pb, γ)
    τ = 1.2

    ∇f_norm2 = norm(state.∇f_x)^2

    itmax = 200
    ncalls_f = 0

    it_ls = 0
    while it_ls <= itmax
        state.temppoint_amb .= state.x.amb_repr .- γ .* state.∇f_x

        # f(pb, state.temp) ≤ state.f_x - 1/(2*γ) * norm(state.temp-state.x)^2 && break
        state.ncalls_f += 1
        f(pb, state.temppoint_amb) ≤ state.f_x - γ / 2 * ∇f_norm2 && break

        ncalls_f += 1
        γ = γ / τ
        it_ls += 1
    end

    if it_ls > itmax
        @warn "Gradient backtracking: reached iterations limits."
    end

    return ncalls_f, γ
end


module BlackScholesGreeks

__precompile__(true)

include("allmodules.jl")

end


@polly function kernel_mvt(x1, x2, y1, y2, A)
    n = size(x1,1)
    for i = 1:n, j = 1:n
        x1[i] = x1[i] + A[i,j] * y1[j]
    end
    for i = 1:n, j = 1:n
        x2[i] = x2[i] + A[j,i] * y2[j]
    end
end

let
    n = 2000

    A = zeros(Float32, n, n)
    x1 = zeros(Float32, n)
    x2 = zeros(Float32, n)
    y1 = zeros(Float32, n)
    y2 = zeros(Float32, n)

    for i = 1:n
        x1[i] = (i % n) / n
        x2[i] = ((i + 1) % n) / n
        y1[i] = ((i + 3) % n) / n
        y2[i] = ((i + 4) % n) / n
        for j = 1:n
            A[i,j] = (i*j % n) / n
        end
    end

    SUITE["mvt"] = @benchmarkable kernel_mvt(x1, x2, y1, y2, A) setup = (x1 = copy($x1); x2 = copy($x2); y1 = copy($y1); y2 = copy($y2); A = copy($A))
end


# Create example data
s = LinRange(-4, 4, 50)
v = cos.(s) .+ cos.(s)' .+ cos.(reshape(s,1,1,:))
# Draw the isosurface
isosurface(v, 0.5, color=0x99ffcc)


using UnsteadyFlowSolvers
using Test

include("ldvmLinTest.jl")


push!(LOAD_PATH, "../LF_common/")
using m_LF3d

include("../LF_common/sparse_LF3d.jl")

using PyPlot
const plt = PyPlot

function test_main( NN::Array{Int64} )

  println("NN = ", NN)

  Nx = NN[1]
  Ny = NN[2]
  Nz = NN[3]
  Npoints = Nx*Ny*Nz

  AA = [0.0, 0.0, 0.0]
  BB = [16.0, 16.0, 16.0]

  # Initialize LF
  LF = init_LF3d_c( NN, AA, BB, verbose=true )

  lin2xyz = LF.lin2xyz
  D2jl_x = LF.LFx.D2jl
  D2jl_y = LF.LFy.D2jl
  D2jl_z = LF.LFz.D2jl

  nnzc = Nx + Ny + Nz - 2
  NNZ = nnzc*Npoints

  rowval = zeros( Int64, NNZ )
  nzval = zeros( Float64, NNZ )
  colptr = zeros( Int64, Npoints + 1 )

  rowGbl_x_orig = zeros( Int64, Nx )
  rowGbl_y_orig = zeros( Int64, Ny )
  rowGbl_z_orig = zeros( Int64, Nz )

  rowGbl_x = zeros( Int64, Nx )
  rowGbl_y = zeros( Int64, Ny )
  rowGbl_z = zeros( Int64, Nz )

  for iz = 1:Nz
    rowGbl_z_orig[iz] = iz
  end

  rowGbl_y_orig[1] = 1
  for iy = 2:Ny
    rowGbl_y_orig[iy] = rowGbl_y_orig[iy-1] + Nz
  end

  rowGbl_x_orig[1] = 1
  for ix = 2:Nx
    rowGbl_x_orig[ix] = rowGbl_x_orig[ix-1] + Ny*Nz
  end

  # use @inbounds ?
  colGbl = 1
  idx_diag = 1

  ip = 0
  # rowLoc = 1:Nx, 1:Ny, 1:Nz for each gblCol
  for colGbl = 1:Npoints

    colLoc_x = ceil(Int,colGbl/(Ny*Nz))
    #println( rowGbl_x_orig + colGbl - 1 - (colLoc_x-1)*Nz*Ny )

    izz = ceil(Int,colGbl/Nz)
    #println( rowGbl_y_orig + colGbl - 1 - (izz-1)*Nz + (colLoc_x-1)*Ny*Nz )

    #println( rowGbl_z_orig + (izz-1)*Nz )

    yy = colGbl - (colLoc_x-1)*Ny*Nz
    colLoc_y = ceil( Int, yy/Nz )

    colLoc_z = colGbl - (izz-1)*Nz

    #println("coLLoc x, y, z:", [colLoc_x, colLoc_y, colLoc_z])
    #println("")

    # diagonal element
    ip = ip + 1
    rowval[ip] = colGbl
    nzval[ip] = D2jl_x[colLoc_x,colLoc_x] + D2jl_y[colLoc_y,colLoc_y] +  D2jl_z[colLoc_z,colLoc_z]
    #println("diagonal: ", rowval[ip])

    # non
    for ir = 1:Nx
      if ir != colLoc_x
        ip = ip + 1
        rowval[ip] = rowGbl_x_orig[ir] + colGbl - 1 - (colLoc_x-1)*Nz*Ny
        nzval[ip] = D2jl_x[ir,colLoc_x]
        #println("x: ", rowval[ip])
      end
    end

    for ir = 1:Ny
      if ir != colLoc_y
        ip = ip + 1
        rowval[ip] = rowGbl_y_orig[ir] + colGbl - 1 - (izz-1)*Nz + (colLoc_x-1)*Ny*Nz
        nzval[ip] = D2jl_y[ir,colLoc_y]
        #println("y: ", rowval[ip])
      end
    end

    for ir = 1:Nz
      if ir != colLoc_z
        ip = ip + 1
        rowval[ip] = rowGbl_z_orig[ir] + (izz-1)*Nz
        nzval[ip] = D2jl_z[ir,colLoc_z]
        #println("z: ", rowval[ip])
      end
    end

  end

  # colptr
  colptr[1] = 1
  for i = 2:Npoints+1
    colptr[i] = colptr[i-1] + nnzc
  end

  #println("ip = ", ip)
  #println("NNZ = ", NNZ)

  #println(colptr)
  #println(rowval)

  nabla2 = SparseMatrixCSC( Npoints, Npoints, colptr, rowval, nzval )

  nabla2_v2 = get_Laplacian3d_kron( LF )

  #println(nabla2)
  #println(nabla2_v2)

  #println( full(nabla2) - full(nabla2_v2) )

  #println( nabla2 - nabla2_v2 )

  srand(123)
  x = rand(Npoints)

  Lx = nabla2 * x
  Lx_v2 = nabla2_v2 * x

  println( sum(Lx-Lx_v2) )

  #plt.clf()
  #plt.spy(full(nabla2))
  #plt.title("nabla")
  #plt.savefig("nabla2.pdf")

  #plt.clf()
  #plt.spy(full(nabla2_v2))
  #plt.title("nabla_v2")
  #plt.savefig("nabla2_v2.pdf")

end

test_main( [30, 30, 30] )


using Test
using StatsFuns
using Base.Iterators: take
using Random
using LinearAlgebra
using DynamicIterators: trace, TimeLift
using TransformVariables: transform, as𝕀, inverse

using MeasureTheory
using MeasureTheory: Const

using Aqua
Aqua.test_all(MeasureTheory; ambiguities=false, unbound_args=false)

function draw2(μ)
    x = rand(μ)
    y = rand(μ)
    while x == y
        y = rand(μ)
    end
    return (x,y)
end

@testset "Parameterized Measures" begin
    @testset "Binomial" begin
        D = Binomial{(:n, :p)}
        par = merge((n=20,),transform(asparams(D, (n=20,)), randn(1)))
        d = D(par)
        (n,p) = (par.n, par.p)
        logitp = logit(p)
        probitp = norminvcdf(p)
        y = rand(d)

        ℓ = logdensity(Binomial(;n, p), y)
        @test ℓ ≈ logdensity(Binomial(;n, logitp), y)
        @test ℓ ≈ logdensity(Binomial(;n, probitp), y)

        @test_broken logdensity(Binomial(n,p), CountingMeasure(ℤ[0:n]), x) ≈ binomlogpdf(n,p,x)
    end

    @testset "NegativeBinomial" begin
        D = NegativeBinomial{(:r, :p)}
        par = transform(asparams(D), randn(2))
        d = D(par)
        (r,p) = (par.r, par.p)
        logitp = logit(p)
        λ = p * r / (1 - p)
        logλ = log(λ)
        y = rand(d)

        ℓ = logdensity(NegativeBinomial(;r, p), y)
        @test ℓ ≈ logdensity(NegativeBinomial(;r, logitp), y)
        @test ℓ ≈ logdensity(NegativeBinomial(;r, λ), y)
        @test ℓ ≈ logdensity(NegativeBinomial(;r, logλ), y)

        sample1 = rand(MersenneTwister(123), NegativeBinomial(;r, λ))
        sample2 = rand(MersenneTwister(123), NegativeBinomial(;r, logλ))
        @test sample1 == sample2

        @test_broken logdensity(Binomial(n,p), CountingMeasure(ℤ[0:n]), x) ≈ binomlogpdf(n,p,x)
    end

    @testset "Poisson" begin
        sample1 = rand(MersenneTwister(123), Poisson(;logλ = log(100)))
        sample2 = rand(MersenneTwister(123), Poisson(;λ = 100))
        @test sample1 == sample2
    end

    @testset "Normal" begin
        D = Normal{(:μ,:σ)}
        par = transform(asparams(D), randn(2))
        d = D(par)
        @test params(d) == par

        μ = par.μ
        σ = par.σ
        σ² = σ^2
        τ = 1/σ²
        logσ = log(σ)
        y = rand(d)

        ℓ = logdensity(Normal(;μ,σ), y)
        @test ℓ ≈ logdensity(Normal(;μ,σ²), y)
        @test ℓ ≈ logdensity(Normal(;μ,τ), y)
        @test ℓ ≈ logdensity(Normal(;μ,logσ), y)
    end

    @testset "LKJCholesky" begin
        D = LKJCholesky{(:k,:η)}
        par = transform(asparams(D, (k=4,)), randn(1))
        d = D(merge((k=4,),par))
        # @test params(d) == par

        η  = par.η
        logη = log(η)

        y = rand(d)
        η = par.η
        ℓ = logdensity(LKJCholesky(4,η), y)
        @test ℓ ≈ logdensity(LKJCholesky(k=4,logη=logη), y)
    end
end

@testset "Kernel" begin
    κ = MeasureTheory.kernel(MeasureTheory.Dirac, identity)
    @test rand(κ(1.1)) == 1.1
end

@testset "SpikeMixture" begin
    @test rand(SpikeMixture(Dirac(0), 0.5)) == 0
    @test rand(SpikeMixture(Dirac(1), 1.0)) == 1
    w = 1/3
    m = SpikeMixture(Normal(), w)
    bm = basemeasure(m)
    @test (bm.s*bm.w)*bm.m == 1.0*basemeasure(Normal())
    @test density(m, 1.0)*(bm.s*bm.w) == w*density(Normal(),1.0)
    @test density(m, 0)*(bm.s*(1-bm.w)) ≈ (1-w)
end

@testset "Dirac" begin
    @test rand(Dirac(0.2)) == 0.2
    @test logdensity(Dirac(0.3), 0.3) == 0.0
    @test logdensity(Dirac(0.3), 0.4) == -Inf
end

@testset "For" begin
    FORDISTS = [
        For(1:10) do j Normal(μ=j) end
        For(4,3) do μ,σ Normal(μ,σ) end
        For(1:4, 1:4) do μ,σ Normal(μ,σ) end
        For(eachrow(rand(4,2))) do x Normal(x[1], x[2]) end
        For(rand(4), rand(4)) do μ,σ Normal(μ,σ) end
    ]

    for d in FORDISTS
        @test logdensity(d, rand(d)) isa Float64
    end
end

import MeasureTheory.:⋅
function ⋅(μ::Normal, kernel)
    m = kernel(μ)
    Normal(μ = m.μ.μ, σ = sqrt(m.μ.σ^2 + m.σ^2))
end
struct AffineMap{S,T}
    B::S
    β::T
end
(a::AffineMap)(x) = a.B*x + a.β
(a::AffineMap)(p::Normal) = Normal(μ = a.B*mean(p) + a.β, σ = sqrt(a.B*p.σ^2*a.B'))

@testset "DynamicFor" begin
    mc = Chain(Normal(μ=0.0)) do x Normal(μ=x) end
    r = rand(mc)

    # Check that `r` is now deterministic
    @test logdensity(mc, take(r, 100)) == logdensity(mc, take(r, 100))

    d2 = For(r) do x Normal(μ=x) end

    @test_broken let r2 = rand(d2)
        logdensity(d2, take(r2, 100)) == logdensity(d2, take(r2, 100))
    end
end

@testset "Univariate chain" begin
    ξ0 = 1.
    x = 1.2
    P0 = 1.0

    Φ = 0.8
    β = 0.1
    Q = 0.2

    μ = Normal(μ=ξ0, σ=sqrt(P0))
    kernel = MeasureTheory.kernel(Normal; μ=AffineMap(Φ, β), σ=Const(Q))

    @test (μ ⋅ kernel).μ == Normal(μ = 0.9, σ = 0.824621).μ

    chain = Chain(kernel, μ)

    dyniterate(iter::TimeLift, ::Nothing) = dyniterate(iter, 0=>nothing)
    tr1 = trace(TimeLift(chain), nothing, u -> u[1] > 15)
    tr2 = trace(TimeLift(rand(Random.GLOBAL_RNG, chain)), nothing, u -> u[1] > 15)
    collect(Iterators.take(chain, 10))
    collect(Iterators.take(rand(Random.GLOBAL_RNG, chain), 10))
end

@testset "Transforms" begin
    t = as𝕀
    @testset "Pushforward" begin
        μ = Normal()
        ν = Pushforward(t, μ)
        x = rand(μ)
        @test logdensity(μ, x) ≈ logdensity(Pushforward(inverse(t), ν), x)
    end

    @testset "Pullback" begin
        ν = Uniform()
        μ = Pullback(t,ν)
        y = rand(ν)
        @test logdensity(ν, y) ≈ logdensity(Pullback(inverse(t), μ), y)
    end
end

using TransformVariables

@testset "Likelihood" begin
    dps = [
        (Normal()                             ,    2.0  )
        # (Pushforward(as((μ=asℝ,)), Normal()^1), (μ=2.0,))
    ]

    ℓs = [
        Likelihood(Normal{(:μ,)},              3.0)
        Likelihood(kernel(Normal, x -> (μ=x, σ=2.0)), 3.0)
    ]

    for (d,p) in dps
        for ℓ in ℓs
            @test logdensity(d ⊙ ℓ, p) == logdensity(d, p) + logdensity(ℓ, p)
        end
    end
end

@testset "Reproducibility" begin

    function repro(D, args, nt=NamedTuple())
        t = asparams(D{args}, nt)
        d = D(transform(t, randn(t.dimension)))
        r(d) = rand(Random.MersenneTwister(1), d)
        logdensity(d, r(d)) == logdensity(d, r(d))
    end

    @testset "Bernoulli" begin
        @test repro(Bernoulli, (:p,))
    end
    @testset "Binomial" begin
        @test repro(Binomial, (:n,:p), (n=10,))
    end

    @testset "Beta" begin
        @test repro(Beta, (:α,:β))
    end

    @testset "Cauchy" begin
        @test repro(Cauchy, (:μ,:σ))
    end

    @testset "Dirichlet" begin
        @test_broken repro(Dirichlet, (:p,))
    end

    @testset "Exponential" begin
        @test repro(Exponential, (:λ,))
    end

    @testset "Gumbel" begin
        @test repro(Gumbel, (:μ,:σ))
    end

    @testset "InverseGamma" begin
        @test_broken repro(InverseGamma, (:p,))
    end

    @testset "Laplace" begin
        @test repro(Laplace, (:μ,:σ))
    end

    @testset "LKJCholesky" begin
        @test repro(LKJCholesky, (:k,:η,), (k=3,))
    end

    @testset "Multinomial" begin
        @test_broken repro(Multinomial, (:n,:p,))
    end

    @testset "MvNormal" begin
        @test_broken repro(MvNormal, (:μ,))
    end

    @testset "NegativeBinomial" begin
        @test repro(NegativeBinomial, (:r, :p))
    end

    @testset "Normal" begin
        @test repro(Normal, (:μ,:σ))
    end

    @testset "Poisson" begin
        @test repro(Poisson, (:λ,))
    end

    @testset "StudentT" begin
        @test repro(StudentT, (:ν, :μ))
    end

    @testset "Uniform" begin
        @test repro(Uniform, ())
    end

end


module TensorProductQuadraturesTests

using Gridap.Integration

using Test

degrees = (2,2)
quad = TensorProductQuadrature(degrees)
test_quadrature(quad)
@test sum(get_weights(quad)) ≈ 1

D = 2
degree = 3
quad = TensorProductQuadrature{D}(degree)
test_quadrature(quad)
@test sum(get_weights(quad)) ≈ 1

end # module


#############################
## This file extends the linear functionality in src/triVolterraFullKernel.jl
## to nonlinear and integro-differential equations. This has many different variations.
## We hard-coded some standard ones here but it is straightforward to modify it for more general cases.

#############################
##   Provides a measure of distance between guess and true solution of non-linear 2nd kind Volterra where f(u) is the nonlinearity
####
function triNonLinearVolterraObjective(uguess,f,K,g::Fun,depth)
    u = Fun(Jacobi(0,1,0..1),uguess)
    fofu = pad(Fun(x->f(u(x)),Jacobi(0,1,0..1)).coefficients,depth)
    V = Conversion(Jacobi(0,0,0..1),Jacobi(0,1,0..1))[1:depth,1:depth]*reflectPabtoPba(depth)*WLoweringP01P00(depth)*triVolterraFullKernelOpP01(K,depth,true)
    return pad(uguess,depth)-V*fofu-pad(g.coefficients,depth)
end

#############################
##   Provides a measure of distance between guess and true solution of non-linear, integro-differential
##   2nd kind Volterra where f(u) is the nonlinearity, DD is the order of the derivative and
##   Init is a vector of length D containing the initial conditions.
##
##   Currently hard-coded for 1st and 2nd derivative problems only but easy to extend to general case when needed.
####
function triNonLinearIntegroDiffVolterraObjective(uguess,f,K,g,depth,DD,init,lincomb,kerneldepth)
    u = Fun(Jacobi(0,1,0..1),uguess)
    gF = Fun(x->g(x),Jacobi(DD,DD+1, 0..1))
    fofu = pad(Fun(x->lincomb(x)+f(u(x)),Jacobi(0,1,0..1)).coefficients,depth)
    Kfun = Fun((x,y)->K(x,y),JacobiTriangle())
    if length(Kfun.coefficients) == 1    # Check if kernel is a constant, in which case explicit monomial method is used
        V = triVolterraOpP01([Kfun.coefficients[1],0.,0.,0.],depth,true)
    else
        V = triVolterraFullKernelOpP01(K,depth,true,kerneldepth)
    end
    V = reflectPabtoPba(depth)*WLoweringP01P00(depth)*V
    V = Conversion(Jacobi(0,0,0..1),Jacobi(DD,1+DD,0..1))[1:depth,1:depth]*V[1:depth,1:depth]
    if DD==1
        return pad([u(0)-init[1];pad((Derivative(DD)*u).coefficients,depth)-V*fofu-pad(gF.coefficients,depth)],depth)
    end
    if DD==2
        return pad([u(0)-init[1];(Derivative(DD-1)*u)(0)-init[2];pad((Derivative(DD)*u).coefficients,depth)-V*fofu-pad(gF.coefficients,depth)],depth)
    end
end
function triNonLinearIntegroDiffVolterraObjective(uguess,f,K,g,depth,DD,init,lincomb)
    u = Fun(Jacobi(0,1,0..1),uguess)
    gF = Fun(x->g(x),Jacobi(DD,DD+1, 0..1))
    fofu = pad(Fun(x->lincomb(x)+f(u(x)),Jacobi(0,1,0..1)).coefficients,depth)
    Kfun = Fun((x,y)->K(x,y),JacobiTriangle())
    if length(Kfun.coefficients) == 1    # Check if kernel is a constant, in which case explicit monomial method is used
        V = triVolterraOpP01([Kfun.coefficients[1],0.,0.,0.],depth,true)
    else
        V = triVolterraFullKernelOpP01(K,depth,true)
    end
    V = reflectPabtoPba(depth)*WLoweringP01P00(depth)*V
    V = Conversion(Jacobi(0,0,0..1),Jacobi(DD,1+DD,0..1))[1:depth,1:depth]*V[1:depth,1:depth]
    if DD==1
        return pad([u(0)-init[1];pad((Derivative(DD)*u).coefficients,depth)-V*fofu-pad(gF.coefficients,depth)],depth)
    end
    if DD==2
        return pad([u(0)-init[1];(Derivative(DD-1)*u)(0)-init[2];pad((Derivative(DD)*u).coefficients,depth)-V*fofu-pad(gF.coefficients,depth)],depth)
    end
end
#############################
##   This is the pochhammer symbol function from FastTransforms.jl. It's reproduced here directly for testing purposes.
####
function pochhammer(x::Number,n::Integer)
    ret = one(x)
    if n≥0
        for i=0:n-1
            ret *= x+i
        end
    else
        ret /= pochhammer(x+n,-n)
    end
    ret
end

#############################
##   alternI: Diagonal matrix with entries (-1)^n
####
function alternI(N::Integer)
    altI=BandedMatrix{Float64}(undef, (N, N), (0,0))
    for n=1:N
        altI[n,n]=(-1)^(n+1)
    end
    return altI
end

#############################
##   EvalJPat1mat: Operator which evaluates a coefficient vector of
##              0..1 Jacobi polynomials in P_n^(α,β)basis at x=0 if given α.
##              Combine with alternI(N) and use β instead of α to evaluate at x=1.
##              Be mindful that in the ApproxFun version used here (though not in later versions), we have Jacobi(β,α) = P_n^(α,β)(x)
####
function EvalJPat1mat(α,N::Integer)
    poch=BandedMatrix{Float64}(undef, (N, N), (0,0))
    for n=1:N
        poch[n,n]=FastTransforms.pochhammer(1+α,n-1)/SpecialFunctions.gamma(n);
    end
    return poch
end
function EvalatLHS(β,N)
    return Ones(1,N)*alternI(N)*EvalJPat1mat(β,N)
end
function EvalatRHS(α,N)
    return Ones(1,N)*EvalJPat1mat(α,N)
end

#############################
## Some specific commonly occuring speccial cases of the above for higher efficiency computations, evaluate LHS of specific polynomial bases
## Prime in name indicates number of derivatives. Can also just use the general function but this is a bit faster if you know what you will need.
####
function DirectEvalLHSP10at0(N::Integer)
    vek=ones(1,N)
    for n=1:N
        vek[1,n]=(-1)^(n-1)
    end
    return vek
end
function DirectEvalLHSP10atPrime0(N::Integer)
    vek=ones(1,N)
    for n=1:N
        vek[1,n]=(-1)^(n-1)*n
    end
    return vek
end
function DirectEvalLHSP10atPPrime0(N::Integer)
    vek=ones(1,N)
    for n=1:N
        vek[1,n]=(-1)^(n-1)*n*(n+1)/2
    end
    return vek
end
function DirectEvalLHSP10atPPPrime0(N::Integer)
    vek=ones(1,N)
    for n=1:N
        vek[1,n]=(-1)^(n-1)*n*(n+1)*(n+2)/6
    end
    return vek
end
function DirectEvalLHSP10atPPPPrime0(N::Integer)
    vek=ones(1,N)
    for n=1:N
        vek[1,n]=(-1)^(n-1)*(n+3)*(n+2)*(n+1)*n/24
    end
    return vek
end


@testset "Edit distance" begin

    gtri = random_regular_graph(3,2)
    gquad = random_regular_graph(4,2)
    gpent = random_regular_graph(5,2)

    for triangle in testgraphs(gtri), quadrangle in testgraphs(gquad), pentagon in testgraphs(gpent)
        d, λ = @inferred(edit_distance(triangle, quadrangle, subst_cost=MinkowskiCost(1:3,1:4)))
        @test d == 1.0
        @test λ == Tuple[(1,1),(2,2),(3,3),(0,4)]

        d, λ = @inferred(edit_distance(quadrangle, triangle, subst_cost=MinkowskiCost(1:4,1:3)))
        @test d == 1.0
        @test λ == Tuple[(1,1),(2,2),(3,3),(4,0)]

        d, λ = @inferred(edit_distance(triangle, pentagon, subst_cost=MinkowskiCost(1:3,1:5)))
        @test d == 2.0
        @test λ == Tuple[(1,1),(2,2),(3,3),(0,4),(0,5)]

        d, λ = @inferred(edit_distance(pentagon, triangle, subst_cost=MinkowskiCost(1:5,1:3)))
        @test d == 2.0
        @test λ == Tuple[(1,1),(2,2),(3,3),(4,0),(5,0)]
    end
    cost = @inferred(MinkowskiCost(1:3,1:3))
    bcost = @inferred(BoundedMinkowskiCost(1:3,1:3))
    for i=1:3
      @test cost(i,i) == 0.
      @test bcost(i,i) == 2/3
    end

    g1c = CompleteGraph(4)
    g2c = CompleteGraph(4)
    rem_edge!(g2c, 1, 2)
    for g1 in testgraphs(g1c), g2 in testgraphs(g2c)
        d, λ = @inferred(edit_distance(g1, g2))
        @test d == 2.0
        @test λ == Tuple[(1,1),(2,2),(3,3),(4,4)]
    end
end


using StochasticSemiDiscretizationMethod

function createHayesProblem(a,β)
    AMx =  ProportionalMX(a*ones(1,1));
    τ1=1.
    BMx1 = DelayMX(τ1,zeros(1,1));
    cVec = Additive(1)
    noiseID = 1
    αMx1 = stCoeffMX(noiseID,ProportionalMX(zeros(1,1)))
    βMx11 = stCoeffMX(noiseID,DelayMX(τ1,β*ones(1,1)))
    σ = stAdditive(1,Additive(ones(1)))
    LDDEProblem(AMx,[BMx1],[αMx1],[βMx11],cVec,[σ])
end

hayes_lddep=createHayesProblem(-6.,2.); # LDDE problem for Hayes equation
method=SemiDiscretization(0,0.1) # 0th order semi discretization with Δt=0.1
τmax=1. # the largest τ of the system
# Second Moment mapping
mapping=DiscreteMapping_M2(hayes_lddep,method,τmax,n_steps=10,calculate_additive=true); #The discrete mapping of the system

@show spectralRadiusOfMapping(mapping); # spectral radius ρ of the mapping matrix (ρ>1 unstable, ρ<1 stable)
statM2=VecToCovMx(fixPointOfMapping(mapping), length(mapping.M1_Vs[1])); # stationary second moment matrix of the hayes equation (equilibrium position)
@show statM2[1,1]

# Moment stability chart for the Hayes equation
using MDBM
using Plots
gr();
using LaTeXStrings

method=SemiDiscretization(0,0.1);
τmax=1.

foo(a,b) = log(spectralRadiusOfMapping(DiscreteMapping_M2(createHayesProblem(a,b),method,τmax,
    n_steps=10))); # No additive term calculated

axis=[Axis(-9.0:1.0,:a),
    Axis(-5.0:5.0,:β)]

iteration=4;
stab_border_points=getinterpolatedsolution(solve!(MDBM_Problem(foo,axis),iteration));

scatter(stab_border_points...,xlim=(-9.,1.),ylim=(-5.,5.),
    label="",title="2nd moment stability border of the Hayes equation",xlabel=L"A",ylabel=L"$\beta$",
    guidefontsize=14,tickfont = font(10),markersize=2,markerstrokewidth=0)


"""
    type Coord

An {x,y,z} coordinate type. Used throughout the ConstructiveSolidGeometry.jl package for speed.

# Constructors
* `Coord(x::Float64, y::Float64, z::Float64)`
"""
type Coord
    x::Float64
    y::Float64
    z::Float64
end

"""
    type Ray

A ray is defined by its origin and a unitized direction vector

# Constructors
* `Ray(origin::Coord, direction::Coord)`
"""
type Ray
    origin::Coord
    direction::Coord
end

"""
    abstract Surface

An abstract class that all surfaces (`Sphere`, `Plane`, `InfCylinder`) inherit from. Implementation of new shapes should inherit from `Surface`.
"""
abstract type Surface end

"""
    type Plane <: Surface

Defined by a point on the surface of the plane, its unit normal vector, and an optional boundary condition.

# Constructors
* `Plane(point::Coord, normal::Coord)`
* `Plane(point::Coord, normal::Coord, boundary::String)`

# Arguments
* `point::Coord`: Any point on the surface of the plane
* `normal::Coord`: A unit normal vector of the plane. Recommended to use `unitize(c::Coord)` if normalizing is needed.
* `boundary::String`: Optional boundary condition, defined as a `String`. Options are "transmission" (default), "vacuum", and "reflective".
"""
type Plane <: Surface
    point::Coord
    normal::Coord
    reflective::Bool
	vacuum::Bool
	Plane(point::Coord, normal::Coord, ref::Bool, vac::Bool) = new(point, normal, ref, vac)
	function Plane(point::Coord, normal::Coord, boundary::String)
		if boundary == "reflective"
			new(point, normal, true, false)
		elseif boundary == "vacuum"
			new(point, normal, false, true)
		else
			new(point, normal, false, false)
		end
	end
	Plane(point::Coord, normal::Coord) = new(point, normal, false, false)
end

"""
    type Cone <: Surface

Defined by the tip of the cone, its direction axis vector, the angle between the central axis and the cone surface, and an optional boundary condition.

# Constructors
* `Cone(tip::Coord, axis::Coord, theta::Float64)`
* `Cone(tip::Coord, axis::Coord, theta::Float64, boundary::String)`

# Arguments
* `tip::Coord`: The vertex (tip) of the cone
* `axis::Coord`: A unit vector representing the central axis of the cone. As the cone equation actually defines two cones eminating in a mirrored fashion from the tip, this direction vector also indicates which cone is the true cone. I.e., following the direction of the axis vector when starting from the tip should lead inside the cone you actually want.  Recommended to use `unitize(c::Coord)` if normalizing is needed.
* `theta::Float64`: The angle (in radians) between the central axis (must be between 0 and pi/2)
* `boundary::String`: Optional boundary condition, defined as a `String`. Options are \"transmission\" (default) or \"vacuum\".
"""
type Cone <: Surface
    tip::Coord
    axis::Coord
	theta::Float64
    reflective::Bool
	vacuum::Bool
	Cone(tip::Coord, axis::Coord, theta::Float64, ref::Bool, vac::Bool) = new(tip, axis, theta, ref, vac)
	function Cone(tip::Coord, axis::Coord, theta::Float64, boundary::String)
		if boundary == "reflective"
			new(tip, axis, theta, true, false)
		elseif boundary == "vacuum"
			new(tip, axis, theta, false, true)
		else
			new(tip, axis, theta, false, false)
		end
	end
	Cone(tip::Coord, axis::Coord, theta::Float64) = new(tip, axis, theta, false, false)
end

"""
    type Sphere <: Surface

Defined by the center of the sphere, its radius, and an optional boundary condition.

# Constructors
* `Sphere(center::Coord, radius::Float64)`
* `Sphere(center::Coord, radius::Float64, boundary::String)`

# Arguments
* `center::Coord`: The center of the sphere
* `radius::Float64`: The radius of the sphere
* `boundary::String`: Optional boundary condition, defined as a `String`. Options are \"transmission\" (default) or \"vacuum\".
"""
type Sphere <: Surface
    center::Coord
    radius::Float64
    reflective::Bool
	vacuum::Bool
	Sphere(c::Coord, r::Float64, ref::Bool, vac::Bool) = new(c, r, ref, vac)
	function Sphere(c::Coord, r::Float64, boundary::String)
		if boundary == "reflective"
			new(c, r, true, false)
		elseif boundary == "vacuum"
			new(c, r, false, true)
		else
			new(c, r, false, false)
		end
	end
	Sphere(c::Coord, r::Float64) = new(c, r, false, false)
end

"""
    type InfCylinder <: Surface

An arbitrary direction infinite cylinder defined by any point on its central axis, its radius, the unit normal direction of the cylinder, and an optional boundary condition. A finite cylinder can be generated by defining the intersection of an infinite cylinder and two planes.

# Constructors
* `InfCylinder(center::Coord, normal::Coord, radius::Float64)`
* `InfCylinder(center::Coord, normal::Coord, radius::Float64, boundary::String)`

# Arguments
* `center::Coord`: The center of the infinite cylinder
* `normal::Coord`: A unit normal direction vector of the cylinder (i.e., a vector along its central axis), Recommended to use `unitize(c::Coord)` if normalizing is needed.
* `radius::Float64`: The radius of the infinite cylinder
* `boundary::String`: Optional boundary condition, defined as a `String`. Options are \"transmission\" (default) or \"vacuum\".
"""
type InfCylinder <: Surface
    center::Coord
    normal::Coord
    radius::Float64
    reflective::Bool
	vacuum::Bool
	InfCylinder(c::Coord, n::Coord, r::Float64, ref::Bool, vac::Bool) = new(c, n, r, ref, vac)
	function InfCylinder(c::Coord, n::Coord, r::Float64, boundary::String)
		if boundary == "reflective"
			new(c, n, r, true, false)
		elseif boundary == "vacuum"
			new(c, n, r, false, true)
		else
			new(c, n, r, false, false)
		end
	end
	InfCylinder(c::Coord, n::Coord, r::Float64) = new(c, n, r, false, false)
end

"""
    type Box

An axis aligned box is defined by the minimum `Coord` and maximum `Coord` of the box. Note that a Box is only used by ConstructiveSolidGeometry.jl for bounding box purposes, and is not a valid surface to define CSG cells with. Instead, you must define all six planes of a box independently.

# Constructors
* `Box(min::Coord, max::Coord)`
"""
type Box
    lower_left::Coord
    upper_right::Coord
end

"""
    type Region

The volume that is defined by a surface and one of its halfspaces

# Constructors
* `Region(surface::Surface, halfspace::Int64)`

# Arguments
* `surface::Surface`: A `Sphere`, `Plane`, or `InfCylinder`
* `halfspace::Int64`: Either +1 or -1
"""
type Region
    surface::Surface
    halfspace::Int64
end

"""
    type Cell

Defined by an array of regions and the logical combination of those regions that define the cell

# Constructors
* `Cell(regions::Array{Region}, definition::Expr)`

# Arguments
* `regions::Array{Region}`: An array of regions that are used to define the cell
* `definition::Expr`: A logical expression that defines the volume of the cell. The intersection operator is ^, the union operator is |, and the complement operator is ~. Regions are defined by their integer indices in the regions array.
"""
type Cell
    regions::Array{Region}
    definition::Expr
end

"""
    type Geometry

The top level object that holds all the cells in the problem. This object contains all data regarding the geometry within a system.

# Constructors
* `Geometry(cells::Array{Cell}, bounding_box::Box)`

# Arguments
* `cells::Array{Cell}`: All cells inside the geometry. The cells must combine to fill the entire space of the bounding box. No two cells should overlap.
* `bounding_box::Box`: The bounding box around the problem.
"""
type Geometry
    cells::Array{Cell}
    bounding_box::Box
end

_p = Coord(0,0,0)
typeassert(_p, Coord)


function toh03()
#
#  generalization of K.C. Toh and L. N. Trefethen
#
    k = [(1:20)...]
    z = 10/11 .- exp2.(-k)
    p = poly(z)
    z = [z ones(20)]

    p, PolyZeros(z)
end


using Rainflow
using Base.Test

srand(1234)
signal = randn(100)
ext, t = sort_peaks(signal)
result = count_cycles(ext,t)
bins = sum_cycles(result,13,1)
bins1 = sum_cycles(result, collect(linspace(0, 100, 13+1)), collect(linspace(0, 100, 1+1)))
bins2 = sum_cycles(result, linspace(0, 100, 13+1), linspace(0, 100, 1+1))
@test bins==bins1==bins2
@test squeeze(bins,2) == [4.0, 6.0, 7.5, 3.0, 2.5, 5.0, 0.0, 2.5, 1.0, 0.0, 0.5, 0.0, 1.0]


# Shallow water equations using a Lax-Friedrich finite volume scheme.
# flat bottom. Equations :
# h_t + div(hU) = 0                        (1)
# (hU)_t + div(h UxU + 1/2 gh^2 Id_2) = 0  (2)
#
# Setting V = (h, hu, hv), where U = (u, v), equations write :
# V_t + F(V)_x + G(V)_y = 0
#
# Where :
# F(V) = (hu, hu^2 + 1/2 gh^2, huv)
# G(V) = (hv, huv, hv^2 + 1/2 gh^2)
#
# Lax-Friedrich for 1D equation v_t + F(v)_x = 0
# v^{n+1}_i = 1/2 (v^n_{i-1} + v^n_{i+1}) - dt / (2 dx) * (F(v^n_{i+1}) - F(v^n_{i-1}))
#
# For our problem, in 2D, with a cartesian mesh :
# V^{n+1}_{i, j} = 1/2 (V^n_{i-1, j} + V^n_{i+1, j} + V^n_{i, j-1} + V^n_{i, j+1})
#                  - dt / (2 dx) * (F(V^n_{i+1, j}) - F(V^n_{i-1, j}))
#                  - dt / (2 dy) * (G(V^n_{i, j+1}) - G(V^n_{i, j-1}))

# perf or animation ?
const INFO = false
const record_animation = false

using Printf
using StaticArrays
if record_animation
    using Makie
end

using Profile

# the type for the domain
struct Domain
    x_start::Float64
    x_end::Float64
    y_start::Float64
    y_end::Float64
end

# Initial solution
function InitSolution!(V::Array{Float64, 3}, domain::Domain)
    ny = size(V, 2)
    nx = size(V, 3)

    hx = (domain.x_end - domain.x_start) / nx
    hy = (domain.y_end - domain.y_start) / ny

    xc = 0.5  * (domain.x_start + domain.x_end) + nx/8 * hx
    yc = 0.5  * (domain.y_start + domain.y_end) - ny/8 * hy

    for i=1:nx
        x = domain.x_start + (i-0.5) * hx

        for j=1:ny
            y = domain.y_start + (j-0.5) * hy

            if (x - xc)^2 + (y - yc)^2 < 0.2^2
                V[1, j, i] = 2
            else
                V[1, j, i] = 1
            end

            V[2, j, i] = 0
            V[3, j, i] = 0
        end
    end
end

# Model
function F!(f, V::Array{Float64, 3}, j::Int64, i::Int64)
    f[1] = V[2, j, i]
    if isapprox(V[1, j, i], 0., atol=eps(Float64), rtol=0)
        f[2] = 0
        f[3] = 0
    else
        f[2] = V[2, j, i]^2 / V[1, j, i] + 0.5 * 9.81 * V[1, j, i]^2
        f[3] = V[2, j, i] * V[3, j, i] / V[1, j, i]
    end
end

function G!(g, V::Array{Float64, 3}, j::Int64, i::Int64)
    g[1] = V[3, j, i]
    if isapprox(V[1, j, i], 0., atol=eps(Float64), rtol=0)
        g[2] = 0
        g[3] = 0
    else
        g[2] = V[2, j, i] * V[3, j, i] / V[1, j, i]
        g[3] = V[3, j, i]^2 / V[1, j, i] + 0.5 * 9.81 * V[1, j, i]^2
    end
end

# Scheme
function LaxFriedrich!(V::Array{Float64, 3}, Vold::Array{Float64, 3}, lambdas::Array{Float64, 2}, dt, dx, dy)
    ny = size(V, 2)
    nx = size(V, 3)

    Cx = dt / dx
    Cy = dt / dy

    flux1 = @MVector [0., 0., 0.]
    flux2 = @MVector [0., 0., 0.]

    # moving along x axis
    for j=1:ny
        F!(flux1, Vold, j, 1)

        # left boundary
        for k=2:3
            V[k, j, 1] += Cx * (flux1[k] - lambdas[j, 1] * Vold[k, j, 1])
        end

        for i=1:nx-1
            F!(flux2, Vold, j, i+1)
            lambda = max(lambdas[j, i], lambdas[j, i+1])

            for k=1:3
                flux = 0.5 * Cx * ((flux2[k] + flux1[k]) - lambda * (Vold[k, j, i+1] - Vold[k, j, i]))
                V[k, j, i] -= flux
                V[k, j, i+1] += flux
            end
            flux1, flux2 = flux2, flux1
        end

        # right boundary
        for k=2:3
            V[k, j, nx] -= Cx * (flux1[k] + lambdas[j, nx] * Vold[k, j, nx])
        end
    end

    # moving along y-axis
    for i=1:nx
        G!(flux1, Vold, 1, i)

        # bottom boundary
        for k=2:3
            V[k, 1, i] += Cy * (flux1[k] - lambdas[1, i] * Vold[k, 1, i])
        end

        for j=1:ny-1
            G!(flux2, Vold, j+1, i)
            lambda = max(lambdas[j, i], lambdas[j+1, i])

            for k=1:3
                flux = 0.5 * Cy * ((flux2[k] + flux1[k]) - lambda * (Vold[k, j+1, i] - Vold[k, j, i]))
                V[k, j, i] -= flux
                V[k, j+1, i] += flux
            end
            flux1, flux2 = flux2, flux1
        end

        # top boundary
        for k=2:3
            V[k, ny, i] -= Cy * (flux1[k] + lambdas[ny, i] * Vold[k, ny, i])
        end
    end
end

# Computes time step satisfying the CFL condition
function UpdateCFL!(lambdas::Array{Float64, 2}, V::Array{Float64, 3}, dx::Float64, dy::Float64)
    nx = size(V, 3)
    ny = size(V, 2)

    M = 0
    for i=1:nx
        for j=1:ny
            if V[1, j, i] > 0
                lambdas[j, i] = sqrt(V[2, j, i]^2 + V[3, j, i]^2) / V[1, j, i] + sqrt(9.81 * V[1, j, i])
                M = max(M, lambdas[j, i])
            else
                lambdas[j, i] = 0.
            end
        end
    end

    return 0.5 * min(dx / M, dy / M)
end

# initialization
const domain = Domain(0., 1., 0., 1.)
const Nx = 512
const Ny = 512

# loop in time
const T = 2.0
const dx = (domain.x_end - domain.x_start) / Nx
const dy = (domain.y_end - domain.y_start) / Ny

let t=0
    V = Array{Float64, 3}(undef, 3, Nx, Ny)
    InitSolution!(V, domain)

    Vold = Array{Float64, 3}(undef, 3, Nx, Ny)
    lambdas = Array{Float64, 2}(undef, Nx, Ny)

    function update_to_time(tframe::Float64)
        while(t < tframe)
            dt = min(UpdateCFL!(lambdas, V, dx, dy), tframe - t)
            @. Vold = V
            LaxFriedrich!(V, Vold, lambdas, dt, dx, dy)
            t += dt
            INFO && @printf("\t%.5f / %.5f\n", t, tframe)
        end
    end

    if record_animation
        nframe_per_second = 1000
        nframe = convert(Int64, ceil(nframe_per_second * T)) + 1

        scene = Scene(resolution = (1920, 1080))
        x = range(domain.x_start, stop=domain.x_end, length=Nx)
        y = range(domain.y_start, stop=domain.y_end, length=Ny)

        surf = surface!(scene, x, y, view(V, 1, :, :))[end]
        record(scene, "./height2d.mp4", range(t, stop=T, length=nframe), framerate=30) do tframe
            println(tframe, " / ", T)
            update_to_time(tframe)
            surf[3] = view(V, 1, :, :)
        end
    else
        println("Initial time t = ", t)
        Profile.clear()
        @profile update_to_time(T)
        println("End of simulation, t = ", t)
    end
end

Profile.print(maxdepth=10)


@testset "PB-representation - creation" begin
    ode = @ODEmodel(
        x1'(t) = x3(t),
        x2'(t) = a * x2(t),
        x3'(t) = x1(t),
        y1(t) = x1(t),
        y2(t) = x2(t) + u(t)
    )
    ioeqs = find_ioequations(ode)
    pbr = PBRepresentation(ode, ioeqs)

    R, (y1_0, y1_1, y1_2, y2_0, y2_1, y2_2) = Nemo.PolynomialRing(Nemo.QQ, ["y1_0", "y1_1", "y1_2", "y2_0", "y2_1", "y2_2"])
    @test find_leader([a, x1, x2, u], pbr) == nothing
    @test find_leader([a, x1, y1_0, x2], pbr) == y1_0
    @test find_leader([y1_0, y1_1, y2_0, y2_1], pbr) == y2_1
    @test find_leader([y1_2, y2_1], pbr) == y2_1
    @test find_leader([y1_2, y2_0], pbr) == y1_2
end


using ApproxFunRational, AbstractIterativeSolvers, Plots

### Scalar case
tol = 1e-14#1.e-4
r = 1.
s0 = 1.0/sqrt(2)
pn = 1.
d = 4.0
L = 1.

Pss = z -> 2*r*s0^2.0/(r^2+z.^2)
#Pss = z -> exp(-z^2/2)
G = SumFun(Fun(zai(Pss),OscLaurent(0.0,L),100) + Fun(OscLaurent(0.0,L),[pn]))
H = SumFun(Fun(zai(Pss),OscLaurent(-d,L),100))
𝓒 = Cauchy(-1)
𝓢 = Cauchy(1)
One = SumFun(pad(Fun(1.,OscLaurent(0.0,L)),100))

sop = x -> x - (G-One)*(𝓒*x)

out = GMRES(sop,H,⋅,10*tol,30, x -> x)
u = sum([out[2][i]*out[1][i] for i=1:length(out[2])])
jump = x -> (𝓢*u)(x) - (𝓒*u)(x)*G(x) - H(x)
jump(.11)

𝓕 = FourierTransform(-1.0)
U = -(𝓕*(𝓒*u))

x = -10:.01:10
y1 = real(map(U,x))
plot(x,y1)

hatH = FourierTransform(-1.0)*H
x = -10:.01:10
y1 = real(map(hatH,x))
y2 = imag(map(hatH,x))
plot(x,y1)
plot!(x,y2)

k = .3
CFun = Fun(x -> u(x)*exp(1im*k*x), -25..25,400)
sum(CFun)

sum(map(x -> x(k),components(U)))
U.space

U(.3)