#global

#adaptative
function niblack( image::Array{T,2}, width::Integer; k = 0.1 ) where {T<:Real}

    intA  = integralArray( image, type=Float64 );
    intA2 = integralArray( image, type=Float64, fun=(x)->(x^2));

    imgc  = zeros( Float32, size(image) );

    h, w = size(image);
    npix = width*width;

    for col in 1:w
        for row in 1:h

            TL = max( 1, row - width ), max( 1, col - width )
            BR = min( h, row + width ), min( w, col + width )

            npix2 = (length(TL[1]:BR[1])+1)*(length(TL[2]:BR[2])+1);

            sumI  = integralArea(  intA, TL, BR );
            sumI2 = integralArea( intA2, TL, BR );

            mean = sumI/npix2;
            m2   =  mean*mean;

            std  = sqrt( (sumI2 + npix2*m2 - 2*sumI*mean)/npix2 );

            th = mean + k*std;

            imgc[row,col] = ( image[row,col] <= th ) ? 1 : 0;
        end
    end

    return imgc;
end

function sauvola( image::Array{T,2}, width::Integer; R=1, k=0.3 ) where {T<:Real}

    intA  = integralArray( image, type=Float64 );
    intA2 = integralArray( image, type=Float64, fun=(x)->(x^2));

    imgc  = zeros( Float32, size(image) );

    h, w = size(image);
    npix = width*width;

    for col in 1:w
        for row in 1:h

            TL = max( 1, row - width ), max( 1, col - width )
            BR = min( h, row + width ), min( w, col + width )

            npix2 = (length(TL[1]:BR[1])+1)*(length(TL[2]:BR[2])+1);

            sumI  = integralArea(  intA, TL, BR );
            sumI2 = integralArea( intA2, TL, BR );

            mean = sumI/npix2;
            m2   =  mean*mean;

            std  = sqrt( (sumI2 + npix2*m2 - 2*sumI*mean)/npix2 );

            th = mean + k*( std/R - 1 )*(mean-1);

            imgc[row,col] = ( image[row,col] <= th ) ? 1 : 0;
            #imgc[row,col] = th
        end
    end

    return imgc;
end

heavensideStep( num ) = ( num <= 0 ) ? 0 : 1;

function adaptOtsu( image::Array{T,2}, width::Integer, step::Integer, bindata::AbstractRange; R=0.1 ) where {T<:Real}

    imgc = zeros( Float32, size(image) )
    img  = copy(image)
    h, w = size(image)

    len  = 2*width + 1;
    #step = 2*width + 1;
    centers_r = [ row - width for row in len:step:h ]
    centers_c = [ col - width for col in len:step:w ]

    thresholds = zeros( Float32, length(len:step:h), length(len:step:w) );

    nhImg,_,_ = normhistogram( imgc, bindata );
    imgOt = otsu( nhImg );

    # computing threshold for each grid element
    contc = 1;
    for col in len:step:w
        contr = 1;
        for row in len:step:h

            TL = max( 1, row - width ), max( 1, col - width )
            BR = min( h, row + width ), min( w, col + width )

            TL = ( row - len + 1, col - len + 1 )
            BR = ( row, col )

            subimg = image[ TL[1]:BR[1], TL[2]:BR[2] ]
            nh,_,_ = normhistogram( subimg, bindata )
            subOt  = otsu( nh )

            th = heavensideStep( R/abs(imgOt - subOt) - 1 )*150 + subOt

            thresholds[contr,contc] = th;
            contr += 1;
        end
        contc += 1;
    end

    ch = length( centers_r );
    cw = length( centers_c );

    # Interpolation
    dist = step + 1;

    # centers
    for idxc in 1:length(centers_c)
        col = centers_c[idxc]
        for idxr in 1:length(centers_r)
            row = centers_r[idxr]
            #imgc[row,col] = thresholds[idxr,idxc]
            imgc[row,col] = img[row,col] > thresholds[idxr,idxc]
        end
    end

    # vertically in line
    for idxc in 1:length(centers_c)
        col = centers_c[idxc];
        for idxr in 1:length(centers_r)-1
            U   = centers_r[ idxr ];
            B   = centers_r[idxr+1];
            thU = thresholds[ idxr , idxc ]
            thB = thresholds[idxr+1, idxc ]
            for row in U+1:B-1
                thI = ( B - row )/dist * thU + ( row - U )/dist * thB
                #imgc[ row, col ] = ( B - row )/dist * thU + ( row - U )/dist * thB
                imgc[ row, col ] = img[ row, col ] > thI
            end
        end
    end

    # laterally in line
    for idxc in 1:length(centers_c)-1
        L = centers_c[idxc]
        R = centers_c[idxc+1];
        for col in L+1:R-1
            fL = ( R - col )/dist;
            fR = ( col - L )/dist;
            for idxr in 1:length(centers_r)
                row = centers_r[idxr]
                thL = thresholds[idxr,  idxc ]
                thR = thresholds[idxr, idxc+1]
                #imgc[ row, col ] = fL * thL + fR * thR;
                imgc[ row, col ] = img[ row, col ] > fL * thL + fR * thR
            end
        end
    end

    # middle regions
    for idxc in 1:length(centers_c)-1
        L = centers_c[idxc]
        R = centers_c[idxc+1]
        for idxr in 1:length(centers_r)-1
            U = centers_r[idxr]
            B = centers_r[idxr+1]
            thUL = thresholds[  idxr ,  idxc  ]
            thUR = thresholds[  idxr , idxc+1 ]
            thBL = thresholds[ idxr+1,  idxc  ]
            thBR = thresholds[ idxr+1, idxc+1 ]

            for col in L+1:R-1
                fR  = ( col - L )/dist;
                fL  = ( R - col )/dist;

                for row in U+1:B-1
                    fU = ( B - row )/dist
                    fB = ( row - U )/dist

                    itpL  = fB * thBL + fU * thUL
                    itpR  = fB * thBR + fU * thUR
                    itpTH = fR * itpR + fL * itpL
                    # imgc[row,col] = itpTH
                    imgc[ row, col ] = img[ row, col ] > itpTH
                end
            end
        end
    end

    # first row
    fr = 1:centers_r[1]-1
    for col in 1:centers_c[1]
        #imgc[fr,col] .= thresholds[1,1]
        imgc[fr,col] .= img[fr,col] .> thresholds[1,1]
    end
    for col in centers_c[end]:w
        #imgc[fr,col] .= thresholds[1,end]
        imgc[fr,col] .= img[fr,col] .> thresholds[1,end]
    end
    for cidx in 1:length(centers_c)-1
        L   = centers_c[cidx]
        R   = centers_c[cidx+1]
        thL = thresholds[1,cidx]
        thR = thresholds[1,cidx+1]

        #imgc[fr,L] .= thL
        imgc[fr,L] .= img[fr,L] .> thL

        for col in L+1:R-1
            fR = ( col - L )/dist;
            fL = ( R - col )/dist;
            #imgc[fr,col] .= fR * thR + fL * thL
            imgc[fr,col] .= img[fr,col] .> ( fR * thR + fL * thL )
        end
    end

    # first col
    fc = 1:centers_c[1]-1
    for row in 1:centers_r[1]
        #imgc[row,fc] .= thresholds[1,1]
        imgc[row,fc] .= img[row,fc] .> thresholds[1,1]
    end
    for row in centers_r[end]:h
        #imgc[row,fc] .= thresholds[end,1]
        imgc[row,fc] .= img[row,fc] .> thresholds[end,1]
    end
    for ridx in 1:length(centers_r)-1
        U = centers_r[ridx]
        B = centers_r[ridx+1]

        thU = thresholds[ ridx , 1]
        thB = thresholds[ridx+1, 1]

        #imgc[U,fc] .= thU
        imgc[U,fc] .= img[U,fc] .> thU

        for row in U+1:B-1
            fB = ( row - U )/dist;
            fU = ( B - row )/dist;
            #imgc[row,fc] .= fU * thU + fB * thB
            imgc[row,fc] .= img[row,fc] .> fU * thU + fB * thB
        end
    end

    # last row
    lr = centers_c[end]:w
    for col in 1:centers_c[1]
        #imgc[lr,col] .= thresholds[end,1]
        imgc[lr,col] .= img[lr,col] .> thresholds[end,1]
    end
    for col in centers_c[end]:w
        #imgc[lr,col] .= thresholds[end,end]
        imgc[lr,col] .= img[lr,col] .> thresholds[end,end]
    end
    for cidx in 1:length(centers_c)-1
        L = centers_c[cidx]
        R = centers_c[cidx+1]

        thL = thresholds[end,cidx]
        thR = thresholds[end,cidx+1]

        #imgc[lr,L] .= thL
        imgc[lr,L] .= img[lr,L] .> thL

        for col in L+1:R-1
            fR = ( col - L )/dist;
            fL = ( R - col )/dist;
            #imgc[ lr,col] .= fR * thR + fL * thL
            imgc[ lr, col ] .= img[ lr, col ] .> ( fR * thR + fL * thL )
        end
    end

    # last col
    lc = centers_r[end]:h

    for row in 1:centers_r[1]
        #imgc[row,lc] .= thresholds[1,end]
        imgc[row,lc] .= img[row,lc] .> thresholds[1,end]
    end
    for row in centers_r[end]:h
        #imgc[row,lc] .= thresholds[end,end]
        imgc[row,lc] .= img[row,lc] .> thresholds[end,end]
    end
    for ridx in 1:length(centers_r)-1
        U = centers_r[ridx]
        B = centers_r[ridx+1]

        thU = thresholds[ ridx , end]
        thB = thresholds[ridx+1, end]

        #imgc[U,lc] .= thU
        imgc[U,lc] .= img[U,lc] .> thU

        for row in U+1:B-1
            fB = ( row - U )/dist;
            fU = ( B - row )/dist;
            #imgc[row,lc] .= fU * thU + fB * thB
            imgc[row,lc] .= img[row,lc] .> ( fU * thU + fB * thB )
        end
    end

    return imgc, thresholds
end


function my_f(x,y)
    return 2*x + 3*y
end

function derivative_of_my_f(x,y)
    return ForwardDiff.derivative(x -> my_f(x,y),x)
end


module Econometrics
using Statistics, LinearAlgebra, Printf, Test
using Plots, GR, StatsPlots
using Optim, NLopt
using Distributions, Distances, StatsBase, StatsFuns, StatsModels
using SpecialFunctions
using DataFrames, KernelDensity, Calculus, DataFramesMeta, CSV
# Utilities
#include("Utilities/montecarlo.jl")
include("Utilities/stnorm.jl")
include("Utilities/ma.jl")
include("Utilities/trim.jl")
include("Utilities/prettyprint.jl")
include("Utilities/eye.jl")
include("Utilities/sortbyc.jl")
include("Utilities/dstats.jl")
include("Utilities/lag.jl")
include("Utilities/lags.jl")
include("Utilities/vech.jl")
include("Utilities/PrintDivider.jl")
include("Utilities/PrintEstimationResults.jl")
include("Utilities/clc.jl")
include("Utilities/BoundByQuantiles.jl")
# linear regression
include("LinearRegression/lsfit.jl")
include("LinearRegression/ols.jl")
include("LinearRegression/ridge.jl")
include("LinearRegression/rsq.jl")
include("LinearRegression/tsls.jl")
include("LinearRegression/TestStatistics.jl")
# Bayesian
include("Bayesian/mcmc.jl")
# nonparametrics
include("NP/npreg.jl")
include("NP/InteriorPoint.jl")
include("NP/npdens.jl")
include("NP/bootstrap.jl")
include("NP/npdensity.jl")
include("NP/kernelweights.jl")
include("NP/NeweyWest.jl")
#=
#include("NN/TrainNet.jl")
#include("NN/AnalyzeNet.jl")
=#
# optimization
include("Optimization/samin.jl")
include("Optimization/fminunc.jl")
include("Optimization/fmincon.jl")

# MLE
include("ML/mle.jl")
include("ML/mleresults.jl")
include("ML/Likelihoods/logit.jl")
include("ML/Likelihoods/poisson.jl")
include("ML/Likelihoods/normal.jl")

# GMM
include("GMM/gmm.jl")
include("GMM/gmmresults.jl")

export stnorm, ma, trim, prettyprint, eye, sortbyc, dstats, lag, lags, vech
export clc, PrintDivider, PrintEstimationResults, BoundByQuantiles!
export lsfit, ols, ridge, rsq, tsls, TestStatistics, NeweyWest
export mcmc
export npreg, npdens, kernelweights, bootstrap
export npdensity, InteriorPoint
#export TrainNet, AnalyzeNet
export samin, fminunc, fmincon
export mle, mleresults
export logit, poisson, normal
export gmm, gmmresults
# export montecarlo
end


# ## Coupled Van der Pol oscillator

# ## Model

module coupled_vanderpol #jl

using ReachabilityAnalysis

@taylorize function coupled_vanderpol!(du, u, p, t)
    du[1] = u[2]
    du[2] = (1.0 - u[1]^2)*u[2] - u[1] + (u[3]-u[1])
    du[3] = u[4]
    du[4] = (1.0 - u[3]^2)*u[4] - u[3] + (u[1]-u[3])
    return du
end

function model(X0)
    S = @system(x' = coupled_vanderpol!(x), dim:4)
    return IVP(S, X0)
end

end #jl

# ## References


@testset "Bouncing ball" begin
    H = readsxmodel("../examples/bball/bball_flattened.xml")
    @test nmodes(H) == 1
    @test ntransitions(H) == 1

    flow, invariant = H.modes[1][1], H.modes[1][2]
    @test flow == [:(x' = v), :(v' = -1)]
    @test invariant == [:(x >= 0)]

    assignment, guard = H.resetmaps[1][1], H.resetmaps[1][2]
    @test assignment == [:(x' = x), :(v' = -0.75v)]
    @test guard == [:(x = 0), :(v < 0)]
end

@testset "Van der Pol" begin
    H = readsxmodel("../examples/van_der_pol_fourth_quadrant/van_der_pol_fourth_quadrant.xml")
    @test nmodes(H) == 1
    @test ntransitions(H) == 0

    flow, invariant = H.modes[1][1], H.modes[1][2]
    @test flow == [:(x' = y), :(y' = -x + (1 - x ^ 2) * y)]
    @test invariant == [:(x <= 0), :(y >= 0)]
end

@testset "Lotka-Volterra" begin
    H = readsxmodel("../examples/lotka_volterra_fourth_quadrant/lotka_volterra_fourth_quadrant.xml")
    @test nmodes(H) == 1
    @test ntransitions(H) == 0

    flow, invariant = H.modes[1][1], H.modes[1][2]
    @test flow == [:(x' = x * (1 - y)), :(y' = -((1 - x) * y))]
    @test invariant == [:(x >= 0), :(y >= 0)]
end

@testset "Circle" begin
    H = readsxmodel("../examples/circle/circle_flattened.xml")
    @test nmodes(H) == 2
    @test ntransitions(H) == 2
    for (i, ti) in enumerate(transitions(H))
        if i == 1
            @test source(H, ti) == 1 && target(H, ti) == 2
        elseif i == 2
            @test source(H, ti) == 2 && target(H, ti) == 1
        end
    end

    m = H.modes[1]
    flow, invariant = m[1], m[2]
    @test flow == [:(x' = -y), :(y' = x)] && invariant == [:(y >= 0)]
    m = H.modes[2]
    flow, invariant = m[1], m[2]
    @test flow == [:(x' = -y), :(y' = x)] && invariant == [:(y <= 0)]

    # assignments in each mode 1, 2
    @test (H.resetmaps[1][1], H.resetmaps[2][1]) == ([:(x' = x), :(y' = y)], [:(x' = x), :(y' = y)])

    # guards in each mode 1, 2
    @test (H.resetmaps[1][2], H.resetmaps[2][2]) == ([:(y = 0)], [:(y >= 0)])
end


@doc raw"""
```
DiscreteShapeBound(iterations::Int = 2000, smoothness::Int = 1000, albedo::Real = Inf, illumination_direction::Vector{T} where T <: Real = [Inf, Inf, Inf])
```
Same as [`DiscreteShape`](@ref)
except it has its initial conditions bound by the image as per bellow where
``E`` is the brightness of the image:
```math
\begin{gathered}
Z_{i,j}=\begin{cases}-100E_{i,j} &\text{if } E_{i,j}>0.75\\0 &\text{otherwise}\\
\end{cases}\\\\p,q=\nabla E
\end{gathered}
```
# Example
Compute the heightmap for a synthetic image generated by `generate_surface`.
```julia
using Images, Makie, ShapeFromShading

#generate synthetic image
img = generate_surface(SynthSphere(50), 1.0, [0.2,0,0.9])

#calculate the heightmap (using 500 iterations)
discreteShapeBound = DiscreteShapeBound(iterations = 500, 1.0, [0.2,0,0.9])
Z,p,q = discreteShapeBound(img)

#normalize to maximum of 1 (not necessary but makes displaying easier)
Z = Z./maximum(Z)

#display using Makie (Note: Makie can often take several minutes first time)
r = 0.0:0.1:2
surface(r, r, Z)
```
# Reference
1. S. Elhabian, "Hands on Shape from Shading", Computer Vision and Image Processing, 2008.
"""
function (algorithm::DiscreteShapeBound)(img::AbstractArray)
    ρ = algorithm.albedo
    I = algorithm.illumination_direction
    smoothness = algorithm.smoothness
    iterations = algorithm.iterations

    if ρ == Inf || I == [Inf, Inf, Inf]
        ρ,I,σ,τ = estimate_img_properties(img)
    end
    E = Array{Float64}(img)

    #initialize variables
    p = zeros(Complex{Float64},axes(E))
    q = zeros(Complex{Float64},axes(E))
    R = zeros(Complex{Float64},axes(E))
    Z = zeros(axes(E))

    #apply bounding conditions
    for i in CartesianIndices(E)
        if E[i] > 0.75
            Z[i] = -100*E[i]
        else
            Z[i] = 0.0
        end
    end
    q, p = Array{Complex{Float64}}.(imgradients(Z, KernelFactors.sobel, "replicate"))
    return solve_EulerLagrange(ρ, I, iterations, p, q, R, smoothness, E, Z)
end


shape_map(x,upper,lower) = min(max(x,lower), upper)

"""
    reshape_values(xvals::Vector{<:Real}, yvals::Vector{<:Real}; increasing::Bool = true,
                       concave::Bool = true, shape_map::Function = shape_map)
This reshapes a vector of yvalues. For instance if we are doing fixed point acceleration that
should result in a monotonic concave function (ie the consumption smoothing problem from the documentation examples)
then we may end up with occasional non monotonic/concave values due to a dodgy optimiser or some other numerical issue.
So we can use reshape_values to adjust the values that cannot be true.
### Inputs
* `xvals` - A vector of x coordinates.
* `yvals` - A vector of y coordinates
* `increasing` - Should the y values be increasing. If false then they must be decreasing
* `concave` - Should the y values be concave. If false then they must be convex
* `shape_map` - A function used to adjust values to be increasing-concave (or whatever settings)
### Returns
* An updated vector of y values.
"""
function reshape_values(xvals::Vector{<:Real}, yvals::Vector{<:Real}; increasing::Bool = true,
                        concave::Bool = true, shape_map::Function = shape_map)
    lenlen = length(yvals)
    new_shape_vec = Array{Float64,1}(undef, lenlen)
    if lenlen > 0 new_shape_vec[1] = yvals[1] end
    if lenlen > 1
        upper = increasing ? Inf : new_shape_vec[1]
        lower = increasing ? new_shape_vec[1] : -Inf
        new_shape_vec[2] = shape_map(yvals[2],upper, lower)
    end
    for i in 3:lenlen
        stepp = xvals[i] - xvals[i-1]
        previous_grad = (new_shape_vec[i-1]-new_shape_vec[i-2])/(xvals[i-1] - xvals[i-2])
        upper =  new_shape_vec[i-1] + (increasing ? (concave ? previous_grad * stepp : Inf) : (concave ? previous_grad * stepp : 0))
        lower =  new_shape_vec[i-1] + (increasing ? (concave ? 0 :  previous_grad * stepp) : (concave ? -Inf : previous_grad * stepp))
        new_shape_vec[i] = shape_map(yvals[i],upper, lower)
    end
    return new_shape_vec
end


# Sensitivity analysis for c-posterior of autoregressive model of variable order.
module Run
using PyPlot
draw() = (pause(0.001); get_current_fig_manager()[:window][:raise_]())
logsumexp(x) = (m=maximum(x); (m==-Inf ? -Inf : log(sum(exp(x-m)))+m))

# Settings
n = 10^4
alphas = [50,100,500,1200,2000] # robustification param (max sample size)
K = 25 # maximum value of k to use

# Data generation parameters
s0 = 1.0 # std dev of noise
sm = 0.5 # scale of misspecification
a0 = [0.25,0.25,-0.25,0.25] # autoregression coefficients
k0 = length(a0)
f_noise(t) = randn()*s0 + sm*sin(t) # Time-varying noise

# Model parameters
s = s0
sa = 1.0 # std dev of prior on a's

# Code for autoregressive model
include("AR.jl")

figure(1,figsize=(8,3.2)); clf(); hold(true)
subplots_adjust(bottom=0.2)

srand(0) # Reset RNG
x = generate(n,a0,f_noise) # Generate data
for (i_a,alpha) in enumerate(alphas)
    zeta = (1/n)/(1/n + 1/alpha) # robustification param

    # Compute marginal likelihood for each k
    log_m = zeros(K+1)
    for k = 0:K
        log_m[k+1] = log_marginal(x,k,s,sa,zeta)
    end

    # Compute posterior on k, using an improper uniform prior on k
    pk = exp(log_m - logsumexp(log_m))

    # Plot
    colors = "bgyrm"
    shapes = "ds^v*"
    lab = "\$\\alpha = " * (alpha<Inf ? repr(int(alpha)) : "\\infty") * "\$"
    plot(0:K,pk,"$(colors[i_a])$(shapes[i_a])-",label=lab,markersize=8,linewidth=2)
end

title("\$\\mathrm{Sensitivity\\,analysis}\$",fontsize=17)
ylabel("\$\\Pi(k|\\mathrm{data})\$",fontsize=15)
xlabel("\$k \\,\\,\\mathrm{(\\#\\,of\\,coefficients)}\$",fontsize=16)
xlim(0,12)
xticks(0:12)
ylim(0,1)
legend(numpoints=1)
draw()
savefig("AR-sensitivity.png",dpi=150)

end # module


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

using IntervalArithmetic
using Base.Test

setprecision(Interval, Float64)
# # setrounding(Interval, :narrow)

@testset "Numeric tests" begin

    a = @interval(0.1, 1.1)
    b = @interval(0.9, 2.0)
    c = @interval(0.25, 4.0)

    ## Basic arithmetic
    @test @interval(0.1) == Interval(9.9999999999999992e-02, 1.0000000000000001e-01)
    @test +a == a
    @test a+b == Interval(9.9999999999999989e-01, 3.1000000000000001e+00)
    @test -a == Interval(-1.1000000000000001e+00, -9.9999999999999992e-02)
    @test a-b == Interval(-1.9000000000000001e+00, 2.0000000000000018e-01)
    @test Interval(1//4,1//2) + Interval(2//3) == Interval(11//12, 7//6)
    @test Interval(1//4,1//2) - Interval(2//3) == Interval(-5//12, -1//6)

    @test 10a == @interval(10a)
    #@test 10Interval(1//10) == one(@interval(1//10))
    @test Interval(-30.0,-15.0) / Interval(-5.0,-3.0) == Interval(3.0, 10.0)
    @test @interval(-30,-15) / @interval(-5,-3) == Interval(3.0, 10.0)
    @test b/a == Interval(8.18181818181818e-01, 2.0000000000000004e+01)
    @test a/c == Interval(2.4999999999999998e-02, 4.4000000000000004e+00)
    @test c/4.0 == Interval(6.25e-02, 1e+00)
    @test c/zero(c) == emptyinterval(c)
    @test Interval(0.0, 1.0)/Interval(0.0,1.0) == Interval(0.0, Inf)
    @test Interval(-1.0, 1.0)/Interval(0.0,1.0) == entireinterval(c)
    @test Interval(-1.0, 1.0)/Interval(-1.0,1.0) == entireinterval(c)
    a = @interval(1.e-20)
    @test a == Interval(1.0e-20, 1.0000000000000001e-20)
    @test diam(a) == eps(1.e-20)
end

@testset "Power tests" begin
    @test @interval(0,3) ^ 2 == Interval(0, 9)
    @test @interval(2,3) ^ 2 == Interval(4, 9)
    @test @interval(-3,0) ^ 2 == Interval(0, 9)
    @test @interval(-3,-2) ^ 2 == Interval(4, 9)
    @test @interval(-3,2) ^ 2 == Interval(0, 9)
    @test @interval(0,3) ^ 3 == Interval(0, 27)
    @test @interval(2,3) ^ 3 == Interval(8, 27)
    @test @interval(-3,0) ^ 3 == @interval(-27., 0.)
    @test @interval(-3,-2) ^ 3 == @interval(-27, -8)
    @test @interval(-3,2) ^ 3 == @interval(-27., 8.)
    @test Interval(0,3) ^ -2 == Interval(1/9, Inf)
    @test Interval(-3,0) ^ -2 == Interval(1/9, Inf)
    @test Interval(-3,2) ^ -2 == Interval(1/9, Inf)
    @test Interval(2,3) ^ -2 == Interval(1/9, 1/4)
    @test Interval(1,2) ^ -3 == Interval(1/8, 1.0)
    @test Interval(0,3) ^ -3 == @interval(1/27, Inf)
    @test Interval(-1,2) ^ -3 == entireinterval()
    @test_throws ArgumentError interval(-1, -2) ^ -3  # wrong way round
    @test Interval(-3,2) ^ (3//1) == Interval(-27, 8)
    @test Interval(0.0) ^ 1.1 == Interval(0, 0)
    @test Interval(0.0) ^ 0.0 == emptyinterval()
    @test Interval(0.0) ^ (1//10) == Interval(0, 0)
    @test Interval(0.0) ^ (-1//10) == emptyinterval()
    @test ∅ ^ 0 == ∅
    @test Interval(2.5)^3 == Interval(15.625, 15.625)
    #@test Interval(5//2)^3.0 == Interval(125//8)

    x = @interval(-3,2)
    @test x^3 == @interval(-27, 8)

    @test @interval(-3,4) ^ 0.5 == @interval(0, 2)
    @test @interval(-3,4) ^ 0.5 == @interval(-3,4)^(1//2)
    @test @interval(-3,2) ^ @interval(2) == Interval(0.0, 4.0)
    @test @interval(-3,4) ^ @interval(0.5) == Interval(0, 2)
    @test @biginterval(-3,4) ^ 0.5 == @biginterval(0, 2)

    @test dist(@interval(1,27)^@interval(1/3), Interval(1., 3.)) < 2*eps(Interval(1,3))
    @test dist(@interval(1,27)^(1/3), Interval(1., 3.)) < 2*eps(Interval(1,3))
    @test Interval(1., 3.) ⊆ @interval(1,27)^(1//3)
    @test @interval(0.1,0.7)^(1//3) == Interval(0.46415888336127786, 0.8879040017426008)
    @test dist(@interval(0.1,0.7)^(1/3),
        Interval(0.46415888336127786, 0.8879040017426008)) < 2*eps(@interval(0.1,0.7)^(1/3))

    setprecision(Interval, 256)
    x = @biginterval(27)
    y = x^(1//3)
    @test (0 < diam(y) < 1e-76)
    y = x^(1/3)
    @test (0 < diam(y) < 1e-76)

end

setprecision(Interval, Float64)

@testset "Exp and log tests" begin
    @test exp(@biginterval(1//2)) ⊆ exp(@interval(1//2))
    @test exp(@interval(1//2)) == Interval(1.648721270700128, 1.6487212707001282)
    @test exp(@biginterval(0.1)) ⊆ exp(@interval(0.1))
    @test exp(@interval(0.1)) == Interval(1.1051709180756475e+00, 1.1051709180756477e+00)
    @test diam(exp(@interval(0.1))) == eps(exp(0.1))

    @test log(@biginterval(1//2)) ⊆ log(@interval(1//2))
    @test log(@interval(1//2)) == Interval(-6.931471805599454e-01, -6.9314718055994529e-01)
    @test log(@biginterval(0.1)) ⊆ log(@interval(0.1))
    @test log(@interval(0.1)) == Interval(-2.3025850929940459e+00, -2.3025850929940455e+00)
    @test diam(log(@interval(0.1))) == eps(log(0.1))

    @test exp2(@biginterval(1//2)) ⊆ exp2(@interval(1//2))
    @test exp2(Interval(1024.0)) == Interval(1.7976931348623157e308, Inf)
    @test exp10(@biginterval(1//2)) ⊆ exp10(@interval(1//2))
    @test exp10(Interval(308.5)) == Interval(1.7976931348623157e308, Inf)

    @test log2(@biginterval(1//2)) ⊆ log2(@interval(1//2))
    @test log2(@interval(0.25, 0.5)) == Interval(-2.0, -1.0)
    @test log10(@biginterval(1//10)) ⊆ log10(@interval(1//10))
    @test log10(@interval(0.01, 0.1)) == @interval(log10(0.01), log10(0.1))
end

@testset "Comparison tests" begin
    d = @interval(0.1, 2)

    @test d < 3
    @test d <= 2
    @test (d < 2) == false
    @test -1 < d
    @test !(d < 0.15)

    # abs
    @test abs(@interval(0.1, 0.2)) == Interval(9.9999999999999992e-02, 2.0000000000000001e-01)
    @test abs(@interval(-1, 2)) == Interval(0, 2)

    # real
    @test real(@interval(-1, 1)) == Interval(-1, 1)
end

@testset "Rational tests" begin

    f = 1 // 3
    g = 1 // 3

    @test @interval(f*g) == Interval(1.1111111111111109e-01, 1.1111111111111115e-01)
    @test big(1.)/9 ∈ @interval(f*g)
    @test @interval(1)/9 ⊆ @interval(f*g)
    @test @interval(1)/9 ≠ @interval(f*g)

    h = 1/3
    i = 1/3

    @test @interval(h*i) == Interval(1.1111111111111109e-01, 1.1111111111111115e-01)
    @test big(1.)/9 ∈ @interval(1/9)

    @test @interval(1/9) == @interval(1//9)
end

@testset "Floor etc. tests" begin
    a = @interval(0.1)
    b = Interval(0.1, 0.1)
    @test dist(a,b) <= eps(a)

    @test floor(@interval(0.1, 1.1)) == Interval(0, 1)
    @test round(@interval(0.1, 1.1), RoundDown) == Interval(0, 1)
    @test ceil(@interval(0.1, 1.1)) == Interval(1, 2)
    @test round(@interval(0.1, 1.1), RoundUp) == Interval(1, 2)
    @test sign(@interval(0.1, 1.1)) == Interval(1.0)
    @test trunc(@interval(0.1, 1.1)) == Interval(0.0, 1.0)
    @test round(@interval(0.1, 1.1), RoundToZero) == Interval(0.0, 1.0)
    @test round(@interval(0.1, 1.1)) == Interval(0.0, 1.0)
    @test round(@interval(0.1, 1.5)) == Interval(0.0, 2.0)
    @test round(@interval(-1.5, 0.1)) == Interval(-2.0, 0.0)
    @test round(@interval(-2.5, 0.1)) == Interval(-2.0, 0.0)
    @test round(@interval(0.1, 1.1), RoundTiesToEven) == Interval(0.0, 1.0)
    @test round(@interval(0.1, 1.5), RoundTiesToEven) == Interval(0.0, 2.0)
    @test round(@interval(-1.5, 0.1), RoundTiesToEven) == Interval(-2.0, 0.0)
    @test round(@interval(-2.5, 0.1), RoundTiesToEven) == Interval(-2.0, 0.0)
    @test round(@interval(0.1, 1.1), RoundTiesToAway) == Interval(0.0, 1.0)
    @test round(@interval(0.1, 1.5), RoundTiesToAway) == Interval(0.0, 2.0)
    @test round(@interval(-1.5, 0.1), RoundTiesToAway) == Interval(-2.0, 0.0)
    @test round(@interval(-2.5, 0.1), RoundTiesToAway) == Interval(-3.0, 0.0)

    # # :wide tests
    # # setrounding(Interval, :wide)
    # setprecision(Interval, Float64)
    #
    # a = @interval(-3.0, 2.0)
    # @test a == Interval(-3.0, 2.0)
    # @test a^3 == Interval(-27, 8)
    # @test Interval(-3,2)^3 == Interval(-27, 8)
    #
    # @test Interval(-27.0, 8.0)^(1//3) == Interval(0, 2.0000000000000004)
    #
    # # setrounding(Interval, :narrow)
end

@testset "Fast power" begin

    @testset "Fast integer powers" begin
        x = 1..2
        @test pow(x, 2) == pow(-x, 2) == Interval(1, 4)
        @test pow(-x, 3) == Interval(-8.0, -1.0)

        @test pow(-1..2, 2) == 0..4
        @test pow(-1..2, 3) == -1..8
        @test pow(-1..2, 4) == 0..16

        @test pow(@biginterval(-1, 2), 2) == 0..4
        @test pow(@biginterval(-1, 2), 3) == -1..8
        @test pow(@biginterval(1, 2), 2) == 1..4

        x = @interval(pi)
        @test x^100 ⊆ pow(x, 100)
        @test x^50 ⊆ pow(x, 50)
        @test isinterior(x^50, pow(x, 50))

        x = Interval(2)
        @test pow(x, 2000) == Interval(realmax(), Inf)
    end

    @testset "Fast real powers" begin
        x = 1..2
        @test pow(x, 0.5) == Interval(1.0, 1.4142135623730951)
        @test pow(x, 0.5) == x^0.5

        @test pow(x, 17.3) != x^17.3

        y = 2..3
        @test pow(y, -0.5) == Interval(0.5773502691896257, 0.7071067811865476)

        y = -2..3
        @test pow(y, 2.1) == Interval(0.0, 10.045108566305146)
        @test pow(y, 2.1) != y^2.1
        @test y^2.1 ⊆ pow(y, 2.1)
    end

    @testset "Fast interval powers" begin
        x = 1..2
        @test x^Interval(-1.5, 2.5) == Interval(0.35355339059327373, 5.656854249492381)

        y = -2..3
        @test pow(y, 2.1) == Interval(0.0, 10.045108566305146)
        @test pow(y, Interval(-2, 3)) == Interval(0, Inf)

        @test pow(y, @interval(2.1)) == Interval(0.0, 10.045108566305146)
    end

    # comment out for now:
    #=
    @testset "Float32 intervals" begin

        a = Interval{Float32}(1e38)
        b = Interval{Float32}(1e2)
        @test a * b == Interval{Float32}(realmax(Float32), Inf)
    end
    =#
end


#
# gram-tests.jl -
#
# Test Gram operator.
#
module TestingLazyAlgebraRules
using LazyAlgebra
using LazyAlgebra.Foundations
using Test

@testset "Gram operators" begin
    @test gram(Id) === Id
    rows = (3,4,)
    cols = (2,5,)
    T = Float32
    a = rand(T, rows..., cols...)
    A = GeneralMatrix(a)
    AtA = gram(A)
    @test isa(AtA, Gram)
    @test isa(A'*A, Gram)
    @test A'*A === AtA
    @test AtA' === AtA
    x = rand(T, cols)
    y = A'*(A*x)
    z = AtA*x
    @test z ≈ y
end

end # module


@testset "avoid overflow during add/subtract" begin
    x = 2*10^11
    @test FastRational(1, 2x) + FastRational(1, 3x) == FastRational(5, 6x)
    @test FastRational(1, 2x) - FastRational(1, 3x) == FastRational(1, 6x)
end

@testset "constructors1" begin
    @test FastRational(2,4).num == 1
    @test FastRational(2,4).den == 2
    @test FastRational((2,4)).num == 2
    @test FastRational((2,4)).den == 4
end

@testset "convert from Rational" begin
    @test FastRational(1//2) == FastRational(1, 2)
    @test FastRational(2, 4) == FastRational(1, 2)
end

@testset "conversion type" begin
    @test typeof(convert(FastRational{Int64,FastRationals.MayReduce}, FastRational(1,2))) ==
    FastRational{Int64,FastRationals.MayReduce}
end

@testset "inverse matrix" begin
    B = FastRational.([(1,2) (1,3); (1,3) (1,4)])
    @test inv(B) == FastRational.([(18,1) (-24,1); (-24,1) (36,1)])
end


"""
BVP_solve(n = 801, T = Float64; bfact=qr!)
Solve the BVP for the Chapter 2 figures and testing.
"""
function BVP_solve(n = 801, T = Float64; bfact = qr!)
    # Set it up
    bdata = bvpinit(n, T)
    #
    U0 = zeros(2n)
    FV = zeros(2n)
    # Banded matrix with the correct number of bands
    FPV = BandedMatrix{T}(Zeros(2n, 2n), (2, 4))
    #
    # Build the initial iterate
    #
    BVP_U0!(U0, n, bdata)
    #
    if bfact == qr!
        bvpout = nsol(
            Fbvp!,
            U0,
            FV,
            FPV,
            Jbvp!;
            rtol = 1.e-10,
            sham = 1,
            pdata = bdata,
            jfact = bfact,
        )
    else
        # Test for default of qr. Used for CI only.
        bvpout = nsol(Fbvp!, U0, FV, FPV, Jbvp!; rtol = 1.e-10, sham = 1, pdata = bdata)
    end
    return (bvpout = bvpout, tv = bdata.tv)
end

function BVP_U0!(U0, n, bdata)
    tv = bdata.tv
    view(U0, 1:2:2n-1) .= exp.(-.1 .* tv .* tv)
    view(U0, 2:2:2n) .= -.2 .* view(U0, 1:2:2n-1) .* tv
end


import FinanceLib.FixedIncomes.Bonds as Bd
import FinanceLib.FixedIncomes.Bonds.Rates as Rt
import FinanceLib as Fl

@testset "FinanceLib.FixedIncomes.Bonds                               " begin

    # @test Bd.priceCouponBond(Fl.PeriodSeries([(1, 0.09), (2, 0.10)]), 0.06) * 1000 == 931.0789294108725
    # @test Bd.priceCouponBond(Fl.PeriodSeries([(1, 0.09), (2, 0.10), (3, 0.11)]), 0.05) * 100 == 85.49448240486119

    @test Bd.priceCouponBond(0.03, Bd.CouponBond(0.05, 2, 3)) == 1.0569718716547531

    @test Bd.priceCouponBond(Fl.RateCurve{Fl.NomRate}([0.0016, 0.0021, 0.0027, 0.0033, 0.0037, 0.0041],2), Bd.CouponBond(0.05, 2, 3)) == 1.1369147941993403

    @test Bd.ytmCouponBond(Bd.CouponBond(0.05, 2, 3), 1.1369147941993403) == 0.004038639185260929

    # rS = Fl.PeriodSeries([(1, 0.05), (2, 0.06), (3, 0.07), (4, 0.08), (5, 0.09)])
    # bd = Bd.priceCouponBond(rS, 0.1)*100
    # @test bd == 105.42950371364294
    # @test Bd.ytmCouponBond(rS, 0.1) == 0.0861792633877846

    @testset "FinanceLib.FixedIncomes.Bonds.Rates                     " begin

      @test Rt.parToSpotRates(Fl.RateCurve{Fl.NomRate}([0.05, 0.0597, 0.0691, 0.0781], 1)).rate[4] ≈ 0.08002666689660898

      er = Rt.parToSpotRates(Fl.RateCurve{Fl.NomRate}([0.020000, 0.024000, 0.027600, 0.030840, 0.033756, 0.036380], 2)).rate

      er[1] ≈ 0.02
      er[4] ≈ 0.030974
      er[5] ≈ 0.033975
      er[6] ≈ 0.036701

      et = Rt.spotToParRates(Fl.RateCurve{Fl.NomRate}(er, 2)).rate

      @test et[3] ≈ 0.027600
      @test et[4] ≈ 0.030840
      @test et[6] ≈ 0.036380

    end

end


RealOrComplex = Union{Real,Complex}

## User should just use TransferFunction
immutable SisoZpk <: SisoTf
    z::Vector{Complex{Float64}}
    p::Vector{Complex{Float64}}
    k::Float64
    function SisoZpk(z::Vector{Complex{Float64}}, p::Vector{Complex{Float64}}, k::Float64)
        if k == zero(k)
            p = []
            z = []
        end
        new(z, p, k)
    end
end

SisoZpk{T<:RealOrComplex,S<:RealOrComplex}(z::AbstractArray{T}, p::AbstractArray{S}, k::Real) = SisoZpk(Complex128[z...], Complex128[p...], Float64(k))

# Taking care of empty vectors being of type Any
function SisoZpk(z::AbstractArray, p::AbstractArray, k::Real)
    if length(z) > 0
        if !(eltype(z) <: RealOrComplex)
            error("Zeros must be real or complex")
        end
    else
        z = Array(Float64,0)
    end
    if length(p) > 0
        if !(eltype(p) <: RealOrComplex)
            error("poles must be real or complex")
        end
    else
        p = Array(Float64,0)
    end
    SisoZpk(z, p, k)
end

function minreal(sys::SisoZpk, eps::Real)
    newZ = copy(sys.z)
    newP = Vector{Complex{Float64}}()
    doubles = Vector{Int64}()
    newZ = copy(sys.z)
    for  p in sys.p
        if !isempty(newZ)
            val, zi = findmin(abs(p-newZ))
        else
            val = Inf #Keep looping over p, adding poles
        end
        if val < eps
            deleteat!(newZ, zi)
            continue
        else
            push!(newP, p)
        end
    end
    SisoZpk(newZ, newP, sys.k)
end

function Base.num(t::SisoZpk)
    return copy(t.z)
end

function Base.den(t::SisoZpk)
    return copy(t.p)
end

tzero(sys::SisoZpk) = num(sys)
pole(sys::SisoZpk) = den(sys)

function zp2polys(vec)
    polys = Array{Poly{Float64},1}(0)
    polesiLeft = Set(1:length(vec))
    while length(polesiLeft) > 0
        p = vec[pop!(polesiLeft)]
        if abs(imag(p)) < sqrt(eps())
            push!(polys,Poly(float([1, -real(p)])))
        else
            polesiLeftVec = [i for i in polesiLeft]
            polesTest = Complex128[vec[polesiLeftVec]...]
            val, i = findmin(abs(polesTest-conj(p)))
            val > 2*sqrt(eps()) && error("Could not find conjugate to pole")
            push!(polys,Poly(float([1, -2*real(p), real(p)^2+imag(p)^2])))
            pop!(polesiLeft,polesiLeftVec[i])
        end
    end
    polys
end

function numpoly(G::SisoZpk)
    zpolys = zp2polys(G.z)
end

function denpoly(G::SisoZpk)
    ppolys = zp2polys(G.p)
end

function evalfr(sys::SisoZpk, s::Number)
    S = promote_type(typeof(s), Float64)
    den = reduce(*, (poly) -> polyval(poly, s), zp2polys(sys.p))
    if den == zero(S)
        return convert(S, Inf)
    else
        num = reduce(*, (poly) -> polyval(poly, s), zp2polys(sys.z))
        return sys.k*num/den
    end
end

function print_siso(io::IO, t::SisoZpk, var=:s)
    zpolys = zp2polys(t.z)
    ppolys = zp2polys(t.p)
    # Convert the numerator and denominator to strings
    if length(zpolys) < 2
        numstr = ( length(zpolys) == 0 ) ? "1.0" : sprint(print_poly, zpolys[1], var)
    else
        numstr = reduce(*,"",["("*sprint(print_poly, z, var)*")" for z in zpolys])
    end
    if length(ppolys) < 2
        denstr = ( length(ppolys) == 0 ) ? "1.0" : sprint(print_poly, ppolys[1], var)
    else
        denstr = reduce(*,"",["("*sprint(print_poly, p, var)*")" for p in ppolys])
    end
    # Figure out the length of the separating line
    len_num = length(numstr)
    len_den = length(denstr)
    dashcount = max(len_num, len_den)

    # Center the numerator or denominator
    if len_num < dashcount
        numstr = "$(repeat(" ", div(dashcount - len_num, 2)))$numstr"
    else
        denstr = "$(repeat(" ", div(dashcount - len_den, 2)))$denstr"
    end

    gainstr = string(t.k)
    #Add spaces to account for gain string
    numstr = " "^(length(gainstr))*numstr
    denstr = " "^(length(gainstr))*denstr
    println(io, numstr)
    println(io, gainstr*repeat("-", dashcount))
    println(io, denstr)
end

Base.promote_rule{T<:Real}(::Type{SisoZpk}, ::Type{T}) = SisoZpk
Base.convert(::Type{SisoZpk}, b::Real) = SisoZpk([], [], b)

Base.zero(::Type{SisoZpk}) = SisoZpk([],[],0.0)
Base.zero(::SisoZpk) = Base.zero(SisoZpk)

Base.length(t::SisoZpk) = max(length(t.z), length(t.p))

==(t1::SisoZpk, t2::SisoZpk) = (t1-t2).k == 0.0
function isapprox(t1::SisoZpk, t2::SisoZpk; rtol::Real=sqrt(eps()), atol::Real=sqrt(eps()))
    tdiff = t1 - t2
    isapprox(tdiff.k, 0, atol=atol, rtol=rtol)
end

function +(t1::SisoZpk, t2::SisoZpk)
  numPoly = t1.k*prod(zp2polys(t1.z))*prod(zp2polys(t2.p))+t2.k*prod(zp2polys(t2.z))*prod(zp2polys(t1.p))
  z = roots(numPoly)
  if length(numPoly) > 0
      k = numPoly[1]
      p = [t1.p;t2.p]
  else
      k = 0
      p = []
  end
  SisoZpk(z,p,k)
end

+(t::SisoZpk, n::Real) = t + SisoZpk([],[],n)
+(n::Real, t::SisoZpk) = SisoZpk([],[],n) + t
.+(t::SisoZpk, n::Real) = t + n
.+(n::Real, t::SisoZpk) = t + n

-(t1::SisoZpk, t2::SisoZpk) = +(t1,-t2)
-(n::Real, t::SisoZpk) = SisoZpk([],[],n) - t
-(t::SisoZpk, n::Real) = +(t, -n)
.-(t::SisoZpk, n::Real) = -(t, n)
.-(n::Real, t::SisoZpk) = -(n, t)

-(t::SisoZpk) = SisoZpk(t.z, t.p, -t.k)

*(t1::SisoZpk, t2::SisoZpk) = SisoZpk([t1.z;t2.z], [t1.p;t2.p], t1.k*t2.k)
*(t::SisoZpk, n::Real) = SisoZpk(t.z, t.p, t.k*n)
*(n::Real, t::SisoZpk) = *(t, n)
.*(t1::SisoZpk, t2::SisoZpk) = *(t1, t2)
.*(t::SisoZpk, n::Real) = *(t, n)
.*(n::Real, t::SisoZpk) = *(t, n)

/(n::Real, t::SisoZpk) = SisoZpk(t.p, t.z, n/t.k)
/(t::SisoZpk, n::Real) = SisoZpk(t.z, t.p, t.k/n)
/(t1::SisoZpk, t2::SisoZpk) = t1*(1/t2)


function AMFMdemod(ψ::Vector{ComplexF64}, t::Vector{Float64}; derivMethod="center11")::AMFMcompN
#References: Sandoval, Steven, and Phillip L. De Leon. "The Instantaneous Spectrum: A General Framework for Time-Frequency Analysis." IEEE Transactions on Signal Processing 66.21 (2018): 5679-5693.
  return AMFMcompN( abs.(ψ),
                    derivApprox(unwrap(angle.(ψ)),fs=1/(t[2]-t[1]), method=derivMethod),
                    real.(ψ),
                    imag.(ψ),
                    unwrap(angle.(ψ)),
                    t,
                    1/(t[2]-t[1]) )
end
function AMFMdemod(ψ::Vector{ComplexF64}, t::StepRangeLen; derivMethod="center11")::AMFMcompN
  return AMFMdemod(ψ::Vector{ComplexF64}, collect(t), derivMethod=derivMethod)
end

function derivApprox(x::Vector{Float64}; fs=1.0, method="center11")::Vector{Float64}
#References: http://www.holoborodko.com/pavel/numerical-methods/numerical-derivative/central-differences/
    if method == "forward"
        if length(x)<2; error("derivApprox:vector too short for selected method"); end
        x′ = append!([NaN],fs.*diff(x))
    end
    if method == "backward"
        if length(x)<2; error("derivApprox:vector too short for selected method"); end
        x′ = append!(fs.*diff(x),[NaN])
    end
    if method == "center3"
        if length(x)<3; error("derivApprox:vector too short for selected method"); end
        x′ = append!( append!([NaN], fs.*(x[3:end]-x[1:end-2])./2.0 ) ,[NaN])
    end
    if method == "center5"
        if length(x)<5; error("derivApprox:vector too short for selected method"); end
        x′ = append!( append!(NaN*ones(Float64,2), fs.*(x[1:end-4]-8.0.*x[2:end-3]+8.0.*x[4:end-1]-x[5:end])./12.0 ) ,NaN*ones(Float64,2))
    end
    if method == "center7"
        if length(x)<7; error("derivApprox:vector too short for selected method"); end
        x′ = append!( append!(NaN*ones(Float64,3), fs.*(-x[1:end-6]+ 9.0.*x[2:end-5]- 45.0.*x[3:end-4] +45.0.*x[5:end-2] -9.0.*x[6:end-1] + x[7:end]   )./60.0
 ) ,NaN*ones(Float64,3))
    end
    if method == "center9"
        if length(x)<9; error("derivApprox:vector too short for selected method"); end
        x′ = append!( append!(NaN*ones(Float64,4), fs.*(3.0.*x[1:end-8] - 32.0.*x[2:end-7] + 168.0.*x[3:end-6] -672.0.*x[4:end-5] + 672.0.*x[6:end-3] -168.0.*x[7:end-2] + 32.0.*x[8:end-1] -3.0.*x[9:end]  )./840.0
 ) ,NaN*ones(Float64,4))
    end
    if method == "center11"
        if length(x)<11; error("derivApprox:vector too short for selected method"); end
        x′ = append!( append!(NaN*ones(Float64,5), fs.*(-2.0.*x[1:end-10] +25.0.*x[2:end-9] -150.0.*x[3:end-8] +600.0.*x[4:end-7] -2100.0.*x[5:end-6] + 2100.0.*x[7:end-4]  -600.0.*x[8:end-3] +150.0.*x[9:end-2] -25.0.*x[10:end-1] +2.0.*x[11:end]  )./2520.0
 ) ,NaN*ones(Float64,5))
    end
    if method == "center13"
        if length(x)<13; error("derivApprox:vector too short for selected method"); end
        x′ = append!( append!(NaN*ones(Float64,6), fs.*(5.0.*x[1:end-12] - 72.0.*x[2:end-11] + 495.0.*x[3:end-10] -2200.0.*x[4:end-9]   +7425.0.*x[5:end-8]  -23760.0.*x[6:end-7]  +23760.0.*x[8:end-5]   + -7425.0.*x[9:end-4]   + 2200.0.*x[10:end-3] -495.0.*x[11:end-2] + 72.0.*x[12:end-1] -5.0.*x[13:end]  )./27720.0
 ) ,NaN*ones(Float64,6))
    end
    return x′
end


#=
For details, please visit:
https://computationalmindset.com/en/neural-networks/experiments-with-neural-odes-in-julia.html#exp2
=#

using Flux, DiffEqFlux, DifferentialEquations, Plots

math_law(u) = sin.(2. * u) + cos.(2. * u)

function true_ode(du,u,p,t)
    true_A = [-0.15 2.10; -2.10 -0.10]
    du .= ((math_law(u))'true_A)'
end

tbegin = 0.0
tend = 4.0
datasize = 51
t = range(tbegin,tend,length=datasize)
u0 = [2.5; 0.5]
tspan = (tbegin,tend)
trange = range(tbegin,tend,length=datasize)
prob = ODEProblem(true_ode, u0, tspan)
dataset_ts = Array(solve(prob, Tsit5(), saveat=trange))

dudt = Chain(u -> math_law(u),
             Dense(2, 50, tanh),
             Dense(50, 2))

reltol = 1e-7
abstol = 1e-9
n_ode = NeuralODE(dudt, tspan, Tsit5(), saveat=trange, reltol=reltol,abstol=abstol)
ps = Flux.params(n_ode.p)

function loss_n_ode()
  pred = n_ode(u0)
  loss = sum(abs2, dataset_ts[1,:] .- pred[1,:]) +
         sum(abs2, dataset_ts[2,:] .- pred[2,:])
  loss
end

n_epochs = 400
learning_rate = 0.01
data = Iterators.repeated((), n_epochs)
opt = ADAM(learning_rate)

cb = function ()
  loss = loss_n_ode()
  println("Loss: ", loss)
end

println();
cb()

Flux.train!(loss_n_ode, ps, data, opt, cb=cb)

pl = plot(
  trange,
  dataset_ts[1,:],
  linewidth=2, ls=:dash,
  title="Neural ODE for forecasting",
  xaxis="t",
  label="original timeseries x(t)",
  legend=:right)
display(pl)

pl = plot!(
  trange,
  dataset_ts[2,:],
  linewidth=2, ls=:dash,
  label="original timeseries y(t)")
display(pl)

pred = n_ode(u0)

pl = plot!(
  trange,
  pred[1,:],
  linewidth=1,
  label="predicted timeseries x(t)")
display(pl)

pl = plot!(
    trange,
    pred[2,:],
    linewidth=1,
    label="predicted timeseries y(t)")
display(pl)

tbegin_forecast = tend
tend_forecast = tbegin_forecast + 5.0
tspan_forecast = (tbegin_forecast, tend_forecast)
datasize_forecast = 351
trange_forecast = range(tspan_forecast[1], tspan_forecast[2], length=datasize_forecast)
u0_forecast = [dataset_ts[1,datasize], dataset_ts[2,datasize]]

n_ode_forecast = NeuralODE(
  n_ode.model, tspan_forecast;
  p=n_ode.p, saveat=trange_forecast, reltol=reltol, abstol=abstol)
forecast = n_ode_forecast(u0_forecast)

pl = plot!(
  trange_forecast,
  forecast[1,:],
  linewidth=1,
  label="forecast x(t)")
display(pl)

pl = plot!(
    trange_forecast,
    forecast[2,:],
    linewidth=1,
    label="forecast y(t)")
display(pl)


using BasicFWI
using jInv.Mesh
using jInv.Utils
using jInv.ForwardShare
using DelimitedFiles

# if nworkers()==1
	# addprocs(2)
# end

println("IM here")
#@everywhere begin
	using BasicFWI
	using jInv.Mesh
	using jInv.Utils
	using jInv.ForwardShare
#end

# setup model and attenuation function
nx = 164; nz = 80
domain = [0.0,10.0,0.0,4.0]
Mr = getRegularMesh(domain,[nx,nz])

# the slowness model
m = 3.0*ones(nx,nz);
m[20:70,10:32] .= 5.0; # velocity...
m = 1.0./(m.^2); # convert to slowness squared

# m = readdlm("SEGmodel2Dsalt.dat");
# m = m*1e-3;
# m = m';
# m = 1.0./m^2

# attenuation for BC
padx = 20; padz = 20
a    = 2.0;
xc = getCellCenteredGrid(Mr)
# gammaxL = a*(xc[:,1] .- xc[padx,1]).^2;
# gammaxL[padx+1:end,:] .= 0
# gammaxR = a*(xc[:,1] .- xc[end-padx+1,1]).^2
# gammaxR[1:end-padx,:] .= 0

# gammax = gammaxL + gammaxR

# gammaz = a*(xc[:,2] .- xc[end-padz+1,2]).^2
# gammaz[:,1:end-padz] .= 0

# gamma = gammax + gammaz

gamma = getABL(Mr,true,[padx;padz],a);

# parameters for the Helmholtz (units in km)
h = Mr.h;
n = [nx; nz;]
omega = 2*pi*[1.0;1.2;1.4;1.5;2.0;3.0]
nfreq = length(omega)

# generate sources
q = zeros(tuple(n.+1...)); q[padx+1:2:end-padx-1,1] .= 1e4
Q = sdiag(vec(q))
Q = Q[:,(LinearIndices(sum(Q,dims=2) .!= 0))[findall(sum(Q,dims=2) .!= 0)]]
nsrc = size(Q,2)

# receivers
p = zeros(tuple(n.+1...)); p[padx+1:end-padx-1,1] .= 1
P = sdiag(vec(p))
P = P[:,(LinearIndices(sum(P,dims=2) .!= 0))[findall(sum(P,dims=2) .!= 0)]]
nrec = size(P,2)

pFor  = getFWIparam(omega,gamma,Q,P,Mr)
pForp = getFWIparam(omega,gamma,Q,P,Mr,true)

# inversion mesh and forward mesh are the same here
M2Mp = ones(length(pForp))


int_rules_8_10 = @theory begin

    #= ::Subsection::Closed:: =#
    #= 8.10*Bessel*functions =#
    @apply_utils Antiderivative(BesselJ(1, ~(a') + ~(b') * ~x), ~x) => -(BesselJ(0, ~a + ~b * ~x)) / ~b <-- FreeQ([~a, ~b], ~x)
    @apply_utils Antiderivative(BesselJ(~n, ~(a') + ~(b') * ~x), ~x) => (-2 * BesselJ(~n - 1, ~a + ~b * ~x)) / ~b + Antiderivative(BesselJ(~n - 2, ~a + ~b * ~x), ~x) <-- FreeQ([~a, ~b], ~x) && IGtQ((~n - 1) / 2, 0)
    #= Antiderivative(BesselJ((~n),(~a')+(~b')*(~x)),~x) :=  (-1)^n*Antiderivative(BesselJ(-n,a+b*x),x) <-- FreeQ([a,b],x) && ILtQ(n,0) =#
    @apply_utils Antiderivative(BesselJ(~n, ~(a') + ~(b') * ~x), ~x) => ((~a + ~b * ~x) ^ (~n + 1) * HypergeometricPFQ([(~n + 1) / 2], [(~n + 3) / 2, ~n + 1], (-1 / 4) * (~a + ~b * ~x) ^ 2)) / (2 ^ ~n * ~b * Gamma(~n + 2)) <-- FreeQ([~a, ~b, ~n], ~x)
end

src/rules/8 Special functions/8.10 Bessel functions.jl


#Hypotrochoid

using Luxor

@png begin
    origin()
    background("grey15")
    sethue("antiquewhite")
    setline(1)
    p = hypotrochoid(100, 25, 55, :stroke, stepby=0.01, vertices=true)
    for i in 0:3:15
    Luxor.poly(offsetpoly(p, i), :stroke, close=true)
    end
end


using DataFrames
using RDatasets

function col_normalize(X::Array{Float64})
    xmin = minimum(X, dims=1)
    xmax = maximum(X, dims=1)
    for i in 1:size(X, 2)
        X[:, i] = (X[:, i] .- xmin[i]) ./ (xmax[i] - xmin[i])
    end
    Array{Float64}(X')
end

function make_x_y(data::DataFrame, seed::Int64)
    x_data = convert(Array{Float64}, data[1:(size(data, 2) - 1)])
    X = col_normalize(x_data)

    labels = unique(data[end])
    Y = convert(Array{Int64}, indexin(data[end], labels))

    rng = MersenneTwister(seed)
    inds = randperm(rng, length(Y))
    X[:, inds], Y[inds]
end

function get_iris(seed::Int64)
    make_x_y(dataset("datasets", "iris"), seed)
end

function get_diabetes(seed::Int64)
    make_x_y(vcat(dataset("MASS", "Pima.te"), dataset("MASS", "Pima.tr")),
             seed)
end

function get_glass(seed::Int64)
    make_x_y(dataset("MASS", "fgl"), seed)
end


module TestWiringDiagramExpressions

using Test

using Catlab.Theories, Catlab.Graphs, Catlab.WiringDiagrams
using Catlab.Syntax: syntax_module
using Catlab.WiringDiagrams.WiringDiagramExpressions: find_parallel,
  find_series, transitive_reduction!

# Expression -> Diagram
#######################

# Directed wiring diagrams
#-------------------------

A, B, C = Ob(FreeSymmetricMonoidalCategory, :A, :B, :C)
I = munit(FreeSymmetricMonoidalCategory.Ob)
f, g = Hom(:f,A,B), Hom(:g,B,C)

# Functorality of conversion.
fd, gd = to_wiring_diagram(f), to_wiring_diagram(g)
@test to_wiring_diagram(compose(f,g)) == compose(fd,gd)
@test to_wiring_diagram(otimes(f,g)) == otimes(fd,gd)
@test to_wiring_diagram(I) == munit(Ports)

# Undirected wiring diagrams
#---------------------------

const to_uwd = to_undirected_wiring_diagram
const HDOb = HypergraphDiagramOb{Symbol,Symbol}
const HDHom = HypergraphDiagramHom{Symbol,Symbol}

A, B, C = Ob(FreeHypergraphCategory, :A, :B, :C)
I = munit(FreeHypergraphCategory.Ob)
f, g = Hom(:f,A,B), Hom(:g,B,C)

# Generators.
@test to_uwd(A) == HDOb([:A])
@test to_uwd(f) == HDHom(singleton_diagram(HDOb([:A,:B]), name=:f),
                         dom=BitArray([1,0]))

# Functorality of conversion.
fd, gd = to_uwd(f), to_uwd(g)
@test to_uwd(compose(f,g)) == compose(fd,gd)
@test to_uwd(otimes(f,g)) == otimes(fd,gd)
@test to_uwd(I) == munit(HypergraphDiagramOb{Symbol,Symbol})

# Diagram -> Expression
#######################

roundtrip(A::ObExpr) = to_ob_expr(syntax_module(f), to_wiring_diagram(A))
roundtrip(f::HomExpr) = to_hom_expr(syntax_module(f), to_wiring_diagram(f))

# Symmetric monoidal category
#----------------------------

A, B, C, D = Ob(FreeSymmetricMonoidalCategory, :A, :B, :C, :D)
I = munit(FreeSymmetricMonoidalCategory.Ob)
f, g, h, k = Hom(:f,A,B), Hom(:g,B,C), Hom(:h,C,D), Hom(:k,D,C)

# Base case.
@test roundtrip(f) == f

# Series reduction.
@test roundtrip(compose(f,g)) == compose(f,g)
@test roundtrip(compose(f,g,h)) == compose(f,g,h)

# Parallel reduction.
@test roundtrip(otimes(f,g)) == otimes(f,g)
@test roundtrip(otimes(f,g,h)) == otimes(f,g,h)

# Series-parallel reduction.
@test roundtrip(otimes(compose(f,g),compose(h,k))) ==
  # roundtrip(compose(otimes(f,h),otimes(g,k))) ==
  otimes(compose(f,g),compose(h,k))
@test roundtrip(otimes(compose(f,g),h)) == otimes(compose(f,g),h)
@test roundtrip(otimes(f,compose(g,h))) == otimes(f,compose(g,h))

m = Hom(:m, otimes(B,B), otimes(C,C))
expr = compose(otimes(f,f),m,otimes(h,h))
@test roundtrip(expr) == expr

m, n = Hom(:m, A, otimes(A,B)), Hom(:n, otimes(B,C), C)
@test roundtrip(compose(m,otimes(f,g),n)) == compose(m,otimes(f,g),n)

m, n = Hom(:m, A, otimes(B,B)), Hom(:n, otimes(B,B), C)
expr = compose(otimes(m,f),otimes(g,n))
@test roundtrip(expr) == expr

m, n = Hom(:m, A, otimes(B,B)), Hom(:n, otimes(C,C), D)
l = Hom(:l, otimes(B,B), otimes(C,C))
expr = compose(otimes(m,m),otimes(g,l,g),otimes(n,n))
@test roundtrip(expr) == expr

# Transitive reduction (necessarily with series and/or parallel reduction).
@test roundtrip(otimes(f,id(C))) == otimes(f,id(C))
@test roundtrip(otimes(id(A),g)) == otimes(id(A),g)

m = Hom(:m, otimes(B,B), otimes(C,C))
expr = compose(otimes(f,id(B)),m,otimes(id(C),h))
@test roundtrip(expr) == expr

m = Hom(:m, otimes(B,B,B), C)
@test roundtrip(compose(otimes(f,id(otimes(B,B))),m)) ==
  compose(otimes(f,id(B),id(B)),m)

m, n = Hom(:m, A, otimes(A,B)), Hom(:n, otimes(B,B), C)
expr = compose(m,otimes(f,id(B)),n)
@test roundtrip(expr) == expr

m = Hom(:m, otimes(A,A), A)
expr = compose(otimes(m,id(A),id(A)), otimes(m,id(A)), m)
@test roundtrip(expr) == expr

m, n = Hom(:m, A, otimes(B,B)), Hom(:n, otimes(B,B), C)
expr = compose(otimes(m,id(B)), otimes(id(B),n))
@test roundtrip(expr) == expr
b = Hom(:b, B, B)
expr = compose(otimes(m,id(B)), otimes(id(B),b,id(B)), otimes(id(B),n))
@test roundtrip(expr) == expr

# Identities.
@test roundtrip(id(A)) == id(A)
@test roundtrip(otimes(id(A),id(B))) == id(otimes(A,B))

# Monoidal units.
@test roundtrip(Hom(:m,A,I)) == Hom(:m,A,I)
@test roundtrip(Hom(:m,I,B)) == Hom(:m,I,B)
@test roundtrip(Hom(:m,I,I)) == Hom(:m,I,I)
@test roundtrip(Hom(:m,I,B)⋅Hom(:n,B,I)) == Hom(:m,I,B)⋅Hom(:n,B,I)
@test roundtrip(Hom(:m,A,I)⋅Hom(:n,I,B)) == Hom(:m,A,I)⊗Hom(:n,I,B)
@test roundtrip(Hom(:m,I,I)⊗Hom(:n,I,I)) == Hom(:m,I,I)⊗Hom(:n,I,I)

# Monoidal units with transitive reduction.
@test roundtrip(Hom(:m,A,I)⊗id(B)) == Hom(:m,A,I)⊗id(B)
@test roundtrip(id(A)⊗Hom(:m,I,B)) == id(A)⊗Hom(:m,I,B)
@test roundtrip(id(A)⊗Hom(:m,I,I)) == id(A)⊗Hom(:m,I,I)

expr = compose(id(A)⊗Hom(:m,I,A), Hom(:l, A⊗A, B⊗B), Hom(:n,B,I)⊗id(B))
@test roundtrip(expr) == expr

# Braidings.
@test roundtrip(braid(A,B)) == braid(A,B)
@test roundtrip(otimes(id(A),braid(B,C))) == otimes(id(A),braid(B,C))

# Diagonals and codiagonals
#--------------------------

A, B, C = Ob(FreeBiproductCategory, :A, :B, :C)
f, g = Hom(:f,A,B), Hom(:g,B,C)

# Diagonals.
@test roundtrip(mcopy(A)) == mcopy(A)
@test roundtrip(delete(A)) == delete(A)
@test roundtrip(mcopy(A)⋅(mcopy(A)⊗id(A))) == mcopy(A)⋅(mcopy(A)⊗id(A))
@test roundtrip(mcopy(A)⋅(id(A)⊗mcopy(A))) == mcopy(A)⋅(mcopy(A) ⊗ id(A))
@test roundtrip(mcopy(A⊗B)) == (mcopy(A)⊗mcopy(B))⋅(id(A)⊗braid(A,B)⊗id(B))
@test roundtrip(delete(A⊗B)) == delete(A)⊗delete(B)

# Codiagonals.
@test roundtrip(mmerge(A)) == mmerge(A)
@test roundtrip(create(A)) == create(A)
@test roundtrip((mmerge(A)⊗id(A)⋅mmerge(A))) == (mmerge(A)⊗id(A))⋅mmerge(A)
@test roundtrip((id(A)⊗mmerge(A))⋅mmerge(A)) == (mmerge(A)⊗id(A))⋅mmerge(A)

# Compound morphisms.
@test roundtrip(compose(f,mcopy(B))) == compose(f,mcopy(B))
@test roundtrip(compose(mcopy(A),otimes(f,f))) == compose(mcopy(A),otimes(f,f))
@test roundtrip(compose(f,delete(B))) == compose(f,delete(B))
@test roundtrip(braid(A,B)⋅(mcopy(B)⊗mcopy(A))) == braid(A,B)⋅(mcopy(B)⊗mcopy(A))

# Dagger category
#----------------

A, B, C = Ob(FreeDaggerSymmetricMonoidalCategory, :A, :B, :C)
f, g = Hom(:f,A,B), Hom(:g,B,C)

@test roundtrip(dagger(f)) == dagger(f)
@test roundtrip(dagger(compose(f,g))) == compose(dagger(g),dagger(f))

# Compact closed category
#------------------------

A, B, C = Ob(FreeCompactClosedCategory, :A, :B, :C)
f, g = Hom(:f,A,B), Hom(:g,B,C)

# Duals.
@test roundtrip(dual(A)) == dual(A)
@test roundtrip(dual(otimes(A,B))) == otimes(dual(B),dual(A))

# Units and counits.
@test roundtrip(dunit(A)) == dunit(A)
@test roundtrip(dcounit(A)) == dcounit(A)
# TODO: The following expressions are correct, but it would be nice to return
# them in the commented simplified form.
@test roundtrip(dunit(otimes(A,B))) ==
  #dunit(B) ⋅ (id(dual(B)) ⊗ dunit(A) ⊗ id(B))
  (dunit(A) ⊗ dunit(B)) ⋅
    (id(dual(A))⊗braid(A,dual(B))⊗id(B)) ⋅ (braid(dual(A),dual(B))⊗id(A⊗B))
@test roundtrip(dcounit(otimes(A,B))) ==
  #(id(A) ⊗ dcounit(B) ⊗ id(dual(A))) ⋅ dcounit(A)
  (id(A⊗B)⊗braid(dual(B),dual(A))) ⋅ (id(A)⊗braid(B,dual(A))⊗id(dual(B))) ⋅
    (dcounit(A) ⊗ dcounit(B))

# Adjoint mates.
@test roundtrip(mate(f)) == mate(f)
@test roundtrip(mate(compose(f,g))) == compose(mate(g),mate(f))

# Graph operations
##################

# Parallel compositions in graph.
graph = Graph(6)
add_edges!(graph, [1,2,3,3,4,5], [2,3,4,5,6,6])
@test find_parallel(graph) == Dict((3 => 6) => [4,5])

# Series compositions in graph.
graph = Graph(13)
add_edges!(graph, [5,8,9,11,12], [6,9,10,12,13])
@test Set(find_series(graph)) == Set([[5,6],[8,9,10],[11,12,13]])

end


import LinearAlgebra: cholesky, diag, Diagonal, Hermitian
import Printf: @printf
import Random
import Statistics: mean, median, std
using Test
import RandomCorrelationMatrices

# This is not a numerical test. The code just generates some examples.
# However, if it fails to run, thats a good sign something is wrong.
Random.seed!(12345)
println("Random correlation matrix")
n = 4
for η in [2, 8, 32, 128]
    r = RandomCorrelationMatrices.randcormatrix(n, η)
    @show η
    for i in 1:n
        for j in 1:n
            @printf("%5.2f ", r[i,j])
        end
        println()
    end

    ranges = Float64[]
    for s in 1:1000
        r = RandomCorrelationMatrices.randcormatrix(n, η)
        r0 = r - Matrix(Diagonal((diag(r))))
        push!(ranges, maximum(vec(r0)) - minimum(vec(r0)))
    end
    @show mean(ranges)
    @show median(ranges)
end

@testset "Random covariance matrix" begin
    Random.seed!(12345)
    σ = [100, 200, 300, 400, 500]
    Σ = RandomCorrelationMatrices.randcovmatrix(5, 10, σ)
    L = cholesky(Hermitian(Σ)).L
    samples = 100_000
    results = zeros(5, samples)
    for i in 1:samples
        results[:,i] = L*randn(5)
    end
    means = mean(results, dims=2)
    @test all(abs.(means) .<= 5)
    stddevs = std(results, dims=2)
    @test all(abs.(stddevs - σ) .<= 5)
    @show σ
    @show round.(stddevs, digits=1)
end


using PolygonMinkowskiSum
using Test

using GeometryTypes: Point
using PolygonMinkowskiSum: SimplePolygon, cross, find_ccw_violations

sweepme = SimplePolygon{Array{Point{2,Float64},1}}(Point{2,Float64}[[-0.35, -0.35], [0.35, -0.35], [0.35, 0.35], [-0.35, 0.35]])
alongme = SimplePolygon{Array{Point{2,Float64},1}}(Point{2,Float64}[[0.19857737682165164, 0.0], [0.19857737682165164, 0.14427490936067594], [0.06452170095561843, 0.19857737682165164], [-0.06452170095561849, 0.19857737682165164], [-0.19857737682165166, 0.1442749093606759], [-0.19857737682165166, 0.0], [-0.19857737682165166, -0.14427490936067594], [-0.06452170095561849, -0.19857737682165166], [0.06452170095561843, -0.19857737682165166], [0.19857737682165164, -0.144274909360676]])

@test find_ccw_violations(sweepme) == []
@test find_ccw_violations(alongme) == []

result = minkowski_sum(sweepme, alongme)

@test find_ccw_violations(result) == []


using PooksoftOptionsKit
using DataFrames
using CSV
using Dates

function load_actual_option_price_array(path_to_data_file::String, type::Symbol)::Array{Float64,2}

    # load -
    full_data_frame = CSV.read(path_to_data_file)

    # ok, so we need to extract the reqd cols -
    strike = Symbol("Strike")
    bid = Symbol("Bid")
    ask = Symbol("Ask")

    # find rows of type -
    idx_type = findall(x->x==String(type), full_data_frame[!,:Type])
    number_of_type_rows = length(idx_type)
    data_array = zeros(number_of_type_rows, 3)
    for (index, type_index) in enumerate(idx_type)

        data_array[index,1] = full_data_frame[type_index, strike]
        data_array[index,2] = full_data_frame[type_index, bid]
        data_array[index,3] = full_data_frame[type_index, ask]
    end

    # return -
    return data_array
end

function estimated_options_price_array()

    # setup the call test -
    stock_price = 54.17
    implied_volatility = 0.67
    DTE = (31.0/365.0)
    number_of_levels = 12
    strike_price_array = collect(range(45.0, step=1.0, stop=80.0))
    number_of_strike_prices = length(strike_price_array)
    risk_free_rate = 0.15 # this is in percent
    dividend_rate = 0.0 # this is in percent

    # initialize -
    estimated_price_array = zeros(number_of_strike_prices,2)

    # setup options calculation -
    amd_option_parameters = PSOptionKitPricingParameters(implied_volatility, DTE, number_of_levels, risk_free_rate, dividend_rate)

    # compute -
    for (index,strike_price) in enumerate(strike_price_array)

        # initialize -
        assetSet = Set{PSAbstractAsset}()

        # setup an amd call option -
        callOptionContract = PSCallOptionContract("AMD", Date(2020,6,25), strike_price, 0.0, 1; sense=:buy, contractMultiplier=1.0)
        push!(assetSet, callOptionContract)

        # build the pricing tree -
        tree = build_ternary_price_tree(assetSet, amd_option_parameters, stock_price)

        # compute -
        result = option_contract_price(tree, amd_option_parameters)

        # grab - we should check the return type ... but for now ...
        estimated_price_array[index,1] = strike_price
        estimated_price_array[index,2] = result.value
    end

    return estimated_price_array
end

# compute the estimated prices -
estimated_price_array = estimated_options_price_array()

# load the data -
path_to_data_file = "$(pwd())/tests/data/AMD-31D.csv"
actual_price_array = load_actual_option_price_array(path_to_data_file, :Call)


include("dwt.jl")
include("audiodwt.jl")
include("audiodct.jl")
using WAV
using LinearAlgebra
using Plots

# Read song, Am I Blue
# Sung by Kevin Conroy, listen for free at http://www.realkevinconroy.com/
filename = "Am I Blue.wav"
channels, Fs, nbits, opt = wavread(filename)

# Generate wavelet filters
ps = 2:6
ls = []
hs = []
ils = []
ihs = []
for p = ps
    l, h, il, ih = filtergen(p)
    push!(ls, l)
    push!(hs, h)
    push!(ils, il)
    push!(ihs, ih)
end

# Set thresholds
thresholds = 10 .^(-4:.2:-1)
wsizes = zeros(length(thresholds), length(ps))
fsizes = zeros(length(thresholds))
werrors = zeros(length(thresholds), length(ps))
ferrors = zeros(length(thresholds))

for i = 1:length(thresholds)
    threshold = thresholds[i]
    for j = 1:length(ps)
        # Fetch wavelet filters
        l = ls[j]
        h = hs[j]
        il = ils[j]
        ih = ihs[j]
        p = ps[j]

        # Compress DWT
        wcompression, wsizes[i,j] = compressaudio(channels, p, l, h, threshold)

        # Decompress DWT
        wrecovered = decompressaudio(wcompression, p, il, ih)
        werrors[i,j] = norm(wrecovered[:] - channels[:]) / norm(channels[:])
    end

    # Compress DCT
    fcompression, fsizes[i] = compressaudiodct(channels, threshold)

    # Decompress DCT
    frecovered = decompressaudiodct(fcompression)
    ferrors[i] = norm(frecovered[:] - channels[:]) / norm(channels[:])
end

# Plot
x = 100 / sizeof(channels)
plot(werrors[:,1], wsizes[:,1] .* x, label=string("DAUB", 2ps[1]));
for i = 2:length(ps)
    p = ps[i]
    plot!(werrors[:,i], wsizes[:,i] .* x, label=string("DAUB", 2p));
end
plot!(ferrors, fsizes .* x, label="DCT");
title!("Comparison of Compression Accuracy of DWT and DCT");
xaxis!("Relative Root Mean Squared Error of Reconstruction", :log10);
yaxis!("Percentage of original file size (%)")


export StackOfStarsTrajectory, stackOfStarsNodes, stackOfStarsDensity

"""
    StackOfStarsTrajectory(numProfiles, numSamplingPerProfile
                  ; TE::Float64=0.0
                  , AQ::Float64=1.e-3
                  , numSlices=1
                  , angleOffset= :equispaced
                  , kargs...)

returns a 2d radial trajectory.

# Arguments
* `numProfiles::Int64`            - number of profiles
* `numSamplingPerProfile::Int64`  - number of sampling points per profile
* (`TE::Float64=0.0`)             - echo time in s
* (`AQ::Float64=1.e-3`)           - readout duration in s (per profile)
* (`numSlices=1`)                 - number of slices
* (`angleOffset= :equispaced`)    - spacing of profile angles (`:equispaced` sampling, `:golden` angle sampling or `:random` sampling)
"""
function StackOfStarsTrajectory(::Type{T}, numProfiles, numSamplingPerProfile
                  ; TE=0.0
                  , AQ=1.e-3
                  , numSlices=1
                  , angleOffset = :equispaced
                  , kargs...) where T
  nodes = cartesian3dNodes(T, numProfiles, numSamplingPerProfile; numSlices=numSlices, angleOffset = :equispaced)
  times = readoutTimes(T, numProfiles, numSamplingPerProfile, numSlices; TE=TE, AQ=AQ)
  return  Trajectory("StackOfStars", nodes, times, TE, AQ, numProfiles, numSamplingPerProfile, numSlices, false, true)
end

function stackOfStarsNodes(::Type{T}, numProfiles, numSamplingPerProfile
              ; numSlices=1, angleOffset = :equispaced, kargs...) where T

  angles = collect(pi.*(0:numProfiles-1)/numProfiles)
  nodes = zeros(T, 3, numSamplingPerProfile, numProfiles, numSlices)
  if angleOffset == :golden
    angles = [i*getGoldenAngleRad()  for i=0:numProfiles-1 ]
  elseif angleOffset == :random
    angles = sort(pi .* rand(numProfiles))
  elseif angleOffset == :equispaced
    angles = collect(pi.*(0:numProfiles-1)/numProfiles)
  end

  pos = collect((0:numSamplingPerProfile-1)/numSamplingPerProfile .- 0.5)
  posZ = collect((0:numSlices-1)/numSlices .- 0.5)
  for j = 1:numSlices
    for l = 1:numProfiles
      for k = 1:numSamplingPerProfile
        nodes[1,k,l,j] = (-1)^l * pos[k]*cos(angles[l])
        nodes[2,k,l,j] = (-1)^l * pos[k]*sin(angles[l])
        nodes[3,k,l,j] = posZ[j]
      end
    end
  end
  return reshape(nodes, 3, numSamplingPerProfile*numProfiles*numSlices)
end

function stackOfStarsDensity(::Type{T}, numSamplingPerProfile::Int64, numProfiles::Int64, numSlices::Int64) where T
  density = zeros(T, numSamplingPerProfile, numProfiles, numSlices)
  pos = collect((0:numSamplingPerProfile-1)/numSamplingPerProfile .- 0.5)
  for j= 1:numSlices
    for l = 1:numProfiles
      for k = 1:numSamplingPerProfile
        density[k,l,j] = abs(pos[k])
      end
    end
  end
  return vec(density)
end


using Distributions, Random

function simulateLR(;Nd = 4, Nc = 1,β0 = 1., β = fill(.5, Nc), σ = 1, kwargs...)
    x = rand(Normal(10,5),Nd,Nc)
    y = β0 .+ x*β .+ rand(Normal(0,σ),Nd)
    return (x=x, y=y, Nd=Nd, Nc=Nc)
 end


include("failure_model.jl")

###
###  Calibrate: Ensemble Kalman Inversion
###
N_ens = 50
N_iter = 20 # number of UKI iterations
initial_par = construct_initial_ensemble(priors, N_ens; rng_seed)
ekiobj = EnsembleKalmanProcess(initial_par, truth_sample, truth.obs_noise_cov, Inversion(), Δt = 0.1)

# UKI iterations
err = zeros(N_iter)
for i in 1:N_iter

    params_i = get_u_final(ekiobj)

    g_ens = run_G_ensemble(params_i)

    # analysis step
    EnsembleKalmanProcessModule.update_ensemble!(ekiobj, g_ens)

    err[i] = get_error(ekiobj)[end] #mean((params_true - mean(params_i,dims=2)).^2)
    println(
        "Iteration: " *
        string(i) *
        ", Error: " *
        string(err[i]),
    )
end

println("True parameters: ")
println(params_true)

println("\nEKI results:")
println(mean(get_u_final(ekiobj), dims = 2))

####
N_θ = length(param_names)
θ_mean_arr = zeros(Float64, (N_θ, N_iter + 1))
θθ_std_arr = zeros(Float64, (N_θ, N_iter + 1))
for i in 1:(N_iter + 1)
    θ_mean_arr[:, i] = mean(get_u(ekiobj, i), dims = 2)
    θ_cov = cov(get_u(ekiobj, i), dims = 2)
    for j in 1:N_θ
        θθ_std_arr[j, i] = sqrt(θ_cov[j, j])
    end
end

ites = Array(LinRange(1, N_iter + 1, N_iter + 1))
plot(ites, grid = false, θ_mean_arr[1, :], yerror = 3.0 * θθ_std_arr[1, :], label = "θ1")
plot!(ites, fill(params_true[1], N_iter + 1), linestyle = :dash, linecolor = :grey, label = nothing)

plot!(ites, grid = false, θ_mean_arr[2, :], yerror = 3.0 * θθ_std_arr[2, :], label = "θ2", xaxis = "Iterations")
plot!(ites, fill(params_true[2], N_iter + 1), linestyle = :dash, linecolor = :grey, label = nothing)

#plots
gr(size = (600, 600))
xlims = extrema(append!(hcat([get_u(ekiobj, i) for i=1:N_iter+1]...)[1,:], params_true[1]))
ylims = extrema(append!(hcat([get_u(ekiobj, i) for i=1:N_iter+1]...)[2,:], params_true[2]))
u_init = get_u_prior(ekiobj)
for i in 1:N_iter
    u_i = get_u(ekiobj, i)

    p1 = plot(u_i[1, :], u_i[2, :], seriestype = :scatter, xlims = xlims, ylims = ylims)
    plot!(
        p1,
        [params_true[1]],
        xaxis = "θ1",
        yaxis = "θ2",
        seriestype = "vline",
        linestyle = :dash,
        linecolor = :red,
        label = false,
        title = "EKI iteration = " * string(i),
    )
    plot!(p1, [params_true[2]], seriestype = "hline", linestyle = :dash, linecolor = :red, label = "optimum")
    display(p1)
    sleep(0.5)
end


export rsvd_fnkz

immutable OuterProduct{T}
    X :: Matrix{T}
    Y :: Matrix{T}
end

X ⊗ Y = OuterProduct{promote_type(eltype(X), eltype(Y))}(X, Y)

@doc """
Compute the randomized SVD by iterative refinement from randomly selected
columns/rows.

TODO: Currently works only on tall and skinny A.

Arguments:

    A: Matrix whose SVD is desired
    k: Desired rank of approximation. Must be k ≤ min(m, n).
    l: Number of columns/rows to sample at each iteration.
       Must be 1 ≤ l ≤ k. Default: k
    N: Maximum number of iterations. Default: min(size(A))
    ϵ: Relative threshold for convergence, as measured by growth of the spectral norm.
       Default: prod(size(A))*eps(real(float(one(eltype(A)))))
    method: :eig - Solve eigenproblem (Default)
            :svd - Solve singular problem
    verbose: If true, prints convergence information at each iteration. Default: false

Returns:

    S: An SVD object of rank ≤ k.

Reference:

    @inproceedings{,
        author={Friedland, S. and Niknejad, A. and Kaveh, Mostafa and Zare, H.},
        booktitle={System of Systems Engineering, 2006 IEEE/SMC International Conference on},
        title={Fast Monte-Carlo low rank approximations for matrices},
        year={2006},
        month={April},
        pages={218--223},
        doi={10.1109/SYSOSE.2006.1652299}
    }
""" ->
function rsvd_fnkz(A, k::Int;
    l::Int=k, N::Int=minimum(size(A)), verbose::Bool=false,
    method::Symbol=:eig,
    ϵ::Real=prod(size(A))*eps(real(float(one(eltype(A))))))

    const m, n = size(A)
    const dosvd = method == :svd
    @assert 1 ≤ l ≤ k ≤ min(m, n)

    #Initialize k-rank approximation B₀ according to (III.9)
    const tallandskinny = m ≥ n
    B₀ = if tallandskinny
        #Select k columns of A
        π = randperm(n)[1:k]
        A[:, π]
    else
        warn("Not implemented")
        #Select k rows of A
        π = randperm(m)[1:k]
        A[π, :]
    end
    X, RRR = qr(B₀)
    X = X[:, abs(diag(RRR)) .> ϵ] #Remove linear dependent columns
    B = tallandskinny ? X ⊗ A'X : X ⊗ X'A

    oldnrmB = 0.0
    Λ = 0
    X̃ = 0
    V = 0
    for t=1:N
        π = randperm(k)[1:l]
        #Update B using Theorem 2.4
        X, RRR = qr([B.X A[:, π]]) #We are doing more work than needed here
        X = X[:, abs(diag(RRR)) .> ϵ] #Remove linearly dependent columns
        Y = A'X
        if dosvd
            S = svdfact!(Y)
            Λ = S[:S].^2
            B = (X * S[:V]) ⊗ (S[:U]*Diagonal(S[:S]))
        else
            E = eigfact!(Symmetric(Y'Y))
            π = sortperm(E[:values], rev=true)[1:min(length(E[:values]), k)]
            Λ = E[:values][π]
            O = E[:vectors][:, π] #Eq. 2.6
            X̃ = X * O
            B = X̃ ⊗ A'X̃
        end

        length(Λ)==0 && break

        nrmB = √Λ[1]
        verbose && println("iteration $t: Norm: $nrmB")
        oldnrmB > (1-ϵ)*nrmB && break
        oldnrmB = nrmB
    end
    for i=1:size(B.Y, 2)
        scale!(slice(B.Y, :, i), 1/√Λ[i])
    end
    Base.LinAlg.SVD(B.X, √Λ, B.Y')
end


import SoftSurfaceDynamics

msh = SoftSurfaceDynamics.unitsphere(3)

normals = SoftSurfaceDynamics.normals(msh.vertices,msh.faces)
curvatures = SoftSurfaceDynamics.meancurvatures(msh.vertices,msh.faces)
vareas = SoftSurfaceDynamics.vertexareas(msh.vertices,msh.faces)

volume = SoftSurfaceDynamics.surfacevolume(msh.vertices,msh.faces)


cfl = 0.45
c_omp = 100.0 #skin depth in cell units
delgam = 1.0e-3
Nx = 400
Rm = 5
Nt = 25000

dx = 1/c_omp
dt = cfl*dx

Lx = Nx*dx
lambda = Lx/Rm
lambda_per_cell = lambda/dx

vth = sqrt(delgam)
deb = vth
deb_per_cell = deb/dx

khat = (2pi/lambda)*vth

T = Nt*dt

println("dx                   : $dx")
println("dt                   : $dt")
println("cells per skin depth : $c_omp")
println("thermal velocity     : $vth")
println("Debye length         : $deb")
println("debye per cell       : $deb_per_cell")

println("modes                : $Rm")
println("lambda               : $lambda")
println("lambda per cell      : $lambda_per_cell")
println("khat                 : $khat")

println("Lx                   : $Lx")
println("Nx                   : $Nx")
println("T                    : $T")
println("Nt                   : $Nt")

ompr = 1 + (3/2)*khat^2 + (15/8)*khat^4 + (147/16)*khat^6
ompi = -0.5*sqrt(pi/2)*(1/khat^3 - 6*khat)*exp(-0.5/khat^2 - 3/2 -3*khat^2 -12*khat^4)

println("omp Re: $ompr")
println("omp Im: $ompi")


# a simple LP example
#
#   maximize x + y
#
#   s.t. 50 x + 24 y <= 2400
#        30 x + 33 y <= 2100
#        x >= 45, y >= 5
#
#   solution: x = 45, y = 6.25, objv = 51.25

using Gurobi

env = Gurobi.Env()
setparams!(env, Method=2)  # using barrier method

method = getparam(env, "Method")
println("method = $method")

model = Gurobi.Model(env, "lp_01", :maximize)

# add variables
add_cvars!(model, [1., 1.], [45., 5.], Inf)
update_model!(model)

# add constraints
add_constrs!(model, Cint[1, 3], Cint[1, 2, 1, 2],
    [50., 24., 30., 33.], '<', [2400., 2100.])
update_model!(model)

println(model)

# perform optimization
optimize(model)

# show results
info = get_optiminfo(model)
println()
println(info)

sol = get_solution(model)
println("soln = $(sol)")

objv = get_objval(model)
println("objv = $(objv)")

gc()  # test finalizers


abstract type Smoothers end
struct RTTSmoother <: Smoothers end
RauchTungStriebel() = RTTSmoother()
struct MBFmoother <: Smoothers end
ModifiedBrysonFrazier() = MBFmoother()

## Rauch Tung Striebel
struct RTSSmoother
	xk::Vector
	P::AbstractMatrix
end

function kalman_smooth(
	s::RTTSmoother,
	x₀::Vector,
	xk::Vector,
	xkp1::Vector,
	Pk::AbstractMatrix,
	Pkp1::AbstractMatrix,
	F::AbstractMatrix,
	)
	A = similar(F)
	mul!(A, Pk, F')
	F = qr(Pkp1)
	A = _return_type(Pk, F.R\(F.q' * A))
	P = Pkp1 .- Pk
    P = Pk .+ _return_type(Pk, A*P*A')
	x̂ = x₀ .- xkp1
	x̂ .= muladd(A, x̂, xk)
    return RTSSmoother(vec(x̂), P)
end

## Modified Bryson–Frazier smoother
struct MBFSmoother
	xk::Vector
	P::AbstractMatrix
	L::AbstractMatrix
	l::Vector
end

function kalman_smooth(
	s::MBFmoother,
	xk::Vector,
	Pk::AbstractMatrix,
	K::AbstractMatrix,
	F::AbstractMatrix,
	H::AbstractMatrix,
	Λ::AbstractMatrix,
	λ::Vector,
	S::AbstractMatrix,
	y::Vector,
	)
	A = _return_type(Pk, muladd(-K, H, eye(length(xk))))
	Aᵀ, Fᵀ = A', F'
	Sqr = qr(S)
	SH = Sqr.R \ (Sqr.Q' * H')
	λ̂ = mul!(similar(λ), Fᵀ, λ)
	Λ̂ = Fᵀ*Λ*F
	x̂ = muladd(Pk, -λ̂, xk)
	P = _return_type(Pk, Pk .- Pk*Λ̂*Pk)
	Λ̂ = muladd(SH, H, Aᵀ*Λ̂*A)
	λ̂ = muladd(Aᵀ, λ̂, -SH*y)
	return MBFSmoother(vec(x̂), P, Λ̂, vec(λ̂))
end


using LinearAlgebraicRepresentation
LAR = LinearAlgebraicRepresentation
using ViewerGL
GL = ViewerGL

function randomshapes(n::Int=100)
	V, EV = LAR.randomcuboids(n, .4)
	V = GL.normalize2(V, flag=true)
	GL.VIEW([ GL.GLLines(V, EV, GL.COLORS[1]) ])

	W, EW = LAR.fraglines(1.5,1.5,1.5)((V,EV))
	GL.VIEW([ GL.GLLines(W, EW, GL.COLORS[1]) ])

	model = Lar.Model(V)
	Lar.addModelCells!(model, 1, EV)
	model = Lar.Arrangement.planar_arrangement(model)

	FVs = Lar.triangulate2D(model)
	GL.VIEW(GL.GLExplode(model.G, FVs, 1.2,1.2,1.2,99,1));
	GL.VIEW(GL.GLExplode(model.G, FVs, 1,1,1,99,1));
end


#=
Copyright (c) 2018-2022 Chris Coey, Lea Kapelevich, and contributors

This Julia package Hypatia.jl is released under the MIT license; see LICENSE
file in the root directory or at https://github.com/chriscoey/Hypatia.jl

for variables X in R^{n, m} and Y in S^n:
maximize    tr(C*X) :
subject to  Y - X*X' in S^n_+
            Y_ij = P_ij for (i, j) in Omega
where Omega is a set of fixed indices and P is a random PSD matrix

the nonlinear constraint Y - X*X' in S^n_+ is equivalent to
the conic constraint (Y, 0.5, X) in MatrixEpiPerSquareCone(),
and also to the larger conic constraint [I X'; X Y] in S^{n + m}_+

simple case of nonlinfear matrix inequality example from Lectures on Modern
Convex Optimization by Aharon Ben-Tal and Arkadi Nemirovski, pg 154
=#

using SparseArrays

struct MatrixQuadraticJuMP{T <: Real} <: ExampleInstanceJuMP{T}
    num_rows::Int
    num_cols::Int
    use_matrixepipersquare::Bool # use matrixepipersquare cone, else PSD cone
end

function build(inst::MatrixQuadraticJuMP{T}) where {T <: Float64}
    (num_rows, num_cols) = (inst.num_rows, inst.num_cols)
    C = randn(num_cols, num_rows)
    P = randn(num_rows, num_rows)
    P = Symmetric(P * P')
    (row_idxs, col_idxs, _) = findnz(tril!(sprand(Bool, num_rows,
        num_rows, inv(sqrt(num_rows)))) + I)

    model = JuMP.Model()
    JuMP.@variable(model, X[1:num_rows, 1:num_cols])
    JuMP.@variable(model, Y[1:num_rows, 1:num_rows], Symmetric)
    JuMP.@objective(model, Max, tr(C * X))
    JuMP.@constraint(model, [(row, col) in zip(row_idxs, col_idxs)],
        Y[row, col] == P[row, col])

    if inst.use_matrixepipersquare
        U_svec = zeros(JuMP.GenericAffExpr{T, JuMP.VariableRef},
            Cones.svec_length(num_rows))
        U_svec = Cones.smat_to_svec!(U_svec, 1.0 * Y, sqrt(2))
        JuMP.@constraint(model, vcat(U_svec, 0.5, vec(X)) in
            Hypatia.MatrixEpiPerSquareCone{T, T}(num_rows, num_cols))
    else
        JuMP.@constraint(model, Symmetric(
            [Matrix(I, num_cols, num_cols) X'; X Y]) in JuMP.PSDCone())
    end

    return model
end


using Yao
using Yao.Intrinsics
using Compat

######################### CONTINUOUS ################################
uniform_state(num_bit) = register(ones(Complex128, 1<<num_bit)/sqrt(1<<num_bit))

######################### PEAKS ################################
"""
GHZ wave function.
"""
function ghz(num_bit::Int; x::Int=0)
    v = zeros(ComplexF64, 1<<num_bit)
    v[x+1] = 1/sqrt(2)
    v[flip(x, bmask(1:num_bit))+1] = 1/sqrt(2)
    return v
end

"""
onehot wave function.
"""
function onehot(num_bit::Int, x::Int)
    v = zeros(ComplexF64, 1<<num_bit)
    v[x+1] = 1
    return v
end


# include("simulate.jl")

using ForwardDiff
using FiniteDifferences
using LazyArrays

## helper functions for matrix derivatives
function kronSum(A,B)
  # kronecker sum of 2 arrays
  out = kronProd(A, Matrix(I,(size(B)))) + kronProd(Matrix(I,(size(A))),B)
  return out
end

function kronProd(A,B)
    K = ApplyArray(kron,A[:,:],B[:,:]);
    out = Matrix{Float64}(undef,size(K)...); copyto!(out,K);
    return out
end

function createCommMat(M)
  # commutation matrix
  r,m = size(M);
  K = zeros(m*r,m*r);
  for i=1:r
      for j=1:m
          ei = 1.0I(r)[:,i] # ith-canonical unit vector of dimension r
          ej = 1.0I(m)[:,j] # jth-canonical unit vector of dimension m
          K += kronProd(ei*permutedims(ej), ej*permutedims(ei))
      end
  end
  return K
end

## main function for linearize
function linearize(xv,uv,j,GravityInInertial)
    function Mdiff1(x)
        # First derivatives of mass matrix and powers (1/2, -1/2, -1)
        M = assembleM(x,j)
        Ms = real(sqrt(M))
        M_inv = inv(M)
        Ms_inv = real(sqrt(M_inv))

        dM = ForwardDiff.jacobian(z -> assembleM(z,j),x)
        dM_inv = -(kronProd(permutedims(M_inv),M_inv))*dM;
        dMs = inv(kronSum(permutedims(Ms), Ms))*dM
        dMs_inv = -kronProd(permutedims(Ms_inv),Ms_inv)*(dMs);
        return M, Ms, Ms_inv, dM, dMs, dMs_inv, M_inv, dM_inv
    end
    M, Ms, Ms_inv, dM, dMs, dMs_inv, M_inv, dM_inv = Mdiff1(xv);

    function Adiff1(x)
        A, _ = Ab_VecOfMat(x,j);
        dA = ForwardDiff.jacobian(z->Ab_VecOfMat(z,j)[1],x);
        return A, dA
    end
    A, dA = Adiff1(xv);

    function bdiff1(x)
        _, b = Ab_VecOfMat(x,j);
        db = ForwardDiff.jacobian(z->Ab_VecOfMat(z,j)[2],x);
        return b, db
    end
    b, db = bdiff1(xv);

    function Gpdiff1(x)
        function t1diff1(x)
            # t1 = A'*(AM)^{-1}*A';
            sz_A1 = size(A,1)
            t1 = permutedims(A)*inv(A*inv(M)*permutedims(A))

            dA_tr = createCommMat(A)*dA;

            Y = A*M_inv*permutedims(A); invY = inv(Y);
            dY1 = kronProd(A*M_inv,1.0I(sz_A1))*dA;
            dY2 = kronProd(A,A)*dM_inv;
            dY3 = kronProd(1.0I(sz_A1), A*M_inv)*dA_tr;
            dY = dY1 + dY2 + dY3;
            dY_inv = -kronProd(permutedims(invY),invY)*dY;

            dt1_1 = kronProd(permutedims(invY),1.0I(size(A,2)))*dA_tr;
            dt1_2 = kronProd(1.0I(sz_A1),permutedims(A))*dY_inv;
            dt1 = dt1_1 + dt1_2;
            return t1, dt1
        end
        G = A*Ms_inv;
        Gp = permutedims(G)*((G*permutedims(G))\I(size(G,1)))

        t1, dt1 = t1diff1(x)
        dGp1 = kronProd(permutedims(t1),1.0I(size(M,1)))*dMs_inv

        sz_t2 =size(t1,2)
        t2 = kronProd(1.0I(sz_t2), Ms_inv)
        dGp2 = t2*dt1;

        dGp = dGp1 + dGp2
    return Gp, dGp
    end
    Gp, dGp = Gpdiff1(xv);

    function Fudiff1(x,u)
        Fu = assembleF(x,u,j,g);
        dFu = ForwardDiff.jacobian(y->assembleF(y,u,j,g),x);
        dFu_u = ForwardDiff.jacobian(y->assembleF(x,y,j,g),u);
        return Fu, dFu, dFu_u
    end
    Fu, dFu, dFu_u = Fudiff1(xv,uv);

    function hdiff1(x,u)
        # has derivatives wrt both x,u
        h = b - A*inv(M)*Fu;

        # dh
        term1_1 = kronProd(permutedims(M_inv*Fu), 1.0I(size(A,1)));
        term1 = term1_1*dA;
        term2 = kronProd(permutedims(Fu), A)*dM_inv;
        term3 = A*M_inv*dFu;
        dh_x = db - (term1 + term2 + term3);

        # dh_u
        dh_u = -A*M_inv*dFu_u;

        return h, dh_x, dh_u
    end
    h, dh_x, dh_u = hdiff1(xv,uv);

    function Fcdiff1(x,u)
        Fc = Ms*Gp*h

        dFc1 = kronProd(permutedims(Gp*h),I(size(M,1)))*dMs
        dFc2 = kronProd(permutedims(h),Ms)*dGp
        dFc3 = Ms*Gp*dh_x;

        dFc = dFc1 + dFc2 + dFc3;

        dFc_u = Ms*Gp*dh_u;
        return Fc, dFc, dFc_u
    end
    Fc, dFc, dFc_u = Fcdiff1(xv,uv);

    function accdiff1(x,u)
        # derivative of qdd w.r.t x
        dacc1 = kronProd(permutedims(Fc+Fu),1.0I(size(M_inv,1)))*dM_inv
        dacc2 = M_inv*(dFc + dFu)
        dqdd_x = dacc1 + dacc2

        return dqdd_x
    end
    dqdd_x = accdiff1(xv,uv);
    dqdd_u = M_inv*(dFc_u + dFu_u);

    function fxdotDiff(x,u)
        len_x = length(x); len_u = length(u);
        df_x = zeros(len_x,len_x)

        df_u = zeros(len_x, len_u);

        for k=1:length(j)
            df_x[14*k+1:14*k+7,14*k+8:14*k+14] = 1.0*I(7)
            df_x[14*k+8:14*k+14,:] = dqdd_x[7*(k-1)+1:7*k,:]

            df_u[14*k+8:14*k+14,:] = dqdd_u[7*(k-1)+1:7*k,:];
        end
        df_x_woIn = df_x[15:end,15:end]; # without the inertial frame
        df_u_woIn = df_u[15:end,7:end];  # without the inertial frame
        return df_x_woIn, df_u_woIn
    end

    dfx_x, dfx_u = fxdotDiff(xv,uv);
    return dfx_x, dfx_u
end
## Test for different robots
# x0Orig, u0Orig = getXU_0(j);
# fdJ_A, fdJ_B = linearize(x0Orig, u0Orig, j, g);
#
# m = central_fdm(5,1); # first order differential using fifth order central difference method
# finJ_A = FiniteDifferences.jacobian(m, z->fxdot(z,u0Orig,j,g),x0Orig)[1];
# finJ_B = FiniteDifferences.jacobian(m, z->fxdot(x0Orig,z,j,g),u0Orig)[1];
#
# println("err_A = ", norm(fdJ_A - finJ_A))
# println("err_B = ", norm(fdJ_B - finJ_B))


trig1arg = [
(:acos, :(-1./sqrt(1-abs2(x))), (-1,1)),
(:acosd, :(-(180/pi)./sqrt(1-abs2(x))), (-1,1)),
(:acosh, :(1./sqrt(abs2(x)-1)), (1,Inf)),
(:acot, :(-1./(1+abs2(x))), (-Inf,Inf)),
(:acotd, :(-(180/pi)./(1+abs2(x))), (-Inf,Inf)),
(:acoth, :(1./(1-abs2(x))), (-Inf,-1,1,Inf)),
(:acsc, :(-1./sqrt(x.*x.*(x-1).*(x+1))), (-Inf,-1,1,Inf)),
(:acscd, :(-(180/pi)./sqrt(x.*x.*(x-1).*(x+1))), (-Inf,-1,1,Inf)),
(:acsch, :(-1./sqrt(x.^4+x.^2)), (-Inf,Inf)),
(:asec, :(1./sqrt(x.^4-x.^2)), (-Inf,-1,1,Inf)),
(:asecd, :((180/pi)./sqrt(x.^4-x.^2)), (-Inf,-1,1,Inf)),
(:asech, :(-1./sqrt(x.^2-x.^4)), (0,1)),
(:asin, :(1./sqrt(1-abs2(x))), (-1,1)),
(:asind, :((180/pi)./sqrt(1-abs2(x))), (-1,1)),
(:asinh, :(1./sqrt(1+abs2(x))), (-Inf,Inf)),
(:atan, :(1./(1+abs2(x))), (-Inf,Inf)),
(:atand, :((180/pi)./(1+abs2(x))), (-Inf,Inf)),
(:atanh, :(1./(1-abs2(x))), (-1,1)),
(:cos, :(-sin(x)), (-Inf,Inf)),
# cos_kernel: Not exported
(:cosc, :(-2y./x-sinc(x)*(pi^2)), (-Inf,Inf)),
(:cosd, :(-sind(x)*pi/180), (-Inf,Inf)),
(:cosh, :(sinh(x)), (-Inf,Inf)),
(:cospi, :(-sinpi(x).*pi), (-Inf,Inf)),
(:cot, :(-abs2(csc(x))), (-Inf,Inf)),
(:cotd, :(-abs2(cscd(x))*pi/180), (-Inf,Inf)),
(:coth, :(-abs2(csch(x))), (-Inf,Inf)),
(:csc, :(-y.*cot(x)), (-Inf,Inf)),
(:cscd, :(-y.*cotd(x)*pi/180), (-Inf,Inf)),
(:csch, :(-y.*coth(x)), (-Inf,Inf)),
# deg2rad_ext: Not exported
# mulpi_ext: Not exported
(:sec, :(y.*tan(x)), (-Inf,Inf)),
(:secd, :(y.*tand(x)*pi/180), (-Inf,Inf)),
(:sech, :(-y.*tanh(x)), (-Inf,Inf)),
(:sin, :(cos(x)), (-Inf,Inf)),
# sin_kernel: Not exported
(:sinc, :(cosc(x)), (-Inf,Inf)), # sin(πx)/(πx)
(:sind, :(cosd(x)*pi/180), (-Inf,Inf)),
(:sinh, :(cosh(x)), (-Inf,Inf)),
(:sinpi, :(cospi(x).*pi), (-Inf,Inf)),
(:tan, :(1+abs2(y)), (-Inf,Inf)),
(:tand, :((1+abs2(y))*pi/180), (-Inf,Inf)),
(:tanh, :(1-abs2(y)), (-Inf,Inf)),
]

for (f,g,r) in trig1arg
    @eval @primitive $f(x),dy,y  (dy.*($g))
    addtest1(f,r)
end


module MR
using Distances, StatsBase

export Isomap, mds, procrustes, IOMR, MR_Missing

function Kmin(D,K)
    (n,n) = size(D)
    for i = 1:n
        M = findKmin(D[i,:], K+1)
        for j = 1:n
            if D[i,j] > M
                D[i,j] = Inf
            end
        end
    end

    for i = 1:n
        for j = 1:n
            if D[i,j] != Inf
                D[j,i] = D[i,j]
            end
        end
    end

    return D
end

function findKmin(A,k)
    A = Set(A)
    for i = 1:k-1
        m = minimum(A)
        A = delete!(A,m)
        if isempty(A)
            return m
        end
    end

    return minimum(A)
end

function apsp(G)
    (n,n) = size(G)
    U = Inf*ones(size(G))
    for i = 1:n
        U[i,i] = 0
    end

    for i = 1:n
        for j = 1:n
            if G[i,j] != Inf
                U[i,j] = G[i,j]
            end
        end
    end

    for k = 1:n
        for i=1:n
            for j=1:i-1
                if U[i,j] > U[i,k] + U[k,j]
                    U[i,j] = U[i,k]+U[k,j]
                    U[j,i] = U[i,j]
                end
            end
        end
    end

    return U
end

function mds(D, d; center=true,align=true)
    n = size(D, 1)

    P = eye(n) - ones(n, n) / n
    K = -0.5*(P*(D.^2)*P')
    K = (K+K')/2

    e, V = eig(K)
    idx = sortperm(e, rev=true)[1:d]

    if (d > sum(e .> 0))
        println("Using some eigenvaectors for negative eigenvalues")
    end

    X =  V[:,idx]*diagm(sqrt.(e[idx]))

    if center == true
        X = X .- mean(X,1)
    end

    if align == true
        for i =1:d
            j = indmax(abs.(X[:,i]))
            X[:,i] .*= sign.(X[j,i])
        end
    end

    return X
end

function procrustes(X, Y)
    X₀ = X - ones(size(X)[1],1)*mean(X, 1)
    Y₀ = Y - ones(size(X)[1],1)*mean(Y, 1)
    Z = X₀'*Y₀

    U,S,V = svd(Z, thin=true)
    Q = V*U'
    α = trace(Z*Q)/trace(Z)
    Ya = α*(Y₀*Q) .+ mean(X, 1)

    return Ya
end

function Isomap(X,k,d)
    D = pairwise(Euclidean(1e-12),X',X')
    Dmin = Kmin(D,k)
    Dmani = apsp(Dmin)
    Xhat = mds(Dmani,d)
    return Xhat
end

function IOMR(Dpert)
    n = size(Dpert)[1]

    Dnew = copy(Dpert)

    for k = 1:n
        for i = 1:n
            for j = 1:i-1
                if i!=j && i!=k && j!=k
                    if Dnew[i,j] > Dnew[i,k]+Dnew[j,k]
                        Dnew[i,k] = Dnew[i,j]-Dnew[j,k]
                        Dnew[k,i] = Dnew[i,j]-Dnew[j,k]
                    end
                end
            end
        end
    end

    return Dnew-Dpert
end

function sumInf(x)
    n = length(x)
    s = 0.0
    for i = 1:n
        if (x[i] < Inf)
            s += x[i]
        end
    end

    return s
end

function distance(X)
    (n,m) = size(X)
    D = zeros(n,n)
    for i =1:n
        for j = 1:i-1
            b = (X[i,:]-X[j,:]).^2
            D[i,j] = sumInf(b)^0.5
            D[j,i] = (D[i,j])
        end
    end

    return D
end

function MR_Missing(X,Q,k)
    (n,m) = size(Q)
    for i = 1:n
        for j = 1:m
            if Q[i,j] == 0
                X[i,j] = Inf
            end
        end
    end

    Dmiss = distance(X)
    Dfixed = Dmiss + IOMR(Dmiss)
    Dmin = Kmin(Dfixed,k)

    return Dmin
end

end


function solve()
    n, k = [parse(Int, x) for x in split(readline())]
    sol = 0
    for l=k:n+1
        maxsum = l*(2n-l+1)÷2
        minsum = l*(l-1)÷2
        sol += maxsum - minsum + 1
        sol %= 1_000_000_007
    end
    return sol
end

println(solve())


using Parameters
# Potassium channels
@with_kw struct IKParams
    k_o = 5.4
    G_KP = 8.28E-3
    P_NA_K = 0.0183
    G_K = 0.282 * sqrt(k_o / 5.4)
    G_K1 = 0.748 * sqrt(k_o / 5.4)
end

# Rate of change of Xk gating variable
function dxk(x_k, vm)
    Δv = vm + 30
    α = -7.9e-5 * Δv / expm1(-0.148 * Δv)
    β = 1.31e-4 * Δv / expm1(0.0687 * Δv)
    d_x_k = α - x_k * (α + β)
end

# Time-dependent K current (iK) (μA/cm²)
function ik(vm, eNaK, x_k, G_K)
    x1_inv = 1 + exp((vm - 40) / 40)
    i_k = G_K * x_k^2 / x1_inv * (vm - eNaK)
end

# Plataeu K current (iKp) (uA/cm^2)
ikp(vm, eK, G_KP) = G_KP * (vm - eK) / (1 + exp((7.488 - vm) / 5.98))

# Time-independent K current (iK1) (uA/cm^2)
function ik1(vm, eK, G_K1)
    Δv = vm - eK
    α = 1.02 / (1 + exp(0.2385 * (Δv - 59.215)))
    β = (0.4912 * exp(0.08032 * (Δv + 5.476)) + exp(0.06175 * (Δv - 594.31))) / (1 + exp(-0.5143 * (Δv + 4.753)))
    ik1 = G_K1 * Δv * α / (α + β)
end


function thiele4(pt₁, pt₂, pt₃, pt₄, x)
    result = thiele4(pt₁[1], pt₂[1], pt₃[1], pt₄[1],
                     pt₁[2:end], pt₂[2:end], pt₃[2:end], pt₄[2:end], x)
    # check for degenerate case
    if !allfinite(result)
        if x <= pt₃[1]
            result = thiele3(pt₁, pt₂, pt₃, x)
        else
            result = thiele3(pt₂, pt₃, pt₄, x)
        end
    end
    return result
end

function thiele4(pt₁x, pt₂x, pt₃x, pt₄x, pt₁y, pt₂y, pt₃y, pt₄y, x)
    t1 = @. pt₁y - pt₂y
    t2 = @. pt₂y - pt₃y
    t2 = @. inv(t2)
    t1 = @. inv(t1)
    t2 = @. (pt₂x - pt₃x) * t2
    t1 = @. (pt₁x - pt₂x) * t1
    t3 = @. -t2 + t1
    t4 = @. pt₃y - pt₄y
    t4 = @. inv(t4)
    t4 = @. -(pt₃x - pt₄x) * t4 + t2
    t4 = @. inv(t4)
    t3 = @. inv(t3)
    t3 = @. (pt₁x - pt₃x) * t3
    t4 = @. -(pt₂x - pt₄x) * t4 + pt₂y - pt₃y + t3
    t4 = @. inv(t4)
    t2 = @. -(pt₁x - pt₄x) * t4 + t1 - t2
    t2 = @. inv(t2)
    t2 = @. (pt₃x - x) * t2 - pt₁y + pt₂y + t3
    t2 = @. inv(t2)
    t1 = @. -(pt₂x - x) * t2 + t1
    t1 = @. inv(t1)
    return @. (-(pt₁x - x) * t1 + pt₁y)
end


using JTetra
using Base.Test
include("derivative-test.jl")

println("==== Test derivatives of mass matrices ====")

# read tetrahedral mesh
mesh = getTetraMesh("points.txt","tetras.txt")

# scalar and tensor cell properties for mass matrices
a = exp(randn(mesh.nc))
A = zeros(mesh.nc,6)
for i = 1:mesh.nc
  # generate symmetric positive definite 3x3 matrix
  Ai  = randn(3,3)
  u,V = eig(Ai + Ai')
  B   = V' * diagm(exp(u)) * V
  A[i,1] = B[1,1]
  A[i,2] = B[2,2]
  A[i,3] = B[3,3]
  A[i,4] = B[1,2]
  A[i,5] = B[1,3]
  A[i,6] = B[2,3]
end

# random edge vector
u = rand(mesh.ne)

# derivative test
f(m)    = getEdgeMassMatrix(mesh, m) * u
df(v,m) = getdEdgeMassMatrix(mesh, m, u) * vec(v)

println("Edge mass matrix: isotropic")
checkDerivativeIsotropic,error,order = checkDerivativeMax(f,df,a)
@test checkDerivativeIsotropic

println("Edge mass matrix: anisotropic")
checkDerivativeAnisotropic,error,order = checkDerivativeMax(f,df,A)
@test checkDerivativeAnisotropic

print("Passed!\n")


import StatsFuns.gammacdf

seed!(12345)

function testGamma(α, β, low, high, h, N)
  function cdf(x::Float64)
    return gammacdf(α, 1/β, x)
  end
  function sampler()
    return sampleGamma(α, β)
  end
  return testGOFContinuous(cdf, sampler, low, high, h, N)
end

@test testGamma(3.0, 2.0, 0.01, 6.0, 0.01, 2^21) > 0.01
@test testGamma(0.1, 1.0, 0.00, 4.0, 0.01, 2^21) > 0.01
@test testGamma(0.8, 1.0, 0.00, 7.0, 0.01, 2^21) > 0.01
@test testGamma(10.0, 1.0, 3.0, 23.0, 0.01, 2^21) > 0.01


# export jlmie_mx, jlmie_abcd, jlmie_pt, jlmie_S12

function jlmie_abcd(m,x,n::Int)
# calculate n-th order Mie coefficients a,b,c,d
# input:
# m: relative refractive index (n_material / n_environment)
# x: size parameter (wavenumber * radius)

    # Bessel/Hankel functions
    jx = sbesselj.(n,x)
    jmx = sbesselj.(n,m.*x)
    hx = shankelh1.(n,x)

    # derivative
    d1xjx = x.*sbesselj.(n-1,x) .- n.*sbesselj.(n,x)
    d1mxjmx = m.*x.*sbesselj.(n-1,m.*x) .- n.*sbesselj.(n,m.*x)
    d1xhx = x.*shankelh1.(n-1,x) .- n.*shankelh1.(n,x)

    an = (m.^2 .*jmx.*d1xjx .- jx.*d1mxjmx) ./
            (m.^2 .*jmx.*d1xhx .- hx.*d1mxjmx)
    bn = (jmx.*d1xjx .- jx.*d1mxjmx) ./
            (jmx.*d1xhx .- hx.*d1mxjmx)
    cn = (jx.*d1xhx .- hx.*d1xjx) ./
            (jmx.*d1xhx .- hx.*d1mxjmx)
    dn = (m.*jx.*d1xhx .- m.*hx.*d1xjx) ./
            (m.^2 .*jmx.*d1xhx .- hx.*d1mxjmx)
    return an,bn,cn,dn
end

function jlmie_pt(mu,n::Int)
# See Sec. 4.3.1 of B&H
# ref of pi,tau: [D. Deirmendjian, “Electromagnetic Scattering on Spherical Polydispersions"]
    ck = sum(mu .< -1) .+ sum(mu .> 1)
    if ck > 0
        error("mu is cos(theta), -1 <= mu <= 1")
    end

    if n == 0
        pin = 0*mu  # pi(0,mu) = 0
        taun = []  # not used but tau(0,mu) = 0
    elseif n == 1
        pin = 0*mu.+1  # pi(1,mu) = 1
        taun = n.*mu.*pin  # note that pi(0,mu) = 0
    elseif n > 1
        # recurrence, eq.(4.47) of B&H
        pin = (2*n-1)/(n-1).*mu.*jlmie_pt(mu,n-1)[1] .-
                (n)/(n-1).*jlmie_pt(mu,n-2)[1]
        taun = n.*mu.*pin .- (n+1).*jlmie_pt(mu,n-1)[1]
    else
        error("n should be larger than 0")
    end
    return pin,taun
end

function jlmie_S12(m,x,mu,n::Int)
# calculate n-th order S1,S2 component of scattering matrix.
    an,bn,_,_ = jlmie_abcd(m,x,n)
    pin,taun = jlmie_pt(mu,n)

    S1n = (2*n+1)/(n*(n+1)).*(an.*pin.+bn.*taun)
    S2n = (2*n+1)/(n*(n+1)).*(an.*taun.+bn.*pin)
    return S1n,S2n
end

function jlmie_mx(nmat,radius,lbd0,nenv=1.00)
# conversion of parameters for calculation of light scattering
# by a small sphere based on Mie theory
# Input:
# nmat: n_material; refractive index of the material of the sphere
# radius: radius of the sphere
# lbd0: target vacuum wavelength (range)
# nenv: n_environement; refractive index of the environment
#       around the sphere (default: air)
# Output:
# m: relative refractive index (n_material / n_environment)
# x: size parameter (wavenumber * radius)
    m = nmat./nenv
    lbd = lbd0./nenv  # effective wavelength in the environment
    k = 2*pi./lbd  # free space wavenumber in the environment
    x = k.*radius
    return m,x
end

function largenmax(x)
    nmax = round(maximum(x.+x.^(1/3).+2))
    nmax = Int(nmax)
    # println("nmax = "*string(nmax))
    return nmax
end


function second_order_filter(f, q, fs)
    cft = f ./ fs
    rho = exp.(-pi .* cft ./ q)
    theta = 2pi .* cft .* sqrt.(1 .- 1.0 ./ (4 .* q .^ 2))
    filts = hcat(ones(size(rho)), -2rho .* cos.(theta), rho .^ 2)
end

function freq_resp(filter, f, fs)
    cf = exp(1im * 2 * pi * f / fs)
    denom = filter[5] + filter[4] * cf + cf ^ 2
    mag = (filter[3] + filter[2] * cf + filter[1] * cf ^ 2) / denom
    mag = 20 * log10(abs(mag))
end

function set_gain(filter, desired, f, fs)
    old_gain = 10 ^ (freq_resp(filter, f, fs) / 20)
    filter[1:3] *= desired / old_gain
    filter
end

function design_lyon_filters(fs, ear_q = 8, step_factor = ear_q / 32)
    Eb = 1000.0
    EarZeroOffset = 1.5
    EarSharpness = 5.0
    EarPremphCorner = 300

    # Find top frequency, allowing space for first cascade filter.
    topf = fs / 2.0
    topf_neg_delta = sqrt(topf ^ 2 + Eb ^ 2) / ear_q * step_factor * EarZeroOffset
    topf = topf - topf_neg_delta + sqrt(topf ^ 2 + Eb ^ 2) / ear_q * step_factor

    # Find place where CascadePoleQ < .5
    lowf = Eb / sqrt(4 * ear_q ^ 2 - 1)
    _log_val_low = log(lowf + sqrt(lowf ^ 2 + Eb ^ 2))
    _log_val_top = log(topf + sqrt(Eb ^ 2 + topf ^ 2))
    NumberOfChannels = floor(Int, (ear_q * (-_log_val_low + _log_val_top)) / step_factor)

    # Now make an array of CenterFreqs..... This expression was derived by
    # Mathematica by integrating 1/EarBandwidth(cf) and solving for f as a
    # function of channel number.
    cn = 1:NumberOfChannels
    denom = exp.((cn .* step_factor) ./ ear_q)
    nom_add = topf + sqrt(Eb ^ 2 + topf ^ 2)
    center_freqs = (-1 .* ((exp.((cn .* step_factor) ./ ear_q) .* Eb ^ 2) ./ nom_add) .+ nom_add ./ denom) ./ 2.0

    # OK, now we can figure out the parameters of each stage filter.
    EarBandwidth = sqrt.(center_freqs .^ 2 .+ Eb ^ 2) ./ ear_q
    CascadeZeroCF = center_freqs .+ EarBandwidth .* step_factor .* EarZeroOffset
    CascadeZeroQ = EarSharpness .* CascadeZeroCF ./ EarBandwidth
    CascadePoleCF = center_freqs
    CascadePoleQ = center_freqs ./ EarBandwidth

    # Now lets find some filters.... first the zeros then the poles
    zerofilts = second_order_filter(CascadeZeroCF, CascadeZeroQ, fs)
    polefilts = second_order_filter(CascadePoleCF, CascadePoleQ, fs)
    # filters = np.vstack([zerofilts, polefilts[1:, :]])
    filters = cat(zerofilts, polefilts[:, 2:end], dims = 2)

    # Now we can set the DC gain of each stage.
    dcgain = zeros(NumberOfChannels)

    dcgain[2:end] .= center_freqs[1:end-1] ./ center_freqs[2:end]
    dcgain[1] = dcgain[2]

    for i in 1:NumberOfChannels
        filters[i, :] = set_gain(filters[i, :], dcgain[i], 0, fs)
    end

    # Finally, let's design the front filters.
    front = zeros(2, 5)
    front_0 = [0, 1, -exp(-2 * pi * EarPremphCorner / fs), 0, 0]
    front[1, :] = set_gain(front_0, 1, fs / 4, fs)
    top_poles = second_order_filter(topf, CascadePoleQ[1], fs)
    front_1 = [1, 0, -1, top_poles[2], top_poles[3]]
    front[2, :] = set_gain(front_1, 1, fs / 4, fs)

    # Now, put them all together.
    filters = cat(front, filters, dims = 1)
    return filters, center_freqs
end

function epsilon_from_tau(tau, sample_rate)
    1 - exp(-1.0 / tau / sample_rate)
end


function eigen(x::Matrix{WElem})
    @info "use wolframscript"
    u, v = WS"Eigensystem"(x)
    Eigen(u, transpose(v))
end

function svd(x::Matrix{WElem})
    @info "use wolframscript"
    u, v, w = WS"SingularValueDecomposition"(x)
    SVD(u, diag(v), w')
end

function qr(x::Matrix{WElem})
    @info "use wolframscript"
    q, r = WS"QRDecomposition"(x)
    (Q = q', R = r)
end

function lq(x::Matrix{WElem})
    @info "use wolframscript"
    q, r = WS"QRDecomposition"(permutedims(x, (2,1)))
    (L = transpose(r), Q = conj(q))
end

function schur(x::Matrix{WElem})
    @info "use wolframscript"
    z, t = WS"SchurDecomposition"(x)
    Schur(t, z, diag(t))
end

inv(x::Matrix{WElem}) = WS"Inverse"(x, true)
pinv(x::Matrix{WElem}) = WS"PseudoInverse"(x, true)
nullspace(x::Matrix{WElem}) = permutedims(WS"NullSpace"(x, true), (2,1))


function epipole(𝐅::AbstractArray)
    𝐔, 𝐒, 𝐕 = svd(𝐅)
    𝐞 = 𝑛(MVector{3}(𝐕[:,end]))
    Point2DH(𝐞)
end


function init_pot_Hcoul( LF, center )
  Npoints = LF.Nx * LF.Ny * LF.Nz
  Vpot = zeros( Float64,Npoints )
  for ip = 1:Npoints
    r = norm( LF.lingrid[:,ip] - center[:] )
    Vpot[ip] = -1.0/r
  end
  return Vpot
end


#File: minVar.jl
#Author:
#Date: 29-June-2020

#OBJECTIVE FUNCTION: MININIMISE VARIANCE
function minVar(W,VCV)
###########################################################################
#INPUTS:
#W      - weight vector
#VCV    - VCV matrix
###########################################################################

    fval = W'*VCV*W;
    return fval
end