input = joinpath(@__DIR__, "input")
raw = readlines(input)

struct Rule
    outer
    inner
    quantity
end

rcontainer = r"^([a-z]+ [a-z]+) bags contain "
rbags = r"(\d+) ([a-z]+ [a-z]+)"

function parserule(rule)
    color = match(rcontainer, rule).captures[1]
    return map(m -> Rule(color, m.captures[2], parse(Int, m.captures[1])), collect(eachmatch(rbags, rule)))
end

rules = reduce(append!, map(parserule, raw))

function cancontain(rules, color)
    return map(r -> Pair(r.inner, r.quantity), filter(r -> r.outer == color, rules))
end

function containedby(rules, color)
    return unique(map(r -> r.outer, filter(r -> r.inner == color, rules)))
end

function countcontainers(rules, colors)
    queue = Set(colors)
    tried = Set{String}()
    while !isempty(queue)
        color = pop!(queue)
        more = containedby(rules, color)
        for m in more
            push!(queue, m)
        end
        push!(tried, color)
    end
    return length(tried)
end

function countcontained(rules, colors)
    queue = Set(colors)
    count = 0
    while !isempty(queue)
        next = pop!(queue)
        col = next.first
        qty = next.second
        count += qty
        for (k, v) in cancontain(rules, col)
            push!(queue, Pair(k, qty * v))
        end
    end
    return count
end

p1 = countcontainers(rules, containedby(rules, "shiny gold"))
p2 = countcontained(rules, cancontain(rules, "shiny gold"))

@assert(p1 == 226)
@assert(p2 == 9569)

println("-----------------------------------------------------------------------")
println("handy haversacks -- part one :: $p1")
println("handy haversacks -- part two :: $p2")
println("-----------------------------------------------------------------------")


filter(x -> iseven(x), 1:10)
# 5-element Array{Int64,1}:
#   2
#   4
#   6
#   8
#  10
mapreduce(x -> sqrt(x), +, 1:10) #> 22.4682781862041
# equivalent to:
sum(map(x -> sqrt(x), [1:10])) #> 22.4682781862041
#
1:10 |> x -> sqrt(x) |> sum #> 22.4682781862041
1:5 |> exp |> sum #> 233.2041839862982

# clipboard:
a = 42 #> 42
clipboard(a)
workspace()
# a #> ERROR: a not defined
a = clipboard() #> "42"


module TestVine

using Copulas
using Base.Test

## equSets
##--------

@test Copulas.equSets([1, 2, 3, 4], [2, 1, 4, 3])
@test Copulas.equSets([1, 2, 2], [2, 1])

## pairMatch
##----------

## does match exist between two trees?

tr1 = Copulas.Tree(3, [1, 2], [4, 5, 6])
tr2 = Copulas.Tree(7, [2, 1], [4], [5, 6])
@test Copulas.pairMatch(tr1, tr2, 3) == [1 1]

tr2 = Copulas.Tree(7, [1], [1, 2], [5, 6])
@test Copulas.pairMatch(tr1, tr2, 3) == [1 2]

tr1 = Copulas.Tree(3, [1, 2], [4, 5])
tr2 = Copulas.Tree(7, [1], [1, 2], [5, 4])
@test Copulas.pairMatch(tr1, tr2, 3) == [1 2; 2 3]

tr1 = Copulas.Tree(3, [1, 2])
tr2 = Copulas.Tree(7, [3, 4])
@test isempty(Copulas.pairMatch(tr1, tr2, 3))

## findPairMatch
##--------------

## for array of trees, where do matches exist?
## also: incomplTrees tests

trs = [Copulas.Tree(1, [2], [3]),
       Copulas.Tree(2, [1, 3]),
       Copulas.Tree(3, [1, 2])
       ]

@test isempty(Copulas.incomplTrees(trs))

trs = [Copulas.Tree(1, [3]);
       Copulas.Tree(2, [3], [5]);
       Copulas.Tree(3, [1], [2]);
       Copulas.Tree(4, [5], [6]);
       Copulas.Tree(5, [2], [4]);
       Copulas.Tree(6, [4])]

@test Copulas.incomplTrees(trs) == [1, 2, 3, 4, 5, 6]

expMatches = [Copulas.CondEdge(1, 2, [3], [3]),
              Copulas.CondEdge(2, 4, [5], [5]),
              Copulas.CondEdge(3, 5, [2], [2]),
              Copulas.CondEdge(5, 6, [4], [4])]

@test expMatches == Copulas.findPairMatch(trs, 2, Copulas.incomplTrees(trs))

## vertexTable
##------------

expVTable = Array{Int, 1}[[1],
                          [1, 2],
                          [3],
                          [2],
                          [3, 4],
                          [4]]
@test expVTable == Copulas.vertexTable(expMatches, 6)

## findPairMatch
##--------------

## given first dvine layer
trs = [Copulas.Tree(1, [2]),
       Copulas.Tree(2, [1], [3]),
       Copulas.Tree(3, [2], [4]),
       Copulas.Tree(4, [3])]

expMatches = [Copulas.CondEdge(1, 3, [2], [2]),
              Copulas.CondEdge(2, 4, [3], [3])]

foundPairs = Copulas.findPairMatch(trs, 2, Copulas.incomplTrees(trs))
@test expMatches == foundPairs

## reachedVertices
##----------------

@test [1, 2, 3, 4] == Copulas.reachedVertices(foundPairs)

edges = [Copulas.CondEdge(1, 3, [2], [2]),
         Copulas.CondEdge(1, 4, [3], [3])]

@test [1, 3, 4] == Copulas.reachedVertices(edges)

## mandatoryEdges
##---------------

edges = [Copulas.CondEdge(1, 3, [2], [2]),
         Copulas.CondEdge(2, 4, [3], [3])]
verticesToBeLinked = [1, 2, 3, 4]

expChosen = Bool[1, 1]
actChosen = Copulas.mandatoryEdges(edges, verticesToBeLinked, 4)[1]
@test expChosen == actChosen

expRemainLinks = []
actRemainLinks = Copulas.mandatoryEdges(edges, verticesToBeLinked, 4)[2]
@test expRemainLinks == actRemainLinks

edges = [Copulas.CondEdge(1, 3, [2], [2]),
         Copulas.CondEdge(2, 3, [4], [4]),
         Copulas.CondEdge(2, 4, [3], [3]),
         Copulas.CondEdge(3, 4, [2], [2])]
verticesToBeLinked = [1, 2, 3, 4]

expChosen = Bool[1, 0, 0, 0]
actChosen = Copulas.mandatoryEdges(edges, verticesToBeLinked, 4)[1]
@test expChosen == actChosen

expRemainLinks = [2, 4]
actRemainLinks = Copulas.mandatoryEdges(edges, verticesToBeLinked, 4)[2]
@test expRemainLinks == actRemainLinks

## selectLinks
##------------

## second layer d-Vine
trs = [Copulas.Tree(1, [2]),
       Copulas.Tree(2, [1], [3]),
       Copulas.Tree(3, [2], [4]),
       Copulas.Tree(4, [3])]

actLinks = Copulas.selectLinks(trs, 2)
expLinks = [Copulas.CondEdge(1, 3, [2], [2]),
            Copulas.CondEdge(2, 4, [3], [3])]
@test actLinks == expLinks

## third layer d-Vine
##-------------------

trs = [Copulas.Tree(1, [2, 3]),
       Copulas.Tree(2, [1], [3, 4]),
       Copulas.Tree(3, [2, 1], [4]),
       Copulas.Tree(4, [3, 2])]

actLinks = Copulas.selectLinks(trs, 3)
expLinks = [Copulas.CondEdge(1, 4, [2, 3], [3, 2])]
@test actLinks == expLinks

## test DVine construction
##------------------------

## expected result
dVn4 = Copulas.Vine([0 2 2 2;
                   1 0 3 3;
                   2 2 0 4;
                   3 3 3 0])

## given first dvine layer
trs = [Copulas.CTreePaths(1, [2]),
       Copulas.CTreePaths(2, [1], [3]),
       Copulas.CTreePaths(3, [2], [4]),
       Copulas.CTreePaths(4, [3])]
vn = Copulas.Vine(trs)

## construct D-Vine
vnConstructed = Copulas.vineFrom2(vn)

@test vnConstructed == dVn4

end


#!/usr/bin/env julia
using Dierckx
using Test
using Random: seed!

# Answers 'ans' are from scipy.interpolate,
# generated with genanswers.py script.

# -----------------------------------------------------------------------------
# Spline1D

x = [1., 2., 3.]
y = [0., 2., 4.]
spl = Spline1D(x, y; k=1, s=length(x))

yi = evaluate(spl, [1.0, 1.5, 2.0])
@test yi ≈ [0.0, 1.0, 2.0]
@test evaluate(spl, 1.5) ≈ 1.0
@test get_knots(spl) ≈ [1., 3.]
@test get_coeffs(spl) ≈ [0., 4.]
@test isapprox(get_residual(spl), 0.0, atol=1.e-30)

@test spl([1.0, 1.5, 2.0]) ≈ [0.0, 1.0, 2.0]
@test spl(1.5) ≈ 1.0

# test that a copy is returned by get_knots()
knots = get_knots(spl)
knots[1] = 1000.
@test get_knots(spl) ≈ [1., 3.]

# test ported from scipy.interpolate testing this bug:
# http://mail.scipy.org/pipermail/scipy-dev/2008-March/008507.html
x = [-1., -0.65016502, -0.58856235, -0.26903553, -0.17370892,
     -0.10011001, 0., 0.10011001, 0.17370892, 0.26903553, 0.58856235,
     0.65016502, 1.]
y = [1.,0.62928599, 0.5797223, 0.39965815, 0.36322694, 0.3508061,
     0.35214793, 0.3508061, 0.36322694, 0.39965815, 0.5797223,
     0.62928599, 1.]
w = [1.00000000e+12, 6.88875973e+02, 4.89314737e+02, 4.26864807e+02,
     6.07746770e+02, 4.51341444e+02, 3.17480210e+02, 4.51341444e+02,
     6.07746770e+02, 4.26864807e+02, 4.89314737e+02, 6.88875973e+02,
     1.00000000e+12]
spl = Spline1D(x, y; w=w, s=Float64(length(x)))
desired = [0.35100374, 0.51715855, 0.87789547, 0.98719344]
actual = evaluate(spl, [0.1, 0.5, 0.9, 0.99])
@test isapprox(actual, desired, atol=5e-4)

# test periodic
x = [1., 2., 3., 4., 5.]
y = [4., 1., 4., 1., 4.]
spl = Spline1D(x, y, periodic=true)

@test derivative(spl, 1) ≈ derivative(spl, 5)
@test derivative(spl, 1, nu=2) ≈ derivative(spl, 5, nu=2)

# tests for out-of-range
x = [0.0:4.0;]
y = x.^3

xp = range(-8.0, stop=13.0, length=100)
xp_zeros = Float64[(0. <= xi <= 4.) ? xi : 0.0 for xi in xp]
xp_clip = Float64[(0. <= xi <= 4.) ? xi : (xi < 0.0) ? 0.0 : 4. for xi in xp]

spl = Spline1D(x, y)
t = get_knots(spl)[2: end-1]  # knots, excluding those at endpoints
spl2 = Spline1D(x, y, t)

@test evaluate(spl, xp) ≈ xp_clip.^3
@test evaluate(spl2, xp) ≈ xp_clip.^3

# test other bc's
spl = Spline1D(x, y; bc="extrapolate")
@test evaluate(spl, xp) ≈ xp.^3
spl = Spline1D(x, y; bc="zero")
@test evaluate(spl, xp) ≈ xp_zeros.^3
spl = Spline1D(x, y; bc="error")
@test_throws ErrorException evaluate(spl, xp)

# test unknown bc
@test_throws ErrorException Spline1D(x, y; bc="unknown")

# test derivative
x = range(0, stop=1, length = 70)
y = x.^3
spl = Spline1D(x, y)
xt = [0.3, 0.4, 0.5]
@test derivative(spl, xt) ≈ 3xt.^2

# test integral
x = range(0, stop=10, length = 70)
y = x.^2
spl = Spline1D(x, y)
@test integrate(spl, 1.0, 5.0) ≈ 5.0^3/3 - 1/3

# test roots
x = range(0, stop=10, length = 70)
y = (x .- 4).^2 .- 1
spl = Spline1D(x, y)
@test roots(spl) ≈ [3, 5]

# test that show works.
io = IOBuffer()
show(io, spl)
seek(io, 0)
s = read(io, String)
@test s[1:9] == "Spline1D("

# test equality
seed!(0)
x = sort(rand(10))
y = rand(10)
sp1 = Spline1D(x, y)
sp2 = Spline1D(x, y)
sp3 = Spline1D(x.+1, y)
sp4 = Spline1D(x, y.+1)
@test sp1 == sp2
@test allunique([sp1,sp3,sp4])

# -----------------------------------------------------------------------------
# ParametricSpline

u = [1., 2., 3.]
x = [1. 2. 3.; 0. 2. 4.]
spl = ParametricSpline(u, x, k=1, s=size(x, 2))

xi = evaluate(spl, [1.0, 1.5, 2.0])
@test xi ≈ [1.0 1.5 2.0; 0.0 1.0 2.0]
@test evaluate(spl, 1.5) ≈ [1.5, 1.0]
@test get_knots(spl) ≈ [1., 3.]
@test get_coeffs(spl) ≈ [1.0 3.0; 0.0 4.0]
@test isapprox(get_residual(spl), 0.0, atol=1.e-30)

@test spl([1.0, 1.5, 2.0]) ≈ [1.0 1.5 2.0; 0.0 1.0 2.0]
@test spl(1.5) ≈ [1.5, 1.0]

# test that a copy is returned by get_knots()
knots = get_knots(spl)
knots[1] = 1000.
@test get_knots(spl) ≈ [1., 3.]

# test periodic
x = [23. 24. 25. 25. 24. 23.;
     13. 12. 12. 13. 13. 13.]
spl = ParametricSpline(x, periodic=true)
@test evaluate(spl, 0) ≈ evaluate(spl, 1)
@test derivative(spl, 0) ≈ derivative(spl, 1)
@test derivative(spl, 0, nu=2) ≈ derivative(spl, 1, nu=2)

# tests for out-of-range
u = 0.0:4.0
x = [u'.^2; u'.^3]

up = range(-8.0, stop=13.0, length = 100)
up_zeros = Float64[(0. <= ui <= 4.) ? ui : 0.0 for ui in up]
up_clip = Float64[(0. <= ui <= 4.) ? ui : (ui < 0.0) ? 0.0 : 4. for ui in up]

spl = ParametricSpline(u, x)
t = get_knots(spl)[2: end-1]  # knots, excluding those at endpoints
spl2 = ParametricSpline(u, x, t)

@test evaluate(spl, up) ≈ [up_clip'.^2; up_clip'.^3]
@test evaluate(spl2, up) ≈ [up_clip'.^2; up_clip'.^3]

# test other bc's
spl = ParametricSpline(u, x; bc="extrapolate")
@test evaluate(spl, up) ≈ [up'.^2; up'.^3]
spl = ParametricSpline(u, x; bc="zero")
@test evaluate(spl, up) ≈ [up_zeros'.^2; up_zeros'.^3]
spl = ParametricSpline(u, x; bc="error")
@test_throws ErrorException evaluate(spl, up)

# test unknown bc
@test_throws ErrorException ParametricSpline(u, x; bc="unknown")

# test derivative
u = range(0, stop=1, length = 70)
x = [u'.^2; u'.^3]
spl = ParametricSpline(u, x)
ut = [0.3, 0.4, 0.5]
@test derivative(spl, 0.3) ≈ [2*0.3, 3*0.3^2]
@test derivative(spl, ut) ≈ [2*ut'; 3*ut'.^2]
@test derivative(spl, 0.3, nu=2) ≈ [2.0, 6*0.3]
@test derivative(spl, ut, nu=2) ≈ [2*ones(3)'; 6*ut']

# test integral
u = range(0, stop=10, length = 70)
x = [u'.^2; u'.^3]
spl = ParametricSpline(u, x)
@test integrate(spl, 1.0, 5.0) ≈ [5.0^3/3 - 1/3, 5.0^4/4 - 1/4]

# test that show works.
io = IOBuffer()
show(io, spl)
seek(io, 0)
s = read(io, String)
@test s[1:17] == "ParametricSpline("

# test equality
seed!(0)
x = sort(rand(10))
y = rand(3, 10)
sp1 = ParametricSpline(x, y)
sp2 = ParametricSpline(x, y)
sp3 = ParametricSpline(x.+1, y)
sp4 = ParametricSpline(x, y.+1)
@test sp1 == sp2
@test allunique([sp1,sp3,sp4])

# test too many roots warning
x = (0:100)
y = (-1).^(0:100)
sp = Spline1D(x,y)
@test_logs (:warn,Regex("number of zeros exceeded")) roots(sp)

# -----------------------------------------------------------------------------
# Spline2D

# test linear
x = [1., 1., 1., 2., 2., 2., 3., 3., 3.]
y = [1., 2., 3., 1., 2., 3., 1., 2., 3.]
z = [0., 0., 0., 2., 2., 2., 4., 4., 4.]
spl = Spline2D(x, y, z; kx=1, ky=1, s=length(x))
tx, ty = get_knots(spl)
@test tx ≈ [1., 3.]
@test ty ≈ [1., 3.]
@test isapprox(get_residual(spl), 0.0, atol=1e-16)
@test evaluate(spl, 2.0, 1.5) ≈ 2.0
@test evalgrid(spl, [1.,1.5,2.], [1.,1.5]) ≈ [0. 0.; 1. 1.; 2. 2.]

# test 1-d grid arrays
@test evalgrid(spl, [2.0], [1.5])[1, 1] ≈ 2.0

# In this setting, lwrk2 is too small in the default run.
x = range(-2, stop=2, length = 80)
y = range(-2, stop=2, length = 80)
z = x .+ y
spl = Spline2D(x, y, z; s=length(x))
@test evaluate(spl, 1.0, 1.0) ≈ 2.0

# In this setting lwrk2 is too small multiple times!
# Eventually an error about s being too small is thrown.
seed!(0)
x = rand(100)
y = rand(100)
z = sin.(x) .* sin.(y)
@test_throws ErrorException Spline2D(x, y, z; kx=1, ky=1, s=0.0)

# test grid input creation
x = [0.5, 2., 3., 4., 5.5, 8.]
y = [0.5, 2., 3., 4.]
z = [1. 2. 1. 2.;  # shape is (nx, ny)
     1. 2. 1. 2.;
     1. 2. 3. 2.;
     1. 2. 2. 2.;
     1. 2. 1. 2.;
     1. 2. 3. 1.]
spl = Spline2D(x, y, z)

# element-wise output
xi = [1., 1.5, 2.3, 4.5, 3.3, 3.2, 3.]
yi = [1., 2.3, 5.3, 0.5, 3.3, 1.2, 3.]
ans = [2.94429906542,
       1.25537598131,
       2.00063588785,
       1.0,
       2.93952664,
       1.06482509358,
       3.0]
zi = evaluate(spl, xi, yi)
@test zi ≈ ans

zi = spl(xi, yi)
@test zi ≈ ans

# grid output
xi = [1., 1.5, 2.3, 4.5]
yi = [1., 2.3, 5.3]
ans = [2.94429906542  1.16946130841  1.99831775701;
       2.80393858478  1.25537598131  1.99873831776;
       1.67143209613  1.94853338542  2.00063588785;
       1.89392523364  1.8126946729  2.01042056075]
zi = evalgrid(spl, xi, yi)
@test zi ≈ ans

# Test 2-d integration
test2d_1(x, y) = 1 - x^2 -y^2
test2d_2(x, y) = cos(x) + sin(y)
test2d_3(x, y) = x*exp(x-y)
for (f, domain, exact) in [(test2d_1, (0.0, 1.0, 0.0, 1.0), 1.0/3.0),
                           (test2d_2, (0.0, pi, 0.0, pi), 2.0*pi),
                           (test2d_3, (0.0, 1.0, 0.0, 1.0), (ℯ-1.0)/ℯ)]
    (x0, x1, y0, y1) = domain

    # define grids for x and y dimensions:
    npoints = 50
    xgrid = range(x0, stop=x1, length = npoints)
    ygrid = range(y0, stop=y1, length = npoints)

    fxygrid = zeros(npoints, npoints)
    for (j, y) in enumerate(ygrid)
        local j, y
        for (i, x) in enumerate(xgrid)
            local i, x
            fxygrid[i,j] = f(x, y)
        end
    end

    spl1 = Spline2D(xgrid, ygrid, fxygrid)
    @test isapprox(integrate(spl1, x0, x1, y0, y1), exact, atol=1e-6)
end

# test equality
seed!(0)
x = sort(rand(10))
y = sort(rand(10))
z = rand(10,10)
sp1 = Spline2D(x, y, z)
sp2 = Spline2D(x, y, z)
sp3 = Spline2D(x.+1, y, z)
sp4 = Spline2D(x, y.+1, z)
sp5 = Spline2D(x, y, z.+1)
@test sp1 == sp2
@test allunique([sp1, sp3, sp4, sp5])

println("All tests passed.")


# using SymPy

# x, y = @vars x y
# u = x*(1-x)*y*(1-y)
# v = x*(1-x)*y*(1-y)^2

# p = x*(1-x)*y*(1-y)

# ux = diff(u,x)
# uy = diff(u,y)
# vx = diff(v,x)
# vy = diff(v,y)
# px = diff(p,x)
# py = diff(p,y)
# f = u*ux + v*uy - (diff(ux,x)+diff(uy,y)) + px
# g = u*vx + v*vy - (diff(vx,x)+diff(vy,y)) + py
# h = diff(u, x) + diff(v, y)
# println(replace(replace(sympy.julia_code(simplify(f)), ".^"=>"^"), ".*"=>"*"))
# println(replace(replace(sympy.julia_code(simplify(g)), ".^"=>"^"), ".*"=>"*"))
# println(replace(replace(sympy.julia_code(simplify(h)), ".^"=>"^"), ".*"=>"*"))

function u_exact(x,y)
    if y == 0.0
        return 1.0
    else
        return 0.0
    end
end

function v_exact(x,y)
    0.0
end

function p_exact(x,y)
    0.0
end

function ffunc(x, y)
    #-x^2*y*(x - 1)^2*(y - 1)^2*(2*y - 1) + x*y^2*(x - 1)*(2*x - 1)*(y - 1)^2 + x*y*(y - 1) - 2*x*(x - 1) + y*(x - 1)*(y - 1) - 2*y*(y - 1)
    0.0
end
function gfunc(x, y)
    #x^2*y*(x - 1)^2*(y - 1)^3*(3*y - 1) - x*y^2*(x - 1)*(2*x - 1)*(y - 1)^3 + 3*x*y*(x - 1) + 5*x*(x - 1)*(y - 1) + 2*y*(y - 1)^2
    0.0
end

function hfunc(x,y)
    # (y - 1)*(-2*x*y*(x - 1) + x*y - x*(x - 1)*(y - 1) + y*(x - 1))
    0.0
end

using LinearAlgebra
using MAT
using AdFem
using PyPlot
using SparseArrays
using ADCMEKit

m = 100
n = 100
h = 1/n
nu = placeholder(0.1)

F1 = compute_fem_source_term1(eval_f_on_gauss_pts(ffunc, m, n, h), m, n, h)
F2 = compute_fem_source_term1(eval_f_on_gauss_pts(gfunc, m, n, h), m, n, h)
H = h^2*eval_f_on_fvm_pts(hfunc, m, n, h)
B = constant(compute_interaction_matrix(m, n, h))

# compute F
Laplace = nu * constant(compute_fem_laplace_matrix1(m, n, h))
function compute_residual(S)
    u, v, p = S[1:(m+1)*(n+1)], S[(m+1)*(n+1)+1:2(m+1)*(n+1)], S[2(m+1)*(n+1)+1:2(m+1)*(n+1)+m*n]
    G = eval_grad_on_gauss_pts([u;v], m, n, h)
    ugauss = fem_to_gauss_points(u, m, n, h)
    vgauss = fem_to_gauss_points(v, m, n, h)
    ux, uy, vx, vy = G[:,1,1], G[:,1,2], G[:,2,1], G[:,2,2]

    interaction = compute_interaction_term(p, m, n, h) # julia kernel needed
    f1 = compute_fem_source_term1(ugauss.*ux, m, n, h)
    f2 = compute_fem_source_term1(vgauss.*uy, m, n, h)
    f3 = -interaction[1:(m+1)*(n+1)]
    f4 = Laplace*u
    f5 = -F1
    F = f1 + f2 + f3 + f4 + f5

    g1 = compute_fem_source_term1(ugauss.*vx, m, n, h)
    g2 = compute_fem_source_term1(vgauss.*vy, m, n, h)
    g3 = -interaction[(m+1)*(n+1)+1:end]
    g4 = Laplace*v
    g5 = -F2
    G = g1 + g2 + g3 + g4 + g5

    H0 = -B * [u;v] + H

    R = [F;G;H0]
    return R
end

function compute_jacobian(S)
    u, v, p = S[1:(m+1)*(n+1)], S[(m+1)*(n+1)+1:2(m+1)*(n+1)], S[2(m+1)*(n+1)+1:2(m+1)*(n+1)+m*n]
    G = eval_grad_on_gauss_pts([u;v], m, n, h)
    ugauss = fem_to_gauss_points(u, m, n, h)
    vgauss = fem_to_gauss_points(v, m, n, h)
    ux, uy, vx, vy = G[:,1,1], G[:,1,2], G[:,2,1], G[:,2,2]

    M1 = constant(compute_fem_mass_matrix1(ux, m, n, h))
    M2 = constant(compute_fem_advection_matrix1(constant(ugauss), constant(vgauss), m, n, h)) # a julia kernel needed
    M3 = Laplace
    Fu = M1 + M2 + M3

    Fv = constant(compute_fem_mass_matrix1(uy, m, n, h))

    N1 = constant(compute_fem_mass_matrix1(vy, m, n, h))
    N2 = constant(compute_fem_advection_matrix1(constant(ugauss), constant(vgauss), m, n, h))
    N3 = Laplace
    Gv = N1 + N2 + N3

    Gu = constant(compute_fem_mass_matrix1(vx, m, n, h))

    J0 = [Fu Fv
          Gu Gv]
    J = [J0 -B'
        -B spdiag(zeros(size(B,1)))]
end

NT = 5
# S = zeros(m*n+2(m+1)*(n+1), NT+1)
# S[1:m+1, 1] = ones(m+1,1)

bd = bcnode("all", m, n, h)
# bd = [bd; bd .+ (m+1)*(n+1); ((1:m) .+ 2(m+1)*(n+1))]
bd = [bd; bd .+ (m+1)*(n+1); 2*(m+1)*(n+1)+(n-1)*m+1] # only apply Dirichlet to velocity

function solve_steady_cavityflow_one_step(S)
    residual = compute_residual(S)
    J = compute_jacobian(S)

    J, _ = fem_impose_Dirichlet_boundary_condition1(J, bd, m, n, h)
    # residual[bd] .= 0.0
    residual = scatter_update(residual, bd, zeros(length(bd)))

    d = J\residual
    S_new = S - d
    return S_new
end

function condition(i, S_arr)
    i <= NT + 1
end

function body(i, S_arr)
    S = read(S_arr, i-1)
    op = tf.print("i=",i)
    i = bind(i, op)
    S_new = solve_steady_cavityflow_one_step(S)
    S_arr = write(S_arr, i, S_new)
    return i+1, S_arr
end

# for i = 1:NT
#     residual = compute_residual(S[:,i])
#     J = compute_jacobian(S[:,i])

#     J, _ = fem_impose_Dirichlet_boundary_condition1(J, bd, m, n, h)
#     residual[bd] .= 0.0

#     d = J\residual
#     S[:,i+1] = S[:,i] - d
#     @info i, norm(residual)
# end

xy = fem_nodes(m, n, h)
x, y = xy[:,1], xy[:,2]
u0 = @. u_exact(x,y)
v0 = @. v_exact(x,y)

xy = fvm_nodes(m, n, h)
x, y = xy[:,1], xy[:,2]
p0 = @. p_exact(x,y)

S_arr = TensorArray(NT+1)
S_arr = write(S_arr, 1, [u0; v0; p0])

i = constant(2, dtype=Int32)

_, S = while_loop(condition, body, [i, S_arr])
S = set_shape(stack(S), (NT+1, 2*(m+1)*(n+1)+m*n))

velo = S[end, 1:2*(m+1)*(n+1)]

velo_data = matread("steady_cavity_data.mat")["V"]
sample_size = 20
idx = rand(1:(m+1)*(n+1), sample_size)
idx = [idx; idx .+ (m+1)*(n+1)] # observe velocity in y direction at the same location
observed_data = velo_data[idx]

noise = true
noise_level = 0.01
if noise
    noise_ratio = (1 - noise_level) .+ 2 * noise_level * rand(Float64, size(observed_data)) # uniform on (1-noise_level, 1+noise_level)
    observed_data = observed_data .* noise_ratio
end

loss = mean((velo[idx] .- observed_data)^2)
loss = loss * 1e6 # use this to improve optimization accuracy

@info run(sess, loss, nu=>0.01)
# lineview()

# max_iter = 100
# sess = Session(); init(sess)
# loss_ = BFGS!(sess, loss, max_iter)
# figure(); semilogy(loss_); title("loss vs. iteration")

# if noise
#     savefig(string("loss_steady_cavityflow",string(n),"_",string(sample_size),"_",string(noise_level),".png"))
# else
#     savefig(string("loss_steady_cavityflow_",string(n),"_",string(sample_size),".png"))
# end


using Test
using SumOfSquares
using DynamicPolynomials

function quartic_constant_test(optimizer,
                   config::MOIT.TestConfig,
                   cone::SumOfSquares.PolyJuMP.PolynomialSet)
    atol = config.atol
    rtol = config.rtol

    @polyvar x
    p = x^4 + 4

    model = _model(optimizer)
    @variable(model, γ)

    cref = @constraint(model, p - γ in cone, basis=FixedPolynomialBasis([x^2]))

    @objective(model, Max, γ)

    optimize!(model)

    @test termination_status(model) == MOI.OPTIMAL

    @test primal_status(model) == MOI.FEASIBLE_POINT

    @test objective_value(model) ≈ 4 atol=atol rtol=rtol

    p = gram_matrix(cref)
    @test p isa SumOfSquares.GramMatrix
    @test getmat(p) ≈ ones(1, 1) atol=atol rtol=rtol
    @test p.basis isa FixedPolynomialBasis
    @test p.basis.polynomials == [x^2]

    S = SumOfSquares.SOSPolynomialSet{
        SumOfSquares.FullSpace, Monomial{true}, MonomialVector{true},
        SumOfSquares.Certificate.FixedBasis{typeof(cone),typeof(p.basis)}
    }
    @test list_of_constraint_types(model) == [(Vector{AffExpr}, S)]
    test_delete_bridge(
        model, cref, 1,
        ((MOI.VectorOfVariables, MOI.Nonnegatives, 0),
         (MOI.VectorOfVariables, MOI.PositiveSemidefiniteConeTriangle, 0)
        ))
end
sos_quartic_constant_test(optimizer, config)   = quartic_constant_test(optimizer, config, SOSCone())
sd_tests["sos_quartic_constant"] = sos_quartic_constant_test
sdsos_quartic_constant_test(optimizer, config) = quartic_constant_test(optimizer, config, SDSOSCone())
soc_tests["sdsos_quartic_constant"] = sdsos_quartic_constant_test
dsos_quartic_constant_test(optimizer, config)  = quartic_constant_test(optimizer, config, DSOSCone())
linear_tests["dsos_quartic_constant"] = dsos_quartic_constant_test


"""

    spring_layout(mpr[; C=2.0, maxiter=100, seed=0])

Calculate a 1-skeleton of the Mapper complex `mpr` layout using spring/repulsion model.

This borrows with modifications from [GraphPlot](https://github.com/JuliaGraphs/GraphPlot.jl)

# Arguments
- `mpr::Mapper`: the Mapper complex.
- `C::Real`: the constant to fiddle with density of resulting layout.
- `maxiter::Integer`: the number of iterations we apply the forces.
- `starttemp::Real`: the initial "temperature", controls movement per iteration.
- `seed::Integer`: the seed for pseudorandom generation of locations (default = 0).
"""
function spring_layout(adj::AbstractMatrix, locs_x, locs_y; C=2.0, maxiter=100, starttemp=2.0) where {T<:Integer}

    N = size(adj, 1)

    # The optimal distance bewteen vertices
    K = C * sqrt(4.0 / N)

    # Store forces and apply at end of iteration all at once
    force_x = zeros(N)
    force_y = zeros(N)

    @inbounds for iter = 1:maxiter
        # Calculate forces
        for i = 1:N
            force_vec_x = 0.0
            force_vec_y = 0.0
            for j = 1:N
                i == j && continue
                d_x = locs_x[j] - locs_x[i]
                d_y = locs_y[j] - locs_y[i]
                d   = sqrt(d_x^2 + d_y^2)
                if adj[i,j] != zero(eltype(adj)) || adj[j,i] != zero(eltype(adj))
                    # F = d^2 / K - K^2 / d
                    F_d = d / K - K^2 / d^2
                else
                    # Just repulsive
                    # F = -K^2 / d^
                    F_d = -K^2 / d^2
                end
                # d  /          sin θ = d_y/d = fy/F
                # F /| dy fy    -> fy = F*d_y/d
                #  / |          cos θ = d_x/d = fx/F
                # /---          -> fx = F*d_x/d
                # dx fx
                force_vec_x += F_d*d_x
                force_vec_y += F_d*d_y
            end
            force_x[i] = force_vec_x
            force_y[i] = force_vec_y
        end
        # Cool down
        TEMP = starttemp / iter
        # Now apply them, but limit to temperature
        for i = 1:N
            force_mag  = sqrt(force_x[i]^2 + force_y[i]^2)
            scale      = min(force_mag, TEMP)/force_mag
            locs_x[i] += force_x[i] * scale
            #locs_x[i]  = max(-1.0, min(locs_x[i], +1.0))
            locs_y[i] += force_y[i] * scale
            #locs_y[i]  = max(-1.0, min(locs_y[i], +1.0))
        end
    end

    # Scale to unit square
    min_x, max_x = minimum(locs_x), maximum(locs_x)
    min_y, max_y = minimum(locs_y), maximum(locs_y)
    function scaler(z, a, b)
        2.0*((z - a)/(b - a)) - 1.0
    end
    map!(z -> scaler(z, min_x, max_x), locs_x, locs_x)
    map!(z -> scaler(z, min_y, max_y), locs_y, locs_y)

    return locs_x, locs_y
end

import Random
function spring_layout(adj::AbstractMatrix; seed::Integer=0, xs=nothing, ys=nothing, kwargs...)
    N = size(adj, 1)
    rng = Random.MersenneTwister(seed)
    spring_layout(adj, xs === nothing ? 2 .* rand(rng, N) .- 1.0 : xs,
                        ys === nothing ? 2 .* rand(rng, N) .- 1.0 : ys ; kwargs...)
end

function spring_layout(mpr::Mapper; seed::Integer=0, random::Bool=false, kwargs...)
    xs = random ? nothing : mpr.centers[1,:]
    ys = random ? nothing : mpr.centers[2,:]
    spring_layout(mpr.adj; xs=xs, ys=ys, kwargs...)
end

"""
    circular_layout(mpr)

Arranges verticies of a 1-skeleton of the Mapper complex `mpr` on a circle.
"""
circular_layout(adj::AbstractMatrix) = circlepoints(size(adj, 1)+1, 1.0)
circular_layout(mpr::Mapper) = circular_layout(mpr.adj)

function constant_layout(data::AbstractMatrix)
    inner_layout(cplx::SimplicialComplex) = (data[1,:], data[2,:])
    return inner_layout
end
constant_layout(mpr::Mapper) = (mpr.centers[1,:], mpr.centers[2,:])


density(f::PolyElem) = length(findall(x->!iszero(x), coefficients(f)))/length(f)

#move elsewhere? Not used in here
function Hecke.representation_matrix(a::ResElem{<:PolyElem})
  R = parent(a)
  S = base_ring(base_ring(R))
  n = degree(modulus(R))
  m = zero_matrix(S, n, n)
  for i=0:n-1
    m[1, i+1] = coeff(a.data, i)
  end
  for j=2:n
    a *= gen(R)
    for i=0:n-1
      m[j, i+1] = coeff(a.data, i)
    end
  end
  return m
end


import LightGraphs.LinAlg: adjacency_matrix

"""
    adjacency_matrix(g[, T=Int; dir=:out])

Return a sparse adjacency matrix for a graph, indexed by `[u, v]`
vertices. Non-zero values indicate an edge between `u` and `v`. Users may
override the default data type (`Int`) and specify an optional direction.

### Optional Arguments
`dir=:out`: `:in`, `:out`, or `:both` are currently supported.

### Implementation Notes
This function is optimized for speed and directly manipulates CSC sparse matrix fields.
"""
function adjacency_matrix(g::Graph, T::DataType=Float64; dir::Symbol=:out)
   n_v = nv(g)
   nz = ne(g)
   colpt = ones(Int64, n_v + 1)

   rowval = sizehint!(Vector{Int64}(), nz)
   weights   = sizehint!(Vector{T}(), nz)
    for j in 1:n_v  # this is by column, not by row.
       wgts = sizehint!(Vector{T}(), g.vertices[j].number_of_edges)
       dsts = sizehint!(Vector{Int64}(), g.vertices[j].number_of_edges)
        for e in g.vertices[j]
            push!(wgts,e.weight)
            push!(dsts,target_vertex(e,g.vertices[j]).id)
        end
        colpt[j + 1] = colpt[j] + length(dsts)

       dsts_indices = sortperm(dsts)
        append!(rowval, dsts[dsts_indices])
        append!(weights, wgts[dsts_indices])
    end
    return SparseMatrixCSC(n_v, n_v, colpt, rowval, weights)

end


using DrWatson
@quickactivate "MinimizeSR"

include("SubFunction/Subsampling_1D.jl")
include("SubFunction/Opt_SpectralRatio_SA.jl")

ns = 300;  # number of sources
p = 5;     # subsampling factor

# initial by using optimally-jittered subsampling
Ind1 = Subsampling_1D(ns,p);

r = 0.2;   #To be relocated ratio of the sources
Ind3, Initial_SR, Final_SR = Opt_SpectralRatio_SA(ns, Ind1, r, p); # Spectral ratio Optimization

println(string("Initial_SR ="), Initial_SR)
println(string("Final_SR ="), Final_SR)


module TestTwoInertiasAndIdealGearWithUnitsAndUncertainties

using Modia
@usingModiaPlot
using Modia.Measurements

TwoInertiasAndIdealGear = Model(
    J1 = 0.0025u"kg*m^2",
    J2 = (150.0 ± 20.0)u"kg*m^2",
    r  = 105.0,
    tau_max = 1u"N*m",

    phi2 = Var(start = (0.5 ± 0.05)u"rad"),
    w2   = Var(start = 0.0u"rad/s"),
    tau2 = Var(start = 0u"N*m"),

    equations = :[
        tau = if time < 1u"s"; tau_max elseif time < 2u"s"; 0u"N*m" elseif time < 3u"s"; -tau_max else 0u"N*m" end,

        # inertia1
        w1 = der(phi1),
        J1*der(w1) = tau - tau1,

        # gear
        phi1   = r*phi2,
        r*tau1 = tau2,

        # inertia2
        w2 = der(phi2),
        J2*der(w2) = tau2
    ]
)

twoInertiasAndIdealGear = @instantiateModel(TwoInertiasAndIdealGear, FloatType = Measurement{Float64})

simulate!(twoInertiasAndIdealGear, Tsit5(), stopTime = 4.0, logParameters=true, logStates=true)

plot(twoInertiasAndIdealGear, ["phi2", "w2", "der(w2)"])

# Linearize
println("\n... Analytic linearization")
(A1, x1) = linearize!(twoInertiasAndIdealGear, stopTime=4, analytic = true)
xNames = get_xNames(twoInertiasAndIdealGear)
@show xNames
println(IOContext(stdout, :error_digits=>15), "A1 = ", A1, ", x1 = ", x1)

println("\n... Numeric linearization with Float64")
(A2, x2) = linearize!(twoInertiasAndIdealGear, stopTime=4, analytic=false)
println(IOContext(stdout, :error_digits=>15), "A2 = ", A2, ", x2 = ", x2)

#= DoubleFloats not defined
using Test
println("\n... Numeric linearization with Double64")
using DoubleFloats
twoInertiasAndIdealGear2 = SimulationModel{Measurement{Double64}}(twoInertiasAndIdealGear)
(A3, x3) = linearize!(twoInertiasAndIdealGear2, stopTime=3, analytic=false)
println(IOContext(stdout, :error_digits=>15), "A3 = ", A3, ", x3 = ", x3)
=#

end


"""
Calculate the moving average on an array of b-values
"""
function  _moving_average(arrb::AbstractVector{Float64}, nwin::Int)

    bavrg = fill(NaN,length(arrb))

    for i in 1:(length(arrb) - (nwin - 1))
        bavrg[i] = mean( @view arrb[i:(i+nwin-1)] )
    end

    return bavrg
end

"""
    bMcstep(mag, bincenters, ΔM, minevents)

Calculate b-value for each possible cut-off magnitude

# Arguments
*  `mags`: array storing the magnitude values of an incomplete catalog.
* `bincenters`: array storing the central value of the magnitude bins.
* `ΔM`: bin width.
* `minevents`: minimum number of events allowed when calculating b-values.

# Return
* `Bvals`: 1D array with the b values calculated varying the cut-off magnitude
* `Stds`: 1D array with the uncertainties associated with the calculated b values
"""
function _bMcstep(mag::AbstractVector{T}, bincenters::AbstractVector{Float64}, ΔM::S, minevents::Int) where {T,S<:Real}

    N = length(bincenters)
    bvals = fill(NaN,N)
    stds  = fill(NaN,N)

    for i in 1:N
        Mc = bincenters[i]
        submag = @view mag[mag .>= Mc-ΔM/2]

        if length(submag) >= minevents
            fitgr = fit_mle(GRclassic, submag, Mc-ΔM/2)
            bvals[i] = bvalue(fitgr)
            stds[i] = fit_stderr(fitgr, submag)
        else
            break
        end
    end

    return bvals, stds
end

"""
    mbs(mags, ΔM; winsize=5, minevents=50)

Compute the magnitude of completeness with the b−value stability (MBS)
method.

# Arguments
* `mags`: array storing the magnitude values of an incomplete catalog.
* `ΔM`: bin width to be used to group the magnitude values.`

# Keyword arguments
* `winsize`: window length used for the moving average. Default is 5 magnitude bins
* `minevents`: minimum number of events allowed when calculating b-values. Defaul is
   50 events.

# References:
* Cao and Gao, 2002, Temporal variations of seismic b-values beneath northeastern
  japan island arc, Geophysical Research Letters, 29, https://doi.org/10.1029/2001GL013775
* Woessner and Wiemer (2005), Assessing the quality of earthquake catalogues:
  Estimating the magnitude of completeness and its uncertainty, Bulletin of the Seismological Society of America,
  95, https://doi.org/10.1785/0120040007
"""
function mbs(mag::AbstractVector{T}, ΔM::S; winsize::Int=5, minevents::Int=50) where {T,S<:Real}

    bincenters = histmag(mag,ΔM)[1]

    # Calculate b-with magnitude
    bvals, stds = _bMcstep(mag, bincenters, ΔM, minevents)

    baverages = _moving_average(bvals,winsize)

    # If fBave is in in between the error estimate of the b-value of the first
    # cut-off magnitude, take it as guess
    goodSteps = abs.(baverages .- bvals) .<= stds
    latest_good = findfirst(goodSteps)

    if !isnothing(latest_good)
        return bincenters[latest_good]
    else
        return NaN
    end

end


data(f::Taylor1Model) = (f.n, f.x0, f.I)

function +(f::Taylor1Model, g::Taylor1Model)
    @assert data(f) == data(g)

    return Taylor1Model(f, f.p + g.p, f.Δ + g.Δ)
end

function -(f::Taylor1Model, g::Taylor1Model)
    @assert data(f) == data(g)

    return Taylor1Model(f, f.p - g.p, f.Δ - g.Δ)
end

+(α::Real, f::Taylor1Model) = Taylor1Model(f, f.p + α, f.Δ)
+(f::Taylor1Model, α::Real)= α + f

+(f::Taylor1Model, α::Real)= α + f

-(f::Taylor1Model) = (-1 * f)
-(α::Real, f::Taylor1Model) = α + (-f)
-(f::Taylor1Model, α::Real) = f + (-α)

*(α::Real, f::Taylor1Model) = Taylor1Model(f, α*f.p, α*f.Δ)
*(f::Taylor1Model, α::Real) = α * f

function *(f::Taylor1Model, g::Taylor1Model)
    @assert data(f) == data(g)

    n, x0, I = f.n, f.x0, f.I

    c = zeros(eltype(g.p), 2n+1)

    a = f.p
    b = g.p

    for i in 0:n
        for j in 0:n
            c[i+j+1] += a[i] * b[j]
        end
    end

    d = zeros(eltype(g.p), 2n+1)

    d[n+2:end] = c[n+2:end]

    B = bound(Taylor1(d), x0, I)
    Bf = bound(a, x0, I)
    Bg = bound(b, x0, I)

    Δ = B + (f.Δ * Bg) + (g.Δ * Bf) + (f.Δ * g.Δ)

    return Taylor1Model(f, Taylor1(c[1:n+1]), Δ)

end


"""
$(TYPEDEF)

# Fields

Accessing fields directly is part of the API. The additional property `cap` is supported.

$(FIELDS)
"""
struct HomogeneousBasis{N,U}
    "The univariate family, eg `Chebyshev()`."
    univariate::U
    "Indexes by dimensions."
    I::NTuple{N,Int}
    "The level (`≥ 0`) of the Smolyak approximation. `cap = level + N`."
    level::Int
end

function Base.propertynames(basis::HomogeneousBasis, private = false)
    (fieldnames(HomogeneousBasis)..., :cap)
end

function Base.getproperty(basis::HomogeneousBasis{N}, name::Symbol) where {N}
    if name ≡ :cap
        N + basis.level
    else
        getfield(basis, name)
    end
end

function degrees_of_freedom(basis::HomogeneousBasis)
    @unpack univariate, I, cap = basis
    C = mapfoldl((C1, C2) -> count_combinations(cap, C1, C2), I) do i
        indexes = 1:min(i, cap)
        Counts(indexes, set_length.(univariate, indexes))
    end
    sum(C)
end

"""
$(SIGNATURES)

Calculate the matrix `A[R[i[end]], R[j[end]] ⊗ … ⊗ A[R[i[2]], R[j[2]] ⊗ A[R[i[1]], R[j[1]]`,
where

- `A` is the basis matrix for all points and basis functions,

- `R` contains the ranges for each index,

- `i` and `j` are iterables of indexes in `R`.

Internal helper function used in building basis matrices.
"""
function _basis_block(A, R, i, j)
    mapreduce(((i, j), ) -> A[R[i], R[j]], (A1, A2) -> kron(A2, A1), zip(i, j))
end

"""
$(SIGNATURES)

Calculate all combinations of `x[R[i[1]]], x[R[i[2]]], …`, with the first index changing the
fastest (column major).

Internal helper function used in building coordinates.
"""
function _coordinate_block(x, R, i)
    mapfoldl(i -> x[R[i]],
             (y, x) -> vec(((x, y) -> (x, y...)).(x, permutedims(y))),
             reverse(i), init = [()])
end

"""
$(SIGNATURES)

Return `A, v` where `A` is a ``d × d`` matrix and `v` is a `d`-element vector, where

- `d = degrees_of_freedom(basis)`,

- `v` is a vector of *coordinates*,

- `A \\ f.(x)` would calculate the *coefficients* of approximating `f`.
"""
function basis_matrix_and_coordinates(basis::HomogeneousBasis)
    @unpack univariate, I, cap = basis
    iter = CappedCartesianIndices(cap, I)
    K = maximum(largest_indices(iter))

    # univariate building blocks
    R = set_range.(univariate, 1:K)
    x0 = all_gridpoints(univariate, K)
    A0 = evaluate(last(last(R)), univariate, x0)

    # combinations
    # FIXME these could be made much more efficient by pre-allocating, and making
    # `_basis_block` and `_coordinate_block` mutate a pre-allocated matrix
    A = mapreduce(j -> mapreduce(i -> _basis_block(A0, R, i, j), vcat, iter), hcat, iter)
    x = mapreduce(i -> _coordinate_block(x0, R, i), vcat, iter)
    A, SVector.(x)
end

"""
$(SIGNATURES)

Helper function for iterating when interpolating `basis` at `x`.

Return a `NamedTuple` of

- `iter`: an iterator for the set indices,
- `R`: a vector of set ranges
- `A0s`: basis matrices for each coordinate, to be indexed by elements of `R`
"""
function _interpolation_helper(basis::HomogeneousBasis{N}, x::AbstractVector) where {N}
    @argcheck length(x) == N
    @unpack univariate, I, cap = basis
    iter = CappedCartesianIndices(cap, I)
    Ks = largest_indices(iter)
    R = set_range.(univariate, 1:maximum(Ks))
    A0s = map((K, x) -> evaluate(last(R[K]), univariate, x), Ks, x)
    (iter = iter, R = R, A0s = A0s)
end

"""
$(SIGNATURES)

Interpolate `basis` with coefficients `c` at `x`.
"""
function interpolate(basis::HomogeneousBasis, c::AbstractVector{T1},
                     x::AbstractVector{T2}) where {T1, T2}
    @unpack iter, R, A0s = _interpolation_helper(basis, x)
    S = float(promote_type(T1, T2))
    v = zero(S)
    p = 1
    for i in iter
        for I in CartesianIndices(map(i -> R[i], i))
            v += prod(map(getindex, A0s, Tuple(I))) * c[p]
            p += 1
        end
    end
    v
end

"""
$(SIGNATURES)

In-place version of [`interpolated_basis`](@ref). Overwrites the first argument,
which should have length `degrees_of_freedom(basis)`.
"""
function interpolated_basis!(a::AbstractVector, basis::HomogeneousBasis, x::AbstractVector)
    @unpack iter, R, A0s = _interpolation_helper(basis, x)
    p = 1
    for i in iter
        for I in CartesianIndices(map(i -> R[i], i))
            a[p] = prod(map(getindex, A0s, Tuple(I)))
            p += 1
        end
    end
    a
end

"""
$(SIGNATURES)

Return a vector `a` such that

```julia
dot(a, c) ≈ interpolate(basis, c, x)
```

This is useful when repeatedly interpolating at the same set of coordinates `x` (`a` is
specific to `x`).
"""
function interpolated_basis(basis::HomogeneousBasis, x::AbstractVector{T}) where{T}
    interpolated_basis!(Vector{float(T)}(undef, degrees_of_freedom(basis)), basis, x)
end


using BenchmarkTools
using Random
using VPTrees

Random.seed!(1)
function hamming(a::Integer, b::Integer)
    count_ones(xor(a, b))
end
randints = [rand(UInt) for _ in 1:10000]
@benchmark VPTree(randints, hamming)
# BenchmarkTools.Trial:
#   memory estimate:  36.16 MiB
#   allocs estimate:  96189
#   --------------
#   minimum time:     14.749 ms (10.84% GC)
#   median time:      17.335 ms (21.43% GC)
#   mean time:        17.348 ms (20.07% GC)
#   maximum time:     63.897 ms (73.87% GC)
#   --------------
#   samples:          288
#   evals/sample:     1
randints = [rand(UInt) for _ in 1:40000]
vptree = VPTree(randints, hamming);
query = rand(UInt)
radius = 20
@benchmark find(vptree, query, radius)

# BenchmarkTools.Trial:
#   memory estimate:  2.22 KiB
#   allocs estimate:  8
#   --------------
#   minimum time:     230.819 μs (0.00% GC)
#   median time:      253.774 μs (0.00% GC)
#   mean time:        276.296 μs (0.40% GC)
#   maximum time:     11.471 ms (97.37% GC)
#   --------------
#   samples:          10000
#   evals/sample:     1
@benchmark find_nearest(vptree, query, 20)
# BenchmarkTools.Trial:
#   memory estimate:  1.38 KiB
#   allocs estimate:  9
#   --------------
#   minimum time:     321.168 μs (0.00% GC)
#   median time:      343.786 μs (0.00% GC)
#   mean time:        360.818 μs (0.00% GC)
#   maximum time:     1.245 ms (0.00% GC)
#   --------------
#   samples:          10000
#   evals/sample:     1

using StringDistances
using Statistics

metric(a::AbstractString, b::AbstractString) = evaluate(Levenshtein(), a,b)
prefixes = readlines("resources/benchmark_data.csv")

Random.seed!(2)
t = VPTree(prefixes, metric)
rng = Random.MersenneTwister(1)
@benchmark find(t, prefixes[rand(rng, 1:length(prefixes))], 2)
rng = Random.MersenneTwister(1)
@benchmark findall(a -> metric(prefixes[rand(rng, 1:length(prefixes))], a) <= 2, prefixes)


export Berlage,
Ormsby,
Ricker
include("Berlage.jl")
include("Ormsby.jl")
include("Ricker.jl")


using MultiwayNumberPartitioning, HiGHS, JuMP
using DataFrames, UUIDs, StableRNGs, Chain

# In this example, we have a collection of individual animals, identified by UUID, each with a species.
# We wish to obtain partition our animals into 4 groups,
n_partitions = 4
# such that:
# * the groups are of roughly equal size
# * all animals of the same species are in the same group

# First, let's generate our data:
function make_table(n_animals)
    rng = StableRNG(324)
    possible_species = ["Aardvark", "Albatross", "Alligator", "Alpaca", "Anole", "Ant",
                        "Anteater"]
    return DataFrame(:species => [rand(rng, possible_species) for i in 1:n_animals],
                     :id => [uuid4(rng) for i in 1:n_animals])
end

n_animals = 100
df = make_table(n_animals)

# Now, we will group them by species and count how many individuals we have:
count_by_species = combine(groupby(df, :species), nrow => :n_individuals)

# Now, we will partition them.
# First, we choose our optimizer. We will use the new MIT licensed HiGHS solver (https://github.com/ERGO-Code/HiGHS)
optimizer = optimizer_with_attributes(HiGHS.Optimizer, MOI.Silent() => true) # quiet, please!

# Now, perform the partitioning:
transform!(count_by_species,
           :n_individuals => (v -> partition(v, n_partitions; optimizer)) => :partition)

# Already here we can look at `count_by_species` to see the results. However, we can also
# `leftjoin!` these partitions back onto our original DataFrame so that we can see which individual is in which partition:
partitioned_df = leftjoin(df, count_by_species; on=:species)

#####
##### Comparing objective functions
#####

# We can also explore trying different objective functions. Let's start again with our `count_by_species` DataFrame:
count_by_species = combine(groupby(df, :species), nrow => :n_individuals)

# Now, let us try all three algorithms supported by the package:
for alg in (partition_min_largest!, partition_max_smallest!, partition_min_range!)
    alg_name = repr(alg; context=:compact => true)
    transform!(count_by_species,
               :n_individuals => (v -> partition(alg, v, n_partitions; optimizer)) => alg_name)
end
# Let's also generate two random partitions to compare to:
count_by_species.partition_random_1 = rand(StableRNG(431), 1:n_partitions,
                                           nrow(count_by_species))
count_by_species.partition_random_2 = rand(StableRNG(891), 1:n_partitions,
                                           nrow(count_by_species))

# We can summarize the results with a little DataFrames manipulation.
# See <https://bkamins.github.io/julialang/2021/05/28/pivot.html> for more on `stack` and `unstack`.
summary_df = @chain count_by_species begin
    stack(r"partition", :n_individuals; variable_name=:algorithm, value_name=:group)
    groupby([:algorithm, :group])
    combine(:n_individuals => sum => :group_size)
    unstack(:group, :algorithm, :group_size)
end

# We can see the random partitions looks much worse in terms of having groups of equal size; e.g. the first
# has a group of size 12 and one of size 47, while the other partitions have groups of closer sizes.

# We can do some simple checks:
@testset "Partitions add up to the whole" begin
    for col in names(summary_df, r"partition")
        @test sum(summary_df[:, col]) == n_animals
    end
end

@testset "Partitions are optimal (rough check)" begin
    for col in names(summary_df, r"partition")
        # "max smallest" really holds, compared to the other algorithms and to the random partition:
        @test minimum(summary_df[:, :partition_max_smallest!]) >=
              minimum(summary_df[:, col])

        # "min largest" really holds, compared to the other algorithms and to the random partition:
        @test maximum(summary_df[:, :partition_min_largest!]) <= maximum(summary_df[:, col])

        # "min range" really holds:
        r = v -> maximum(v) - minimum(v)
        @test r(summary_df[:, :partition_min_range!]) <= r(summary_df[:, col])
    end
end


import Base: +, -, ∘, real, imag, abs

# Set it to negative of itself
function (-)(p_in::ScalarData)
  p = deepcopy(p_in)
  @. p.data = -p.data
  return p
end

function (-)(p_in::VectorData)
  p = deepcopy(p_in)
  @. p.u = -p.u
  @. p.v = -p.v
  return p
end

# Add and subtract the same type
function (-)(p1::T,p2::T) where {T<:ScalarData}
  return T(p1.data .- p2.data)
end

function (+)(p1::T,p2::T) where {T<:ScalarData}
  return T(p1.data .+ p2.data)
end

function (-)(p1::T,p2::T) where {T<:VectorData}
  return T(p1.u - p2.u, p1.v - p2.v)
end

function (+)(p1::T,p2::T) where {T<:VectorData}
  return T(p1.u + p2.u, p1.v + p2.v)
end

# Multiply and divide by a constant
function (*)(p::T,c::Number) where {T<:ScalarData}
  return T(c*p.data)
end

function (/)(p::T,c::Number) where {T<:ScalarData}
  return T(p.data ./ c)
end

function (*)(p::T,c::Number) where {T<:VectorData}
  return T(c*p.u,c*p.v)
end

(*)(c::Number,p::T) where {T<:PointData} = *(p,c)

function (/)(p::T,c::Number) where {T<:VectorData}
  return T(p.u / c, p.v / c)
end

function (*)(p1::T,p2::T) where {T<:ScalarData}
  return T(p1.data .* p2.data)
end

function (*)(p1::T,p2::T) where {T<:VectorData}
  return T(p1.u * p2.u, p1.v * p2.v)
end

"""
    (+)(X::VectorData,a::Tuple{T,T}) where {T<:Number} -> VectorData
    (-)(X::VectorData,a::Tuple{T,T}) where {T<:Number} -> VectorData

Adds or subtracts the tuple `a` component by component to each element of `X`. All data in `a` are
converted to Float64. Can also switch the arguments.

# Example

```jldoctest
julia> f = VectorData(5);

julia> f + (2,3)
5 points of vector-valued Float64 data
5×2 Array{Float64,2}:
 2.0  3.0
 2.0  3.0
 2.0  3.0
 2.0  3.0
 2.0  3.0
```
"""
function (+)(A::VectorData,a::Tuple{T,T}) where {T <: Number}
   B = deepcopy(A)
    u, v = a
    B.u .+= u
    B.v .+= v
    return B
end

function (-)(A::VectorData,a::Tuple{T,T}) where {T <: Number}
   B = deepcopy(A)
    u, v = a
    B.u .-= u
    B.v .-= v
    return B
end

### Hadamard products

"""
    product!(out::PointData,p::PointData,q::PointData)

Compute the Hadamard (i.e. element by element) product of point data
data `p` and `q` and return the result in `out`. Note that `p` and `q`
can be of mixed type (scalar, vector, tensor), as long as one of them is
a scalar. Also, `out` must have element type that is consistent with the promoted
type of `p` and `q`.

# Example
```jldoctest
julia> fcs = ScalarData(5,dtype=ComplexF64);

julia> fill!(fcs,2im)
5 points of scalar-valued Complex{Float64} data
5-element Array{Complex{Float64},1}:
 0.0 + 2.0im
 0.0 + 2.0im
 0.0 + 2.0im
 0.0 + 2.0im
 0.0 + 2.0im

julia> frt = TensorData(fcs,dtype=Float64);

julia> fill!(frt,1.0)
5 points of tensor-valued Float64 data dudx, dudy, dvdx, dvdy
5×4 Array{Float64,2}:
 1.0  1.0  1.0  1.0
 1.0  1.0  1.0  1.0
 1.0  1.0  1.0  1.0
 1.0  1.0  1.0  1.0
 1.0  1.0  1.0  1.0

julia> out = similar(frt,element_type=ComplexF64);

julia> product!(out,frt,fcs)
5 points of tensor-valued Complex{Float64} data dudx, dudy, dvdx, dvdy
5×4 Array{Complex{Float64},2}:
 0.0+2.0im  0.0+2.0im  0.0+2.0im  0.0+2.0im
 0.0+2.0im  0.0+2.0im  0.0+2.0im  0.0+2.0im
 0.0+2.0im  0.0+2.0im  0.0+2.0im  0.0+2.0im
 0.0+2.0im  0.0+2.0im  0.0+2.0im  0.0+2.0im
 0.0+2.0im  0.0+2.0im  0.0+2.0im  0.0+2.0im
```
"""
function product!(out::ScalarData{N},
                  p::ScalarData{N},
                  q::ScalarData{N}) where {N}

    @inbounds for x in 1:N
        out.data[x] = p.data[x] * q.data[x]
    end
    out
end

function product!(out::VectorData{N},
                  p::VectorData{N},
                  q::VectorData{N}) where {N}

    @inbounds for x in 1:N
        out.u[x] = p.u[x] * q.u[x]
        out.v[x] = p.v[x] * q.v[x]
    end
    out
end

function product!(out::TensorData{N},
                  p::TensorData{N},
                  q::TensorData{N}) where {N}

    @inbounds for x in 1:N
        out.dudx[x] = p.dudx[x] * q.dudx[x]
        out.dudy[x] = p.dudy[x] * q.dudy[x]
        out.dvdx[x] = p.dvdx[x] * q.dvdx[x]
        out.dvdy[x] = p.dvdy[x] * q.dvdy[x]
    end
    out
end

function product!(out::VectorData{N},
                  p::ScalarData{N},
                  q::VectorData{N}) where {N}

    @inbounds for x in 1:N
        out.u[x] = p[x] * q.u[x]
        out.v[x] = p[x] * q.v[x]
    end
    out
end

function product!(out::TensorData{N},
                  p::ScalarData{N},
                  q::TensorData{N}) where {N}

    @inbounds for x in 1:N
        out.dudx[x] = p[x] * q.dudx[x]
        out.dudy[x] = p[x] * q.dudy[x]
        out.dvdx[x] = p[x] * q.dvdx[x]
        out.dvdy[x] = p[x] * q.dvdy[x]
    end
    out
end

"""
    product(p::PointData,q::PointData) -> PointData
    (∘)(p::PointData,q::PointData) -> PointData

Compute the Hadamard (i.e. element by element) product of point data
data `p` and `q`. Works similarly to `product!`.
"""
function product end

for f in (:ScalarData, :VectorData, :TensorData)
    @eval function product(p::$f{N}, q::$f{N}) where {N}
        product!($f(N,dtype=promote_type(eltype(p),eltype(q))), p, q)
    end

    @eval (∘)(p::$f{N}, q::$f{N}) where {N} = product(p,q)
end

for f in (:VectorData, :TensorData)
    @eval product!(out::$f{N},p::$f{N},q::ScalarData{N}) where {N} = product!(out,q,p)

    @eval function product(p::$f{N},q::ScalarData{N}) where {N}
        product!($f(N,dtype=promote_type(eltype(p),eltype(q))), p, q)
    end

    @eval product(q::ScalarData{N},p::$f{N}) where {N} = product(p,q)

    @eval (∘)(p::$f{N}, q::ScalarData{N}) where {N} = product(p,q)

    @eval (∘)(q::ScalarData{N}, p::$f{N}) where {N} = product(p,q)

end

### Operations between tuples and vectors

a::(Tuple{T,T} where {T}) + A::VectorData = A + a
a::(Tuple{T,T} where {T}) - A::VectorData = (B = A - a; @. B.u = -B.u; @. B.v = -B.v; return B)

"""
    cross(a::Number/ScalarData,A::VectorData) -> VectorData
    ×(a::Number/ScalarData,A::VectorData) -> VectorData

Compute the cross product between the scalar `a` (treated as an out-of-plane component of a vector)
and the planar vector data `A`.
"""
function cross(a::Union{Number,ScalarData},A::VectorData)
    B = deepcopy(A)
    @. B.u = -a*A.v
    @. B.v = a*A.u
    return B
end

### Vector operations

"""
    cross(A::VectorData,B::VectorData) -> ScalarData
    ×(A::VectorData,A::VectorData) -> ScalarData

Compute the cross product between the vector point data `A` and `B`
and return the result as scalar data (treated as an out-of-plane
component of a vector).
"""
function cross(A::VectorData{N},B::VectorData{N}) where N
    C = ScalarData(N,dtype=promote_type(eltype(A),eltype(B)))
    @. C = A.u*B.v - A.v*B.u
    return C
end

"""
    dot(A::Tuple{T,T},B::VectorData) where {T<:Number} -> ScalarData
    ⋅(A::Tuple{T,T},B::VectorData) where {T<:Number} -> ScalarData

Computes the dot product between the tuple `v` and the elements of a tensor `B` on
a set of points and returns scalar data on the same set of points.
"""
function dot(A::Tuple{T,T},B::VectorData) where {T<:Number}
    C = ScalarData(B)
    x, y = A
    @. C.data = x*B.u + y*B.v
    return C
end

"""
    dot(A::Tuple{T,T},B::TensorData) where {T<:Number} -> VectorData
    ⋅(A::Tuple{T,T},B::TensorData) where {T<:Number} -> VectorData

Computes the dot product between the tuple `A` and the elements of a tensor `B` on
a set of points and returns vector data on the same set of points.
"""
function dot(A::Tuple{T,T},B::TensorData) where {T<:Number}
    C = VectorData(B)
    x, y = A
    @. C.u = x*B.dudx + y*B.dudy
    @. C.v = x*B.dvdx + y*B.dvdy
    return C
end

### Operations on complex point data

for f in (:real, :imag, :abs)
  @eval function $f(A::PointData{N,T}) where {N,T <: ComplexF64}
      Acopy = similar(A,element_type=Float64)
      Acopy .= broadcast($f,A)
      return Acopy
  end
end


using Gadfly, RDatasets

set_default_plot_size(5inch, 3inch)

iris = dataset("datasets", "iris")

fs = [ x->0.3x+1, x->0.3x+1.1 ]
cs = ["versicolor", "virginica"]
plot(iris,
  layer(x=:SepalLength, y=:SepalWidth, color=:Species),
  layer(fs, 4, 8, color=cs, order=2)
)


function set_volume(nodes, vecAx, vecAy, vecAz, cellxmax, cellymax, cellzmax)
    volume = zeros(cellxmax, cellymax, cellzmax)
    for i in 1:cellxmax
        for j in 1:cellymax
            for k in 1:cellzmax
                vec_rx = nodes[i+1,j+1,k+1,1] - nodes[i,j,k,1]
                vec_ry = nodes[i+1,j+1,k+1,2] - nodes[i,j,k,2]
                vec_rz = nodes[i+1,j+1,k+1,3] - nodes[i,j,k,3]

                Ax = vecAx[i,j,k,1] + vecAy[i,j,k,1] + vecAz[i,j,k,1]
                Ay = vecAx[i,j,k,2] + vecAy[i,j,k,2] + vecAz[i,j,k,2]
                Az = vecAx[i,j,k,3] + vecAy[i,j,k,3] + vecAz[i,j,k,3]

                volume[i,j,k] = abs(Ax*vec_rx + Ay*vec_ry + Az*vec_rz) /3
            end
        end
    end
    return volume
end

function set_cellcenter(nodes, cellxmax, cellymax, cellzmax)
    cellcenter = zeros(cellxmax, cellymax, cellzmax, 3)
    for i in 1:cellxmax
        for j in 1:cellymax
            for k in 1:cellzmax
                for l in 1:3
                    xl = 0.125*(nodes[i+1,j+1,k+1,l] + nodes[i+1,j,k+1,l] + nodes[i,j+1,k+1,l] + nodes[i,j,k+1,l] +
                                nodes[i+1,j+1,k,l] + nodes[i+1,j,k,l] + nodes[i,j+1,k,l] + nodes[i,j,k,l])

                    cellcenter[i,j,k,l] = xl
                end
            end
        end
    end
    return cellcenter
end

function set_mu(Qbase, cellxmax, cellymax, cellzmax, specific_heat_ratio, Rd)
    mu = zeros(cellxmax, cellymax, cellzmax)

    # サザーランドの式
    # https://www.jstage.jst.go.jp/article/jsam1937/37/4/37_4_694/_pdf/-char/ja
    mu0 = 1.82e-5 # 基準粘度[Pa s]
    T0 = 293.15   # 基準温度[K]
    C = 117       # サザーランド定数[K]

    for i in 1:cellxmax
        for j in 1:cellymax
            for k in 1:cellzmax
                # T = p/(rho Rd )
                T = Qbase[i,j,k,5]/(Qbase[i,j,k,1]*Rd)

                if T <0
                    println("T is munus ( 'A' ) !!")
                    println("i : "*string(i)*"  j : "*string(j)*"  k : "*string(k))
                    println(Qbase[i,j,k,:])
                end

                mu[i,j,k] = mu0 * (T/T0)^1.5 * (T0+C)/(T+C)
            end
        end
    end

    return mu
end

function set_lambda(Qbase, cellxmax, cellymax, cellzmax, mu, specific_heat_ratio, Rd)

    lambda = zeros(cellxmax, cellymax, cellzmax)

    # サザーランドの式
    lam0 = 22.3*10^(-3)  # 基準熱伝導率　[W/mK]
    T0 = 273.15       # 基準温度[K]
    C = 125       # サザーランド定数[K]

    for i in 1:cellxmax
        for j in 1:cellymax
            for k in 1:cellzmax
                T = Qbase[i,j,k,5]/(Qbase[i,j,k,1]*Rd)
                lambda[i,j,k] = lam0*((T0+C)/(T+C))*(T/T0)^1.5
            end
        end
    end

    return lambda
end


@testset "Test Constructors" begin
    @testset "Kalman Filter Constructors" begin
        x₀ = 1
        F = 2
        H = 3
        P₀ = 5
        Q = 7
        R = 11
        kf = KalmanFilter(x₀, F, H, P₀, Q, R)
        @test typeof(kf) == KalmanFilter{Float64}
        # Promotion
        x₀ = [2;0]
        F = Float32.([0.5 1; 0 1])
        B = Int32.([1;0])
        H = [1 0]
        P₀ = I(2)
        Q = 10*I(2)
        R = 0.7*I(1)
        kf = KalmanFilter(x₀, F, H, B, P₀, Q, R)
        @test typeof(kf.B) == Matrix{Float64}
        @test kf.pdy[] == 1

        x₀ = [0.;2.]
        F = [1. 1;0 1]
        H = [1. 0]
        B = Matrix{Float64}(undef, 2,1)
        B[:,:] = [0; 1.]
        P₀ = [10. 0; 0 10.]
        Q = [1. 0; 0 1.]
        R = fill(1.,1,1)
        offset = [1.;1.]
        kf = KalmanFilter(x₀, F, H, B, P₀, Q, R, offset)
        expected = (x₀, F, H, B, P₀, Q, R, offset)
        reality = (kf.x, kf.F, kf.H, kf.B, kf.P, kf.Q, kf.R, kf.offset)

        @test expected==reality
    end

    @testset "Minimax & Bayesian MMAE Constructors" begin
        x₀ = [0.;2.]
        F = [1. 1;0 1]
        H = [1. 0]
        B = Matrix{Float64}(undef, 2,1)
        B[:,:] = [0; 1.]
        P₀ = [10. 0; 0 10.]
        Q = [1. 0; 0 1.]
        R = fill(1.,1,1)
        offset = [1.;1.]
        kf1 = KalmanFilter(x₀, F, H, B, P₀, Q, R, offset)
        kf2 = KalmanFilter(x₀, -F, H, B, P₀, Q, R, offset)
        mini = MinimaxMMAE([kf1, kf2], () ->  Hypatia.Optimizer())
        @test mini.filterbank[1].F == -mini.filterbank[2].F
        bayesian = BayesianMMAE([kf1, kf2])
        @test bayesian.filterbank[1].pdy[] == 1/2
        @test bayesian.filterbank[1].F == -bayesian.filterbank[2].F
    end
end


## https://github.com/macd/BaryRational.jl/blob/main/src/aaa.jl
## https://arxiv.org/pdf/1612.00337.pdf for paper

import BaryRational: aaa, prz
export aaa, prz

"""
    BaryRationalFunction(xs, ys)
    fit(BaryRationalFunction, xs, ys)

Fit a rational function to the points `(xs,ys)` using the `aaa` method from [BaryRational](https://github.com/macd/BaryRational.jl/).

References: Nakatsukasa, Sete, and Trefethen; THE AAA ALGORITHM FOR RATIONAL APPROXIMATION [	arXiv:1612.00337](https://arxiv.org/pdf/1612.00337.pdf)

> Even on a disk or interval the algorithm may outperform existing methods, and on more complicated domains it is especially competitive. The core ideas are (1) representation of the rational approximant in barycentric form with interpolation at certain support points and (2) greedy selection of the support points to avoid exponential instabilities. The name AAA stands for "adaptive Antoulas--Anderson" in honor of the authors who introduced a scheme based on [Approximation of Large-Scale Dynamical Systems](https://doi.org/10.1137/1.9780898718713).

"""
struct BaryRationalFunction{T,X,P} <: AbstractRationalFunction{T,X,P}
    aaa::BaryRational.AAAapprox{Array{T,1}}
    function BaryRationalFunction(xs, ys; var=:x, kwargs...)
        o = BaryRational.aaa(collect(xs), collect(ys), kwargs...)
        T = eltype(o.x)
        X = var
        P = Polynomial{T, X}
        new{T,X,P}(o)
    end
end

## need a convert(BaryRationalFunction, p::RationalFunction) method...
BaryRationalFunction(p::AbstractPolynomial, q::AbstractPolynomial) =
    throw(ArgumentError("Method not defined for BaryRationalFunction"))

Polynomials.fit(::Type{<:BaryRationalFunction}, xs, ys; var=:x, kwargs...) =
    BaryRationalFunction(xs, ys; var=var, kwargs...)

Base.show(io::IO, F::BaryRationalFunction) = print(io, "BaryRational approximation")

Base.numerator(pq::BaryRationalFunction) = pqs(pq)[1]
Base.denominator(pq::BaryRationalFunction) = pqs(pq)[2]

# evaluation
(F::BaryRationalFunction)(x) = F.aaa(x)

function Base.convert(PQ::Type{RationalFunction}, F::BaryRationalFunction)
    P = eltype(PQ)
    z = variable(P) // one(P)
    F.aaa(z)
end

function pqs(F::BaryRationalFunction{T,X,P}) where {T,X,P}
    pq = convert(RationalFunction, F)
    pqs(pq)
end


using DataDrivenDiffEq
using ModelingToolkit
using LinearAlgebra
using OrdinaryDiffEq
using Test
using DataDrivenDiffEq.Optimize

include("./basis.jl")
include("./koopman.jl")
include("./sindy.jl")
include("./isindy.jl")
include("./utils.jl")
include("./optimize.jl")


"""
    detectPWPeaks(signal::Vector{<:Real}, fs::Real; windowLength::Int = 300, tuning::Real = 0.7)

Find Peaks in a pulsewave signal with an algorithm proposed by Nenova, B., & Iliev, I. (2010). An automated algorithm for fast pulse wave detection. International Journal Bioautomation, 14(3), 203.

# Args:

* signal: Pulswave data
* fs: Sampling Frequency [Hz]
* windowLenght: Optional length of the window in ms. (standard: 300ms)
* tuning: fine tuning to filter Peaks below a certain height

# Return:

Returns a Vector containing the Peak indices. Use signal[indices] to access the Peak values.
"""
function detectPWPeaks(signal::Vector{<:Real}, fs::Real; windowLength::Int = 300, tuning::Real = 0.7)
    # bandpass filter
    lowPass = Lowpass(12, fs = fs)
    highPass = Highpass(0.5, fs = fs)
    butterWorth = Butterworth(1)
    filterLowPass = digitalfilter(lowPass, butterWorth)
    filterHighPass = digitalfilter(highPass, butterWorth)
    lowFiltered = filt(filterLowPass, signal)
    filtered = filt(filterHighPass, lowFiltered)

    # getting right window length

    window = Int(ceil(windowLength/1000 * fs))
    winLen = 1:window:length(signal)
    vals = Vector{Float64}(undef, length(winLen) - 1)
    indices = Vector{Int}(undef, length(winLen) - 1)
    for ii = 1:length(winLen)-1
        (val, ind) = findmax(filtered[winLen[ii]:winLen[ii+1]-1])
        vals[ii] = val
        indices[ii] = ind + (ii - 1) * window
    end
    #remove all below mean
    #avg = mean(filtered)

    avg2 = tuning * mean(vals)
    deleteat!(indices, findall(x -> x < avg2, vals))
    deleteat!(vals, findall(x -> x < avg2, vals))

    #remove all with a dist of less than 200ms?
    indDiff = diff(indices)
    for ii = 1:length(indDiff)
        if indDiff[ii] <= window
            if vals[ii] > vals[ii+1]
                vals[ii+1] = 0
            else
                vals[ii] = 0
            end
        end
    end
    deleteat!(indices, findall(x -> x == 0, vals))

    return (indices)
end


using Test
using Distributions, DistributionsAD
using AdvancedVI

include("optimisers.jl")

target = MvNormal(ones(2))
logπ(z) = logpdf(target, z)
advi = ADVI(10, 1000)

# Using a function z ↦ q(⋅∣z)
getq(θ) = TuringDiagMvNormal(θ[1:2], exp.(θ[3:4]))
q = vi(logπ, advi, getq, randn(4))

xs = rand(target, 10)
@test mean(abs2, logpdf(q, xs) - logpdf(target, xs)) ≤ 0.05

# OR: implement `update` and pass a `Distribution`
function AdvancedVI.update(d::TuringDiagMvNormal, θ::AbstractArray{<:Real})
    return TuringDiagMvNormal(θ[1:length(q)], exp.(θ[length(q) + 1:end]))
end

q0 = TuringDiagMvNormal(zeros(2), ones(2))
q = vi(logπ, advi, q0, randn(4))

xs = rand(target, 10)
@test mean(abs2, logpdf(q, xs) - logpdf(target, xs)) ≤ 0.05


using Metatheory

@testset "Type Assertions in Ematcher" begin
    some_theory = @theory begin
        a * b => b * a
        a::Number * b::Number => matched(a,b)
        a::Int64 * b::Int64 => specific(a,b)
        a * (b * c) => (a * b) * c
    end

    G = EGraph(:(2*3))

    @test true == areequal(G, some_theory, :(2 * 3), :(matched(2,3)))
    @test true == areequal(G, some_theory, :(matched(2,3)), :(specific(3,2)))
end


using Parameters
using Printf
using LinearAlgebra

export calc_radius_factor, sort_and_cut, get_channels

"""
Object containing the scattering channels and the asympotic energy
"""
struct ScatteringChannels
    "reciprical lattice points"
    g
    "reciprical lattice index"
    gi
    "kz2 after scattering"
    kz2::Array{Real}
end

function Base.show(ch::ScatteringChannels)
    @unpack g,gi,kz2 = ch
    println("g","\t","index","\t","kz²")
    for i in 1:length(g)
        @printf("%8.2f %8.2f %3d %3d %8.2f\n", g[i][1],g[i][2],gi[i][1],gi[i][1],kz2[i])
        end
end

"""
Calculate a hexagonal grid with a radius n and a lattice constant of 1
angle between a1 and a2 = 120 deg

# Arguments
- `n:Int64`
- `a₁=1`: base vector length 1
- `a₂=1`: base vector length 2

pattern (n = 3):
```
  + + +
 + + + +
+ + + + +
 + + + +
  + + +
```

# returns (g,gi)
 - g .. 2d array of lattice point coordinates
 - gi ... index of lattice point
"""
function hexgrid(n::Int64, a₁ = 1,a₂ = 1)
    a = [1.0 -0.5;
         0.0 sqrt(3.0)/2.0]

    max_index = 2*n+1

    nelements = 1+6*n+3*(n)*(n-1)
    g = zeros(Float64,nelements,2)
    gi = zeros(Int32,nelements,2)
    index = 1
    for i = 1:max_index
        maxj = i+n
        if maxj > max_index
            maxj = max_index
        end
        minj = i-n
        if minj < 1
            minj = 1
        end
        for j = minj:maxj
            t = a*[i-n-1 ; j-n-1]
            g[index,:]=t
            gi[index,:]=[i-n-1; j-n-1]
            index+=1
        end
    end
    return g,gi
end

"""
# Arguments
* lattice Lattice object which is used for the reciprical lattice vectors
* ki wavevector of incident beam
* theta angle of incident beam and the surface normal in radians
* n range of generated channels
"""
function get_channels(b1,b2,ki, theta, n::T = 10) where (T <: Int)
    g = []
    gi = []
    kz2= []
    for i in -n:n, j in -n:n
        push!(gi,[i,j])
        cur_g = b1.*i+b2.*j
        push!(g,cur_g)
        tmp1 = ki.*[sin(theta),0].+cur_g
        push!(kz2,ki^2-tmp1'tmp1)
    end
    ScatteringChannels(g,gi,kz2)
end

function filter_channels_radius(ch::ScatteringChannels,
                                max_norm)
    @unpack kz2,g,gi = ch
    new_g = []
    new_gi = []
    new_kz²= []
    for (i,cur_g) in enumerate(g)
        if norm(cur_g) <= max_norm
            push!(new_g,cur_g)
            push!(new_gi,gi[i])
            push!(new_kz²,kz²[i])
        end
    end
    ScatteringChannels(new_g,new_gi,new_kz²)
end

"""
Calculate radius of a circle fitting in 4 cells
cells are drawn by g = i*b1+j+b2 for i and j in -1:1
"""
function calc_radius_factor(lattice::Generic2DLattice)
    @unpack b1,b2 = lattice
    d1 = [b1[2],-b1[1]]./norm(b1)
    d2 = [b2[2],-b2[1]]./norm(b2)
    r1 = abs(d1'b2)
    r2 = abs(d2'b1)
    min(r1,r2)
end

"""
sort by kz² and cut away channels
# Arguments
* ch channels
* n_open number of open channels to keep
* n_closed number of closed channels to keep

"""
function sort_and_cut(ch::ScatteringChannels, n_open::T, n_closed::U) where {T <: Int, U <:Int}

    @unpack g,gi,kz2 = ch

    index = sortperm(collect(zip(kz2,getindex.(gi,1),getindex.(gi,2))),rev=true)
    sorted_kz2 = kz2[index]

    #find lowest open channel
    first_closed = findfirst(x -> x < 0, sorted_kz2)

    open_index = 1:first_closed-1
    if n_open >= 0 && n_open < length(open_index)
        open_index = (first_closed-n_open:first_closed-1)
    end

    close_index = first_closed:length(kz2)
    if n_closed >= 0 &&  n_closed < length(close_index)
        close_index = first_closed:first_closed+n_closed-1
    end

    selected_index = vcat(open_index,close_index)

    ScatteringChannels(g[index,:][selected_index],
                       gi[index,:][selected_index],
                       sorted_kz2[selected_index])

end


struct State
    position::Position
    attitude::Attitude
    velocity::Array{T, 1} where T<:Number  # body (u, v, w) in m/s
    angular_velocity::Array{T, 1} where T<:Number  # body (p, q, r) in rad/s
    acceleration::Array{T, 1} where T<:Number  # body (ax, ay, az) in m/s^2
    angular_acceleration::Array{T, 1} where T<:Number  # body (p_dot, q_dot, r_dot) in rad/s^2
end

show(io::IO, state::State) = print(
    io,
    """State:
    \t$(state.position),
    \t$(state.attitude),
    \tVelocity: u=$(state.velocity[1]) m/s, v=$(state.velocity[2]) m/s, w=$(state.velocity[3]) m/s",
    \tAngular velocity: p=$(rad2deg(state.angular_acceleration[1])) deg/s, q=$(rad2deg(state.angular_acceleration[2])) deg/s, r=$(rad2deg(state.angular_acceleration[3])) deg/s,
    \tAcceleration: ax=$(state.acceleration[1]) m/s², ay=$(state.acceleration[2]) m/s², az=$(state.acceleration[3]) m/s²,
    \tAngular Velocity: p_dot=$(rad2deg(state.angular_acceleration[1])) deg/s², q_dot=$(rad2deg(state.angular_acceleration[2])) deg/s², r_dot=$(rad2deg(state.angular_acceleration[3])) deg/s²
    """
    )

# Position getters
get_position(state::State) = state.position
get_llh(state::State) = get_llh(state.position)
get_xyz_earth(state::State) = get_xyz_earth(state.position)
get_xyz_ecef(state::State) = get_xyz_ecef(state.position)
get_height(state::State) = get_height(state.position)

# Attitude getters
get_attitude(state::State) = state.attitude
get_euler_angles(state::State) = get_euler_angles(state.attitude)
get_quaternions(state::State) = get_quaternions(state.attitude)

# Velocity getters
get_body_velocity(state::State) = state.velocity
get_horizon_velocity(state::State) = body2hor(
    get_body_velocity(state)...,
    get_quaternions(state)...,
)
get_ecef_velocity(state::State) = body2ecef(
    get_body_velocity(state)...,
    get_llh(state)[1:2]...,
    get_euler_angles(state)...,
)

function get_flight_path_angle(state::State)
    vn, ve, vz = get_horizon_velocity(state)
    atan(-vz, norm([vn, ve]))
end

# Angular velocity getters
get_body_ang_velocity(state::State) = state.angular_velocity
get_turn_rate(state::State) = body2hor(
    get_body_ang_velocity(state)...,
    get_quaternions(state)...,
)[3]
get_euler_angles_rates(state::State) = body_angular_velocity_to_euler_angles_rates(
    get_body_ang_velocity(state)...,
    get_euler_angles(state)[2:3]...,
)
get_quaternions_rate(state::State) = body_angular_velocity_to_quaternion_rates(
    get_body_ang_velocity(state)...,
    get_quaternions(state)...
)

# Acceleration getters
get_body_accel(state::State) = state.acceleration
get_horizon_accel(state::State) = body2hor(
    get_body_accel(state)...,
    get_quaternions(state)...,
)

# Angular acceleration getters
get_body_ang_accel(state::State) = state.angular_acceleration

function isapprox(x::State, y::State; rtol=1e-8, atol=0.0, nans=false)

    result = all([
        isapprox(x.position, y.position; rtol=rtol, atol=atol, nans=nans),
        isapprox(x.attitude, y.attitude; rtol=rtol, atol=atol, nans=nans),
        isapprox(x.velocity, y.velocity; rtol=rtol, atol=atol, nans=nans),
        isapprox(x.angular_velocity, y.angular_velocity, rtol=rtol, atol=atol, nans=nans),
        isapprox(x.acceleration, y.acceleration, rtol=rtol, atol=atol, nans=nans),
        isapprox(x.angular_acceleration, y.angular_acceleration, rtol=rtol, atol=atol, nans=nans),
    ])

    return result
end


# lstm weights initialization
# w[2k-1], w[2k] : weight and bias for kth layer respectively
function initweights(atype, hiddens, embedding, vocab, init=xavier)
    weights = Array(Any, 2length(hiddens))
    input = embedding
    for k = 1:length(hiddens)
        weights[2k-1] = init(input+hiddens[k], 4hiddens[k])
        weights[2k] = zeros(1, 4hiddens[k])
        weights[2k][1:hiddens[k]] = 1 # forget gate bias
        input = hiddens[k]
    end
    return map(w->convert(atype, w), weights)
end

# state initialization
# s[2k-1], s[2k] : hidden and cell respectively
function initstate(atype, hiddens, batchsize)
    state = Array(Any, 2length(hiddens))
    for k=1:length(hiddens)
        state[2k-1] = atype(zeros(batchsize, hiddens[k]))
        state[2k] = atype(zeros(batchsize, hiddens[k]))
    end
    return state
end

function initmodel(atype, hiddens, embedding, vocabsize, init=xavier)
    model = Dict{Symbol, Any}()
    model[:forw] = initweights(atype, hiddens, embedding, vocabsize, init)
    model[:back] = initweights(atype, hiddens, embedding, vocabsize, init)
    model[:fembed] = atype(init(vocabsize, embedding))
    model[:bembed] = atype(init(vocabsize, embedding))
    model[:soft] = [ atype(init(2hiddens[end], vocabsize)), atype(init(1, vocabsize)) ]
    return model
end

function lstm(weight,bias,hidden,cell,input)
    gates   = hcat(input,hidden) * weight .+ bias
    hsize   = size(hidden,2)
    forget  = sigm(gates[:,1:hsize])
    ingate  = sigm(gates[:,1+hsize:2hsize])
    outgate = sigm(gates[:,1+2hsize:3hsize])
    change  = tanh(gates[:,1+3hsize:end])
    cell    = cell .* forget + ingate .* change
    hidden  = outgate .* tanh(cell)
    return (hidden,cell)
end

# multilayer lstm forward, returns the final hidden
function forward(weight, states, input)
    x = input
    for i=1:2:length(states)
        (states[i], states[i+1]) = lstm(weight[i], weight[i+1], states[i], states[i+1], x)
        x = states[i]
    end
    return x
end

# dropout layer
function dropout(x, p)
    if p > 0
        return x .* (rand!(similar(AutoGrad.getval(x))) .> p) * (1/(1-p))
    else
        return x
    end
end

function logprob(output, ypred)
    nrows,ncols = size(ypred)
    index = similar(output)
    @inbounds for i=1:length(output)
        index[i] = i + (output[i]-1)*nrows
    end
    o1 = logp(ypred,2)
    o2 = o1[index]
    o3 = sum(o2)
    return o3
end


@symbol_func function K_WM(cur_reactor::AbstractReactor)
  cur_K = volume(cur_reactor)

  cur_K /= 2

  cur_K /= 4 * cur_reactor.pi * 1e-7

  cur_K /= safe_symbol(cur_reactor, :R_0) ^ 3

  cur_K *= 1e-9

  cur_K
end


module Ex5_13
using FaultDetectionTools
using DescriptorSystems
using LinearAlgebra
using Polynomials
using Test

# Example 5.13 - Solution of an EMMP using Procedure EMMS
println("Example 5.13")

# define s as an improper transfer function
s = rtf('s');
# enter Gu(s), Gf(s) and Mr(s)
Gu = [s/(s^2+3*s+2) 1/(s+2);
     s/(s+1) 0;
      0 1/(s+2)];
Gf = [s/(s^2+3*s+2) 1/(s+2);
     s/(s+1) 0;
      0 1/(s+2)];
Mr = dss(eye(2));                  # enter Mr(s)
p, mf = size(Gf); mu = size(Gu,2);

# compute left nullspace basis as Q1(s) = [ I -Gu(s) ]
Q1 = dss([I -Gu]); Rf = dss(Gf)

# check solvability condition
rank(evalfr(Rf,rand())) == mf || error("No solution exist")

# check for unstable or infinite zeros
gzero(Rf)   # zeros at infinity and in the origin exist

atol = 1.e-7                # tolerance for rank tests
sdeg = -1                   # set stability degree

# using minimal dynamic covers, compute Q2 such that Q2*Q1 is a lower order
# left annihilator and Q2*Rf full row rank;
# select basis vectors [2 3] of Q1 to combine them with basis vector 1
# to obtain Rf_Q = Q2*[Rf Q1], with Q2*Rf in the leading mf columns and
# Q2*Q1 in the trailing p+mu columns
cinp = [ 2, 3, 1]
Rf_Q = glmcover1([Rf[cinp,:] Q1[cinp,:]], mf; atol)[1]

# compute the irreducible realization of Qtilde = Mr*(inv(Rf)*Q) by
# first solving the linear rational matrix equation Rf*X = Q
X = grsol(Rf_Q, p+mu; atol)[1]
Qtilde = gir(Mr*X; atol)

# compute stable and proper Q = Q4*Qtilde with suitable diagonal Q4 = M
Q = dss(zeros(0,p+mu)); M = dss(zeros(0,0));
for i = 1:mf
    Qi, Mi = glcf(Qtilde[i,:]; atol, sdeg);
    # scale with gain to fit example
    sc = zpk(dss2rm(Mi; atol1 = atol, atol2 = atol)[1,1])[3]
    global Q = [Q; Qi/sc];
    global M = append(M, Mi/sc);
end

# convert to standard state space representation
Q = gss2ss(Q)[1]; M = gss2ss(M)[1]

# display results
println("Q = $(dss2rm(Q, atol = 1.e-7))")
println("M = $(dss2rm(M, atol = 1.e-7))")

# check solution
G = dss([Gu Gf;eye(mu,mu+mf)]); F = [zeros(mf,mu) M*Mr];
@test iszero(Q*G-F; atol)

end # module


using MOCNeutronTransport
@testset "LineSegment_2D" begin
    for F in [Float32, Float64, BigFloat]
        @testset "Constructors" begin
            p₁ = Point_2D{F}(1, 0)
            p₂ = Point_2D{F}(2, 0)
            l = LineSegment_2D(p₁, p₂)
            @test l[1] == p₁
            @test l[2] == p₂
        end
        @testset "Methods" begin
            # interpolation
            p₁ = Point_2D{F}(1, 1)
            p₂ = Point_2D{F}(3, 3)
            l = LineSegment_2D(p₁, p₂)
            @test l(0) ≈ p₁
            @test l(1) ≈ p₂
            @test l(1//2) ≈ Point_2D{F}(2, 2)

            # arclength
            p₁ = Point_2D{F}(1, 2)
            p₂ = Point_2D{F}(2, 4)
            l = LineSegment_2D(p₁, p₂)
            @test arclength(l) ≈ sqrt(5)
            @test typeof(arclength(l)) == F

            # intersect
            # -------------------------------------------
            # basic intersection
            l₁ = LineSegment_2D(Point_2D{F}(0,  1), Point_2D{F}(2, -1))
            l₂ = LineSegment_2D(Point_2D{F}(0, -1), Point_2D{F}(2,  1))
            hit, p₁ = intersect(l₁, l₂)
            @test hit
            @test p₁ == Point_2D{F}(1, 0)
            @test typeof(p₁) == Point_2D{F}

            # vertex intersection
            l₂ = LineSegment_2D(Point_2D{F}(0, -1), Point_2D{F}(2, -1))
            hit, p₁ = l₁ ∩ l₂
            @test hit
            @test p₁ ≈ Point_2D{F}(2, -1)

            # vertical
            l₁ = LineSegment_2D(Point_2D{F}(0,  1), Point_2D{F}(2,   1))
            l₂ = LineSegment_2D(Point_2D{F}(1, 10), Point_2D{F}(1, -10))
            hit, p₁ = intersect(l₁, l₂)
            @test hit
            @test p₁ ≈ Point_2D{F}(1, 1)

            # nearly vertical
            l₁ = LineSegment_2D(Point_2D{F}(-1, -100000), Point_2D{F}(1,  100000))
            l₂ = LineSegment_2D(Point_2D{F}(-1,   10000), Point_2D{F}(1,  -10000))
            hit, p₁ = l₁ ∩ l₂
            @test hit
            @test p₁ ≈ Point_2D{F}(0, 0)

            # parallel
            l₁ = LineSegment_2D(Point_2D{F}(0, 1), Point_2D{F}(1, 1))
            l₂ = LineSegment_2D(Point_2D{F}(0, 0), Point_2D{F}(1, 0))
            hit, p₁ = intersect(l₁, l₂)
            @test !hit

            # collinear
            l₁ = LineSegment_2D(Point_2D{F}(0, 0), Point_2D{F}(2, 0))
            l₂ = LineSegment_2D(Point_2D{F}(0, 0), Point_2D{F}(1, 0))
            hit, p₁ = intersect(l₁, l₂)
            @test !hit

            # line intersects, not segment (invalid s)
            l₁ = LineSegment_2D(Point_2D{F}(0, 0), Point_2D{F}(2, 0    ))
            l₂ = LineSegment_2D(Point_2D{F}(1, 2), Point_2D{F}(1, 1//10))
            hit, p₁ = l₁ ∩ l₂
            @test !hit

            # line intersects, not segment (invalid r)
            l₂ = LineSegment_2D(Point_2D{F}(0, 0), Point_2D{F}(2, 0    ))
            l₁ = LineSegment_2D(Point_2D{F}(1, 2), Point_2D{F}(1, 1//10))
            hit, p₁ = intersect(l₁, l₂)
            @test !hit

            # isleft
            l = LineSegment_2D(Point_2D{F}(0, 0), Point_2D{F}(1, 0))
            @test isleft(Point_2D{F}(0, 1) , l)
            @test !isleft(Point_2D{F}(0, -1) , l)
            @test !isleft(Point_2D{F}(0, -1e-6) , l)
            @test isleft(Point_2D{F}(0, 1e-6) , l)
            @test isleft(Point_2D{F}(0.5, 0) , l)
        end
    end
end


facts("Non-negative CANDECOMP") do

r = 2
T = rand_kruskal3(r, (10, 20, 30), true)

@time factors = nncp(T, r, compute_error=true)
@fact length(factors.factors) --> ndims(T)
@fact map(size, factors.factors) --> (collect(zip(size(T), (r, r, r)))...)
@fact rank(factors) --> r
@fact rel_residue(factors) --> less_than(0.05)

end


using Distributions
using Random

"""
    sample(data::DeltaData)

Generate a matrix containing the model inputs for Delta-method sensitivity analysis
with the information in the `data`. The resulting matrix has `N` rows and `D`
columns, where `D` is the number of parameters and `N` is the number of samples.
"""
function sample(data::DeltaData)

    # check for parameters
    if data.params === nothing
        error("Cannot sample to generate model inputs because there are no parameters")
    end

    # constants
    D = length(data.params) # number of uncertain parameters in problem
    N = data.N # number of samples
    d = 1.0 / N

    # preallocate
    result = zeros(N, D)
    temp = zeros(N)

    # generate samples
    for i = 1:D
        for j = 1:N

            low = (j - 1) * d
            high = j * d
            uniform_distrib = Uniform(low, high)

            temp[j] = rand(uniform_distrib)
        end
        shuffle!(temp)
        result[:, i] = temp
    end

    scale_samples!(result, [values(data.params)...]) # scale
    return result

end


# Find Ω(t), dΩ(t), and computational stencils
# Alex Tam, 02/11/2021

"Data structure for an interior grid point"
struct GridPoint
    xInd::Int # Index of x-coordinate
    yInd::Int # Index of y-coordinate
    Ω::Bool # Boolean for whether grid point is inside Ω
    dΩ::Bool # Boolean for whether grid point is close to dΩ
    θxp::Float64 # Relative distance to plus stencil node (x)
    θyp::Float64 # Relative distance to plus stencil node (y)
    θxm::Float64 # Relative distance to minus stencil node (x)
    θym::Float64 # Relative distance to minus stencil node (y)
end

"Data structure for interface point"
struct InterfacePoint
    m::GridPoint # Grid point to negative side of interface
    p::GridPoint # Grid point to positive side of interface
end

"Classify and find stencils for interior grid points"
function find_domain(par, ϕ)
    D = Vector{GridPoint}() # Pre-allocate empty vector of interior grid points
    for i = 2:par.Nx-1 # Loop over interior grid points
        for j = 2:par.Ny-1 # Loop over interior grid points
            # Grid points inside Ω
            if ϕ[i,j] < 0
                if ϕ[i+1,j] >= 0
                    θxp = ϕ[i,j]/(ϕ[i,j] - ϕ[i+1,j]) # Linear interpolation to find interface
                else
                    θxp = 1.0
                end
                if ϕ[i-1,j] >= 0
                    θxm = ϕ[i,j]/(ϕ[i,j] - ϕ[i-1,j]) # Linear interpolation to find interface
                else
                    θxm = 1.0
                end
                if ϕ[i,j+1] >= 0
                    θyp = ϕ[i,j]/(ϕ[i,j] - ϕ[i,j+1]) # Linear interpolation to find interface
                else
                    θyp = 1.0
                end
                if ϕ[i,j-1] >= 0
                    θym = ϕ[i,j]/(ϕ[i,j] - ϕ[i,j-1]) # Linear interpolation to find interface
                else
                    θym = 1.0
                end
                if (θxp < par.θb) || (θxm < par.θb) || (θyp < par.θb) || (θym < par.θb) # If point is close to interface
                    push!(D, GridPoint(i, j, true, true, θxp, θyp, θxm, θym))
                else # If point is far enough from interface
                    push!(D, GridPoint(i, j, true, false, θxp, θyp, θxm, θym))
                end
            # Grid points outside Ω
            else
                if ϕ[i+1,j] < 0
                    θxp = ϕ[i,j]/(ϕ[i,j] - ϕ[i+1,j]) # Linear interpolation to find interface
                else
                    θxp = 1.0
                end
                if ϕ[i-1,j] < 0
                    θxm = ϕ[i,j]/(ϕ[i,j] - ϕ[i-1,j]) # Linear interpolation to find interface
                else
                    θxm = 1.0
                end
                if ϕ[i,j+1] < 0
                    θyp = ϕ[i,j]/(ϕ[i,j] - ϕ[i,j+1]) # Linear interpolation to find interface
                else
                    θyp = 1.0
                end
                if ϕ[i,j-1] < 0
                    θym = ϕ[i,j]/(ϕ[i,j] - ϕ[i,j-1]) # Linear interpolation to find interface
                else
                    θym = 1.0
                end
                if (θxp < par.θb) || (θxm < par.θb) || (θyp < par.θb) || (θym < par.θb) # If point is close to interface
                    push!(D, GridPoint(i, j, false, true, θxp, θyp, θxm, θym))
                else # If point is far enough from interface
                    push!(D, GridPoint(i, j, false, false, θxp, θyp, θxm, θym))
                end
            end
        end
    end
    return D
end

"Locate interface and associated grid points"
function find_interface(D, ϕ)
    dΩ = Vector{InterfacePoint}() # Pre-allocate interface
    for gp in D # Loop over all grid points
        if gp.Ω == true # Check points inside domain
            if ϕ[gp.xInd+1, gp.yInd] >= 0
                for p in D # Find grid point opposite interface
                    if (p.xInd == gp.xInd+1) && (p.yInd == gp.yInd)
                        push!(dΩ, InterfacePoint(gp, p))
                    end
                end
            end
            if ϕ[gp.xInd-1, gp.yInd] >= 0
                for m in D # Find grid point opposite interface
                    if (m.xInd == gp.xInd-1) && (m.yInd == gp.yInd)
                        push!(dΩ, InterfacePoint(m, gp))
                    end
                end
            end
            if ϕ[gp.xInd, gp.yInd+1] >= 0
                for p in D # Find grid point opposite interface
                    if (p.xInd == gp.xInd) && (p.yInd == gp.yInd+1)
                        push!(dΩ, InterfacePoint(gp, p))
                    end
                end
            end
            if ϕ[gp.xInd, gp.yInd-1] >= 0
                for m in D # Find grid point opposite interface
                    if (m.xInd == gp.xInd) && (m.yInd == gp.yInd-1)
                        push!(dΩ, InterfacePoint(m, gp))
                    end
                end
            end
        end
    end
    return dΩ
end


using RollingFunctions

data = parse.(Int, split(read(joinpath(@__DIR__, "./1.txt"), String), "\n"))

sum(map((x, y) -> x > y, data[2:end], data[1:end-1]))

sw = rolling((x)->sum(x),data,3)

sum(map((x, y) -> x > y, sw[2:end], sw[1:end-1]))


using BenchmarkTools
using FixedPolynomials
import DynamicPolynomials: @polyvar

@polyvar x y
f1 = x + y + 1
f2 = (x + 1)^4 + y
f3 = 50x^3+83x^2*y+24x*y^2+y^3+392x^2+414x*y+50y^2-28x+59y-100
f4 = x+x^2+y+x*y^3+x^4*y^2+3x^3*y+10*x*y+10x^2*y+10x*y^2+15x^2*y^2+10x^3*y^2

w = rand(2)

function make(f, w)
    p = Polynomial{Float64}(f)
    result = @benchmark evaluate($p, $w)
    println(STDOUT, f)
    show(STDOUT, MIME"text/plain"(), result)
    println("\n")
end

println("FixedPolynomials", "\n")
make(f1, w)
make(f2, w)
make(f3, w)
make(f4, w)


@testset "Test Type Constructor" begin
    lin = Linear(2, 3)
    @test lin.ninputs == 2
    @test lin.noutputs == 3
    @test lin.nparams == 6
end

@testset "Test dx Buffer" begin
    lin = Linear(2, 3)
    dx = dx_buffer(lin)
    @test typeof(dx) == Matrix{Float64}
    @test size(dx) == (lin.noutputs, lin.ninputs)
end

@testset "Test dΘ Buffer" begin
    @testset "Test Single Output" begin
        lin = Linear(3, 1)
        dΘ = dΘ_buffer(lin)
        @test typeof(dΘ) == Matrix{Float64}
        @test size(dΘ) == (lin.noutputs, lin.ninputs)
    end
    @testset "Test Multiple Output" begin
        lin = Linear(2, 3)
        dΘ = dΘ_buffer(lin)
        dΘ_expected = hcat((speye(lin.noutputs) for i=1:lin.ninputs)...)
        @test typeof(dΘ) == typeof(dΘ_expected)
        @test dΘ.m  == dΘ_expected.m
        @test dΘ.n  == dΘ_expected.n
        @test dΘ.colptr == dΘ.colptr
        @test dΘ.rowval == dΘ.rowval
    end
end

@testset "Test Linear Evaluation" begin
    lin = Linear(3, 2)
    A = [1 2 3;
         4 5 6]
    z = z_buffer(lin)
    evaluate!(lin, [2, 4, 6], vec(A), z)
    @test z == [28; 64]
    evaluate!(lin, [1, 1, 1], vec(A), z)
    @test z == [6; 15]
    evaluate!(lin, [3, 3, 3], vec(A), z)
    @test z == [18; 45]
end

@testset "Test Jacobian Matrix Computation" begin
    @testset "Test Dense" begin
        lin = Linear(3, 2)
        A = [1 2 3;
             4 5 6]
        z = z_buffer(lin)
        dx = dx_buffer(lin)
        dΘ = dΘ_buffer(lin)
        evaluate!(lin, [2, 3, 4], vec(A), z,
                  dx=dx, dΘ=dΘ)
        @test dx == A
        @test dΘ == [2 0 3 0 4 0;
                     0 2 0 3 0 4]
    end

    @testset "Test Finite Diferences" begin
        lin = Linear(4, 5)
        A = [1.0 2.0 3.1 1.2;
             4.0 5.0 6.1 1.2;
             7.0 8.0 9.1 1.2;
             1.0 2.0 3.1 1.2;
             4.0 5.0 6.1 1.2]
        x = [1.2, 2.3, 4.3, 5]
        Θ = vec(A)

        function cal_x(y)
            z = z_buffer(lin)
            evaluate!(lin, y, Θ, z)
            return z
        end
        function cal_Θ(theta)
            z = z_buffer(lin)
            evaluate!(lin, x, theta, z)
            return z
        end
        dx_numeric = Calculus.finite_difference_jacobian(cal_x, x)
        dΘ_numeric = Calculus.finite_difference_jacobian(cal_Θ, Θ)

        z = z_buffer(lin)
        dx = dx_buffer(lin)
        dΘ = dΘ_buffer(lin)
        evaluate!(lin, x, Θ, z, dx=dx, dΘ=dΘ)
        @test dx ≈ dx_numeric
        @test dΘ ≈ dΘ_numeric
    end

    @testset "Test Single Output" begin
        lin = Linear(6, 1)
        Θ = [1, 2, 3, 4, 5, 6]
        x = [10, 20, 30, 40, 50, 60]

        z = z_buffer(lin)
        dx = dx_buffer(lin)
        dΘ = dΘ_buffer(lin)
        evaluate!(lin, x, Θ, z, dx=dx, dΘ=dΘ)
        @test dx ≈ reshape(Θ, 1, 6)
        @test dΘ ≈ reshape(x, 1, 6)
    end
end

@testset "Test Initial Guess" begin
    N = 1000
    srand(0)
    lin = Linear(5, 2)
    init = ScaledGaussianInitializer()

    mean_estimated = 0
    var_estimated = 0
    for i=1:N
        params = initial_guess(lin, init)
        mean_estimated += sum(params)/(N*lin.nparams)
        var_estimated += sum(params.^2)/(N*lin.nparams)
    end

    std_estimated = sqrt(var_estimated)

    @test std_estimated ≈ 1/sqrt(5) atol=1e-1
    @test mean_estimated ≈ 0.0 atol=1e-1
end