#using Random
# using Interpolations

U_func_time_trivial(t) = 0.0;
U_func_time_trivial(t, u) = zeros(size(u));
U_func_space_trivial(u) = zeros(size(u));

#####
##### Spike-like controllers
#####
# FROM: https://discourse.julialang.org/t/diffeqflux-with-time-as-additional-input-to-neural-ode/26456/3
function U_func_time(t, u, U_width, U_starts;
                        F_dim=2, amplitude=1.0)
    F = zeros(size(u))
    if any(i -> i < U_width, abs.(U_starts .- t))
        F[F_dim] = amplitude
    end
    return F
end

function U_func_time_multivariate(t, u, U_widths, U_starts;
                                F_dim=[2], amplitudes=nothing)
    if amplitudes == nothing
        amplitudes = ones(size(F_dim))
    end
    F = zeros(size(u))
    # Each forced dimension can have different timing
    for i_dim in 1:length(F_dim)
        if any(i -> i < U_widths[i_dim], abs.(U_starts[i_dim,:] .- t))
            F[i_dim] = amplitudes[i_dim]
        end
    end
    return F
end

function spring_forcing_example(tspan)
    Random.seed!(11)
    num_ctr = 2;
    U_starts = rand(num_ctr,1) .* tspan[2]
    U_width = 0.05;
    U_func(t, u) = U_func_time(t, u, U_width, U_starts)

    return U_func
end

#####
##### Piecewise constant controllers
#####

# Returns the function with signature:
#   f = generate_U_func_piecewise(t, u)
#   f(t, u) # control at that time
function generate_U_func_piecewise(tspan, u_size;
                                    num_jumps = 10,
                                    U_min=-1, U_max=1,
                                    F_dim=2)
    jump_locations = sort(rand(num_jumps) .* tspan[end])
    jump_locations = [jump_locations; tspan[end]]
    U_series = zeros(u_size)
    my_rand() = (U_max-U_min)*rand() + U_min
    let jump_ind=1, current_val=my_rand()
        for i in 1:length(tspan)
            if tspan[i] > jump_locations[jump_ind]
                jump_ind += 1
                current_val = my_rand()
            end
            U_series[F_dim, i] = current_val
        end
    end
    # Generates a function that can be called like f(t)
    # return U_series
    U_func = generate_map_rows_function(
                (x)->CubicSplineInterpolation(tspan, x), U_series)
    return (t, tmp)->U_func(t) # Needs the right signature
end

#####
##### Coordinate (spatial) controllers
#####
function U_func_hard_wall(X, x_dim=1, v_dim=2, wall_strength=100.0)
    F = zeros(size(X))
    X[x_dim]<0.0 ? F[v_dim]=wall_strength : return F
    # F = [wall_strength*σ(-5.0(x+0.0)), 0.0, 0.0]
    return F
end

function U_func_soft_wall(X, x_dim=1, v_dim=2, wall_strength=100.0)
    F = zeros(size(X))
    F[y_dim] = wall_strength*σ(-10.0*X[x_dim])
    return F
end

function U_func_spring(X; k=1000.0, r=0.1, damping=1.0,
                       r_dim=1, F_dim=2, damp_dim=2)
    F = zeros(size(X))
    # Damped spring at the 0 coordinate
    if r - X[r_dim] > 0.0
        Δ = r - X[r_dim]
        F[F_dim] = k*Δ + -damping*abs(X[damp_dim])
    end
    return F
end

function U_func_spring_sphere(X; k=1000.0,
                              r=0.1, location=nothing, damping=1.0,
                              F_dim=2, damp_dim=2)
    F = zeros(size(X))
    if location == nothing
        # Default is the origin
        location = zeros(size(X))
    end
    Δ = sum(abs2,location .- X)
    if Δ < r
        F[F_dim] = k*Δ + -damping*abs(X[damp_dim])
    end
    return F
end

#####
##### Export
#####
export U_func_time, U_func_time_multivariate,
        spring_forcing_example, U_func_spring_sphere,
        U_func_hard_wall, U_func_soft_wall, U_func_spring,
        U_func_time_trivial, U_func_space_trivial,
        generate_U_func_piecewise


###########
# Library #
###########
# using Random
using MatrixLMnet
using Helium
using Test

#####################################################################
# TEST Cross Validation Lasso vs Elastic Net (𝛼=1) - Simulated Data #
#####################################################################

#=
Description:
-----------

Model: 𝐘 = 𝐗 𝛃 𝐙' + 𝜎𝜖, with 𝜖∼𝑁(0,1)

Simulate data set consisting  of 20/20/200 observations and 8 predictors.
We let 𝛽₁ = (3, 1.5, 0, 0, 2, 0, 0, 0), 𝛽₂ = (0, 1.5, 0, 3.5, 2, 0, 0 , 2) where
𝛃 = [𝛽₁, 𝛽₂] and 𝜎 = 3.
The pairwise correlation between 𝑋ᵢ and 𝑋ⱼ was set to be 𝑐𝑜𝑟(𝑖,𝑗)=(0.5)^|𝑖−𝑗|.
Here, the Z matrix is an identity matrix.
=#

# Data testing directory name
dataDir = realpath(joinpath(@__DIR__,"data"))

# Get predictors
X = Helium.readhe(joinpath(dataDir, "Xmat.he"))

# Get response
Y = Helium.readhe(joinpath(dataDir, "Ymat.he"))

# Get Z matrix
Z = Helium.readhe(joinpath(dataDir, "Zmat.he"))

# Build raw data object from MatrixLM.jl
dat = RawData(Response(Y), Predictors(X, Z));

# Hyper parameters
λ = [10.0, 5.0, 3.0]
α = [1.0, 0.5]

rng = 2021#MatrixLMnet.Random.MersenneTwister(2021)

numVersion = VERSION
if Int(numVersion.minor) < 7
      tolVersion=2e-1
else
      tolVersion=1e-6
end

##########################################
# TEST 1 Summary cross-validation - ista #
##########################################

# Elastic net penalized regression
MatrixLMnet.Random.seed!(rng)
est1 = MatrixLMnet.mlmnet_cv(dat, λ, α, 10, 1, method = "ista", hasZIntercept = false, hasXIntercept = false, isVerbose = false);
# Summaries
smmr_Net1 = MatrixLMnet.mlmnet_cv_summary(est1);
smmr_min_Net1 = MatrixLMnet.lambda_min(est1);

idxSmmr = argmin(smmr_Net1[:, :AvgMSE])
test_ElasticNet = (smmr_Net1[idxSmmr, :AvgMSE] == smmr_min_Net1[1, :AvgMSE]) &&
        (smmr_Net1[idxSmmr, :Lambda] == smmr_min_Net1[1, :Lambda]) &&
        (smmr_Net1[idxSmmr, :Alpha] == smmr_min_Net1[1, :Alpha])

# Elastic net penalized regression
MatrixLMnet.Random.seed!(rng)
est2 = MatrixLMnet.mlmnet_cv(dat, λ, 10, 1, method = "ista",  hasZIntercept = false, hasXIntercept = false, isVerbose = false);
# Summaries
smmr_Net2 = MatrixLMnet.mlmnet_cv_summary(est2);
smmr_min_Net2 = MatrixLMnet.lambda_min(est2);

idxSmmr = argmin(smmr_Net2[:, :AvgMSE])
test_Lasso = (smmr_Net2[idxSmmr, :AvgMSE] == smmr_min_Net2[1, :AvgMSE]) &&
        (smmr_Net2[idxSmmr, :Lambda] == smmr_min_Net2[1, :Lambda]) &&
        (smmr_Net2[idxSmmr, :Alpha] == smmr_min_Net2[1, :Alpha])

println("Summary cross-validation test 1 - ista: ",
         @test (test_ElasticNet && test_Lasso))

###########################################
# TEST 2 Summary cross-validation - fista #
###########################################

# Elastic net penalized regression
MatrixLMnet.Random.seed!(rng)
est1 = MatrixLMnet.mlmnet_cv(dat, λ, α, 10, 1, method = "fista", hasZIntercept = false, hasXIntercept = false, isVerbose = false);
# Summaries
smmr_Net1 = MatrixLMnet.mlmnet_cv_summary(est1);
smmr_min_Net1 = MatrixLMnet.lambda_min(est1);

idxSmmr = argmin(smmr_Net1[:, :AvgMSE])
test_ElasticNet = (smmr_Net1[idxSmmr, :AvgMSE] == smmr_min_Net1[1, :AvgMSE]) &&
        (smmr_Net1[idxSmmr, :Lambda] == smmr_min_Net1[1, :Lambda]) &&
        (smmr_Net1[idxSmmr, :Alpha] == smmr_min_Net1[1, :Alpha])

# Elastic net penalized regression
MatrixLMnet.Random.seed!(rng)
est2 = MatrixLMnet.mlmnet_cv(dat, λ, 10, 1, method = "fista",  hasZIntercept = false, hasXIntercept = false, isVerbose = false);
# Summaries
smmr_Net2 = MatrixLMnet.mlmnet_cv_summary(est2);
smmr_min_Net2 = MatrixLMnet.lambda_min(est2);

idxSmmr = argmin(smmr_Net2[:, :AvgMSE])
test_Lasso = (smmr_Net2[idxSmmr, :AvgMSE] == smmr_min_Net2[1, :AvgMSE]) &&
        (smmr_Net2[idxSmmr, :Lambda] == smmr_min_Net2[1, :Lambda]) &&
        (smmr_Net2[idxSmmr, :Alpha] == smmr_min_Net2[1, :Alpha])

println("Summary cross-validation test 2 - fista: ",
         @test (test_ElasticNet && test_Lasso))

########################################################
# TEST 3 Summary cross-validation - fista backtracking #
########################################################

# Elastic net penalized regression
MatrixLMnet.Random.seed!(rng)
est1 = MatrixLMnet.mlmnet_cv(dat, λ, α, 10, 1, method = "fista_bt", hasZIntercept = false, hasXIntercept = false, isVerbose = false);
# Summaries
smmr_Net1 = MatrixLMnet.mlmnet_cv_summary(est1);
smmr_min_Net1 = MatrixLMnet.lambda_min(est1);

idxSmmr = argmin(smmr_Net1[:, :AvgMSE])
test_ElasticNet = (smmr_Net1[idxSmmr, :AvgMSE] == smmr_min_Net1[1, :AvgMSE]) &&
        (smmr_Net1[idxSmmr, :Lambda] == smmr_min_Net1[1, :Lambda]) &&
        (smmr_Net1[idxSmmr, :Alpha] == smmr_min_Net1[1, :Alpha])

# Elastic net penalized regression
MatrixLMnet.Random.seed!(rng)
est2 = MatrixLMnet.mlmnet_cv(dat, λ, 10, 1, method = "fista_bt",  hasZIntercept = false, hasXIntercept = false, isVerbose = false);
# Summaries
smmr_Net2 = MatrixLMnet.mlmnet_cv_summary(est2);
smmr_min_Net2 = MatrixLMnet.lambda_min(est2);

idxSmmr = argmin(smmr_Net2[:, :AvgMSE])
test_Lasso = (smmr_Net2[idxSmmr, :AvgMSE] == smmr_min_Net2[1, :AvgMSE]) &&
        (smmr_Net2[idxSmmr, :Lambda] == smmr_min_Net2[1, :Lambda]) &&
        (smmr_Net2[idxSmmr, :Alpha] == smmr_min_Net2[1, :Alpha])

println("Summary cross-validation test 3 - fista_bt: ",
         @test (test_ElasticNet && test_Lasso))

##########################################
# TEST 4 Summary cross-validation - admm #
##########################################

# Elastic net penalized regression
MatrixLMnet.Random.seed!(rng)
est1 = MatrixLMnet.mlmnet_cv(dat, λ, α, 10, 1, method = "admm", hasZIntercept = false, hasXIntercept = false, isVerbose = false);
# Summaries
smmr_Net1 = MatrixLMnet.mlmnet_cv_summary(est1);
smmr_min_Net1 = MatrixLMnet.lambda_min(est1);

idxSmmr = argmin(smmr_Net1[:, :AvgMSE])
test_ElasticNet = (smmr_Net1[idxSmmr, :AvgMSE] == smmr_min_Net1[1, :AvgMSE]) &&
        (smmr_Net1[idxSmmr, :Lambda] == smmr_min_Net1[1, :Lambda]) &&
        (smmr_Net1[idxSmmr, :Alpha] == smmr_min_Net1[1, :Alpha])

# Elastic net penalized regression
MatrixLMnet.Random.seed!(rng)
est2 = MatrixLMnet.mlmnet_cv(dat, λ, 10, 1, method = "admm",  hasZIntercept = false, hasXIntercept = false, isVerbose = false);
# Summaries
smmr_Net2 = MatrixLMnet.mlmnet_cv_summary(est2);
smmr_min_Net2 = MatrixLMnet.lambda_min(est2);

idxSmmr = argmin(smmr_Net2[:, :AvgMSE])
test_Lasso = (smmr_Net2[idxSmmr, :AvgMSE] == smmr_min_Net2[1, :AvgMSE]) &&
        (smmr_Net2[idxSmmr, :Lambda] == smmr_min_Net2[1, :Lambda]) &&
        (smmr_Net2[idxSmmr, :Alpha] == smmr_min_Net2[1, :Alpha])

println("Summary cross-validation test 4 - admm: ",
         @test (test_ElasticNet && test_Lasso))

########################################
# TEST 5 Summary cross-validation - cd #
########################################

# Elastic net penalized regression
MatrixLMnet.Random.seed!(rng)
est2 = MatrixLMnet.mlmnet_cv(dat, λ, 10, 1, method = "admm",  hasZIntercept = false, hasXIntercept = false, isVerbose = false);
# Summaries
smmr_Net2 = MatrixLMnet.mlmnet_cv_summary(est2);
smmr_min_Net2 = MatrixLMnet.lambda_min(est2);

idxSmmr = argmin(smmr_Net2[:, :AvgMSE])
test_Lasso = (smmr_Net2[idxSmmr, :AvgMSE] == smmr_min_Net2[1, :AvgMSE]) &&
        (smmr_Net2[idxSmmr, :Lambda] == smmr_min_Net2[1, :Lambda]) &&
        (smmr_Net2[idxSmmr, :Alpha] == smmr_min_Net2[1, :Alpha])

println("Summary cross-validation test 5 - admm: ",
         @test (test_Lasso))


const act_fn = relu

convx(ni, nf; ks=3, stride=1) = Conv(
        (ks, ks), ni => nf, stride = stride, pad = ks ÷ 2, init = Flux.kaiming_normal)

function convxlayer(ni, nf; ks=3, stride=1, zero_bn=false, act=true)
    bn = BatchNorm(nf, act ? act_fn : identity)
    fill!(bn.γ, zero_bn ? 0 : 1)
    return Chain(convx(ni, nf; ks = ks, stride = stride), bn)
end

struct ResBlock
    convs
    idconv
    pool
end

function ResBlock(expansion::Int, ni::Int, nh::Int; stride::Int = 1)
    nf, ni = nh * expansion, ni * expansion
    if expansion == 1
        layers = [
            convxlayer(ni, nh; stride = stride),
            convxlayer(nh, nf; zero_bn = true, act = false),
        ]
    else
        layers = [
            convxlayer(ni, nh; ks = 1),
            convxlayer(nh, nh; stride = stride),
            convxlayer(nh, nf; ks = 1, zero_bn = true, act = false)
        ]
    end

    return ResBlock(
        Chain(layers...),
        ni == nf ? identity : convxlayer(ni, nf; ks = 1, stride = 1),
        stride == 1 ? identity : MeanPool((2, 2))
    )
end
Flux.@functor ResBlock

(r::ResBlock)(x) = act_fn.(r.convs(x) .+ r.idconv(r.pool(x)))

function make_layer(expansion, ni, nf, n_blocks, stride)
    return Chain([
            ResBlock(expansion, i == 1 ? ni : nf, nf; stride =(i == 1 ? stride : 1))
            for i in 1:n_blocks
            ]...)
end

function XResNet(expansion, layers; c_in = 3)
    nfs = [c_in, (c_in+1)*8, 64, 64]
    stem = [convxlayer(nfs[i], nfs[i+1]; stride = i == 1 ? 2 : 1) for i in 1:3]

    nfs = [64 ÷ expansion, 64, 128, 256, 512]
    res_layers = [
        make_layer(expansion, nfs[i], nfs[i+1], l, i == 1 ? 1 : 2)
        for (i, l) in enumerate(layers)
    ]

    return Chain(
        stem...,
        MaxPool((3, 3); pad = 1, stride = 2),
        res_layers...,
    )
end

xresnet18(; kwargs...) = XResNet(1, [2, 2, 2, 2]; kwargs...)
xresnet50(; kwargs...) = XResNet(4, [3, 4, 6, 3]; kwargs...)


#    Copyright (c) 2021 J.A. Duffek
#    Copyright (c) 2000 D.M. Spink
#
#    This program is free software; you can redistribute it and/or modify
#    it under the terms of the GNU General Public License as published by
#    the Free Software Foundation; either version 2 of the License, or
#    (at your option) any later version.
#
#    This program is distributed in the hope that it will be useful,
#    but WITHOUT ANY WARRANTY; without even the implied warranty of
#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
#    GNU General Public License for more details.
#
#    You should have received a copy of the GNU General Public License
#    along with this program; if not, see <http://www.gnu.org/licenses/>.

using Plots;

"""
    democoons()

Construction of a bilinearly blended Coons surface.

# Examples:
```julia
julia> democoons()
```
"""
function democoons()
# Boundary curve 1
pnts = [0.0  3.0  4.5  6.5 8.0 10.0;
        0.0  0.0  0.0  0.0 0.0  0.0;
        2.0  2.0  7.0  4.0 7.0  9.0];
crv1 = nrbmak(pnts, vec([0 0 0 1/3 0.5 2/3 1 1 1]));

# Boundary curve 2
pnts= [ 0.0  3.0  5.0  8.0 10.0;
       10.0 10.0 10.0 10.0 10.0;
        3.0  5.0  8.0  6.0 10.0];
crv2 = nrbmak(pnts, vec([0 0 0 1/3 2/3 1 1 1]));

# Boundary curve 3
pnts= [0.0 0.0 0.0 0.0;
       0.0 3.0 8.0 10.0;
       2.0 0.0 5.0 3.0];
crv3 = nrbmak(pnts, vec([0 0 0 0.5 1 1 1]));

# Boundary curve 4
pnts= [10.0 10.0 10.0 10.0 10.0;
       0.0   3.0  5.0  8.0 10.0;
       9.0   7.0  7.0 10.0 10.0];
crv4 = nrbmak(pnts, vec([0 0 0 0.25 0.75 1 1 1]));

srf = nrbcoons(crv1, crv2, crv3, crv4);

# Draw the surface
nrbplot(srf,[20;20],
        title = "Construction of a bilinearly blended Coons surface.",
        c = :roma,
        legend = nothing,
        linewidth = 0.5,
        linecolor = :black,
        camera = (-60,30));
end # democoons


module Day13

using SparseArrays

struct Problem
    holes::Vector{NTuple{2, Int}}
    folds::Vector{Tuple{NTuple{2, Int}, Int}}
end

function parse_problem(input)
    holes_str, instructions = split(input, "\n\n")
    holes = NTuple{2, Int}[]
    for line in split(holes_str, "\n")
        a, b = split(line, ",")
        push!(holes, (parse(Int, a), parse(Int, b)))
    end

    folds_str = split(instructions, "\n")
    folds = Tuple{NTuple{2, Int}, Int}[]
    for line in filter(!isempty, folds_str)
        s = last(split(line, " "))
        p, q = split(s, "=")
        q = parse(Int, q)
        push!(folds, p == "x" ? ((q, 0), 1) : ((0, q), 2))
    end

    return Problem(holes, folds)
end

apply(fold, hole) = begin
    h = hole .- fold[1]
    if fold[2] == 1
        (fold[1][1] - abs(h[1]), h[2])
    else
        (h[1], fold[1][2] - abs(h[2]))
    end
end

function part1(problem)
    fold = problem.folds[1]
    folded = map(problem.holes) do tuple
        apply(fold, tuple)
    end
    return length(unique(folded))
end

function part2(problem)
    holes = problem.holes
    for fold in problem.folds
        holes = map(holes) do tuple
            apply(fold, tuple)
        end |> unique
    end

    xmax = maximum(hole[1] for hole in holes)
    ymax = maximum(hole[2] for hole in holes)
    xmin = minimum(hole[1] for hole in holes)
    ymin = minimum(hole[2] for hole in holes)

    M = spzeros(Int, ymax - ymin + 1, xmax - xmin + 1)
    for hole in holes
        x, y = hole
        M[y - ymin + 1, x - xmin + 1] = 1
    end
    display(M)
end

function run()
    test = """6,10
0,14
9,10
0,3
10,4
4,11
6,0
6,12
4,1
0,13
10,12
3,4
3,0
8,4
1,10
2,14
8,10
9,0

fold along y=7
fold along x=5
"""

    # @assert test |> parse_problem |> part1 == 17

    input = read("resources/day13_input.txt", String)

    input |> parse_problem |> part1 |> display

    part2(parse_problem(test))
    part2(parse_problem(input))

    # display(parse_problem(input))
end

end


export axisar

"""
    axisar(axis, angle)

Construct a rotation matrix that rotates vectors by a specified `angle` about a specified `axis`.

### Arguments ###

- `axis`: Rotation axis
- `angle`: Rotation angle in radians

### Output ###

Rotation matrix corresponding to `axis` and `angle`

### References ###

- [NAIF Documentation](https://naif.jpl.nasa.gov/pub/naif/toolkit_docs/C/cspice/axisar_c.html)
"""
function axisar(axis, angle)
    @checkdims 3 axis
    r = Matrix{Float64}(undef, 3,3)
    ccall((:axisar_c, libcspice), Cvoid, (Ref{SpiceDouble}, SpiceDouble, Ref{SpiceDouble}), axis, angle, r)
    permutedims(r)
end


using Distributions
using JSON

r = 3
p = 0.25
d = NegativeBinomial( r, p )

x = [ -0.5, -0.25, 0, 0.25, 0.5 ]

dmgf(t) = mgf(d, t )
y = map( dmgf, x )
println( y )

data = Dict([
	("r", r),
	("p", p),
	("data", x),
	("expected", y)
])

outfile = open("./test/fixtures/number.json", "w")
JSON.json(data)

write( outfile, JSON.json(data) )


include("../src/ZZDiffusionBridge.jl")

using Random

function BM(ξ, L)
    u = 0.0
    v = randn()
    T = 1.0
    x =fs_expansion(ξ, u, v, L, T)
    t = range(0.0, stop=1, length=2<<(L) + 1)
    a = plot(leg=false)
    plot!(a, t, x, label = "random realization of X⁵", alpha = 0.5, color = :red)
    xaxis!(a, "t")
    scatter!(a, t, x, color= :black, markersize=2)
    return a
end

L =5
plot(BM(randn(2<<L-1), L))

function FSbasis(L)
    a = plot()
    plot!(a, 0:0.001:1, 0:0.001:1, label = "first $L levels basis functions", alpha = 0.5)
    for i in 0:L
        for k in 0:2^(i)-1
            plot!(a, 0:0.001:1, [Λ(t, i , k, 1.0) for t in 0:0.001:1], alpha = 0.5, leg=false)
            xaxis!(a, "t")
        end
    end
    return a
end

L = 6
Random.seed!(4)
ξ = randn(2^(L+1) - 1)
plot( FSbasis(L), BM(ξ, L), layout = (1,2))
savefig("../../output/bm_6levels.pdf")

using LinearAlgebra
using Plots
using Random

function zomming()
    dt = 10^(-4)
    t = 0.0:dt:100.0
    d = length(t)
    ν = 2.0
    μ = 0.0
    σ = 1.0
    N = randn(d-1)
    x = zeros(d)
    for i = 1:d-1
        x[i+1] = x[i] + ν*(μ -x[i])*dt  + σ*sqrt(dt)*N[i]
    end
    return (t, x)
end

function runall()
    tt, xx = zomming()
    f = length(tt)
    i = 1
    dt = 1000
    a = []
    for rep in 0:6
        id = i:dt:f
        push!(a, plot(tt[id], xx[id], color = :darkred, legend = false, xaxis = "t"))
        #, label = "zoom X$(2^rep)", legend =:bottomleft ))
        i = Int(ceil((i + f)*0.5))
        dt =  Int(ceil(dt/2))
    end
    return a
end

Random.seed!(5)
a = runall()

plot(plot(a[1], a[3], a[5], a[7], size = (750,500),
       layout = (2,2)))
savefig("../../output/bmzooming.pdf")

struct Normal
    μ::Float64
    σ::Float64
    v::Float64
    function Normal(μ, σ)
        new(μ, σ, inv(σ))
    end
end

function ZigZagSampler(N::Normal, ξ0::Float64, T::Float64)
    t = 0.0 ; θ = 1; ξ = ξ0
    Ξ = [ξ]
    Δ = [t]
    while (t<T)
        τ = sqrt.(-2*N.σ*log(rand()) + max(0, θ*(ξ - N.μ))^2) - θ*(ξ - N.μ)
        t = t + τ
        ξ = ξ + θ*τ
        push!(Ξ, ξ)
        push!(Δ, t)
        θ = -θ
    end
    return(Δ, Ξ)
end
function vel(x)
    θ = fill(0, length(x[1]))
    θ[1] = 1
    for i in 2:length(x[1])
        θ[i] = -θ[i-1]
    end
    return θ
end

Random.seed!(0)
x = ZigZagSampler(Normal(0.0,1.0), 0.0, 40.0)
θ = vel(x)
a1 = plot(x[1], x[2], leg= false,xaxis = "t", yaxis = "\\xi")
hline!(a1, [0.0], alpha = 0.4, color = :violet)
a2 =  plot(x[1], θ, linetype=:steppost, leg= false, xaxis = "t", yaxis = "\\theta")

plot( a1, a2, layout = (1,2), size = (500, 200))
savefig("../../output/zigzag1.pdf")


include("euler/euler.jl")
using .Calculus: triangular_numbers
using BenchmarkTools
BenchmarkTools.DEFAULT_PARAMETERS.samples = 100

function compute(path::String)::Int
    triangles = triangular_numbers(20)
    chars = Dict(Char(Int('A') + i) => i + 1 for i ∈ 0:25)
    triangle_words = 0
    for word ∈ split(replace(read(path, String), "\"" => ""), ",")
        value = sum(chars[letter] for letter ∈ word)
        if value ∈ triangles triangle_words += 1 end
    end
    return triangle_words
end

compute("problems/0042/p042_words.txt")

@benchmark compute("problems/0042/p042_words.txt")


# Parts of this code were taken / derived from Graphs.jl. See LICENSE for
# licensing details.

"""
    struct FloydWarshall <: ShortestPathAlgorithm

The structure used to configure and specify that [`shortest_paths`](@ref)
should use the [Floyd-Warshall algorithm](http://en.wikipedia.org/wiki/Floyd–Warshall_algorithm).
No additional configuration parameters are specified or required.

`FloydWarshall` is the default all-pairs algorithm used when no source is specified.

### Implementation Notes
`FloydWarshall` supports the following shortest-path functionality:
- non-negative distance matrices / weights
- all-pairs shortest paths

### Performance
Space complexity is on the order of ``\\mathcal{O}(|V|^2)``.
"""
struct FloydWarshall <: ShortestPathAlgorithm end
struct FloydWarshallResult{T, U<:Integer} <: ShortestPathResult
    dists::Matrix{T}
    parents::Matrix{U}
end

function shortest_paths(g::AbstractGraph{U}, distmx::AbstractMatrix{T}, ::FloydWarshall) where {T, U<:Integer}
    nvg = nv(g)
    # if we do checkbounds here, we can use @inbounds later
    checkbounds(distmx, Base.OneTo(nvg), Base.OneTo(nvg))

    fwdists = fill(typemax(T), (Int(nvg), Int(nvg)))
    parents = zeros(U, (Int(nvg), Int(nvg)))

    @inbounds for v in vertices(g)
        fwdists[v, v] = zero(T)
    end
    undirected = !is_directed(g)
    @inbounds for e in edges(g)
        u = src(e)
        v = dst(e)

        d = distmx[u, v]

        fwdists[u, v] = min(d, fwdists[u, v])
        parents[u, v] = u
        if undirected
            fwdists[v, u] = min(d, fwdists[v, u])
            parents[v, u] = v
        end
    end

    @inbounds for pivot in vertices(g)
        # Relax fwdists[u, v] = min(fwdists[u, v], fwdists[u, pivot]+fwdists[pivot, v]) for all u, v
        for v in vertices(g)
            d = fwdists[pivot, v]
            d == typemax(T) && continue
            p = parents[pivot, v]
            for u in vertices(g)
                ans = (fwdists[u, pivot] == typemax(T) ? typemax(T) : fwdists[u, pivot] + d)
                if fwdists[u, v] > ans
                    fwdists[u, v] = ans
                    parents[u, v] = p
                end
            end
        end
    end
    fws = FloydWarshallResult(fwdists, parents)
    return fws
end

function paths(s::FloydWarshallResult{T, U}, v::Integer) where {T, U<:Integer}
    pathinfo = s.parents[v, :]
    fwpaths = Vector{Vector{U}}()
    for i in 1:length(pathinfo)
        if (i == v) || (s.dists[v, i] == typemax(T))
            push!(fwpaths, Vector{U}())
        else
            path = Vector{U}()
            currpathindex = U(i)
            while currpathindex != 0
                push!(path, currpathindex)
                if pathinfo[currpathindex] == currpathindex
                    currpathindex = zero(currpathindex)
                else
                    currpathindex = pathinfo[currpathindex]
                end
            end
            push!(fwpaths, reverse(path))
        end
    end
    return fwpaths
end

shortest_paths(g::AbstractGraph, alg::FloydWarshall) = shortest_paths(g, weights(g), alg)

# If we don't specify an algorithm AND there's no source, use Floyd-Warshall.
shortest_paths(g::AbstractGraph, distmx::AbstractMatrix=weights(g)) =
    shortest_paths(g, distmx, FloydWarshall())

paths(s::FloydWarshallResult) = [paths(s, v) for v in 1:size(s.parents, 1)]
paths(st::FloydWarshallResult, s::Integer, d::Integer) = paths(st, s)[d]


using StokesDiffEq

## Change plot commands to GR

Δx = 1//2^3 #Super large for quick testing. Lower this for better results.
mesh = FDMMesh(Δx,mins=[-1;-1],maxs=[1;1])
prob = prob_stokes_homogenous

#DGS Convergence
@time sol = solve(prob,mesh,converrors=true,maxiters=200,alg=:DGS)

err1 = sol.converrors[:rul∞]
err2 = sol.converrors[:rvl∞]
err3 = sol.converrors[:rpl∞]

#TEST_PLOT && plot([1:length(err1) 1:length(err2) 1:length(err3)],[err1 err2 err3],yscale=:log10,xguide="Error",yguide="Iterations",title="DGS Convergence, Delta t = 2^{-3}",label=["u" "v" "p"])

#Multigrid Relative Convergence
@time sol = solve(prob,mesh,converrors=true,maxiters=20,alg=:Multigrid,levels=4)

err1 = sol.converrors[:rresul2]
err2 = sol.converrors[:rresvl2]
err3 = sol.converrors[:rrespl2]
#TEST_PLOT && plot([1:length(err1) 1:length(err2) 1:length(err3)],[err1 err2 err3],yscale=:log10,xguide="Error",yguide="Iterations",title="Multigrid Relative Residual Convergence, Delta t = 2^{-3}",label=["u" "v" "p"])

err1[end] < 1e-12


"""
    core_periphery_deg(g)

A simple degree-based core-periphery detection algorithm (see [Lip](http://arxiv.org/abs/1102.5511)).
Returns the vertex assignments (1 for core and 2 for periphery).
"""
function core_periphery_deg(g::Graph)
    degs = degree(g)
    p = sortperm(degs, rev=true)
    s = sum(degs) / 2.
    sbest = +Inf
    kbest = 0
    for k = 1:nv(g)-1
        s = s + k  - 1 - degree(g, p[k])
        if s < sbest
            sbest = s
            kbest = k
        end
    end
    c = 2 + zeros(Int, nv(g))
    c[p[1:kbest]] = 1
    c
end


function test_craig()
  craig_tol = 1.0e-6

  function test_craig(A, b; λ=0.0)
    (x, y, stats) = craig(A, b, λ=λ)
    r = b - A * x
    # if λ > 0
    #   s = r / sqrt(λ);
    #   r = r - sqrt(λ) * s;
    # end
    resid = norm(r) / norm(b)
    @printf("craig: residual: %7.1e\n", resid)
    return (x, y, stats, resid)
  end

  # Underdetermined consistent.
  A = rand(10, 25); b = A * ones(25)
  (x, y, stats, resid) = test_craig(A, b)
  @test(norm(x - A' * y) <= craig_tol * norm(x))
  @test(resid <= craig_tol)
  @test(stats.solved)
  (xI, xmin, xmin_norm) = check_min_norm(A, b, x)
  @test(norm(xI - xmin) <= cond(A) * craig_tol * xmin_norm)

  # Underdetermined inconsistent.
  A = ones(10, 25); A[1, :] .= 0; b = ones(10)
  (x, y, stats, resid) = test_craig(A, b)
  # @test(norm(x - A' * y) <= craig_tol * norm(x))
  show(stats)
  @test(stats.inconsistent)

  # Square consistent.
  A = rand(10, 10); b = A * ones(10)
  (x, y, stats, resid) = test_craig(A, b)
  @test(norm(x - A' * y) <= craig_tol * norm(x))
  @test(resid <= craig_tol)
  @test(stats.solved)
  (xI, xmin, xmin_norm) = check_min_norm(A, b, x)
  @test(norm(xI - xmin) <= cond(A) * craig_tol * xmin_norm)

  # Square inconsistent.
  A = ones(10, 10); A[1, :] .= 0; b = ones(10)
  (x, y, stats, resid) = test_craig(A, b)
  # @test(norm(x - A' * y) <= craig_tol * norm(x))
  @test(stats.inconsistent)

  # Overdetermined consistent.
  A = rand(25, 10); b = A * ones(10)
  (x, y, stats, resid) = test_craig(A, b)
  @test(norm(x - A' * y) <= craig_tol * norm(x))
  @test(resid <= craig_tol)
  @test(stats.solved)
  (xI, xmin, xmin_norm) = check_min_norm(A, b, x)
  @test(norm(xI - xmin) <= cond(A) * craig_tol * xmin_norm)

  # Overdetermined inconsistent.
  A = ones(5, 3); A[1, :] .= 0; b = ones(5)
  (x, y, stats, resid) = test_craig(A, b)
  # @test(norm(x - A' * y) <= craig_tol * norm(x))
  @test(stats.inconsistent)

  # With regularization, all systems are underdetermined and consistent.
  # (x, y, stats, resid) = test_craig(A, b, λ=1.0e-3)
  # @test(norm(x - A' * y) <= craig_tol * norm(x))
  # @test(resid <= craig_tol)
  # @test(stats.solved)
  # (xI, xmin, xmin_norm) = check_min_norm(A, b, x, λ=1.0e-3)
  # @test(norm(xI - xmin) <= cond(A) * craig_tol * xmin_norm)

  # Code coverage.
  (x, y, stats) = craig(sparse(A), b, λ=1.0e-3)
  show(stats)

  # Test b == 0
  (x, y, stats) = craig(A, zeros(size(A,1)), λ=1.0e-3)
  @test x == zeros(size(A,2))
  @test y == zeros(size(A,1))
  @test stats.status == "x = 0 is a zero-residual solution"

  # Test integer values
  A = [I; rand(1:10, 2, 3)]
  b = A * ones(Int, 3)
  (x, y, stats) = craig(A, b)
  @test stats.solved
end

test_craig()


using SemiLagrangian:
    UniformMesh, totuple, dotprod, dotprodother, length, step, prod, points, width, vec_k_fft, newval, traitmodbegin!, traitmodend!,
    meshtostd, stdtomesh

@testset "test tupleshape Mesh" begin
    t_deb = [-1 // 1, -10 // 1, -3 // 1, -1 // 1]
    t_end = [3 // 1, 6 // 1, 5 // 1, 1 // 1]
    t_sz = [20, 10, 8, 16]
    t_width = t_end - t_deb
    t_step = t_width ./ t_sz

    result = Array{Rational{Int},4}(undef, totuple(t_sz))
    ind = zeros(Int, 4)
    for ind[1] = 1:t_sz[1], ind[2] = 1:t_sz[2], ind[3] = 1:t_sz[3], ind[4] = 1:t_sz[4]
        res = 1 // 1
        for j = 1:4
            res *= t_deb[j] + (ind[j] - 1) * t_step[j]
        end
        result[ind[1], ind[2], ind[3], ind[4]] = res
    end

    tt_mesh = UniformMesh.(t_deb, t_end, t_sz)
    for (i, mesh) in enumerate(tt_mesh)
        @test t_width[i] == mesh.width == width(mesh)
    end
    t_mesh = totuple(tt_mesh)
    #     println("typeof(t_mesh)=$(typeof(t_mesh))")
    @time @test result == dotprod(points.(t_mesh))
    @time @test result == dotprodother(points.(t_mesh))

    t_v = points.(t_mesh)
    @time @test result == dotprod(t_v)
    @time @test result == dotprodother(t_v)

    @test prod(t_step) == prod(step, t_mesh)

    @test totuple(t_sz) == length.(t_mesh)
    @test t_sz == length.(tt_mesh)
    @test totuple(t_step) == step.(t_mesh)
    @test t_step == step.(tt_mesh)

end

function test_vec_k_fft(vbeg::T, vend::T, len) where {T}
    mid = div(len, 2)
    ref = circshift((-mid):(mid-1), mid)
    ref *= 2T(pi) / (vend - vbeg)
    @test ref == vec_k_fft(UniformMesh(vbeg, vend, len))
end

@testset "test vec_k_fft" begin
    test_vec_k_fft(-1.0, 1.0, 64)
    test_vec_k_fft(-big"1.0", big"1.0", 64)
end

function test_newval()
    @test newval(1.0, 1.2, 0.5, 1.0) ≈ 1.2
    @test newval(1.0, 0.2, 0.5, 1.0) ≈ 1.2
    @test newval(1.0, -0.8, 0.5, 1.0) ≈ 1.2
    @test newval(1.0, -10.8, 0.5, 1.0) ≈ 1.2
    @test newval(1.0, 3.3, 0.5, 1.0) ≈ 1.3
    @test newval(-2.4, 1.2, 0.5, 1.0) ≈ -2.8
    @test newval(-2.2, 1.2, 0.5, 1.0) ≈ -1.8
    @test newval(0.5, 1.2, 0.5, 1.0) ≈ 0.2
    @test newval(9.9,-9.7, 10.0, 20.0) ≈ 10.3
    @test newval(29.9,-9.7, 10.0, 20.0) ≈ 30.3
    @test newval(-9.7, 29.9, 10.0, 20.0) ≈ -10.1
    @test newval(0.0,-3.2,0.5,1.0) ≈ -0.2
    @test newval(0.0,-2.8,0.5,1.0) ≈ 0.2
    @test newval(0.0,-2.2,0.5,1.0) ≈ -0.2
    @test newval(0.0,-1.8,0.5,1.0) ≈ 0.2
    @test newval(0.0,-1.2,0.5,1.0) ≈ -0.2
    @test newval(0.0,-0.8,0.5,1.0) ≈ 0.2
    @test newval(0.0,-0.2,0.5,1.0) ≈ -0.2
    @test newval(0.0,0.2,0.5,1.0) ≈ 0.2

end

function test_traitmod(c1,c2,c3)

    tab = [ mod(i*c1+j*c2, 1.0)+c3 for i=1:10,j=1:10]

    mesh = UniformMesh(0.0,1.0,64)

    res = copy(tab)
    ret = traitmodbegin!(mesh,res)
    @test ret
    res2 = copy(res)
    ret2 = traitmodbegin!(mesh,res2)
    @test !ret2
    @test res2 == res

    traitmodend!(mesh, res)
    result1 = mod.(tab,1.0)
    result2 = mod.(res,1.0)
    @test result1 == result2 == res

    stdtab = meshtostd(mesh, tab)
    lg=float(length(mesh))
    stdres = copy(stdtab)
    ret = traitmodbegin!(lg, stdres)
    @test ret
    stdres2 = copy(stdres)
    ret2 = traitmodbegin!(lg, stdres2)
    @test !ret2
    @test stdres2 == stdres

    traitmodend!(lg, stdres)
    result1 = mod.(stdtab,lg)
    result2 = mod.(stdres,lg)
    @test result1 == result2 == stdres

    resres = stdtomesh(mesh,stdres)

    @test resres == res

end
@testset "test newval" begin
    test_newval()
end
@testset "test traimtmod" begin
    test_traitmod(0.127615, 0.0871745, -24.89897)
    test_traitmod(0.127615, -0.0871745, -24.89897)
    test_traitmod(-0.345, 0.1871745, 24.89897)
end


using NonlinearEigenproblemsTest
using NonlinearEigenproblems
using Test
using LinearAlgebra
using IterativeSolvers

# The user can create his own orthogonalization function to use in IAR
function doubleGS_function!(VV, vv, h)
    h[:]=VV'*vv; vv[:]=vv-VV*h; g=VV'*vv; vv[:]=vv-VV*g;
    h[] = h[]+g[]; β=opnorm(vv); vv[:]=vv/β; return β
end
# Then it is needed to create a type to access to this function
abstract type DoubleGS <: IterativeSolvers.OrthogonalizationMethod end
# And then introduce a function dispatch for this new type in order to use
# the defined orthogonalization function
import IterativeSolvers.orthogonalize_and_normalize!
function orthogonalize_and_normalize!(V,v,h,::Type{DoubleGS})
    doubleGS_function!(V, v, h) end

@testset "TIAR" begin
    n=100;
    dep=nep_gallery("dep0",n);

    @bench @testset "accuracy eigenpairs" begin
        (λ,Q)=tiar(dep,σ=1.1,γ=3,neigs=2,v=ones(n),maxit=50,tol=eps()*100,errmeasure=ResidualErrmeasure(dep));
        verify_lambdas(2, dep, λ, Q, eps()*100)
    end

    @testset "Compute as many eigenpairs as possible (neigs=Inf)" begin
        (λ,Q)=tiar(dep,σ=1.1,γ=3,neigs=Inf,v=ones(n),maxit=50,tol=eps()*100,errmeasure=ResidualErrmeasure(dep));
        verify_lambdas(7, dep, λ, Q, eps()*100)
    end

    @testset "orthogonalization" begin

    # NOW TEST DIFFERENT ORTHOGONALIZATION METHODS
    @bench @testset "DGKS" begin
        (λ,Q,Z)=tiar(dep,σ=1.1,γ=3,neigs=4,v=ones(n),maxit=50,tol=eps()*100,errmeasure=ResidualErrmeasure(dep));
        @test opnorm(Z'*Z - I) < 1e-6
     end

     @bench @testset "User provided doubleGS" begin
         (λ,Q,Z)=tiar(dep,σ=1.1,γ=3,neigs=4,v=ones(n),maxit=50,tol=eps()*100);
         @test opnorm(Z'*Z - I) < 1e-6
      end

      @bench @testset "ModifiedGramSchmidt" begin
          (λ,Q,Z)=tiar(dep,σ=1.1,γ=3,neigs=4,v=ones(n),maxit=50,tol=eps()*100);
          @test opnorm(Z'*Z - I) < 1e-6
      end

       @bench @testset "ClassicalGramSchmidt" begin
           (λ,Q,Z)=tiar(dep,σ=1.1,γ=3,neigs=4,v=ones(n),maxit=50,tol=eps()*100);
           @test opnorm(Z'*Z - I) < 1e-6
       end
    end

    # iar and tiar ara mathematically equivalent it maxit<<nep
    # verify the equivalence numerically
    @bench @testset "equivalance with IAR" begin
        (λ_tiar,Q_tiar)=tiar(dep,σ=1.1,γ=3,neigs=2,v=ones(n),maxit=50,tol=eps()*100);
        (λ_iar,Q_iar)=   iar(dep,σ=1.1,γ=3,neigs=2,v=ones(n),maxit=50,tol=eps()*100);

        # Relaxed to only test abs-values to avoid problems if iar / tiar finds
        # the other conjugate solution
        for k=1:size(λ_tiar,1);
            @test minimum(abs.(abs(λ_iar[k]) .- abs.(λ_tiar))) < 1e-6
            @test minimum(map(j-> norm(abs.(Q_tiar[:,k]/Q_tiar[1,k])-abs.(Q_iar[:,j]/Q_iar[1,k])),1:size(λ_iar,1)))<1e-6
        end

    end
    @bench @testset "Solve by projection" begin
        np=1000;

        depp=nep_gallery("dep0",np);
        nn=opnorm(compute_Mder(depp,0));
        errmeasure= (λ,v) -> norm(compute_Mlincomb(depp,λ,v))/nn;

        λ,Q = tiar(depp, σ=0, γ=3, neigs=3, v=ones(np), maxit=50,
                   tol=sqrt(eps()), check_error_every=3,
                   proj_solve=true, inner_solver_method=IARInnerSolver(),
                   errmeasure=errmeasure);

        @test errmeasure(λ[1],Q[:,1])<sqrt(eps())*10
    end

    @testset "Errors thrown" begin
        np=100;
        dep=nep_gallery("dep0",np);
        @test_throws NEPCore.NoConvergenceException (λ,Q)=tiar(dep,σ=2.0,γ=3,neigs=4,v=ones(np),maxit=5,tol=eps()*100,errmeasure=ResidualErrmeasure(dep));
    end

end


""" The category of finite sets and relations, and its skeleton.
"""
module FinRelations
export BoolRig, FinRel, FinRelation, FinRelationCallable, FinRelationMatrix,
  force

import Base: +, *
using AutoHashEquals
import FunctionWrappers: FunctionWrapper

using ...GAT
using ...Theories: DistributiveBicategoryRelations
import ...Theories: dom, codom, id, compose, ⋅, ∘, dagger, dunit, dcounit,
  otimes, ⊗, munit, braid, oplus, ⊕, mzero, swap,
  mcopy, Δ, mmerge, ∇, delete, ◊, create, □, plus, zero, coplus, cozero,
  pair, copair, proj1, proj2, coproj1, coproj2, meet, join, top, bottom
import ..FinCats: force
using ..Matrices
using ..Matrices: zero_matrix

# Rig of booleans
#################

""" The rig of booleans.

This struct is needed because in base Julia, the product of booleans is another
boolean, but the sum of booleans is coerced to an integer: `true + true == 2`.
"""
@auto_hash_equals struct BoolRig <: Number
  value::Bool
end

+(x::BoolRig, y::BoolRig) = BoolRig(x.value || y.value)
*(x::BoolRig, y::BoolRig) = BoolRig(x.value && y.value)

Base.zero(::Type{BoolRig}) = BoolRig(false)
Base.one(::Type{BoolRig}) = BoolRig(true)
Base.float(x::BoolRig) = float(x.value)
Base.show(io::IO, x::BoolRig) = print(io, x.value)

# Category of finite relations
##############################

""" Object in the category of finite sets and relations.

See also: [`FinSet`](@ref).
"""
@auto_hash_equals struct FinRel{S,T}
  set::S
end

FinRel(i::Int) = FinRel{Int,Int}(i)
FinRel(set::S) where {T,S<:AbstractSet{T}} = FinRel{S,T}(set)

Base.eltype(::Type{FinRel{S,T}}) where {S,T} = T
Base.iterate(s::FinRel, args...) = iterate(iterable(s), args...)
Base.length(s::FinRel) = length(iterable(s))
Base.in(s::FinRel, elem) = in(s, iterable(s))
iterable(s::FinRel{Int}) = 1:s.set
iterable(s::FinRel{<:AbstractSet}) = s.set

""" Binary relation between finite sets.

A morphism in the category of finite sets and relations. The relation can be
represented implicitly by an arbitrary Julia function mapping pairs of elements
to booleans or explicitly by a matrix (dense or sparse) taking values in the rig
of booleans ([`BoolRig`](@ref)).
"""
abstract type FinRelation{S,S′,T,T′} end

FinRelation(R::Function, args...) = FinRelationCallable(R, args...)
FinRelation(R::AbstractMatrix, args...) = FinRelationMatrix(R, args...)

""" Relation in FinRel defined by a callable Julia object.
"""
@auto_hash_equals struct FinRelationCallable{S,S′,T,T′} <: FinRelation{S,S′,T,T′}
  # Field `rel` is usually a `Function` but can be any Julia callable.
  rel::FunctionWrapper{Bool,Tuple{T,T′}}
  dom::FinRel{S,T}
  codom::FinRel{S′,T′}

  FinRelationCallable(R, dom::FinRel{S,T}, codom::FinRel{S′,T′}) where {S,S′,T,T′} =
    new{S,S′,T,T′}(FunctionWrapper{Bool,Tuple{T,T′}}(R), dom, codom)
end

FinRelationCallable(R, dom::Int, codom::Int) =
  FinRelationCallable(R, FinRel(dom), FinRel(codom))

(R::FinRelationCallable{S,T})(x::T, y::T) where {S,T} = R.rel(x, y)

""" Relation in FinRel represented by a boolean matrix.

Boolean matrices are also known as logical matrices or relation matrices.
"""
@auto_hash_equals struct FinRelationMatrix{M<:AbstractMatrix{BoolRig}} <: FinRelation{Int,Int,Int,Int}
  rel::M
end

function FinRelationMatrix(R::AbstractMatrix{BoolRig}, dom::Int, codom::Int)
  @assert size(R) == (dom, codom)
  FinRelationMatrix(R)
end
function FinRelationMatrix(R::AbstractMatrix{BoolRig},
                           dom::FinRel{Int}, codom::FinRel{Int})
  FinRelationMatrix(R, length(dom), length(codom))
end

(R::FinRelationMatrix)(x, y) = R.rel[y, x].value

function force(::Type{T}, R::FinRelation) where T <: AbstractMatrix{BoolRig}
  m, n = length(dom(R)), length(codom(R))
  M = zero_matrix(T, n, m)
  for i in 1:m, j in 1:n
    if R(i,j)
      M[j,i] = BoolRig(true)
    end
  end
  FinRelationMatrix(M)
end
force(::Type{T}, R::FinRelationMatrix{T}) where T <: AbstractMatrix{BoolRig} = R
force(R::FinRelation) = force(Matrix{BoolRig}, R)

""" FinRel as a distributive bicategory of relations.

FIXME: Many methods only work for `FinRel{Int}`. The default methods should
assume `FinRel{<:AbstractSet}` and the case `FinRel{Int}` should be handled
specially.
"""
@instance DistributiveBicategoryRelations{FinRel, FinRelation} begin
  dom(R::FinRelation) = R.dom
  codom(R::FinRelation) = R.codom

  id(A::FinRel) = FinRelation((x,y) -> x == y, A, A)

  function compose(R::FinRelation, S::FinRelation)
    @assert codom(R) == dom(S)
    FinRelation(dom(R), codom(S)) do x,z
      any(R(x,y) && S(y,z) for y in codom(R))
    end
  end

  # Multiplicative monoidal structure

  otimes(A::FinRel, B::FinRel) = FinRel(length(A) * length(B))
  munit(::Type{FinRel}) = FinRel(1)

  function otimes(R::FinRelation, S::FinRelation)
    # Indexing is consistent with that of Kronecker products.
    dom_proj = CartesianIndices((length(dom(S)), length(dom(R))))
    cod_proj = CartesianIndices((length(codom(S)), length(codom(R))))
    FinRelation(dom(R)⊗dom(S), codom(R)⊗codom(S)) do x, y
      R(dom_proj[x][2], cod_proj[y][2]) && S(dom_proj[x][1], cod_proj[y][1])
    end
  end

  function braid(A::FinRel, B::FinRel)
    dom_proj = CartesianIndices((length(B), length(A)))
    cod_proj = CartesianIndices((length(A), length(B)))
    FinRelation(A⊗B, B⊗A) do x, y
      dom_proj[x][1] == cod_proj[y][2] && dom_proj[x][2] == cod_proj[y][1]
    end
  end

  function mcopy(A::FinRel)
    n = length(A)
    proj = CartesianIndices((n, n))
    FinRelation((x,y) -> x == proj[y][1] && x == proj[y][2], A, A⊗A)
  end
  function mmerge(A::FinRel)
    n = length(A)
    proj = CartesianIndices((n, n))
    FinRelation((x,y) -> proj[x][1] == y && proj[x][2] == y, A⊗A, A)
  end
  delete(A::FinRel) = FinRelation((x,y) -> true, A, munit(FinRel))
  create(A::FinRel) = FinRelation((x,y) -> true, munit(FinRel), A)

  dagger(R::FinRelation) = FinRelation((x,y) -> R(y,x), codom(R), dom(R))
  dunit(A::FinRel) = compose(create(A), mcopy(A))
  dcounit(A::FinRel) = compose(mmerge(A), delete(A))

  # Additive monoidal structure

  oplus(A::FinRel, B::FinRel) = FinRel(length(A) + length(B))
  mzero(::Type{FinRel}) = FinRel(0)

  function oplus(R::FinRelation, S::FinRelation)
    m, n = length(dom(R)), length(codom(R))
    FinRelation(dom(R)⊕dom(S), codom(R)⊕codom(S)) do x, y
      (x <= m && y <= n && R(x, y)) || (x > m && y > n && S(x-m, y-n))
    end
  end

  function swap(A::FinRel, B::FinRel)
    m, n = length(A), length(B)
    FinRelation((x,y) -> x == y-n || x-m == y, A⊕B, B⊕A)
  end

  function plus(A::FinRel)
    n = length(A)
    FinRelation((x,y) -> x == y || x-n == y, A⊕A, A)
  end
  function coplus(A::FinRel)
    n = length(A)
    FinRelation((x,y) -> x == y || x == y-n, A, A⊕A)
  end
  zero(A::FinRel) = FinRelation((x,y) -> false, mzero(FinRel), A)
  cozero(A::FinRel) = FinRelation((x,y) -> false, A, mzero(FinRel))

  pair(R::FinRelation, S::FinRelation) = coplus(dom(R))⋅(R⊕S)
  copair(R::FinRelation, S::FinRelation) = (R⊕S)⋅plus(codom(R))
  proj1(A::FinRel, B::FinRel) = id(A)⊕cozero(B)
  proj2(A::FinRel, B::FinRel) = cozero(A)⊕id(B)
  coproj1(A::FinRel, B::FinRel) = id(A)⊕zero(B)
  coproj2(A::FinRel, B::FinRel) = zero(A)⊕id(B)

  # Lattice structure

  function meet(R::FinRelation, S::FinRelation)
    @assert dom(R) == dom(S) && codom(R) == codom(S)
    FinRelation((x,y) -> R(x,y) && S(x,y), dom(R), codom(R))
  end
  function join(R::FinRelation, S::FinRelation)
    @assert dom(R) == dom(S) && codom(R) == codom(S)
    FinRelation((x,y) -> R(x,y) || S(x,y), dom(R), codom(R))
  end
  top(A::FinRel, B::FinRel) = FinRelation((x,y) -> true, A, B)
  bottom(A::FinRel, B::FinRel) = FinRelation((x,y) -> false, A, B)
end

# For relation matrices, delegate to the category of matrices.
const FinRelMat = FinRelationMatrix

dom(R::FinRelMat) = FinRel(size(R.rel, 2))
codom(R::FinRelMat) = FinRel(size(R.rel, 1))

compose(R::FinRelMat, S::FinRelMat) = FinRelMat(compose(R.rel, S.rel))
otimes(R::FinRelMat, S::FinRelMat) = FinRelMat(otimes(R.rel, S.rel))
oplus(R::FinRelMat, S::FinRelMat) = FinRelMat(oplus(R.rel, S.rel))
dagger(R::FinRelMat) = FinRelMat(transpose(R.rel))

meet(R::FinRelMat, S::FinRelMat) = FinRelMat(R.rel .* S.rel)
join(R::FinRelMat, S::FinRelMat) = FinRelMat(R.rel .+ S.rel)
pair(R::FinRelMat, S::FinRelMat) = FinRelMat(pair(R.rel, S.rel))
copair(R::FinRelMat, S::FinRelMat) = FinRelMat(copair(R.rel, S.rel))

end


using PolynomialTestSystems
using Test

@testset "getters" begin
    F = chandra(5)
    @test length(equations(F)) == 5
    @test nvariables(F) == 5
    @test bezout_number(F) == 32
    @test multi_bezout_number(F) !== nothing
    @test mixed_volume(F) == 16

    @test nsolutions(heart()) == 4
    @test nreal_solutions(heart()) == 2
end

@testset "systems" begin
    for n=3:11
        @test chandra(n) isa TestSystem
        @test cyclic(n) isa TestSystem
        @test katsura(n) isa TestSystem
    end

    @test ipp() isa TestSystem
    @test ipp2() isa TestSystem
    @test fourbar() isa TestSystem
    @test rps10() isa TestSystem

    @test boon() isa TestSystem
    @test heart() isa TestSystem
    @test d1() isa TestSystem
    @test bacillus_subtilis() isa TestSystem
    @test griewank_osborne() isa TestSystem
    @test tritangents() isa TestSystem
    @test cyclooctane() isa TestSystem
end


function fT(x,Param)
str = lower(Param.ProfTheta)
if str == "baldaufsphere"
    (lon,lat,r)=cart2sphere(x[1],x[2],x[3]);
    r=r-Param.RadEarth;
    p=Param.p0*exp(-Param.Grav*r/(Param.Rd*Param.T0));
    Rho=p/(Param.Rd*Param.T0);
    T=p/(Rho*Param.Rd);
    d=acos(sin(Param.lat0)*sin(lat)+cos(Param.lat0)*cos(lat)*cos(lon-Param.lon0));
    T=T+Param.DeltaT*exp(-Param.ExpDist*d)*sin(pi*r/Param.H);
elseif str == "baldaufcart"
    delta=Param.Grav/(Param.Rd*Param.T0);

    dT=Param.DeltaT*exp(-(x[1]-Param.xc)^2/Param.d^2)*sin(pi*x[3]/Param.H);

    T=Param.T0+exp(delta/2*x[3])*dT;
end
return T
end


##############################################################################
##
## Generalized reduced rank tests using the singularvalue decomposition
## Kleibergen Paap, Journal of Econometrics (2006)
##
## See also the Stata command ranktest
## RANKTEST: Stata module to test the rank of a matrix using the Kleibergen-Paap rk statistic
## Authors: Frank Kleibergen, Mark E Schaffer
##
## More precisely, it corresponds to the Stata command:  ranktest  (X) (Z), wald full
##############################################################################

function ranktest!(X::Matrix{Float64},
                    Z::Matrix{Float64},
                    Pi::Matrix{Float64},
                    vcov_method::CovarianceEstimator,
                    df_small::Int,
                    df_absorb::Int)

    if (size(X, 2) == 0) | (size(Z, 2) == 0)
        return NaN
    end

    # Compute theta
    Fmatrix = cholesky!(Symmetric(Z' * Z), check = false).U
    Gmatrix = cholesky!(Symmetric(X' * X), check = false).U
    theta = Fmatrix * (Gmatrix' \ Pi')'

    # compute lambda
    svddecomposition = svd(theta, full = true)
    u = svddecomposition.U
    vt = svddecomposition.Vt

    k = size(X, 2)
    l = size(Z, 2)

    u_sub = u[k:l, k:l]
    a_qq = u[1:l, k:l] * (u_sub \ sqrt(u_sub * u_sub'))

    vt_sub = vt[k,k]
    b_qq = sqrt(vt_sub * vt_sub') * (vt_sub' \ vt[1:k, k]')

    kronv = kron(b_qq, a_qq')
    lambda = kronv * vec(theta)

    # compute vhat
    if vcov_method isa Vcov.SimpleCovariance
        vlab = cholesky!(Hermitian((kronv * kronv') ./ size(X, 1)), check = false)
    else
        K = kron(Gmatrix, Fmatrix)'
        vcovmodel = Vcov.VcovData(Z, K, X, size(Z, 1) - df_small - df_absorb)
        matrix_vcov2 = Vcov.S_hat(vcovmodel, vcov_method)
        vhat = K \ (K \ matrix_vcov2)'
        vlab = cholesky!(Hermitian(kronv * vhat * kronv'), check = false)
    end
    r_kp = lambda' * (vlab \ lambda)
    return r_kp[1]
end


using ApproxFun

M = 50
d₋, d₊ = -M .. 0, 0 ..M
D = Derivative()

V₋ = Fun(x -> sech(x), d₋)
V₊ = Fun(x -> sech(x), d₊)

# ψ'' + (V(x) + k^2) ψ = 0

k = 0.0001

ψ = [ivp(); D^2  + (V₋ + k^2)] \ [exp(im*k*(-M)), im*k*(exp(im*k*(-M))), 0.0]


# energy (epsxc) only
function XC_c_pbe_E( rho, grho )

    ga = 0.0310906908696548950
    be = 0.06672455060314922
    third = 1.0/3.0
    pi34 = 0.6203504908994
    xkf = 1.919158292677513
    xks = 1.128379167095513

    rs = pi34/rho^third
    ec = XC_c_pw_E( rho )

    kf = xkf/rs
    ks = xks * sqrt(kf)
    t = sqrt(grho) / (2.0 * ks * rho)

    expe = exp(-ec/ga)
    af = be / ga * (1.0 / (expe - 1.0) )

    y = af * t * t

    xy = (1.0 + y) / (1.0 + y + y * y)

    qy = y * y * (2.0 + y) / (1.0 + y + y * y)^2

    s1 = 1.0 + be / ga * t * t * xy
    h0 = ga * log(s1)

    sc = rho * h0

    return sc

end


isapproxzero(a) = isapprox(a, 0.0; atol = 1e-8)

function findMaxAbsInColumn(A::Matrix, k)
    rows, _ = size(A)
    maxPosition = k
    for i in k+1:rows
        if abs(A[i, k]) > abs(A[maxPosition, k])
            maxPosition = i
        end
    end

    return maxPosition
end

function column_to_zeroes!(A, k, maxPosition)
    if maxPosition != k
        A[k, :], A[maxPosition, :] = A[maxPosition, :], A[k, :]
    end

    if isapproxzero(A[k,k])
        for i in k:lastindex(A,1)
            A[i, k] = 0
            return
        end
    end

    for i in k+1:(lastindex(A, 1))
        if isapproxzero(A[i,k])
            A[i,k] = 0
            continue
        end
        t = -A[i,k]/A[k,k]
        A[i, i:end] += t *(A[k, i:end])
        A[i,k] = 0
    end
end

function convert!(A)
    rows, columns = size(A)
    for k in 1:rows
        imax = findMaxAbsInColumn(A, k)
        column_to_zeroes!(A, k, imax)
    end
end


using Test, FinancialMonteCarlo;
@show "Black Scholes Model"
S0=100.0;
K=100.0;
r=0.02;
T=1.0;
d=0.01;
D=90.0;

underlying_name="ENJ"
underlying_name_2="ABPL"

underlying_=underlying_name*"_"*underlying_name_2*"_TESL"

Nsim=10000;
Nstep=30;
sigma=0.2;
mc=MonteCarloConfiguration(Nsim,Nstep);
mc1=MonteCarloConfiguration(Nsim,Nstep,FinancialMonteCarlo.AntitheticMC());
toll=0.8

rfCurve=ZeroRate(r);

FwdData=Forward(T)
EUData=EuropeanOptionND(T,K)
AMData=AmericanOption(T,K)
BarrierData=BarrierOptionDownOut(T,K,D)
AsianFloatingStrikeData=AsianFloatingStrikeOption(T)
AsianFixedStrikeData=AsianFixedStrikeOption(T,K)
Model_enj=BlackScholesProcess(sigma,Underlying(S0,d));
Model_abpl=BlackScholesProcess(sigma,Underlying(S0,d));
Model_tesl=BlackScholesProcess(sigma,Underlying(S0,d));
rho=[1.0 0.0 0.0; 0.0 1.0 0.0; 0.0 0.0 1.0];
Model=GaussianCopulaNVariateProcess(rho,Model_enj,Model_abpl,Model_tesl)

###############

p=EuropeanOption(1.0,70.0)+Forward(1.0)
p2=3.0*p;
port_=Array{typeof(p)}(undef,7)
port_[1]=p
port_[2]=p2

pricer(Model,rfCurve,mc,port_)


# Wiktorsson method
# First published by Wiktorsson, 2001

struct Wiktorsson <: AbstractIteratedIntegralAlgorithm end

convorder(::Wiktorsson) = 1//1
errcoeff(m, h, ::Wiktorsson, ::MaxL2) = √(5*m)*h/(√12*π)
norv(m, n, ::Wiktorsson) = 2*m*n+(m^2-m)÷2

"""
    levyarea(W, n, alg::Wiktorsson)

Simulates an approximation of the iterated Itô-integrals ``\\int_0^1W_s\\otimes dW_s``
of the given ``m``-dimensional increment of a Wiener process with step size 1.
The parameter ``n`` specifies the number of terms in the approximation and thus determines the accuracy.
This is an efficient implementation of the algorithm proposed in [Wiktorsson, 2001](@ref wiktorsson2001).
It is based on the Fourier method from Milstein but incorporates an additional tail sum approximation.
The algorithm needs approximately ``2\\cdot m^2+2\\cdot m\\cdot n+m`` Float's.
The time complexity is ``\\mathcal{O}(m^2\\cdot n)``.
"""
function levyarea(W::AbstractVector{T}, n::Integer, alg::Wiktorsson) where {T}
    rng = default_rng()
    m = length(W)
    # Preallocate
    A = similar(W,m,m) # allocates m*m Floats
    G = similar(W,m,m) # allocates m*m Floats
    # 1. Simulate Xₖ and Yₖ and approximate stochastic area integral
    X = randn(rng, T, n, m) # allocates m*n Floats
    Y = randn(rng, T, m, n) # allocates m*n Floats
    Y .= (Y .- √(T(2)).*W) ./ (1:n)'
    mul!(A,Y,X)
    # 2.a Simulate Gₙ
    a = T(√(2*trigamma(n+1)))
    for j=1:m
        @inbounds G[j,j] = zero(T)
        for i=j+1:m
            g = a * randn(rng, T)
            @inbounds G[i,j] = g
            @inbounds G[j,i] = -g
            @inbounds A[i,j] += g
        end
    end
    # 2.b and add the tail-sum approximation
    A .+= inv(1+√(1+W'*W)) .* (G*W) .* W' # allocates m Floats
    # 3. Calculate the iterated integrals
    G .= inv(2*T(π)).*(A .- A') # reuse G to save allocations
    return G
end


module Dim1
import mFEM

# calc gauss_quad on interval [-1,1]
# u: (x::Float64)::Float64
function _gauss(u::Function)
  # c = sqrt(3/5)
  # xw = [-c 5/9; 0 8/9; c 5/9]

  a = 0.9061798
  b = 0.5384693
  c = 0
  wa = 0.2369269
  wb = 0.4786287
  wc = 0.5688889

  xw = [a wa; -a wa; b wb; -b wb; c wc]
  xwn,_ = size(xw)

  D = map(1:xwn) do i
    x,w = xw[i,:]
    return w * u(x)
  end
  return sum(D)
end

function uniform_mesh(a,b,N)::mFEM.Mesh
  @assert N > 1
  @assert a < b

  Nm = N + 1
  h = (b - a)/N

  P = [a + (i - 1)*h for i=1:Nm]
  T = zeros(Int,N,2)
  for i=1:N
    T[i,:] = [i,i+1]
  end

  BNode = [1,Nm]
  BEage = []
  BSurface = []

  # calc gauss_quad on the nth element of mesh
  # u: (x::Tuple)::Float64
  # n index of basis element
  function gauss_quad(u::Function,n::Int)
    a = P[T[n,1],1]
    b = P[T[n,2],1]

    @assert a < b
    return (b-a) * _gauss(x-> u(( (b-a)*x/2 + (a+b)/2, )) ) / 2
  end

  return mFEM.Mesh(P,T,BNode,BEage,BSurface,gauss_quad)
end

function Dϕ_reference_linear(k::Int, xt::Tuple, α::Int)
  @assert α ∈ [1,2]
  @assert k ∈ [0,1]

  local x = xt[1]

  if α == 1
    if k == 0
      return 1 - x
    elseif k == 1
      return  -1
    end
  elseif  α == 2
    if k == 0
      return x
    elseif k == 1
      return 1
    end
  end
end

function Dϕ_reference_quadratic(k::Int, xt::Tuple, α::Int)
  @assert α ∈ [1,2,3]
  @assert k ∈ [0,1,2]

  local x = xt[1]

  if α == 1
    if k == 0
      return 2x^2 - 3x + 1
    elseif k == 1
      return 4x - 3
    elseif k ==2
      return 4
    end
  elseif  α == 2
    if k == 0
      return 2x^2 - x
    elseif k == 1
      return 4x - 1
    elseif k ==2
      return 4
    end
  elseif  α == 3
    if k == 0
      return -4x^2 + 4x
    elseif k == 1
      return -8x + 4
    elseif k ==2
      return -8
    end
  end
end

function Dϕ_reference_cubic(k::Int, xt::Tuple, α::Int)
  @assert α ∈ 1:4
  @assert k ∈ 0:2

  local x = xt[1]

  if α == 1
    if k == 0
      return -4.5x^3 + 9x^2 -5.5x + 1
    elseif k == 1
      return -3*4.5x^2 + 2*9x -5.5
    elseif k == 2
      return -2*3*4.5x + 2*9
    end
  elseif α == 2
    if k == 0
      return 4.5x^3 - 4.5x^2 + x
    elseif k == 1
      return 3*4.5x^2 - 2*4.5x + 1
    elseif k == 2
      return 2*3*4.5x - 2*4.5
    end
  elseif α == 3
    if k == 0
      return 13.5x^3 - 22.5x^2 + 9x
    elseif k == 1
      return 3*13.5x^2 - 2*22.5x + 9
    elseif k == 2
      return 2*3*13.5x - 2*22.5
    end
  elseif α == 4
    if k == 0
      return -13.5x^3 + 18x^2 - 4.5x
    elseif k == 1
      return -3*13.5x^2 + 2*18x - 4.5
    elseif k == 2
      return -2*3*13.5x + 2*18
    end
  end
end

function reference2local(Dϕ_reference::Function;Pb::Array,Tb::Array,Nlb::Int)::Function
  # kth deriavtive of basis function at x
  function Dϕ(k::Int, xt::Tuple, n::Int, α::Int)
    @assert α ∈ 1:Nlb
    @assert k ∈ [0,1,2]
    @assert length(xt) == 1

    x = xt[1]

    xn1 = Pb[Tb[n,1],1]
    xn2 = Pb[Tb[n,2],1]

    x_hat = (x - xn1) / (xn2 - xn1)
    xt_hat = (x_hat,)

    if k == 0
      return Dϕ_reference(k,xt_hat,α)
    elseif k == 1
      return Dϕ_reference(k,xt_hat,α) / (xn2 - xn1)
    elseif k == 2
      @error "TODO"
    end
  end

  return Dϕ
end

function linear_elem(mesh::mFEM.Mesh)::mFEM.Basis
  Pb = copy(mesh.P)
  Tb = copy(mesh.T)
  Nlb = 2
  BNode = copy(mesh.BNode)
  BEage= copy(mesh.BEage)
  BSurface= copy(mesh.BSurface)
  BNode = copy(mesh.BNode)

  return mFEM.Basis(Nlb,Pb,Tb,BNode,BEage,BSurface,
    reference2local(Dϕ_reference_linear;Pb=Pb,Tb=Tb,Nlb=Nlb))
end

function quadatic_elem(mesh::mFEM.Mesh)::mFEM.Basis
  Pb = zeros(Float64,size(mesh.P,1) * 2 - 1,1)
  Tb = zeros(Int,size(mesh.T,1),3)
  Nlb = 3

  for i in 1:size(mesh.P,1)
    Pb[2i - 1] = mesh.P[i]
  end

  for i in 1:(size(mesh.P,1) - 1)
    Pb[2i] = (Pb[2i-1] + Pb[2i+1]) / 2
  end

  for i in 1:size(mesh.T,1)
    Tb[i,1] = 2 * mesh.T[i,1] - 1
    Tb[i,2] = 2 * mesh.T[i,2] - 1
    Tb[i,3] = (Tb[i,1] + Tb[i,2]) / 2
  end

  BNode = zeros(Int,size(mesh.BNode,1),1)
  for i in 1:size(BNode,1)
    BNode[i] = 2 * mesh.BNode[i] - 1
  end

  BEage = copy(mesh.BEage)
  BSurface = copy(mesh.BSurface)

  return mFEM.Basis(Nlb,Pb,Tb,BNode,BEage,BSurface,
    reference2local(Dϕ_reference_quadratic;Pb=Pb,Tb=Tb,Nlb=Nlb)
  )
end

function cubic_elem(mesh::mFEM.Mesh)::mFEM.Basis
  Pb = zeros(Float64,size(mesh.P,1) * 3 - 2,1)
  Tb = zeros(Int,size(mesh.T,1),4)
  Nlb = 4

  for i in 1:size(mesh.P,1)
    Pb[3i - 2] = mesh.P[i]
  end
  for i in 1:(size(mesh.P,1) - 1)
    Pb[3i - 1] = (2/3)* Pb[3i-2] + (1/3) * Pb[3i + 1]
    Pb[3i] = (1/3)* Pb[3i-2] + (2/3) * Pb[3i + 1]
  end

  for i in 1:size(mesh.T,1)
    Tb[i,1] = 3 * mesh.T[i,1] - 2
    Tb[i,2] = 3 * mesh.T[i,2] - 2
    Tb[i,3] = Tb[i,1] + 1
    Tb[i,4] = Tb[i,1] + 2
  end

 BNode = zeros(Int,size(mesh.BNode,1),1)
  for i in 1:size(BNode,1)
    BNode[i] = 3 * mesh.BNode[i] - 2
  end

  BEage = copy(mesh.BEage)
  BSurface = copy(mesh.BSurface)

  return mFEM.Basis(Nlb,Pb,Tb,BNode,BEage,BSurface,
    reference2local(Dϕ_reference_cubic;Pb=Pb,Tb=Tb,Nlb=Nlb)
  )
end

function Possion(mesh::mFEM.Mesh,c::Function,f::Function,g::Function;
    trial::mFEM.Basis = linear_elem(mesh),test::mFEM.Basis = linear_elem(mesh))
  # generate boundary nodes
  boundarynodes = zeros(Int,size(trial.BNode,1),2)
  for i=1:size(boundarynodes,1)
    boundarynodes[i,:] = [-1,trial.BNode[i]]
  end

  A = mFEM.stiffness_mat(mesh,trial,test,c)
  b = mFEM.load_vec(mesh,test,f)

  mFEM.set_boundary_cond!(trial,boundarynodes,A,b,g)
  x = A \ b
  return x
end

function test(n,;finite_elem::Function=linear_elem)
  u(x) = x * cos(x)
  c(x) = exp(x)
  f(x) = - exp(x) *(cos(x) - 2sin(x) - x*cos(x) - x*sin(x))
  g(x,i) = u(x[1])

  function tuplfy1d(f::Function)::Function
    return (x::Tuple) -> f(x[1])
  end

  m = uniform_mesh(0,1,n)
  trial = finite_elem(m)
  test= finite_elem(m)

  U = Possion(m,tuplfy1d(c),tuplfy1d(f),g;trial=trial,test=test)
  return maximum(@.abs(U-map(u,trial.Pb)))
end

test_quadatic(n) = test(n,finite_elem=quadatic_elem)
test_linear(n) = test(n,finite_elem=linear_elem)
test_cubic(n) = test(n,finite_elem=cubic_elem)

end # Dim1


using Test
using ManifoldsBase
using ManifoldsBase: AbstractNumbers, ℝ, ℂ, ℍ, _unify_number_systems

struct NotImplementedNumbers <: ManifoldsBase.AbstractNumbers end

@testset "Number systems" begin
    @test_throws ErrorException real_dimension(NotImplementedNumbers())

    @test ℝ isa ManifoldsBase.RealNumbers
    @test ManifoldsBase.RealNumbers() === ℝ
    @test real_dimension(ℝ) == 1
    @test repr(ℝ) == "ℝ"

    @test ℂ isa ManifoldsBase.ComplexNumbers
    @test ManifoldsBase.ComplexNumbers() === ℂ
    @test real_dimension(ℂ) == 2
    @test repr(ℂ) == "ℂ"

    @test ℍ isa ManifoldsBase.QuaternionNumbers
    @test ManifoldsBase.QuaternionNumbers() === ℍ
    @test real_dimension(ℍ) == 4
    @test repr(ℍ) == "ℍ"

    @test (@inferred _unify_number_systems(ℝ)) === ℝ
    @test (@inferred _unify_number_systems(ℂ)) === ℂ
    @test (@inferred _unify_number_systems(ℍ)) === ℍ
    @test (@inferred _unify_number_systems(ℝ, ℝ)) === ℝ
    @test (@inferred _unify_number_systems(ℝ, ℂ)) === ℂ
    @test (@inferred _unify_number_systems(ℝ, ℍ)) === ℍ
    @test (@inferred _unify_number_systems(ℂ, ℝ)) === ℂ
    @test (@inferred _unify_number_systems(ℂ, ℂ)) === ℂ
    @test (@inferred _unify_number_systems(ℂ, ℍ)) === ℍ
    @test (@inferred _unify_number_systems(ℍ, ℝ)) === ℍ
    @test (@inferred _unify_number_systems(ℍ, ℂ)) === ℍ
    @test (@inferred _unify_number_systems(ℍ, ℍ)) === ℍ
    @test (@inferred _unify_number_systems(ℝ, ℂ, ℝ)) === ℂ
    @test (@inferred _unify_number_systems(ℝ, ℝ, ℝ)) === ℝ
end


using NonlinearEigenproblems: nep_gallery, compute_Mder
using FEASTSolver

function info(Λ, X, residuals, c, r)
    inside(x) = in_contour(x, c, r)
    in_eig = Λ[inside.(Λ)]
    in_res = residuals[inside.(Λ)]
    print("number inside : ")
    println(size(Λ[inside.(Λ)])[1])
    if sum(inside.(Λ)) > 0
        in_res_conv = in_res[in_res .<= 1e-5]
        in_eig_conv = in_eig[in_res .<= 1e-5]
        print("number inside converged : ")
        println(size(in_eig_conv)[1])
        print("max res inside: ")
        println(maximum(residuals[inside.(Λ)]))
        if size(in_res_conv, 1) > 0
            print("max res inside non spurious: ")
            println(maximum(in_res_conv))
        end
    end
    # display(in_eig_conv)
end

nep = nep_gallery("dep_distributed")
T(x) = compute_Mder(nep, x)

n = size(nep, 1)
C = complex(-0.4, 0.0)
R = 1.25

e, v, res = nlfeast!(T, rand(ComplexF64,n,3), 2^4, 5, c=C, r=R, debug=true, ϵ=10e-16, store=true)

info(e,v,res,C,R)


function constant_mortality_mean_age_corrected(mx, nx, boundary = 1e-6)
    result = copy(mx)
    upper = mx .>= boundary
    large_mx  = mx[upper]
    expx = exp.(-large_mx .* nx[upper])
    result[upper] = (1 ./ large_mx) .- (nx[upper] .* expx) ./ (1 .- expx)

    lower = .!upper
    small_mx = mx[lower]
    small_nx = nx[lower]
    result[lower] = small_nx .* (0.5 .+ small_nx .* (
        -small_mx / 12 + small_mx.^3 .* small_nx.^2 / 720))
    result
end

function constant_mortality_mean_age(mx, nx)
    expx = exp.(-mx .* nx)
    (1 ./ mx) .- (nx .* expx) ./ (1 .- expx)
end

# The Langevin function is coth(x) - 1/x
function langevin(x)
    x / 3 / (
        1 + x^2 / 15 / (
            1 + x^2 / 35 / (
                1 + x^2 / 63 / (
                    1 + x^2 / 99
                )
            )
        )
    )
end

function langevin_mean_age(mx, nx)
    (nx / 2) .* (1 .- langevin.(mx .* nx / 2))
end

function test_naive_mean_age()
    mx = vcat([0.9 * 10.0^i for i in 0:-1:-17], [0.0])
    nx = ones(size(mx))
    ax = constant_mortality_mean_age(mx, nx)
    println(ax)
    for check in 2:length(ax)
        @assert ax[check] > ax[check - 1]
    end
    @assert all(ax .< .5)
end


module IntegrationTests

using STLCutters
using Gridap
import Gridap: ∇
using GridapEmbedded
using Test

using STLCutters: compute_stl_model
using STLCutters: read_stl, merge_nodes, get_bounding_box

# Manufactured solution
u0(x) = x[1] + x[2] - x[3]

X,T,N = read_stl(joinpath(@__DIR__,"data/cube.stl"))
stl = compute_stl_model(T,X)
stl = merge_nodes(stl)
# writevtk(stl,"geo")
n = 10
δ = 0.2
pmin,pmax = get_bounding_box(stl)
diagonal = pmax-pmin
pmin = pmin - diagonal*δ
pmax = pmax + diagonal*δ
partition = (n,n,n)

geo = STLGeometry(stl)
bgmodel = CartesianDiscreteModel(pmin,pmax,partition)

# Cut the background model

cutgeo, = cut(bgmodel,geo)

# Setup integration meshes
Ω = Triangulation(cutgeo,PHYSICAL)
Γd = EmbeddedBoundary(cutgeo)
Γg = GhostSkeleton(cutgeo)

# Setup normal vectors
n_Γd = get_normal_vector(Γd)
n_Γg = get_normal_vector(Γg)

#writevtk(Ω,"trian_O")
#writevtk(Γd,"trian_Gd",cellfields=["normal"=>n_Γd])
#writevtk(Γg,"trian_Gg",cellfields=["normal"=>n_Γg])
#writevtk(Triangulation(bgmodel),"bgtrian")

# Setup Lebesgue measures
order = 1
degree = 2*order
dΩ = Measure(Ω,degree)
dΓd = Measure(Γd,degree)
dΓg = Measure(Γg,degree)

vol = sum( ∫(1)*dΩ  )
surf = sum( ∫(1)*dΓd )

@test vol ≈ 1
@test surf ≈ 6

# Setup FESpace
Ω_act = Triangulation(cutgeo,ACTIVE,geo)
V = TestFESpace(Ω_act,ReferenceFE(lagrangian,Float64,order),conformity=:H1)
v = FEFunction(V,rand(num_free_dofs(V)))
u = interpolate(u0,V)

# Check divergence theorem
a = sum( ∫( ∇(v)⋅∇(u) ) * dΩ )
b = sum( ∫( v⋅n_Γd⋅∇(u) ) * dΓd )
@test abs( a-b ) < 1e-9

end


include("calc_settings.jl")
include("../src/density.jl")
xs = reduce(vcat, XS)
ys = reduce(vcat, YS)
X = d1[1] .* xs + d2[1] .* ys
Y = d1[2] .* xs + d2[2] .* ys
res = @showprogress pmap((x, y) -> spectral_bulk(ω, Location(x, y), s), XS, YS)
signal = reduce(vcat, res)
XS = reshape(X, 1, (2*x_pos+1)^2)
YS = reshape(Y, 1, (2*x_pos+1)^2)
signal1 = reshape(signal, 1, (2*x_pos+1)^2)
signal1 = signal1 .- signal1[1]
function FT_component(qx, qy, ρ, XS, YS)
    res = sum(map((x, y, z) -> exp(1im * (x * qx + y * qy)) * z, XS, YS, ρ))
end
qx_min = -1 * π;
qx_max = 1 * π;

n_pts = 200;

qx = range(qx_min, qx_max, length = n_pts)
qy = range(qx_min, qx_max, length = n_pts)

QX = repeat(qx, 1, n_pts)
QY = repeat(qy, 1, n_pts) |> permutedims

## Calculation
FT_res = @showprogress map((qx, qy) -> FT_component(qx, qy, signal1, XS, YS), QX, QY)

writedlm("QX.dat", QX)
writedlm("QY.dat", QY)
writedlm("FT_real.dat", real.(FT_res))
writedlm("FT_imag.dat", imag.(FT_res))


function sinematrix(s::Vector{Float64}, c::Vector{Float64})
	return s.*c' .- c.*s'
end

function cosmatrix(s::Vector{Float64}, c::Vector{Float64})
	return c.*c' + s.*s'
end

function cmatrix(κ::Float64, A::Matrix{Float64}, S::Matrix{Float64})
	return κ.*A.*S
end

function omegadot(ω::Vector{Float64}, θ::Vector{Float64}, A::Matrix{Float64}, P::Vector{Float64}, I::Float64, D::Float64, κ::Float64)
	s = sin.(θ)
	c = cos.(θ)
	smat = sinematrix(s, c)
	C = cmatrix(κ, A, smat)
	return (P .- D.*ω .- C*ones(length(ω)))./I
end

function fswing(ψ::Vector{Float64}, A::Matrix{Float64}, P::Vector{Float64}, I::Float64, D::Float64, κ::Float64)
	m = length(ψ)
	n = div(m,2)
	ω = ψ[1:n]
	θ = ψ[n+1:end]
	ω_dot = omegadot(ω, θ, A, P, I, D, κ)
	return vcat(ω_dot, ω)
end

function fsteadystate(θ::Vector{Float64}, A::Matrix{Float64}, P::Vector{Float64}, κ::Float64)
	n = length(θ)
	s = sin.(θ)
	c = cos.(θ)
	smat = sinematrix(s, c)
	C = cmatrix(κ, A, smat)
	return P .- C*ones(n)
end

function mainjac(θ::Vector{Float64}, E::Matrix{Float64}, I::Float64, κ::Float64)
	weights = κ*cos.(E'*θ)
	W = Diagonal(weights)
	return -E*W*E'
end

function dfswing(ψ::Vector{Float64}, A::Matrix{Float64}, I::Float64, D::Float64, κ::Float64)
	m = length(ψ)
	n = div(m,2)
	θ = ψ[n+1:end]
	DF = zeros(m,m)
	DF[1:n,1:n] = D.*Matrix{Float64}(I, n, n)./I
	DF[1:n,n+1:end] = mainjac(θ, A, I, κ)./I
	DF[n+1:end,1:n] = Matrix{Float64}(I, n, n)
	return DF
end

function edgepower(θ::Vector{Float64}, E::Matrix{Float64}, κ::Float64)
	Δθ = transpose(E)*θ
	return κ.*sin.(Δθ)
end

function centralityvstransient(n::Int64, ns::Int64, nd::Int64, k::Int64, q::Float64, κ::Float64, I::Float64, D::Float64)

	#Set up network
	net, E = smallworldnetwork(n, k, q)
	ψ = rand(2*n)
	A = adjacencymatrix(E)
	sources, sinks = sourcesinklocs(ns, nd, n)
	P = sourcesinkvector(sources, sinks, n, 1.0)
	obj1 = NetStore(A, P, κ)

	#Find initial steady state
	f(u, p, t) = fswing(u, obj1.adjmat, P, I, D, κ)
	tspan = (0.0, 500.0)
	prob = ODEProblem(f, ψ, tspan)
	sol = solve(prob,reltol=1e-8,abstol=1e-8,save_everystep=false,dense=false,save_end=true)
	ψ0 = sol[:,end]
	θ0 = sol[n+1:end,end]
	flow = edgepower(θ0, E, κ)
	meanflow = mean(abs.(flow))

	#Set up callback function
	function conditiontemp(u1, t1, integrator, ob::NetStore)
		n = div(length(u1),2)
		θ = u1[n+1:end,end]
		resid = fsteadystate(θ, ob.adjmat, ob.pvec, ob.kappa)
		if norm(resid,2) < 0.001
			return true
		else
			return false
		end
	end
	condition(u,t,integrator) = conditiontemp(u,t,integrator,obj1)
	affect!(integrator) = terminate!(integrator)

	m = numedges(net)
	relflow = zeros(m)
	transienttimes = zeros(m)
	distances = zeros(m)
	for d=1:m

		#Delete edge and find its load
		E1 = E[1:end, 1:end .≠ d]
		A1 = adjacencymatrix(E1)
		obj1.adjmat = A1
		relflow[d] = abs(flow[d])/meanflow
		flow_old = flow[1:end .≠ d]

		#Time step on reduced network
		u0 = ψ0
		tspan2 = (0.0, 5000.0)
		prob2 = ODEProblem(f, u0, tspan2)
		cb = DiscreteCallback(condition, affect!)
		sol2 = solve(prob2, reltol=1e-8,abstol=1e-8,save_everystep=false,dense=false,save_end=true,callback=cb)

		#Find new flow and measure its distance
		theta_new = sol2[n+1:end,end]
		flow_new = edgepower(theta_new, E1, κ)
		sep = abs.(flow_new .- flow_old)
		transienttimes[d] = sol2.t[end]
		distances[d] = norm(sep, 2)
	end
	return relflow, transienttimes, distances

end

function swingfracture!(net::Graph, ψ::Vector{Float64}, nodeset::Vector{Int64}, P::Vector{Float64}, synctol::Float64, alpha::Float64, kappa::Float64, maxflow::Float64)

	tol = 1e-5

	#Check the status of P, see if has any sources or sinks. If not, halt recursion
	nodecardinality = length(nodeset)
	P1 = zeros(Float64, nodecardinality)
	sourcecounter = 0
	sinkcounter = 0
	for i=1:nodecardinality
		vi = nodeset[i]
		P1[i] = P[vi]
		if P1[i] > tol
			sourcecounter += 1
		end
		if P1[i] < -tol
			sinkcounter += 1
		end
	end
	if (sourcecounter == 0) || (sinkcounter == 0)
		return 0
	end

	#Balance P1 homogeneously
	delta = sum(P1)/2.0
	individualdelta_sink = delta/Float64(sinkcounter)
	individualdelta_source = delta/Float64(sourcecounter)
	for i=1:nodecardinality
		if P1[i] < -tol
			P1[i] -= individualdelta_sink
		end
		if P1[i] > tol
			P1[i] -= individualdelta_source
		end
	end

	# #Balance P1 - if there is power excess, increase loads
	# delta = sum(P1)
	# individualdelta = delta/Float64(sinkcounter)
	# for i=1:nodecardinality
	# 	if P1[i] < -tol
	# 		P1[i] -= individualdelta
	# 	end
	# end

	#Find which edges are connected to the node-set
	edgeset = Array{Int64, 1}([])
	for vi ∈ nodeset
		v = net.nodelist[vi]
		for e ∈ v.outedges
			if (e ∉ edgeset) && (net.edgelist[e].active == true)
				push!(edgeset, e)
			end
		end
		for e ∈ v.inedges
			if (e ∉ edgeset) && (net.edgelist[e].active == true)
				push!(edgeset, e)
			end
		end
	end
	edgecardinality = length(edgeset)

	#Construct E1
	E1 = zeros(Float64, nodecardinality, edgecardinality)
	for i=1:nodecardinality
		vi = nodeset[i]
		v = net.nodelist[vi]
		for j=1:edgecardinality
			if edgeset[j] ∈ v.outedges
				E1[i,j] = 1.0
			end
			if edgeset[j] ∈ v.inedges
				E1[i,j] = -1.0
			end
		end
	end

	#Set up the storage object
	A1 = adjacencymatrix(E1)
	obj1 = NetStore2(A1, P1, E1, kappa, synctol, alpha, maxflow, true, false)

	#Set up callback function
	function conditiontemp(u1, t1, integrator, ob::NetStore2)
		n = div(length(u1),2)
		ω = u1[1:n]
		θ = u1[n+1:end]

		if (t1 > 0.0)
			#Has network synchronised?
			if norm(ω,2) > ob.synctol
				ob.sync = false
				return true
			end

			#Have any edges tripped the capacity
			flow = edgepower(θ, ob.incidence, ob.kappa)./ob.maxflow
			for i=1:length(flow)
				if abs(flow[i]) > ob.alpha
					ob.trip = true
					return true
				end
			end

			#Has the solution converged
			resid = fsteadystate(θ, ob.adjmat, ob.pvec, kappa)
			if norm(resid,2) < 1e-6
				return true
			end
		end

		#If the network is still syncing and no edge trips, continue
		return false
	end
	condition(u,t,integrator) = conditiontemp(u,t,integrator,obj1)
	affect!(integrator) = terminate!(integrator)

	#Time step
	f(u, p, t) = fswing(u, obj1.adjmat, P1, 1.0, 1.0, kappa)
	tspan = (0.0, 500.0)
	prob = ODEProblem(f, ψ, tspan)
	cb = DiscreteCallback(condition, affect!)
	sol = solve(prob,reltol=1e-8,abstol=1e-8,save_everystep=false,dense=false,save_end=true,callback=cb)

	#Get the state vector, split into theta and omega
	ψ_1 = sol[:,end]
	ω_1 = ψ_1[1:nodecardinality]
	θ_1 = ψ_1[nodecardinality+1:end]

	#If the solution has not converged, then return
	if obj1.sync == false
		return 0
	end

	#If the solution has converged with no further overloads, then return
	if (obj1.trip == false) && (obj1.sync == true)
		return edgecardinality
	end

	#If there are overloads, but omega still in acceptable range,
	#then compute number of new connected components and continue
	#recursion on each of them
	#First construct new edgeset and incidence matrix
	overloadcounter = 0
	survivors = Array{Int64, 1}([])
	flow = edgepower(θ_1, E1, kappa)./maxflow
	for i=1:edgecardinality
		ei = edgeset[i]
		if abs(flow[i]) > alpha #*abs(Finit[ei])
			net.edgelist[ei].active = false
			overloadcounter += 1
		else
			push!(survivors, i)
		end
	end
	newedgecard = length(survivors)
	E2 = zeros(Float64, nodecardinality, newedgecard)
	for j=1:newedgecard
		E2[:,j] = E1[:,survivors[j]]
	end

	#Check to see if its broken
	Adj = adjacencymatrix(E2)
	components, table = connectedcomponents(Adj)

	#Clear any superfluous data
	E1 = nothing
	E2 = nothing
	Adj = nothing
	obj1 = nothing

	#Call fracture function on each of the components, passing down info
	descendent_edges = 0
	for i=1:components
		chunk = table[i]
		n_subset = length(chunk)
		nodesubset = zeros(Int64, length(chunk))
		ω_subset = zeros(Float64, length(chunk))
		θ_subset = zeros(Float64, length(chunk))
		ψ_subset = zeros(Float64, 2*n_subset)
		for j=1:length(chunk)
			nodesubset[j] = nodeset[chunk[j]]
			ω_subset[j] = ω_1[chunk[j]]
			θ_subset[j] = θ_1[chunk[j]]
		end
		ψ_subset[1:n_subset] = ω_subset
		ψ_subset[n_subset+1:end] = θ_subset
		descendent_edges += swingfracture!(net, ψ_subset, nodesubset, P, synctol, alpha, kappa, maxflow)
	end

	return descendent_edges
end

function swingfracturewithtime!(net::Graph, ψ::Vector{Float64}, nodeset::Vector{Int64}, P::Vector{Float64}, synctol::Float64, alpha::Float64, kappa::Float64, maxflow::Float64, D::Float64)

	tol = 1e-5

	#Check the status of P, see if has any sources or sinks. If not, halt recursion
	nodecardinality = length(nodeset)
	P1 = zeros(Float64, nodecardinality)
	sourcecounter = 0
	sinkcounter = 0
	for i=1:nodecardinality
		vi = nodeset[i]
		P1[i] = P[vi]
		if P1[i] > tol
			sourcecounter += 1
		end
		if P1[i] < -tol
			sinkcounter += 1
		end
	end
	if (sourcecounter == 0) || (sinkcounter == 0)
		return 0, 0.0
	end

	#Balance P1 homogeneously
	delta = sum(P1)/2.0
	individualdelta_sink = delta/Float64(sinkcounter)
	individualdelta_source = delta/Float64(sourcecounter)
	for i=1:nodecardinality
		if P1[i] < -tol
			P1[i] -= individualdelta_sink
		end
		if P1[i] > tol
			P1[i] -= individualdelta_source
		end
	end

	# #Balance P1 - if there is power excess, increase loads
	# delta = sum(P1)
	# individualdelta = delta/Float64(sinkcounter)
	# for i=1:nodecardinality
	# 	if P1[i] < -tol
	# 		P1[i] -= individualdelta
	# 	end
	# end

	#Find which edges are connected to the node-set
	edgeset = Array{Int64, 1}([])
	for vi ∈ nodeset
		v = net.nodelist[vi]
		for e ∈ v.outedges
			if (e ∉ edgeset) && (net.edgelist[e].active == true)
				push!(edgeset, e)
			end
		end
		for e ∈ v.inedges
			if (e ∉ edgeset) && (net.edgelist[e].active == true)
				push!(edgeset, e)
			end
		end
	end
	edgecardinality = length(edgeset)

	#Construct E1
	E1 = zeros(Float64, nodecardinality, edgecardinality)
	for i=1:nodecardinality
		vi = nodeset[i]
		v = net.nodelist[vi]
		for j=1:edgecardinality
			if edgeset[j] ∈ v.outedges
				E1[i,j] = 1.0
			end
			if edgeset[j] ∈ v.inedges
				E1[i,j] = -1.0
			end
		end
	end

	#Set up the storage object
	A1 = adjacencymatrix(E1)
	obj1 = NetStore2(A1, P1, E1, kappa, synctol, alpha, maxflow, true, false)

	#Set up callback function
	function conditiontemp(u1, t1, integrator, ob::NetStore2)
		n = div(length(u1),2)
		ω = u1[1:n]
		θ = u1[n+1:end]

		if (t1 > 0.0)
			#Has network synchronised?
			if norm(ω,2) > ob.synctol
				ob.sync = false
				return true
			end

			#Have any edges tripped the capacity
			flow = edgepower(θ, ob.incidence, ob.kappa)./ob.maxflow
			for i=1:length(flow)
				if abs(flow[i]) > ob.alpha
					ob.trip = true
					return true
				end
			end

			#Has the solution converged
			resid = fsteadystate(θ, ob.adjmat, ob.pvec, kappa)
			if norm(resid,2) < 1e-6
				return true
			end
		end

		#If the network is still syncing and no edge trips, continue
		return false
	end
	condition(u,t,integrator) = conditiontemp(u,t,integrator,obj1)
	affect!(integrator) = terminate!(integrator)

	#Time step
	f(u, p, t) = fswing(u, obj1.adjmat, P1, 1.0, D, kappa)
	tspan = (0.0, 500.0)
	prob = ODEProblem(f, ψ, tspan)
	cb = DiscreteCallback(condition, affect!)
	sol = solve(prob,reltol=1e-8,abstol=1e-8,save_everystep=false,dense=false,save_end=true,callback=cb)

	#Get the state vector, split into theta and omega
	ψ_1 = sol[:,end]
	ω_1 = ψ_1[1:nodecardinality]
	θ_1 = ψ_1[nodecardinality+1:end]

	#If the solution has not converged, then return
	if obj1.sync == false
		return 0, 0.0
	end

	#If the solution has converged with no further overloads, then return
	if (obj1.trip == false) && (obj1.sync == true)
		return edgecardinality, sol.t[end]
	end

	#If there are overloads, but omega still in acceptable range,
	#then compute number of new connected components and continue
	#recursion on each of them
	#First construct new edgeset and incidence matrix
	overloadcounter = 0
	survivors = Array{Int64, 1}([])
	flow = edgepower(θ_1, E1, kappa)./maxflow
	for i=1:edgecardinality
		ei = edgeset[i]
		if abs(flow[i]) > alpha #*abs(Finit[ei])
			net.edgelist[ei].active = false
			overloadcounter += 1
		else
			push!(survivors, i)
		end
	end
	newedgecard = length(survivors)
	E2 = zeros(Float64, nodecardinality, newedgecard)
	for j=1:newedgecard
		E2[:,j] = E1[:,survivors[j]]
	end

	#Check to see if its broken
	Adj = adjacencymatrix(E2)
	components, table = connectedcomponents(Adj)

	#Clear any superfluous data
	E1 = nothing
	E2 = nothing
	Adj = nothing
	obj1 = nothing

	#Call fracture function on each of the components, passing down info
	descendent_edges = 0
	times = []
	for i=1:components
		chunk = table[i]
		n_subset = length(chunk)
		nodesubset = zeros(Int64, length(chunk))
		ω_subset = zeros(Float64, length(chunk))
		θ_subset = zeros(Float64, length(chunk))
		ψ_subset = zeros(Float64, 2*n_subset)
		for j=1:length(chunk)
			nodesubset[j] = nodeset[chunk[j]]
			ω_subset[j] = ω_1[chunk[j]]
			θ_subset[j] = θ_1[chunk[j]]
		end
		ψ_subset[1:n_subset] = ω_subset
		ψ_subset[n_subset+1:end] = θ_subset
		xz, tz = swingfracturewithtime!(net, ψ_subset, nodesubset, P, synctol, alpha, kappa, maxflow, D)
		descendent_edges += xz
		push!(times, tz)
	end
	tprime = sol.t[end] + maximum(times)
	return descendent_edges, tprime
end

function swingfracturewithbreakdown!(net::Graph, ψ::Vector{Float64}, nodeset::Vector{Int64}, P::Vector{Float64}, synctol::Float64, alpha::Float64, kappa::Float64, maxflow::Float64)

	tol = 1e-5

	#Check the status of P, see if has any sources or sinks. If not, halt recursion
	nodecardinality = length(nodeset)
	P1 = zeros(Float64, nodecardinality)
	sourcecounter = 0
	sinkcounter = 0
	for i=1:nodecardinality
		vi = nodeset[i]
		P1[i] = P[vi]
		if P1[i] > tol
			sourcecounter += 1
		end
		if P1[i] < -tol
			sinkcounter += 1
		end
	end

	#Find which edges are connected to the node-set
	edgeset = Array{Int64, 1}([])
	for vi ∈ nodeset
		v = net.nodelist[vi]
		for e ∈ v.outedges
			if (e ∉ edgeset) && (net.edgelist[e].active == true)
				push!(edgeset, e)
			end
		end
		for e ∈ v.inedges
			if (e ∉ edgeset) && (net.edgelist[e].active == true)
				push!(edgeset, e)
			end
		end
	end
	edgecardinality = length(edgeset)
	if (sourcecounter == 0) || (sinkcounter == 0)
		return 0, 0, 0, edgecardinality
	end

	#Balance P1 homogeneously
	delta = sum(P1)/2.0
	individualdelta_sink = delta/Float64(sinkcounter)
	individualdelta_source = delta/Float64(sourcecounter)
	for i=1:nodecardinality
		if P1[i] < -tol
			P1[i] -= individualdelta_sink
		end
		if P1[i] > tol
			P1[i] -= individualdelta_source
		end
	end

	#Construct E1
	E1 = zeros(Float64, nodecardinality, edgecardinality)
	for i=1:nodecardinality
		vi = nodeset[i]
		v = net.nodelist[vi]
		for j=1:edgecardinality
			if edgeset[j] ∈ v.outedges
				E1[i,j] = 1.0
			end
			if edgeset[j] ∈ v.inedges
				E1[i,j] = -1.0
			end
		end
	end

	#Set up the storage object
	A1 = adjacencymatrix(E1)
	obj1 = NetStore2(A1, P1, E1, kappa, synctol, alpha, maxflow, true, false)

	#Set up callback function
	function conditiontemp(u1, t1, integrator, ob::NetStore2)
		n = div(length(u1),2)
		ω = u1[1:n]
		θ = u1[n+1:end]

		if (t1 > 0.0)
			#Has network synchronised?
			if norm(ω,2) > ob.synctol
				ob.sync = false
				return true
			end

			#Have any edges tripped the capacity
			flow = edgepower(θ, ob.incidence, ob.kappa)./ob.maxflow
			for i=1:length(flow)
				if abs(flow[i]) > ob.alpha
					ob.trip = true
					return true
				end
			end

			#Has the solution converged
			resid = fsteadystate(θ, ob.adjmat, ob.pvec, kappa)
			if norm(resid,2) < 1e-6
				return true
			end
		end

		#If the network is still syncing and no edge trips, continue
		return false
	end
	condition(u,t,integrator) = conditiontemp(u,t,integrator,obj1)
	affect!(integrator) = terminate!(integrator)

	#Time step
	f(u, p, t) = fswing(u, obj1.adjmat, P1, 1.0, 1.0, kappa)
	tspan = (0.0, 500.0)
	prob = ODEProblem(f, ψ, tspan)
	cb = DiscreteCallback(condition, affect!)
	sol = solve(prob,reltol=1e-8,abstol=1e-8,save_everystep=false,dense=false,save_end=true,callback=cb)

	#Get the state vector, split into theta and omega
	ψ_1 = sol[:,end]
	ω_1 = ψ_1[1:nodecardinality]
	θ_1 = ψ_1[nodecardinality+1:end]

	#If the solution has not converged, then return
	if obj1.sync == false
		return 0, 0, edgecardinality, 0
	end

	#If the solution has converged with no further overloads, then return
	if (obj1.trip == false) && (obj1.sync == true)
		return edgecardinality, 0, 0, 0
	end

	#If there are overloads, but omega still in acceptable range,
	#then compute number of new connected components and continue
	#recursion on each of them
	#First construct new edgeset and incidence matrix
	overloadcounter = 0
	survivors = Array{Int64, 1}([])
	flow = edgepower(θ_1, E1, kappa)./maxflow
	for i=1:edgecardinality
		ei = edgeset[i]
		if abs(flow[i]) > alpha
			net.edgelist[ei].active = false
			overloadcounter += 1
		else
			push!(survivors, i)
		end
	end
	newedgecard = length(survivors)
	E2 = zeros(Float64, nodecardinality, newedgecard)
	for j=1:newedgecard
		E2[:,j] = E1[:,survivors[j]]
	end

	#Check to see if its broken
	Adj = adjacencymatrix(E2)
	components, table = connectedcomponents(Adj)

	#Clear any superfluous data
	E1 = nothing
	E2 = nothing
	Adj = nothing
	obj1 = nothing

	#Call fracture function on each of the components, passing down info
	descendent_edges = 0
	descendent_overloads = 0
	descendent_desyncs = 0
	descendent_unbalances = 0
	for i=1:components
		chunk = table[i]
		n_subset = length(chunk)
		nodesubset = zeros(Int64, length(chunk))
		ω_subset = zeros(Float64, length(chunk))
		θ_subset = zeros(Float64, length(chunk))
		ψ_subset = zeros(Float64, 2*n_subset)
		for j=1:length(chunk)
			nodesubset[j] = nodeset[chunk[j]]
			ω_subset[j] = ω_1[chunk[j]]
			θ_subset[j] = θ_1[chunk[j]]
		end
		ψ_subset[1:n_subset] = ω_subset
		ψ_subset[n_subset+1:end] = θ_subset
		xz, oz, dz, uz = swingfracturewithbreakdown!(net, ψ_subset, nodesubset, P, synctol, alpha, kappa, maxflow)
		descendent_edges += xz
		descendent_overloads += oz
		descendent_desyncs += dz
		descendent_unbalances += uz
	end
	return descendent_edges, descendent_overloads, descendent_desyncs, descendent_unbalances
end

function swingcascadeandtvalpha(ensemblesize::Int64, n::Int64, ns::Int64, nd::Int64, q::Float64,
								k::Int64, alphares::Int64, alphamin::Float64, alphamax::Float64,
								I::Float64, D::Float64, κ::Float64)

	alphas = collect(range(alphamin, length=alphares, stop=alphamax))
	edgemat = zeros(Float64, ensemblesize, alphares)
	tmat = zeros(Float64, ensemblesize, alphares)
	fmaxes = zeros(Float64, ensemblesize)
	critvalues = zeros(Float64, ensemblesize)

	println("Starting")
	for z=1:ensemblesize
		println("Ensemble = ", z)

		net, E = smallworldnetwork(n, k, q)
		# net, E = scalefreenetwork(n, 2*n, 3)
		sources, sinks = sourcesinklocs(ns, nd, n)
		P = sourcesinkvector(sources, sinks, n, 1.0)
		m = numedges(net)
		nodeset = collect(1:n)
		edgeset = collect(1:m)
		A = adjacencymatrix(E)
		ψ = rand(2*n)

		#Time step to find steady state
		f(u, p, t) = fswing(u, A, P, I, D, κ)
		tspan = (0.0, 100.0)
		prob = ODEProblem(f, ψ, tspan)
		sol = solve(prob,reltol=1e-8,abstol=1e-8,save_everystep=false,dense=false,save_end=true)

		#Check for convergence
		ψ = sol[:,end]
		ω = ψ[1:n]
		θ = ψ[n+1:end]
		x = fsteadystate(θ, A, P, κ)
		if norm(x,2) > 1e-3
			println("Warning: no steady state found.")
		end

		#Compute the flow and normalise
		flow_swing = edgepower(θ, E, κ)
		flowmax_swing = maximum(abs.(flow_swing))
		fmaxes[z] = flowmax_swing
		flow_swing = flow_swing./flowmax_swing

		redges = zeros(Float64, alphares)
		rtimes = zeros(Float64, alphares)

		for (ind, alpha) ∈ enumerate(alphas)

			#Reactive all the edges
			for i=1:length(net.edgelist)
				net.edgelist[i].active = true
			end

			#Knock out most loaded edge
			d = argmax(abs.(flow_swing))
			edgeset1 = filter(x->x≠d, edgeset)
			E1 = E[1:end, 1:end .≠ d]
			net.edgelist[d].active = false

			#Check to see if its broken
			Adj = adjacencymatrix(E1)
			components, table = connectedcomponents(Adj)

			totedgessurviving = 0
			timetakenvec = []
			for i=1:components
				chunk = table[i]
				n_subset = length(chunk)
				nodesubset = zeros(Int64, length(chunk))
				ω_subset = zeros(Float64, length(chunk))
				θ_subset = zeros(Float64, length(chunk))
				ψ_subset = zeros(Float64, 2*n_subset)
				for j=1:length(chunk)
					nodesubset[j] = nodeset[chunk[j]]
					ω_subset[j] = ω[chunk[j]]
					θ_subset[j] = θ[chunk[j]]
				end
				ψ_subset[1:n_subset] = ω_subset
				ψ_subset[n_subset+1:end] = θ_subset
				edgessurviving, timetaken = swingfracturewithtime!(net, ψ_subset, nodesubset, P, 3.0, alpha, κ, flowmax_swing, D)
				totedgessurviving += edgessurviving
				push!(timetakenvec, timetaken)
			end
			redges[ind] = totedgessurviving/m
			rtimes[ind] = maximum(timetakenvec)
		end

		#Find the critical value
		aftertransition = findall(x->x>0.5, redges)
		if length(aftertransition) < 1
			critvalues[z] = NaN
		else
			ac = alphas[aftertransition[1]]
			critvalues[z] = ac
		end

		#Store S
		edgemat[z,:] = redges
		tmat[z,:] = rtimes
	end
	return alphas, mean(edgemat, dims=1)[1,:], mean(tmat, dims=1)[1,:] ,mean(fmaxes), critvalues
end

function svt(ensemblesize::Int64, n::Int64, ns::Int64, nd::Int64, q::Float64,
								k::Int64, alpha::Float64,
								I::Float64, D::Float64, κ::Float64)

	println("Starting")
	vedges = zeros(Float64, ensemblesize)
	vtimes = zeros(Float64, ensemblesize)
	for z=1:ensemblesize
		println("Ensemble = ", z)

		net, E = smallworldnetwork(n, k, q)
		sources, sinks = sourcesinklocs(ns, nd, n)
		P = sourcesinkvector(sources, sinks, n, 1.0)
		m = numedges(net)
		nodeset = collect(1:n)
		edgeset = collect(1:m)
		A = adjacencymatrix(E)
		ψ = rand(2*n)

		#Time step to find steady state
		f(u, p, t) = fswing(u, A, P, I, D, κ)
		tspan = (0.0, 100.0)
		prob = ODEProblem(f, ψ, tspan)
		sol = solve(prob,reltol=1e-8,abstol=1e-8,save_everystep=false,dense=false,save_end=true)

		#Check for convergence
		ψ = sol[:,end]
		ω = ψ[1:n]
		θ = ψ[n+1:end]
		x = fsteadystate(θ, A, P, κ)
		if norm(x,2) > 1e-3
			println("Warning: no steady state found.")
		end

		#Compute the flow and normalise
		flow_swing = edgepower(θ, E, κ)
		flowmax_swing = maximum(abs.(flow_swing))
		flow_swing = flow_swing./flowmax_swing

		#Reactive all the edges
		for i=1:length(net.edgelist)
			net.edgelist[i].active = true
		end

		#Knock out most loaded edge
		d = argmax(abs.(flow_swing))
		edgeset1 = filter(x->x≠d, edgeset)
		E1 = E[1:end, 1:end .≠ d]
		net.edgelist[d].active = false

		#Check to see if its broken
		Adj = adjacencymatrix(E1)
		components, table = connectedcomponents(Adj)

		totedgessurviving = 0
		timetakenvec = []
		for i=1:components
			chunk = table[i]
			n_subset = length(chunk)
			nodesubset = zeros(Int64, length(chunk))
			ω_subset = zeros(Float64, length(chunk))
			θ_subset = zeros(Float64, length(chunk))
			ψ_subset = zeros(Float64, 2*n_subset)
			for j=1:length(chunk)
				nodesubset[j] = nodeset[chunk[j]]
				ω_subset[j] = ω[chunk[j]]
				θ_subset[j] = θ[chunk[j]]
			end
			ψ_subset[1:n_subset] = ω_subset
			ψ_subset[n_subset+1:end] = θ_subset
			edgessurviving, timetaken = swingfracturewithtime!(net, ψ_subset, nodesubset, P, 3.0, alpha, κ, flowmax_swing, D)
			totedgessurviving += edgessurviving
			push!(timetakenvec, timetaken)
		end
		vedges[z] = totedgessurviving/m
		vtimes[z] = maximum(timetakenvec)
	end
	return vedges, vtimes
end

function swingcascadebisection(ensemblesize::Int64, tol::Float64, n::Int64, ns::Int64, nd::Int64,
								q::Float64, k::Int64, κ::Float64, I::Float64, D::Float64)

	#Make an ensemble of networks, with node placements and initial flow pattern
	ensemble = Array{CascadeNetworkSwing, 1}(undef, ensemblesize)
	fmaxes = Array{Float64, 1}(undef, ensemblesize)
	for i=1:ensemblesize
		println(i)

		# Construct each network
		net, E = smallworldnetwork(n, k, q)
		# net, E = austriannetwork()
		sources, sinks = sourcesinklocsmod(net, ns, nd, n)
		P = sourcesinkvector(sources, sinks, n, 1.0)
		m = numedges(net)

		#Make the initial state
		A = adjacencymatrix(E)
		ψ = rand(2*n)
		f(u, p, t) = fswing(u, A, P, I, D, κ)
		tspan = (0.0, 250.0)
		prob = ODEProblem(f, ψ, tspan)
		sol = solve(prob,reltol=1e-8,abstol=1e-8,save_everystep=false,dense=false,save_end=true)

		#Check for convergence
		ψ = sol[:,end]
		ω = ψ[1:n]
		θ = ψ[n+1:end]
		x = fsteadystate(θ, A, P, κ)
		if norm(x,2) > 1e-3
			println("Warning: no steady state found.")
		end

		#Compute the flow pattern and normalise
		flow_swing = edgepower(θ, E, κ)
		flowmax_swing = maximum(abs.(flow_swing))
		fmaxes[i] = flowmax_swing
		flow_swing = flow_swing./flowmax_swing

		#Store the ensemble member
		ensemblemember = CascadeNetworkSwing(net, E, sources, sinks, flow_swing, ψ)
		ensemble[i] = ensemblemember
	end

	alpha = 0.01
	stepsize = 0.3
	beneath0 = true
	beneath1 = true
	iterations = 1

	while abs(stepsize) > tol

		# println(alpha)
		runningsurvivors = Array{Float64, 1}(undef, ensemblesize)
		for z=1:ensemblesize

			#Unpack the network
			net = ensemble[z].network
			E = ensemble[z].E
			m = numedges(net)
			sources = ensemble[z].sources
			sinks = ensemble[z].sinks
			F = ensemble[z].initialflow
			ψ = ensemble[z].initialnodestate
			ω = ψ[1:n]
			θ = ψ[n+1:end]
			nodeset = collect(1:n)
			edgeset = collect(1:m)
			P = sourcesinkvector(sources, sinks, n, 1.0)

			#Reactivate all edges
			for j=1:length(net.edgelist)
				net.edgelist[j].active = true
			end

			#Knock out largest
			d = argmax(abs.(F))
			edgeset1 = filter(x->x≠d, edgeset)
			E1 = E[1:end, 1:end .≠ d]
			net.edgelist[d].active = false

			#Check to see if its broken
			Adj = adjacencymatrix(E1)
			components, table = connectedcomponents(Adj)

			#Do cascade
			totedgessurviving = 0
			for i=1:components
				chunk = table[i]
				n_subset = length(chunk)
				nodesubset = zeros(Int64, length(chunk))
				ω_subset = zeros(Float64, length(chunk))
				θ_subset = zeros(Float64, length(chunk))
				ψ_subset = zeros(Float64, 2*n_subset)
				for j=1:length(chunk)
					nodesubset[j] = nodeset[chunk[j]]
					ω_subset[j] = ω[chunk[j]]
					θ_subset[j] = θ[chunk[j]]
				end
				ψ_subset[1:n_subset] = ω_subset
				ψ_subset[n_subset+1:end] = θ_subset
				edgessurviving = swingfracture!(net, ψ_subset, nodesubset, P, 3.0, alpha, κ, fmaxes[z])
				totedgessurviving += edgessurviving
			end

			#Record fraction of edges remaining
			runningsurvivors[z] = totedgessurviving/Float64(m)
		end

		#If fraction has crossed the target then reverse and half the step size
		global avremaining = mean(runningsurvivors)
		# println("avremaining", avremaining)
		if avremaining > 0.5
			beneath1 = false
		else
			beneath1 = true
		end
		if beneath1 ≠ beneath0
			stepsize = -stepsize/2.0
		end
		beneath0 = beneath1
		alpha += stepsize
		iterations += 1

	end
	return alpha, mean(fmaxes), iterations
end

function criticalcouplingbisection(ensemblesize::Int64, tol::Float64, n::Int64, ns::Int64, nd::Int64,
								q::Float64, k::Int64, κ::Float64, I::Float64, D::Float64)

	#Iterate over ensemble
	crit = zeros(Float64, ensemblesize)
	κ0 = κ
	for z=1:ensemblesize
		println(z)
		#Make the network
		net, E = austriannetwork()
		sources, sinks = sourcesinklocsmod(net, ns, nd, n)
		P = sourcesinkvector(sources, sinks, n, 1.0)
		m = numedges(net)

		#Make the initial state
		A = adjacencymatrix(E)
		ψlast = rand(2*n)
		ψnew = rand(2*n)
		f(u, p, t) = fswing(u, A, P, I, D, κ)
		tspan = (0.0, 200.0)
		prob = ODEProblem(f, ψlast, tspan)
		sol = solve(prob,reltol=1e-8,abstol=1e-8,save_everystep=false,
			dense=false,save_end=true)
		ψlast = sol[:,end]

		obj1 = NetStore2(A, P, E, κ, 0.0, 0.0, 0.0, true, false)

		tol = 1e-3

		#Iterate over coupling
		stepsize = 0.01
		bisecting = true
		κ = κ0
		κnew = κ
		κold = κ
		while bisecting
			println(κ)

			f(u, p, t) = fswing(u, A, P, I, D, κ)
			obj1.kappa = κ
			κnew = κ
			tspan = (0.0, 100.0)
			prob = ODEProblem(f, ψlast, tspan)

			#Set up callback function
			convergedflag = false
			function conditiontemp(u1, t1, integrator, ob::NetStore2)
				θ = u1[n+1:end]
				if (t1 > 10.0)
					resid = fsteadystate(θ, ob.adjmat, ob.pvec, ob.kappa)
					if norm(resid,2) < 1e-5
						convergedflag = true
						return true
					end
				end
				return false
			end
			condition(u,t,integrator) = conditiontemp(u,t,integrator,obj1)
			affect!(integrator) = terminate!(integrator)
			cb = DiscreteCallback(condition, affect!)
			sol = solve(prob,reltol=1e-8,abstol=1e-8,save_everystep=false,
				dense=false,save_end=true, callback=cb)
			if convergedflag == true
				ψlast = sol[:,end]

				#If bisection done, then stop
				if stepsize < tol
					crit[z] = κ
					bisecting = false
				end

				#Else, continue
				κold = κ
				κ = κ - stepsize
			else
				stepsize = stepsize/2.0
				κ = κold - stepsize
			end
		end
	end
	return crit

end

function swingcascadeouterbisection(ensemblesize::Int64, tol::Float64, n::Int64, ns::Int64, nd::Int64,
								q::Float64, k::Int64, κ::Float64, I::Float64, D::Float64)

	rhos = zeros(Float64, ensemblesize)
	for z=1:ensemblesize
		println("Ensemble ", z)
		net, E = smallworldnetwork(n, k, q)
		sources, sinks = sourcesinklocsmod(net, ns, nd, n)
		P = sourcesinkvector(sources, sinks, n, 1.0)
		m = numedges(net)

		#Make the initial state
		A = adjacencymatrix(E)
		ψ = rand(2*n)
		f(u, p, t) = fswing(u, A, P, I, D, κ)
		tspan = (0.0, 250.0)
		prob = ODEProblem(f, ψ, tspan)
		sol = solve(prob,reltol=1e-8,abstol=1e-8,save_everystep=false,dense=false,save_end=true)

		#Check for convergence
		alpha = 0.01
		ψ = sol[:,end]
		ω = ψ[1:n]
		θ = ψ[n+1:end]
		x = fsteadystate(θ, A, P, κ)
		if norm(x,2) > 1e-3
			println("Warning: no steady state found.")
			alpha = NaN
		else

			#Compute the flow pattern and normalise
			F = edgepower(θ, E, κ)
			fmax = maximum(abs.(F))
			F = F./fmax

			#Now do bisection
			stepsize = 0.3
			beneath0 = true
			beneath1 = true
			iterations = 1
			while abs(stepsize) > tol

				#Reactivate all edges
				nodeset = collect(1:n)
				edgeset = collect(1:m)
				for j=1:length(net.edgelist)
					net.edgelist[j].active = true
				end

				#Knock out largest
				d = argmax(abs.(F))
				edgeset1 = filter(x->x≠d, edgeset)
				E1 = E[1:end, 1:end .≠ d]
				net.edgelist[d].active = false

				#Check to see if its broken
				Adj = adjacencymatrix(E1)
				components, table = connectedcomponents(Adj)

				#Do cascade
				totedgessurviving = 0
				for i=1:components
					chunk = table[i]
					n_subset = length(chunk)
					nodesubset = zeros(Int64, length(chunk))
					ω_subset = zeros(Float64, length(chunk))
					θ_subset = zeros(Float64, length(chunk))
					ψ_subset = zeros(Float64, 2*n_subset)
					for j=1:length(chunk)
						nodesubset[j] = nodeset[chunk[j]]
						ω_subset[j] = ω[chunk[j]]
						θ_subset[j] = θ[chunk[j]]
					end
					ψ_subset[1:n_subset] = ω_subset
					ψ_subset[n_subset+1:end] = θ_subset
					edgessurviving = swingfracture!(net, ψ_subset, nodesubset, P, 2.0, alpha, κ, fmax)
					totedgessurviving += edgessurviving
				end
				S = totedgessurviving/Float64(m)

				#If fraction has crossed the target then reverse and half the step size
				if S > 0.5
					beneath1 = false
				else
					beneath1 = true
				end
				if beneath1 ≠ beneath0
					stepsize = -stepsize/2.0
				end
				beneath0 = beneath1
				alpha += stepsize
				iterations += 1

			end
		end
		rhos[z] = alpha
	end
	return rhos

end

function swingcascadebreakdownvsalpha(ensemblesize::Int64, n::Int64, ns::Int64, nd::Int64, q::Float64,
								k::Int64, alphares::Int64, alphamin::Float64, alphamax::Float64,
								I::Float64, D::Float64, κ::Float64)

	alphas = collect(range(alphamin, length=alphares, stop=alphamax))
	edgemat = zeros(Float64, ensemblesize, alphares)
	overloadmat = zeros(Float64, ensemblesize, alphares)
	desyncmat = zeros(Float64, ensemblesize, alphares)
	unbalancemat = zeros(Float64, ensemblesize, alphares)
	fmaxes = zeros(Float64, ensemblesize)
	critvalues = zeros(Float64, ensemblesize)

	println("Starting")
	for z=1:ensemblesize
		println("Ensemble = ", z)

		net, E = smallworldnetwork(n, k, q)
		# net, E = scalefreenetwork(n, 2*n, 3)
		sources, sinks = sourcesinklocs(ns, nd, n)
		P = sourcesinkvector(sources, sinks, n, 1.0)
		m = numedges(net)
		nodeset = collect(1:n)
		edgeset = collect(1:m)
		A = adjacencymatrix(E)
		ψ = rand(2*n)

		#Time step to find steady state
		f(u, p, t) = fswing(u, A, P, I, D, κ)
		tspan = (0.0, 100.0)
		prob = ODEProblem(f, ψ, tspan)
		sol = solve(prob,reltol=1e-8,abstol=1e-8,save_everystep=false,dense=false,save_end=true)

		#Check for convergence
		ψ = sol[:,end]
		ω = ψ[1:n]
		θ = ψ[n+1:end]
		x = fsteadystate(θ, A, P, κ)
		if norm(x,2) > 1e-3
			println("Warning: no steady state found.")
		end

		#Compute the flow and normalise
		flow_swing = edgepower(θ, E, κ)
		flowmax_swing = maximum(abs.(flow_swing))
		fmaxes[z] = flowmax_swing
		flow_swing = flow_swing./flowmax_swing

		redges = zeros(Float64, alphares)
		roverloads = zeros(Float64, alphares)
		rdesyncs = zeros(Float64, alphares)
		runbalances = zeros(Float64, alphares)

		for (ind, alpha) ∈ enumerate(alphas)

			#Reactive all the edges
			for i=1:length(net.edgelist)
				net.edgelist[i].active = true
			end

			#Knock out most loaded edge
			d = argmax(abs.(flow_swing))
			edgeset1 = filter(x->x≠d, edgeset)
			E1 = E[1:end, 1:end .≠ d]
			net.edgelist[d].active = false

			#Check to see if its broken
			Adj = adjacencymatrix(E1)
			components, table = connectedcomponents(Adj)

			totedgessurviving = 0
			totaloverloads = 0
			totaldesyncs = 0
			totalunbalances = 0
			for i=1:components
				chunk = table[i]
				n_subset = length(chunk)
				nodesubset = zeros(Int64, length(chunk))
				ω_subset = zeros(Float64, length(chunk))
				θ_subset = zeros(Float64, length(chunk))
				ψ_subset = zeros(Float64, 2*n_subset)
				for j=1:length(chunk)
					nodesubset[j] = nodeset[chunk[j]]
					ω_subset[j] = ω[chunk[j]]
					θ_subset[j] = θ[chunk[j]]
				end
				ψ_subset[1:n_subset] = ω_subset
				ψ_subset[n_subset+1:end] = θ_subset
				edgessurviving, overloads, desyncs, unbalances = swingfracturewithbreakdown!(net, ψ_subset, nodesubset, P, 3.0, alpha, κ, flowmax_swing)
				totedgessurviving += edgessurviving
				totaloverloads += overloads
				totaldesyncs += desyncs
				totalunbalances += unbalances
			end
			redges[ind] = totedgessurviving/m
			roverloads[ind] = (m - totedgessurviving - totaldesyncs - totalunbalances)/m
			rdesyncs[ind] = totaldesyncs/m
			runbalances[ind] = totalunbalances/m
		end

		#Find the critical value
		aftertransition = findall(x->x>0.5, redges)
		if length(aftertransition) < 1
			critvalues[z] = NaN
		else
			ac = alphas[aftertransition[1]]
			critvalues[z] = ac
		end

		#Store S
		edgemat[z,:] = redges
		overloadmat[z,:] = roverloads
		desyncmat[z,:] = rdesyncs
		unbalancemat[z,:] = runbalances
	end
	return alphas, mean(edgemat, dims=1)[1,:], mean(overloadmat, dims=1)[1,:], mean(desyncmat, dims=1)[1,:], mean(unbalancemat, dims=1)[1,:], mean(fmaxes), critvalues
end