using PaddedViews

###
export paddedSize

function paddedSize(cLayer::PL, Ai::AoN=nothing) where {PL<:PaddableLayer, AoN <: Union{AbstractArray, Nothing}}
    if Ai == nothing
        Ai = cLayer.prevLayer.A
    end

    return paddedSize(cLayer, size(Ai))
end #function paddedSize(cLayer::PL, Ai::AoN=nothing) where {PL<:PaddableLayer, AoN <: Union{AbstractArray, Nothing}}

function paddedSize(cLayer::PL, AiS::Tuple) where {PL<:PaddableLayer}
    padS = paddingSize(cLayer, AiS)

    outPS = [i for i in AiS]
    for i=1:(length(AiS)-2)
        outPS[i] += padS[2i-1] + padS[2i]
    end

    return Tuple(outPS)
end #function paddedSize(cLayer::PL, AiS::Tuple) where {PL<:PaddableLayer}

###
export paddingSize

function paddingSize(cLayer::PL, Ai::AoN=nothing) where {PL<:PaddableLayer, AoN <: Union{AbstractArray, Nothing}}
    if Ai == nothing
        Ai = cLayer.prevLayer.A
    end

    return paddingSize(cLayer, size(Ai))
end

"""
    function paddingSize(cLayer::PL, Ai::AbstractArray) where {PL<:PaddableLayer}

Helping function that returns the p_H_hi, p_H_lo, and (in case 2D Conv), p_W_hi, p_W_lo, and so on
"""
function paddingSize(cLayer::PL, AiS::Tuple) where {PL<:PaddableLayer}

    ndim = length(AiS)

    if cLayer.padding == :valid
        return Tuple(repeat([0], (ndim-2)*2))
    end

    if ndim == 3
        n_Hi, ci, m = AiS
        s_H = cLayer.s
        f_H = cLayer.f
        if cLayer.padding == :same
            p_H = s_H * (n_Hi - 1) - n_Hi + f_H

            p_H_hi = p_H ÷ 2 + ((p_H % 2 == 0) ? 0 : 1)
            p_H_lo = p_H ÷ 2

            n_H = n_Hi
            return (p_H_hi, p_H_lo)
        end
    elseif ndim == 4
        n_Hi, n_Wi, ci, m = AiS
        (s_H, s_W) = cLayer.s
        (f_H, f_W) = cLayer.f
        if cLayer.padding == :same
            p_H = (s_H * (n_Hi - 1) - n_Hi + f_H)
            p_W = (s_W * (n_Wi - 1) - n_Wi + f_W)

            p_H_hi = p_H ÷ 2 + ((p_H % 2 == 0) ? 0 : 1)
            p_H_lo = p_H ÷ 2
            p_W_hi = p_W ÷ 2 + ((p_W % 2 == 0) ? 0 : 1)
            p_W_lo = p_W ÷ 2

            n_H, n_W = n_Hi, n_Wi
            return (p_H_hi, p_H_lo, p_W_hi, p_W_lo)
        end

    elseif ndim == 5
        n_Hi, n_Wi, n_Di, ci, m = AiS
        s_H, s_W, s_D = cLayer.s
        f_H, f_W, f_D = cLayer.f
        if cLayer.padding == :same
            p_H = (s_H * (n_Hi - 1) - n_Hi + f_H)
            p_W = (s_W * (n_Wi - 1) - n_Wi + f_W)
            p_D = (s_D * (n_Di - 1) - n_Di + f_D)

            p_H_hi = p_H ÷ 2 + ((p_H % 2 == 0) ? 0 : 1)
            p_H_lo = p_H ÷ 2
            p_W_hi = p_W ÷ 2 + ((p_W % 2 == 0) ? 0 : 1)
            p_W_lo = p_W ÷ 2
            p_D_hi = p_D ÷ 2 + ((p_D % 2 == 0) ? 0 : 1)
            p_D_lo = p_D ÷ 2

            n_H, n_W, n_D = n_Hi, n_Wi, n_Di
            return (p_H_hi, p_H_lo, p_W_hi, p_W_lo, p_D_hi, p_D_lo)
        end #if cLayer.padding == :same
    end

end #function paddingSize(cLayer::CL, AiS::Tuple)

###Padding functions

function padding(
    cLayer::P,
    Ai::AoN = nothing,
) where {P<:PaddableLayer,AoN<:Union{AbstractArray,Nothing}}

    ndim = Ai

    if Ai == nothing
        Ai = cLayer.prevLayer.A
    end

    if cLayer.padding == :same
        Ai = padding(Ai, paddingSize(cLayer, Ai)...)
    elseif cLayer.padding == :valid
        Ai = Ai
    end

    return Ai

end #function padding(cLayer::P) where {P <: PaddableLayer}

"""
    function padding(Ai::AbstractArray{T,4},
                     p_H::Integer,
                     p_W::Integer=-1) where {T}

pad zeros to the Array Ai with amount of p values

inputs:
    Ai := Array of type T and dimension N
    p  := integer determinde the amount of zeros padding
          i.e.
          if Ai is a 3-dimensional array the padding will be for the first
              dimension
          if Ai is a 4-dimensional array the padding will be for the first 2
              dimensions
          if Ai is a 5-dimensional array the padding will be for the first 3
              dimensions

output:
    PaddinView array where it contains the padded values and the original
        data without copying it
"""
function padding(
    Ai::AbstractArray{T,4},
    p_H_hi::Integer,
    p_H_lo::Integer,
    p_W_hi::Integer,
    p_W_lo::Integer,
) where {T}

    n_H, n_W, c, m = size(Ai)
    return PaddedView(
        0,
        Ai,
        (p_H_hi + p_H_lo + n_H, p_W_hi + p_W_lo + n_W, c, m),
        (1 + p_H_hi, 1 + p_W_hi, 1, 1),
    )
end #function padding(Ai::Array{T,4}

function padding(
    Ai::AbstractArray{T,3},
    p_H_hi::Integer,
    p_H_lo::Integer,
) where {T}

    n_H, c, m = size(Ai)
    return PaddedView(0, Ai, (p_H_hi + p_H_lo + n_H, c, m), (1 + p_H_hi, 1, 1))
end #function padding(Ai::Array{T,3}

function padding(
    Ai::AbstractArray{T,5},
    p_H_hi::Integer,
    p_H_lo::Integer,
    p_W_hi::Integer,
    p_W_lo::Integer,
    p_D_hi::Integer,
    p_D_lo::Integer,
) where {T}

    n_H, n_W, n_D, c, m = size(Ai)
    return PaddedView(
        0,
        Ai,
        (
            p_H_hi + p_H_lo + n_H,
            p_W_hi + p_W_lo + n_W,
            p_D_hi + p_D_lo + n_D,
            c,
            m,
        ),
        (1 + p_H_hi, 1 + p_W_hi, 1 + p_D_hi, 1, 1),
    )
end #function padding(Ai::Array{T,5}

export padding


#########################################################################
#    testing script for distributions and samplers
#########################################################################

using Distributions # used to provide exact cdf of distributions for testing

#########################################################################
#       testing functions and definitions
#########################################################################

N = 10000  # number of steps in MCMC for testing
KSTHRESHOLD = 1.358  #  5% level confidence for Kolmogorov–Smirnov test
KSTHRESHOLD = 10
# TODO : understand why KS is so bad for most samplers * distribs
#         forcing such a high threshold to pass tests

function ksValue(x, distrib)
	global xs = sort(x)
	y = eval( :(cdf($distrib , xs)))
	dn = max( abs([1:length(x)] / length(x) - y) )
	sqrt(length(x))*dn
end

function ksTest(ex::Expr)
	mex = Expr(:block, :(x ~ $ex))
	distrib = Expr(:call,
					Expr(:., :Distributions, Expr(:quote, ex.args[1])),
					ex.args[2:end]...)
	exactMean = eval( :(mean($distrib)) )
	exactMean = isfinite(exactMean) ? exactMean : 1.0  # some distribs (Cauchy) have no defined mean
	exactStd = eval( :(std($distrib)) )
	exactStd = isfinite(exactStd) ? exactStd : 1.0

	for (k,v) in {	"RWM" => RWM(exactStd),
					"HMC" => HMC(2, exactStd/5),
					"MALA" => MALA(exactStd),
					"NUTS" => NUTS()}  # TODO : add other samplers
		print("testing $k sampler on $ex   -")
		srand(1)
		res = run(model(mex, gradient=true, x=exactMean), v, SerialMC((1000:N) ))
		ksv = ksValue(res.samples["x"], distrib)
		println(" KS measure = $ksv")
		@assert ksv < KSTHRESHOLD "correct distrib hyp. rejected"
	end

end

#########################################################################
#  Tests
#########################################################################

ksTest(:(Normal(1, 1)))
ksTest(:(Normal(3, 12)))

ksTest(:(Weibull(1, 1)))
ksTest(:(Weibull(3, 1)))

ksTest(:(Uniform(0, 2)))

ksTest(:(TDist(2.2)))  # very long on NUTS if df <= 2  (due to infinite variance ?)
ksTest(:(TDist(4)))

ksTest(:(Beta(1,2)))
ksTest(:(Beta(3,2)))

ksTest(:(Gamma(1,2)))
ksTest(:(Gamma(3,0.2)))

ksTest(:(Cauchy(0,1)))
ksTest(:(Cauchy(-1,0.2)))

ksTest(:(Exponential(3)))
ksTest(:(Exponential(0.2)))

ksTest(:(LogNormal(-1, 1)))
ksTest(:(LogNormal(2, 0.1)))

# TODO : find a way to test discrete distributions : Bernoulli, Binomial, Poisson


function orderstatistic(Xs::IIDRandomSequence{Continuous,Uniform},k::Int64)
  if params(Xs.d) == (0.,1.)
    return Beta(k,length(Xs)+1-k)
  else
    error("location/scale aspect not yet implemented")
  end
end

function range(Xs::IIDRandomSequence{Continuous,Uniform})
  if params(Xs.d) == (0.,1.)
    if length(Xs)>1
      return Beta(length(Xs)-1,2)
    else
      error("sequence must have length greater than 1 for range to exist")
    end
  else
    error("location/scale aspect not yet implemented")
  end
end

function spacing(Xs::IIDRandomSequence{Continuous,Uniform},space::Integer)
  if params(Xs.d) == (0.,1.)
    if length(Xs)>1
      return Beta(1,length(Xs))
    else
      error("sequence must have length greater than 1 for range to exist")
    end
  else
    error("location/scale aspect not yet implemented")
  end
end

function jointspacing(Xs::IIDRandomSequence{Continuous,Uniform})
  if params(Xs.d) == (0.,1.)
    if length(Xs)>1
      return Dirichlet(length(Xs)+1,1)
    else
      error("sequence must have length greater than 1 for range to exist")
    end
  else
    error("location/scale aspect not yet implemented")
  end
end


# Riemann solver for standard hydro shocktube following Pfrommer et al 2006
# Use left and right state of initial condition to solve shocktube problems

using Roots

"""
    Helper functions to set up Machnumber dependent IC
"""
function solvePr(Pr::Float64,
                 rhol::Float64, rhor::Float64,
                 Pl::Float64, Mach::Float64,
                 γ::Float64)

    γ_1 = γ - 1.0
    γ_pow = γ_1/(2.0*γ)
    η2 = (γ-1.0)/(γ+1.0)

    c_l = sqrt.(γ*Pl/rhol)
    c_r = sqrt.(γ*Pr/rhor)

    f(x) = ( x/Pr - 1.0 ) * sqrt.( (1.0 - η2) / (γ * ( x/Pr + η2 ) )) -
            2.0 / γ_1 * c_l / c_r * ( 1.0 - ( x/Pl )^γ_pow )

    P_m = find_zero(f, (Pr, Pl), Bisection())

    ρ_mr = rhor * ( ( P_m + η2 * Pr ) / ( Pr + η2 * P_m))
    #ρ_ml = rhol * ( P_m / Pl )^(1.0/γ)

    vm = 2.0 * c_l / γ_1 * ( 1.0 - (P_m/Pl)^γ_pow )

    vs = vm / ( 1.0 - rhor/ρ_mr)

    M = vs / c_r

    return M - Mach
end

function solvePrfromMach(rhol::Float64, rhor::Float64,
                         Pl::Float64, Mach::Float64,
                         γ::Float64)

    # first approx without CRs
    findPrHelper(P) = solvePr(P, rhol, rhor,
                              Pl, Mach, γ)

    Pr = find_zero(findPrHelper, (1.e-5, 1.e5), Bisection() )

    return Pr
end

function solvePlfromMach(rhol::Float64, rhor::Float64,
                         Pr::Float64, Mach::Float64,
                         γ::Float64)

    # first approx without CRs
    findPlHelper(P) = solvePr(Pr, rhol, rhor,
                              P, Mach, γ)

    Pl = find_zero(findPlHelper, (1.e-5, 1.e5), Bisection() )

    return Pl
end

function solveMach(Pl::Float64, Pr::Float64,
                   rhol::Float64, rhor::Float64,
                   γ::Float64)

   γ_1 = γ - 1.0
   γ_pow = γ_1/(2.0*γ)
   η2 = (γ-1.0)/(γ+1.0)

   c_l = sqrt.(γ*Pl/rhol)
   c_r = sqrt.(γ*Pr/rhor)

   f(x) = ( x/Pr - 1.0 ) * sqrt.( (1.0 - η2) / (γ * ( x/Pr + η2 ) )) -
           2.0 / γ_1 * c_l / c_r * ( 1.0 - ( x/Pl )^γ_pow )

   P_m = find_zero(f, (Pr, Pl), Bisection())

   ρ_mr = rhor * ( ( P_m + η2 * Pr ) / ( Pr + η2 * P_m))
   ρ_ml = rhol * ( P_m / Pl )^(1.0/γ)

   vm = 2.0 * c_l / γ_1 * ( 1.0 - (P_m/Pl)^γ_pow )

   vs = vm / ( 1.0 - rhor/ρ_mr)

   M = vs / c_r

   return M
end

"""
    Datatypes for IC Parameters and Solution
"""
mutable struct SodParameters

    rhol::Float64           # denisty left
    rhor::Float64           # density right
    Pl::Float64             # pressure left
    Pr::Float64             # pressure right
    Ul::Float64             # internal energy left
    Ur::Float64             # internal energy right
    cl::Float64             # soundspeed left
    cr::Float64             # soundspeed right
    M::Float64              # Mach number
    t::Float64              # time
    x_contact::Float64      # position of the contact discontinuity along the tube
    γ_th::Float64           # adiabatic index of the gas
    γ_exp::Float64          # helper variable
    η2::Float64             # helper variable

    function SodParameters(;rhol::Float64=1.0,  rhor::Float64=0.125,
                            Pl::Float64=0.0,    Pr::Float64=0.0,
                            Ul::Float64=0.0,    Ur::Float64=0.0,
                            Mach::Float64=0.0,  t::Float64,
                            x_contact::Float64=70.0,
                            γ_th::Float64=5.0/3.0)

        γ_exp    = ( γ_th - 1.0 )/( 2.0 * γ_th )
        η2       = (γ_th-1.0)/(γ_th+1.0)

        # calculate Ul and Pl depending on input
        if (Pl == 0.0) & (Ul != 0.0)
            Pl = ( γ_th - 1.0 ) * rhol * Ul
        elseif (Ul == 0.0) & (Pl != 0.0)
            Ul = Pl / ( (γ_th - 1.0) * rhol )
        end

        # calculate Ur and Pr depending on input
        if (Pr == 0.0) & (Ur != 0.0)
            Pr = ( γ_th - 1.0 ) * rhor * Ur
        elseif (Ur == 0.0) & (Pr != 0.0)
            Ur = Pr / ( (γ_th - 1.0) * rhor )
        end

        # error handling
        if (Pr == 0.0) & (Pl == 0.0)
            error("No initial Pressure or energy values given!")
        end

        # solve right or left initial state from target mach number
        if (Pr == 0.0) & (Mach != 0.0)
            Pr = solvePrfromMach(rhol, rhor, Pl, Mach, γ_th)
            Ur = Pr / ( (γ_th - 1.0) * rhor )
        elseif (Pl == 0.0) & (Mach != 0.0)
            Pl = solvePlfromMach(rhol, rhor, Pr, Mach, γ_th)
            Ul = Pl / ( (γ_th - 1.0) * rhol )
        elseif (Ur == 0.0) & (Pr == 0.0) & (Mach == 0.0)
            error("Ur, Pr and Mach are zero! Can't find solution!")
        elseif (Ul == 0.0) & (Pl == 0.0) & (Mach == 0.0)
            error("Ul, Pl and Mach are zero! Can't find solution!")
        end

        if Mach == 0.0
            Mach = solveMach(Pl, Pr, rhol, rhor, γ_th)
        end

        cl = sqrt.(γ_th * Pl / rhol)
        cr = sqrt.(γ_th * Pr / rhor)

        new(rhol, rhor,
            Pl, Pr,
            Ul, Ur,
            cl, cr,
            Mach, t,
            x_contact,
            γ_th, γ_exp, η2)
    end
end

mutable struct SodHydroSolution
    x::Array{Float64,1}         # array of given positions
    rho::Array{Float64,1}       # array of densities along the tube
    rho4::Float64               # density in postshock region
    rho3::Float64               # density between contact disc. and rarefaction wave
    P::Array{Float64,1}         # array of pressures along the tube
    P34::Float64                # pressure between shock and rarefaction wave
    U::Array{Float64,1}         # array of internal energies along the tube
    v::Array{Float64,1}         # array of velocities along the tube
    v34::Float64                # velocity between shock and rarefaction wave
    vt::Float64                 # velocity of rarefaction wave
    vs::Float64                 # shock velocity
    Mach::Float64               # Mach number

    function SodHydroSolution(x::Array{Float64,1})
        N = length(x)
        new(x,
            zeros(N),   # rho
            0.0,        # rho2
            0.0,        # rho3
            zeros(N),   # P
            0.0,        # P34
            zeros(N),   # U
            zeros(N),   # v
            0.0,        # v23
            0.0,        # vt
            0.0,        # vs
            0.0)        # Mach
    end

    # multiple dispatch, in case gadget data is passed directly to the constructor
    # function SodHydroSolution(x::Array{Float32,1})
    #     N = length(x)
    #     new(x,
    #         zeros(N),   # rho
    #         0.0,        # rho2
    #         0.0,        # rho3
    #         zeros(N),   # P
    #         0.0,        # P34
    #         zeros(N),   # U
    #         zeros(N),   # v
    #         0.0,        # v23
    #         0.0,        # vt
    #         0.0,        # vs
    #         0.0)        # Mach
    # end
end

"""
    Functions to solve individual segments of the shocktube
"""

"""
    Pressure
"""
function solveP34(par::SodParameters)
    # Solves the pressure of the middle state in a shocktube

    P_m(P) = ( P/par.Pr - 1.0 ) * sqrt.( ( 1.0 - par.η2 ) / ( par.γ_th *
             ( P/par.Pr + par.η2 ) )) - 2.0 / (par.γ_th - 1.0 ) * par.cl / par.cr *
             ( 1.0 - ( P/par.Pl )^par.γ_exp )

    P34 = find_zero( P_m, (par.Pr, par.Pl), Bisection() )

    return P34
end

function solveP2(x::Float64, par::SodParameters)
    # solves the pressure along the rarefaction wave
    return par.Pl * ( -par.η2 * x / ( par.cl * par.t ) + ( 1.0 - par.η2 ) )^( 1.0/par.γ_exp )
end

function solveP(x::Float64, par::SodParameters, sol::SodHydroSolution)
    # returns P values according to position in shocktube

    if x <= -par.cl * par.t
        return par.Pl
    elseif -par.cl * par.t < x <= -sol.vt * par.t
        P2 = solveP2(x, par)
        return P2
    elseif -sol.vt * par.t < x <= sol.v34 * par.t
        return sol.P34
    elseif sol.v34 * par.t < x <= sol.vs * par.t
        return sol.P34
    elseif sol.vs * par.t < x
        return par.Pr
    else
        println("Error!")
        return 0.0
    end
end

"""
    Density
"""
function solveRho2(x::Float64, par::SodParameters)
    # solves density along the rarefaction wave
    return par.rhol * ( -par.η2 * x / ( par.cl * par.t ) +
                      ( 1.0 - par.η2 ) )^( 2.0 / (par.γ_th - 1.0 ) )
end

function solveRho3(par::SodParameters, sol::SodHydroSolution)
    # solves density left of contact discontiuity and right of refraction wave
    return par.rhol * ( sol.P34/par.Pl )^( 1.0/par.γ_th )
end

function solveRho4(par::SodParameters)
    # solves density left of shockfront
    return ( ( par.γ_th + 1.0 ) * par.M^2 ) /
           ( 2.0 + ( par.γ_th - 1.0 ) * par.M^2 ) * par.rhor
end

function solveRho(x::Float64, par::SodParameters, sol::SodHydroSolution)
    # returns P values according to position in shocktube

    if x <= -par.cl * par.t
        return par.rhol
    elseif -par.cl * par.t < x <= -sol.vt * par.t
        return solveRho2(x, par)
    elseif -sol.vt * par.t < x <= sol.v34 * par.t
        return sol.rho3
    elseif sol.v34 * par.t < x <= sol.vs * par.t
        return sol.rho4
    elseif sol.vs * par.t < x
        return par.rhor
    else
        println("Error!")
        return 0.0
    end
end

"""
    Velocity
"""
function solveV34(par::SodParameters, sol::SodHydroSolution)
    # solves velocity along the isopressure region 2-3
    return 2.0 * par.cl / ( par.γ_th - 1.0 ) * ( 1.0 - (sol.P34 / par.Pl)^par.γ_exp )
end

function solveV2(x::Float64, par::SodParameters)
    # solves the velocity along the rarefaction wave
    return ( 1.0 - par.η2 ) * ( x / par.t + par.cl )
end

function solveVs(par::SodParameters, sol::SodHydroSolution)
    # solves the shock velocity
    return sol.v34 / ( 1.0 - par.rhor / sol.rho4 )
end

function solveVt(par::SodParameters, sol::SodHydroSolution)
    # solves the velocity of the tail of the rarefaction wave
    return par.cl - sol.v34/( 1.0 - par.η2 )
end

function solveV(x::Float64, par::SodParameters, sol::SodHydroSolution)
    # solves the velocity along the shocktube
    if x <= -par.cl * par.t
        return 0.0
    elseif -par.cl * par.t < x <= -sol.vt * par.t
        return solveV2(x, par)
    elseif -sol.vt * par.t < x <= sol.vs * par.t
        return sol.v34
    elseif sol.vs * par.t < x
        return 0.0
    else
        println("Error!")
        return 0.0
    end
end

"""
    Main function for solver
"""
function solveSodShock(x::Array{Float64,1}; par::SodParameters)
    # solves a standard sod shock with zero initial velocity

    N = length(x)

    # set up datatype to store riemann solution
    sol = SodHydroSolution(x)

    # transform into rest-frame of contact discontiuity
    x_in = sol.x .- par.x_contact

    # solve Pressure
    sol.P34 = solveP34(par)

    sol.rho3 = solveRho3(par, sol)
    sol.rho4 = solveRho4(par)

    # solve velocity
    sol.v34 = solveV34(par, sol)
    sol.vs = solveVs(par, sol)
    sol.vt = solveVt(par, sol)
    for i = 1:N
        sol.v[i] = solveV(x_in[i], par, sol)
    end

    for i = 1:N
        sol.P[i] = solveP(x_in[i], par, sol)
    end

    # solve density

    for i = 1:N
        sol.rho[i] = solveRho(x_in[i], par, sol)
    end

    sol.Mach = par.M

    sol.U = sol.P ./ ( (par.γ_th - 1.0) .* sol.rho )

    return sol
end


using ThreeBodyDecay

tbs = let m1 = 0.938, m2 = 0.49367, m3 = 0.13957, m0 = 2.46867
    ThreeBodySystem(m1,m2,m3, m0=m0)
end

using Plots

let
    σ1v = LinRange(tbs.mthsq[1], tbs.sthsq[1],300)
    σ3m = [σ3of1(-1.0,σ,tbs.msq) for σ in σ1v]
    σ3p = [σ3of1( 1.0,σ,tbs.msq) for σ in σ1v]
    plot(σ1v, [σ3m σ3p], lab="")
end

let
    σ3v, σ1v = flatDalitzPlotSample31(tbs; Nev=1000000)
    histogram2d(σ1v, σ3v, lab="", bins=100)
end


#=
  Eigenvalues
  Eigenvectors
  l1 Norm
  l2 Norm
  loo Norm
  Apply Guassian Elimination
  Apply Guassian Elimination with Pivots
  Reduced-Row-Eschelon Form
  Determine Linear Transformation Matrix
  Rank and Nullity
=#

function eigenvalues(A)
  Lambda = identityMatrix(length(A)[:,1])
  'x' * Lambda
  Lambda -= A
  #TODO:fixed point iteration

function eigenvectors(A)
  Eigenvalues(A)
  Axb(A-   , ,0)

function cond(A)
  #determines condition numebr
  v = eigenvalues(A)
  #returns vector of Eigenvalues
  return abs(loo(v) / loo(1 / v))
  # largest over smallest

function l1(a)
  #accepts vector of n space
  x = 0
  for i=1:length(a)
    x += a[i]
  end

function l2(a)
  #accepts vector of n space
  x = 0
  for i = 1:length(a)
    x += a[i] * a[i]
  end
  return sqrt(x)

function loo(a)
  x = 0
  for i = 1:length(a)
    if abs(a[i]) > x
      x = abs(a[i])
    else
      continue
    end
  end
  return x

function induced_l1(A)
  #accepts matrix
  x = l1(A[:,1])
  for i = 2:length(A)[1,:]
    if l1(A[:,i]) > x
      x = l1(A[:,i])
    else
      continue
    end
  end
  return x

function induced_l2(A)
  #accepts matrix
  return loo(condition(A))
  #TODO: check negative case

function induced_loo(A)
  #accepts matrix
  x = l1(A[1,:])
  for i = 2:length(A)[:,1]
    if l1(A[i,:]) > x
      x = l1(A[i,:])
    else
      continue
    end
  end
  return x

function guass(A)
  # accepts m by n matrix
  B = copy(A)
  x = 0
  if m > n
    x = m
  else
    x = n
  end
  for i = 1:x
    for j = i+1:m
      B[j,:]- ((B[i,j] / B[i,i]) * B[i,:])
    end
  end
  return B

function pivGuass(A) # maybe unessesary
  x = 0
  a = []
  y = 0
  for i=1:(smaller)
    for j=i:m
      x = lcm(A[i,i] , A[j,i])
      a[y] = x
      A[j,:]-
    end
  end

function ltToMatrix(A,B)
  # accepts mxn set a and mxn set LT(a)
  C = [A B]
  D = rref(C)
  return

function rref(A)
  # accepts m by n matrix
  B = copy(A)
  if length(B)[1,:] > length(B)[:,1]
    x = length(B)[:,1]
  else
    x = legnth(B)[1,:]
  end
  for i = 1:x
    B[i,:] /= B[i,i]
    for j = (1 : i-1) & (i+1 : m) #make sure logic

    end
  end
  return B

function nullity(A)
  rank = 0
  nullity = 0
  B = rref(A)
  for i=1:length(A)[:,1]
    #need to fix n length issues
    if B[i,:] = 0
      nullity += 1
    else
      continue
    end
  end
  return nullity

function rank(A)
  #via rank-nullity theorem
  return length(A)[1,:] - nullity(A)


using Soss

p = @model begin
    x ~ Normal()
    y ~ Normal(x,1) |> iid(5)
end;

q = @model λ begin
    μ = λ.x.Ex
    σ = λ.x.Ex²-λ.x.Ex^2
    x ~ Normal(μ, σ)
end;

y = rand(p()).y;

λ = ( _ℓ = -20
    , x = (Ex=0.0, Ex²=1.0)
)

function logweightedmean(ℓ1,x1,ℓ2,x2)
    if ℓ1 < ℓ2
        return logweightedmean(ℓ2,x2,ℓ1,x1)
    elseif isinf(ℓ2)
        return ℓ1
    else
        w = exp(ℓ2 - ℓ1)
        return (x1 + w*x2)/(1+w)
    end
end

importanceSample(p(), q(λ=λ), (y=y,))

function f(λ, n=10)
    for j in 1:n
        ℓx = importanceSample(p(), q(λ=λ), (y=y,))
        val = ℓx.val
        ℓ = ℓx.ℓ

        x = val.x
        x² = x*x
        x³ = x*x²
        x⁴ = x*x³
        λ = @set λ.x.Ex = logweightedmean(λ._ℓ+1, λ.x.Ex, ℓ, x)
        λ = @set λ.x.Ex² = logweightedmean(λ._ℓ+1, λ.x.Ex², ℓ, x²)
        λ = @set λ.x.Ex³ = logweightedmean(λ._ℓ+1, λ.x.Ex³, ℓ, x³)
        λ = @set λ.x.Ex⁴ = logweightedmean(λ._ℓ+1, λ.x.Ex⁴, ℓ, x⁴)

        λ = @set λ._ℓ = logaddexp(λ._ℓ, ℓ)
    end
    return λ
end

λ = f(λ,1000)


module KLDivergences

using Distributions: StatsBase
using Distributions
using LinearAlgebra
using PDMats
using Distances
using SpecialFunctions
using StatsBase: StatsBase, kldivergence

export KL, symmetricKL

"""
    KL(p::Distribution, q::Distribution) -> T
    KL(p::Distribution, q::Distribution, n_samples::Int) -> T

Return the KL divergence of  KL(p||q), either by sampling or analytically
"""
KL

function KLbase(p, q, x)
    # We assume that p(x) > 0 since x is sampled from p
    logpdf(p, x) - logpdf(q, x)
end

## Generic fallback for multivariate Distributions
function KL(p::UnivariateDistribution, q::UnivariateDistribution, n_samples = 1_000)
    return mean(x->KLbase(p, q, x), rand(p, n_samples))
end

function KL(p::MultivariateDistribution, q::MultivariateDistribution, n_samples = 1_000)
    length(p) == length(q) ||
        throw(DimensionMismatch("Dimensions of p and q do not match"))
    return mean(x->KLbase(p, q, x), eachcol(rand(p, n_samples)))
end

function symmetricKL(p, q)
    return KL(p, q) + KL(q, p)
end

include("univariate.jl")
include("multivariate.jl")
include("horrible_ad_workaround.jl")

end


import Base: +, -, *, /
import Base.Broadcast: broadcasted

export add, mul, neg, sub, pow, div

"""
    Add <: Func
"""
@func mutable struct Add end

forward(f::Add, A, B) = A .+ B

function backward(f::Add, gy)
    gx1, gx2 = gy, gy
    x_shape1, x_shape2 = size.(f.args)
    if x_shape1 != x_shape2
        gx1 = sumto(gx1, x_shape1)
        gx2 = sumto(gx2, x_shape2)
    end
    return gx1, gx2
end

add(A, B) = Add()(A, B)

+(A::Var, B::Var) = add(A, B)
+(A::Var, B) = add(A, B)
+(A, B::Var) = add(A, B)

broadcasted(::typeof(+), A::Var, B::Var) = add(A, B)
broadcasted(::typeof(+), A::Var, B) = add(A, B)
broadcasted(::typeof(+), A, B::Var) = add(A, B)

"""
    Mul <: Func
"""
@func mutable struct Mul end

forward(f::Mul, A, B) = A .* B

function backward(f::Mul, gy)
    x1, x2 = f.args
    gx1 = gy .* x2
    gx2 = gy .* x1
    x_shape1, x_shape2 = size.(f.args)
    if x_shape1 != x_shape2
        gx1 = sumto(gx1, x_shape1)
        gx2 = sumto(gx2, x_shape2)
    end
    return gx1, gx2
end

mul(A, B) = Mul()(A, B)

broadcasted(::typeof(*), A::Var, B::Var) = mul(A, B)
broadcasted(::typeof(*), A::Var, B) = mul(A, B)
broadcasted(::typeof(*), A, B::Var) = mul(A, B)

"""
    Neg <: Func
"""
@func mutable struct Neg end

forward(f::Neg, x) = -x

backward(f::Neg, gy) = -gy

neg(x) = Neg()(x)

-(x::Var) = neg(x)

"""
    Sub <: Func
"""
@func mutable struct Sub end

forward(f::Sub, A, B) = A .- B

function backward(f::Sub, gy)
    gx1, gx2 = gy, -gy
    x_shape1, x_shape2 = size.(f.args)
    if x_shape1 != x_shape2
        gx1 = sumto(gx1, x_shape1)
        gx2 = sumto(gx2, x_shape2)
    end
    return gx1, gx2
end

sub(A, B) = Sub()(A, B)

-(A::Var, B::Var) = sub(A, B)
-(A::Var, B) = sub(A, B)
-(A, B::Var) = sub(A, B)

broadcasted(::typeof(-), A::Var, B::Var) = sub(A, B)
broadcasted(::typeof(-), A::Var, B) = sub(A, B)
broadcasted(::typeof(-), A, B::Var) = sub(A, B)

"""
    Pow <: Func
"""
@func mutable struct Pow{T}
    c::T
    Pow(c::T) where T = new{T}(c)
end

forward(f::Pow, x) = x.^f.c

backward(f::Pow, gy) = f.c .* f.args[1]^(f.c - 1) .* gy

pow(x, c) = Pow(c)(x)

Base.:^(x::Var, c) = pow(x, c)

"""
    Div <: Func
"""
@func mutable struct Div end

forward(f::Div, A, B) = A ./ B

function backward(f::Div, gy)
    x1, x2 = f.args
    gx1 = gy / x2
    gx2 = gy .* (-x1 / x2^2)
    x_shape1, x_shape2 = size.(f.args)
    if x_shape1 != x_shape2
        gx1 = sumto(gx1, x_shape1)
        gx2 = sumto(gx2, x_shape2)
    end
    return gx1, gx2
end

div(A, B) = Div()(A, B)

/(A::Var, B::Var) = div(A, B)
/(A::Var, B) = div(A, B)
/(A, B::Var) = div(A, B)

broadcasted(::typeof(/), A::Var, B::Var) = div(A, B)
broadcasted(::typeof(/), A::Var, B) = div(A, B)
broadcasted(::typeof(/), A, B::Var) = div(A, B)


# Drive train example
# ============================

using HybridSystems, MathematicalSystems, LazySets, Plots
import SX:is_halfspace, is_hyperplane, convert, parse_sxmath

x1 = parse_sxmath("x1 - -0.0488 == 0.0005600000000000001 * (x4 - 20.0)")
x2 = parse_sxmath("x2 - -15.67 == 0.46699999999999997 * (x4 - 20.0)")
x3 = parse_sxmath("x3 == 0.0")
x4 = parse_sxmath("x4 <= 40.0")
x5 = parse_sxmath("x5 == 0.0")
x6 = parse_sxmath("x6 - 20.0 == 1.0 * (x4 - 20.0)")
x7 = parse_sxmath("x7 - 240.0 == 12.0 * (x4 - 20.0)")
x8 = parse_sxmath("x8 - -0.0019199999999999998 == 0.00005999999999999998 * (x4 - 20.0)")
x9 = parse_sxmath("x9 - 20.0 == 1.0 * (x4 - 20.0)")
t = parse_sxmath("t == 0.0")

expr_p = parse_sxmath("x1 - -0.0488 == 0.0005600000000000001 * (x4 - 20.0) & x2 - -15.67 == 0.46699999999999997 * (x4 - 20.0) & x3 == 0.0 & x5 == 0.0 & x6 - 20.0 == 1.0 * (x4 - 20.0) & x7 - 240.0 == 12.0 * (x4 - 20.0) & x8 - -0.0019199999999999998 == 0.00005999999999999998 * (x4 - 20.0) & x9 - 20.0 == 1.0 * (x4 - 20.0) & 20.0 <= x4 & x4 <= 40.0 & t == 0.0")
expr = [x1, x2, x3, x4,x5,x6,x7,x8,x9,t]
x0sets = []
vars = Basic[:x1, :x2, :x3, :x4, :x5, :x6, :x7, :x8, :x9, :t]
for expr_i in expr_p
    if is_halfspace(expr_i)
        push!(x0sets, convert(HalfSpace, expr_i, vars=vars))
    elseif is_hyperplane(expr_i)
        push!(x0sets, convert(Hyperplane, expr_i, vars=vars))
    end
end

AFFINE_SYSTEM = ConstrainedLinearControlContinuousSystem
HS = readsxmodel("models/ARCH2018/Drivetrain/drivetrain_theta1_5percent_flat_manually.xml", raw_dict=true)
# initial condition in mode 1

# calculate reachable states up to time T
prob = InitialValueProblem(HS, x0sets)
input_options = Options(:mode=>"reach")

problem_options = Options(:vars=>[1,3,10], :T=>10.0, :δ=>0.005, :plot_vars=>[1, 3], :verbosity=>1);
options_input = merge(problem_options, input_options)
using Polyhedra
sol = solve(HS, options_input);

plot(sol, indices=1:2:length(sol.Xk))


module ACC

using NeuralNetworkAnalysis, LaTeXStrings
using NeuralNetworkAnalysis: FunctionPreprocessing
import Plots, DifferentialEquations
using Plots: plot, plot!, lens!, bbox, savefig, font, Measures.mm, annotations

# problem

u = 0.0001
a_lead = -2.0

@taylorize function ACC!(dx, x, p, t)
    v_lead = x[2]
    γ_lead = x[3]
    v_ego = x[5]
    γ_ego = x[6]
    a_ego = x[7]

    dx[1] = v_lead
    dx[2] = γ_lead
    dx[3] = 2 * (a_lead - γ_lead) - u * v_lead^2
    dx[4] = v_ego
    dx[5] = γ_ego
    dx[6] = 2 * (a_ego - γ_ego) - u * v_ego^2
    dx[7] = zero(a_ego)
    return dx
end

controller = read_nnet_sherlock(@modelpath "" "controller_ACC_sherlock")

X₀ = Hyperrectangle(low= [ 90, 32,   0, 10, 30,   0],
                    high=[110, 32.2, 0, 11, 30.2, 0])
U₀ = ZeroSet(1)
vars_idx = Dict(:state_vars=>1:6, :control_vars=>7)
ivp = @ivp(x' = ACC!(x), dim: 7, x(0) ∈ X₀ × U₀)

v_set = 30.0
T_gap = 1.4
M = zeros(3, 6)
M[1, 5] = 1.0
M[2, 1] = 1.0
M[2, 4] = -1.0
M[3, 2] = 1.0
M[3, 5] = -1.0
function preprocess(X::LazySet)
    Y1 = Singleton([v_set, T_gap])
    Y2 = linear_map(M, X)
    return cartesian_product(Y1, Y2)
end
function preprocess(X::AbstractVector)
    Y1 = [v_set, T_gap]
    Y2 = M * X
    return vcat(Y1, Y2)
end
control_preprocessing = FunctionPreprocessing(preprocess)

period = 0.1
k = 50
T = k * period
T_warmup = 2 * period

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

# simulation

println("simulation")
res = @timed simulate(prob, T=T, trajectories=10, include_vertices=true)
sim = res.value
print_timed(res)

# reachability analysis

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

function benchmark(; T=T, silent::Bool=false)
    silent || println("flowpipe construction")
    res = @timed solve(prob, T=T, alg_nn=alg_nn, alg=alg)
    sol = res.value
    silent || print_timed(res)

    return sol
end

benchmark(T=T_warmup, silent=true)
res = @timed benchmark()
sol = res.value

# plotting

vars = (1, 4)
fig = plot(xlab=L"x_{lead}", ylab=L"x_{ego}",
           xlims=(50, 300), ylims=(0, 200),
           leg=:topleft,
           legendfontsize=25,
           tickfont=font(25, "Times"),
           guidefontsize=25,
           xguidefont=font(25, "Times"),
           yguidefont=font(25, "Times"),
           bottom_margin=0mm, left_margin=0mm, right_margin=5mm, top_margin=2mm, size=(500, 450))
plot!(fig, sol, vars=vars, color=:orange, lab="")
plot!(project(X₀, vars), c=:blue, lab=L"X_0", alpha=0.6)
plot_simulation!(fig, sim; vars=vars, color=:black, lab="")
lens!([89, 111], [9.7, 11.5], inset=(1, bbox(0.65, 0.6, 0.3, 0.25)), xtick=90:10:110, ytick=10:1:11, tickfontsize=15, subplot=2)
savefig("acc_juliareach.png")

end
nothing


using Plots
using LaTeXStrings
pgfplots()

c = 1
a₀ = 1
ω = 1

x(τ) = c*(a₀^2)/4 *(τ - 1/2* ω*sin(2ω*τ))
y(τ) = c*a₀/ω *(1-cos(ω*τ))

p1 = plot(x, y, 0, 15,
  xlabel=L"x", ylabel=L"y",
  xticks=nothing, yticks=nothing,
  legend=:none,
  framestyle=:box,
  tex_output_standalone=true
)

v_drift = c*a₀^2/4

p2 = plot(τ->x(τ)-v_drift*τ, y, 1, 10,
  xlabel=L"x", ylabel=L"y",
  xticks=nothing, yticks=nothing,
  legend=:none,
  framestyle=:box,
  tex_output_standalone=true
)

p = plot(p1, p2,
  layout=grid(1,2,widths=[0.7,0.3]),
  framestyle=:box,
  tex_output_standalone=true
)

savefig(p, "figures/electron-in-plane-wave.tex")


import ViscousFlow: Layers

Δx = 0.02
xlim = (-5.98,5.98)
ylim = (-5.98,5.98)
g = Fields.PhysicalGrid(xlim,ylim,Δx)

w = Nodes(Dual,size(g))
dq = Edges(Dual,w)
n = 150
radius = 1.0
body = Circle(radius,n)
X = VectorData(collect(body))
f = VectorData(X)
ϕ = ScalarData(f)
fill!(ϕ,1.0)

regop = Regularize(X,Δx,I0=origin(g),issymmetric=true,ddftype=Fields.Yang3)
Hv, Ev = RegularizationMatrix(regop,f,dq)
Hs, Es = RegularizationMatrix(regop,ϕ,w)

@testset "Double Layer" begin

  dlayer = DoubleLayer(body,Hv,weight=1/Δx)
  ϕ .= rand(length(ϕ))

  @test abs(sum(dlayer(ϕ))) < 100*eps(1.0)

  dlayer2 = DoubleLayer(body,regop,w)

  @test dlayer2(ϕ) == dlayer(ϕ)

end

@testset "Single Layer" begin

  slayer = SingleLayer(body,Hs)
  ϕ .= 1.0

  @test abs(sum(slayer(ϕ))-2π*radius) < 2e-3

  slayer2 = SingleLayer(body,regop,w)

  @test slayer2(ϕ) == slayer(ϕ)

end

@testset "Masks" begin

  inner = Mask(body,regop,w)

  fill!(w,1)

  @test abs(sum(inner(w))*cellsize(g)^2 - π*radius^2) < 1e-3

  outer = ComplementaryMask(inner)

end


## Test of Assembly Materials

using FiniteElementDiffEq

dx = 1//2^(5)
fem_mesh = notime_squaremesh([0 1 0 1],dx,:dirichlet)

A,M,area = assemblematrix(fem_mesh.node,fem_mesh.elem,lumpflag=false,K=4)

abs.(M[2,1] - 40.6901e-5) < .001 #Non-diagonal


include("differencial.jl")
include("RungeKutta.jl")
"""
`dynamics(q::SatelliteToolbox.Quaternion,ω::Vector,T::Vector,I::Matrix,dt)`
トルク`T`を与えられた剛体の回転を刻み時間`dt`秒ぶん計算する関数
## Arguments
- `q::SatelliteToolbox.Quaternion` :`t`秒時点の姿勢を表す単位クォータニオン 衛星軌道面座標系の基底をボディ座標系の基底に変換する
- `ω::Vector`     :`t`秒時点の角速度ベクトル　ボディ座標系基準
- `T::Vector`     :剛体に与えられるトルク　ボディ座標系基準
- `I::Matrix`     :慣性テンソル
- `dt::Number`          :刻み時間

## Returns
- `(qk/norm(qk), ωk)::Tuple{SatelliteToolbox.Quaternion,Vector}` :`(t+dt)`秒時点の姿勢を表す単位クォータニオンと角速度ベクトルの組

## Example
`qk,ωk=dynamics(q,ω,T,I,dt)`
"""
function dynamics(q::SatelliteToolbox.Quaternion,ω::Vector,T::Vector,I::Matrix,dt::Number)
    qk,ωk=RK4((q,ω),(T,I),dt)
    return qk/norm(qk),ωk
end


"""
    p1b_p1

The manufactured-solution colliding flow example from Elman et al 2014.
Formulation on linear triangles with a cubic bubble for the velocity.

The formulation is the one derived in Reddy, Introduction to the finite element
method, 1993. Page 486 ff.
"""
module p1b_p1

using LinearAlgebra
using StaticArrays
using MeshCore: nrelations, nentities, ir_identity, attribute
using MeshSteward: T3block
using MeshSteward: Mesh, attach!, baseincrel, boundary
using MeshSteward: vselect, geometry, summary, transform
using MeshSteward: vtkwrite
using Elfel.RefShapes: manifdim, manifdimv
using Elfel.FElements: FEH1_T3_BUBBLE, FEH1_T3, refshape, Jacobian
using Elfel.FESpaces: FESpace, ndofs, setebc!, nunknowns, doftype
using Elfel.FESpaces: numberfreedofs!, numberdatadofs!, numberdofs!
using Elfel.FESpaces: scattersysvec!, makeattribute, gathersysvec!, edofcompnt
using Elfel.FESpaces: highestfreedofnum, highestdatadofnum
using Elfel.FEIterators: FEIterator, ndofsperel, elnodes, eldofs, eldofvals
using Elfel.FEIterators: jacjac, location
using Elfel.QPIterators: QPIterator, bfun, bfungrad, weight
using Elfel.Assemblers: SysmatAssemblerSparse, start!, finish!, assemble!
using Elfel.Assemblers: SysvecAssembler
using Elfel.LocalAssemblers: LocalMatrixAssembler, LocalVectorAssembler, init!
using UnicodePlots

mu = 1.0 # dynamic viscosity
A = 1.0 # half of the length of the side of the square
trueux = (x, y) -> 20 * x * y ^ 3
trueuy = (x, y) -> 5 * x ^ 4 - 5 * y ^ 4
truep = (x, y) -> 60 * x ^ 2 * y - 20 * y ^ 3

function genmesh(N)
    # This mesh will be both for the velocities and for the pressure
    mesh = Mesh()
    attach!(mesh, T3block(2 * A, 2 * A, N, N), "velocity+pressure")
    ir = baseincrel(mesh)
    transform(ir, x -> x .- A)
    eidir = ir_identity(ir)
    attach!(mesh, eidir)
    return mesh
end

function assembleK(Uxh, Uyh, Ph, tndof, mu)
    function integrateK!(ass, elits, qpits, mu)
        uxnedof, uynedof, pnedof = ndofsperel.(elits)
        kuxux = LocalMatrixAssembler(uxnedof, uxnedof, 0.0)
        kuyuy = LocalMatrixAssembler(uynedof, uynedof, 0.0)
        kuxuy = LocalMatrixAssembler(uxnedof, uynedof, 0.0)
        kuxp = LocalMatrixAssembler(uxnedof, pnedof, 0.0)
        kuyp = LocalMatrixAssembler(uynedof, pnedof, 0.0)
        for el in zip(elits...)
            uxel, uyel, pel = el
            init!(kuxux, eldofs(uxel), eldofs(uxel))
            init!(kuyuy, eldofs(uyel), eldofs(uyel))
            init!(kuxuy, eldofs(uxel), eldofs(uyel))
            init!(kuxp, eldofs(uxel), eldofs(pel))
            init!(kuyp, eldofs(uyel), eldofs(pel))
            for qp in zip(qpits...)
                uxqp, uyqp, pqp = qp
                Jac, J = jacjac(pel, pqp)
                JxW = J * weight(pqp)
                gradNp = bfungrad(pqp, Jac)
                gradNux = bfungrad(uxqp, Jac)
                gradNuy = bfungrad(uyqp, Jac)
                Np = bfun(pqp)
                for j in 1:uxnedof, i in 1:uxnedof
                    kuxux[i, j] += (mu * JxW) * (2 * gradNux[i][1] * gradNux[j][1] + gradNux[i][2] * gradNux[j][2])
                end
                for j in 1:uynedof, i in 1:uynedof
                    kuyuy[i, j] += (mu * JxW) * (gradNuy[i][1] * gradNuy[j][1] + 2 * gradNuy[i][2] * gradNuy[j][2])
                end
                for j in 1:uynedof, i in 1:uxnedof
                    kuxuy[i, j] += (mu * JxW) * (gradNux[i][1] * gradNuy[j][2])
                end
                for j in 1:pnedof, i in 1:uxnedof
                    kuxp[i, j] += (-JxW) * (gradNux[i][1] * Np[j])
                end
                for j in 1:pnedof, i in 1:uynedof
                    kuyp[i, j] += (-JxW) * (gradNuy[i][2] * Np[j])
                end
            end
            assemble!(ass, kuxux)
            assemble!(ass, kuxuy)
            assemble!(ass, transpose(kuxuy))
            assemble!(ass, kuyuy)
            assemble!(ass, kuxp)
            assemble!(ass, transpose(kuxp))
            assemble!(ass, kuyp)
            assemble!(ass, transpose(kuyp))
        end
        return ass
    end

    elits = (FEIterator(Uxh), FEIterator(Uyh), FEIterator(Ph))
    qargs = (kind = :default, npts = 3,)
    qpits = (QPIterator(Uxh, qargs), QPIterator(Uyh, qargs), QPIterator(Ph, qargs))
    ass = SysmatAssemblerSparse(0.0)
    start!(ass, tndof, tndof)
    integrateK!(ass, elits, qpits, mu)
    return finish!(ass)
end

function solve!(U, K, F, nu)
    KT = K * U
    U[1:nu] = K[1:nu, 1:nu] \ (F[1:nu] - KT[1:nu])
end

function evaluate_pressure_error(Ph)
    function integrate!(elit, qpit, truep)
        pnedof = ndofsperel(elit)
        E = 0.0
        for el in elit
            dofvals = eldofvals(el)
            for qp in qpit
                Jac, J = jacjac(el, qp)
                JxW = J * weight(qp)
                Np = bfun(qp)
                pt = truep(location(el, qp)...)
                pa = 0.0
                for j in 1:pnedof
                    pa += (dofvals[j] * Np[j])
                end
                E += (JxW) * (pa - pt)^2
            end
        end
        return sqrt(E)
    end

    elit = FEIterator(Ph)
    qargs = (kind = :default, npts = 3,)
    qpit = QPIterator(Ph, qargs)
    return integrate!(elit, qpit, truep)
end

function evaluate_velocity_error(Uxh, Uyh)
    function integrate!(elits, qpits, trueux, trueuy)
        uxnedof, uynedof = ndofsperel.(elits)
        E = 0.0
        for el in zip(elits...)
            uxel, uyel = el
            uxdofvals = eldofvals(uxel)
            uydofvals = eldofvals(uyel)
            for qp in zip(qpits...)
                uxqp, uyqp = qp
                Jac, J = jacjac(uxel, uxqp)
                JxW = J * weight(uxqp)
                Np = bfun(uxqp)
                uxt = trueux(location(uxel, uxqp)...)
                uyt = trueuy(location(uyel, uyqp)...)
                uxa = 0.0
                uya = 0.0
                for j in 1:uxnedof
                    uxa += (uxdofvals[j] * Np[j])
                    uya += (uydofvals[j] * Np[j])
                end
                E += (JxW) * ((uxa - uxt)^2 + (uya - uyt)^2)
            end
        end
        return sqrt(E)
    end

    elits = (FEIterator(Uxh), FEIterator(Uyh),)
    qargs = (kind = :default, npts = 3,)
    qpits = (QPIterator(Uxh, qargs), QPIterator(Uyh, qargs),)
    return integrate!(elits, qpits, trueux, trueuy)
end

function run(N)
    mesh = genmesh(N)
    # Velocity spaces
    Uxh = FESpace(Float64, mesh, FEH1_T3_BUBBLE(), 1)
    Uyh = FESpace(Float64, mesh, FEH1_T3_BUBBLE(), 1)
    locs = geometry(mesh)
    inflate = A / N / 100
    # The entire boundary
    boxes = [[-A A -A -A], [-A -A -A A], [A A -A A], [-A A A A]]
    for box in boxes
        vl = vselect(locs; box = box, inflate = inflate)
        for i in vl
            setebc!(Uxh, 0, i, 1, trueux(locs[i]...))
            setebc!(Uyh, 0, i, 1, trueuy(locs[i]...))
        end
    end
    # Pressure space
    Ph = FESpace(Float64, mesh, FEH1_T3(), 1)
    atcenter = vselect(geometry(Ph.mesh); nearestto = [0.0, 0.0])
    setebc!(Ph, 0, atcenter[1], 1, 0.0)
    # Number the degrees of freedom
    numberdofs!([Uxh, Uyh, Ph])
    tndof = ndofs(Uxh) + ndofs(Uyh) + ndofs(Ph)
    tnunk = nunknowns(Uxh) + nunknowns(Uyh) + nunknowns(Ph)
    # Assemble the coefficient matrix
    K = assembleK(Uxh, Uyh, Ph, tndof, mu)
    # p = spy(K, canvas = DotCanvas)
    # display(p)
    # Solve the system
    U = fill(0.0, tndof)
    gathersysvec!(U, [Uxh, Uyh, Ph])
    F = fill(0.0, tndof)
    solve!(U, K, F, tnunk)
    scattersysvec!([Uxh, Uyh, Ph], U)
    # Postprocessing
    makeattribute(Ph, "p", 1)
    makeattribute(Uxh, "ux", 1)
    makeattribute(Uyh, "uy", 1)
    ep = evaluate_pressure_error(Ph)
        ev = evaluate_velocity_error(Uxh, Uyh)
    vtkwrite("p1b_p1-p", baseincrel(mesh), [(name = "p",), ])
    vtkwrite("p1b_p1-v", baseincrel(mesh), [(name = "ux",), (name = "uy",)])
    return (ep, ev)
end

end

let
    N = 4
    for loop in 1:5
        ep, ev = p1b_p1.run(N)
        @show N, ep, ev
        N = N * 2
    end
end


#This program is to test if two Nodes exist
#with only one objective functions.
using Plasmo
using Ipopt
using PlasmoTest

G = OptiGraph()
O = Ipopt.Optimizer

#Add nodes to a ModelGraph
n1 = @optinode(G)
n2 = @optinode(G)

#Node 1 Model
@variable(n1,0 <= x1 <= 2)
@variable(n1,2 <= y1 <= 4)
@constraint(n1,x1 + y1 >= 5)

#Node 2 Model
@variable(n2,x2)
@NLnodeconstraint(n2,ref,exp(x2) >= 2)
@variable(n2,y2 >= 0)
@constraint(n2,x2 + y2 >= 3)

#Add linkconstraint
@linkconstraint(G,n1[:x1]==n2[:y2])

#Graph objective
@objective(G, Min, n1[:x1] + n2[:x2])

#Use Ipopt to solve
optimize!(G,O)

println("x1 = ",digit5(value(n1,n1[:x1])))
println("y1 = ",digit5(value(n1,n1[:y1])))
println("x2 = ",digit5(value(n2,n2[:x2])))
println("y2 = ",digit5(value(n2,n2[:y2])))
println("Objective = ", digit5(objective_value(G)))


__precompile__()

module GradOptim

using Compat
using Compat.LinearAlgebra.BLAS

export Adam, update!, iRProp

"""
    Adam(;lr=0.001, gclip=0, beta1=0.9, beta2=0.999, eps=1e-8)
    update!(w,g,p::Adam)

Container for parameters of the Adam optimization algorithm used by
[`update!`](@ref).

Adam is one of the methods that compute the adaptive learning rate. It
stores accumulated gradients (first moment) and the sum of the squared
of gradients (second).  It scales the first and second moment as a
function of time. Here is the update formulas:

    m = beta1 * m + (1 - beta1) * g
    v = beta2 * v + (1 - beta2) * g .* g
    mhat = m ./ (1 - beta1 ^ t)
    vhat = v ./ (1 - beta2 ^ t)
    w = w - (lr / (sqrt(vhat) + eps)) * mhat

where `w` is the weight, `g` is the gradient of the objective function
w.r.t `w`, `lr` is the learning rate, `m` is an array with the same
size and type of `w` and holds the accumulated gradients. `v` is an
array with the same size and type of `w` and holds the sum of the
squares of the gradients. `eps` is a small constant to prevent a zero
denominator. `beta1` and `beta2` are the parameters to calculate bias
corrected first and second moments. `t` is the update count.

If `vecnorm(g) > gclip > 0`, `g` is scaled so that its norm is equal
to `gclip`.  If `gclip==0` no scaling takes place.

Reference: [Kingma, D. P., & Ba,
J. L. (2015)](https://arxiv.org/abs/1412.6980). Adam: a Method for
Stochastic Optimization. International Conference on Learning
Representations, 1–13.

"""
mutable struct Adam
    lr::AbstractFloat
    gclip::AbstractFloat
    beta1::AbstractFloat
    beta2::AbstractFloat
    eps::AbstractFloat
    t::Int
    fstm
    scndm
end

Adam(; lr=0.001, gclip=0, beta1=0.9, beta2=0.999, eps=1e-8)=Adam(lr, gclip, beta1, beta2, eps, 0, nothing, nothing)

function update!(w, g, p::Adam)
    gclip!(g, p.gclip)
    if p.fstm===nothing; p.fstm=zeros(w); p.scndm=zeros(w); end
    p.t += 1
    BLAS.scale!(p.beta1, p.fstm)
    BLAS.axpy!(1-p.beta1, g, p.fstm)
    BLAS.scale!(p.beta2, p.scndm)
    BLAS.axpy!(1-p.beta2, g .* g, p.scndm)
    fstm_corrected = p.fstm / (1 - p.beta1 ^ p.t)
    scndm_corrected = p.scndm / (1 - p.beta2 ^ p.t)
    BLAS.axpy!(-p.lr, (fstm_corrected ./ (sqrt.(scndm_corrected) + p.eps)), w)
end

function gclip!(g, gclip)
    if gclip == 0
        g
    else
        gnorm = vecnorm(g)
        if gnorm <= gclip
            g
        else
            BLAS.scale!(gclip/gnorm, g)
        end
    end
end

mutable struct iRProp{T}
    eta_pos::T
    eta_neg::T
    dmax::T
    dmin::T
    d0::T
    gpre::Vector{T}
    Δ::Vector{T}
    iRProp(eta_pos::T, eta_neg::T; d0=1.5e-3π, dmin=1e-6π, dmax=6e-3π) where T = new{T}(eta_pos, eta_neg, dmax, dmin, d0)
end

function update!(w, g, p::iRProp{T}) where T
    N = length(g)
    if !isdefined(p, :gpre)
        p.gpre = zeros(T, N)
        p.Δ = p.d0*ones(T, N)
    end
    sgn = sign.(g.*p.gpre)
    posmask = sgn .> 0
    negmask = sgn .< 0
    p.Δ[posmask] = min.(p.eta_pos.*p.Δ[posmask], p.dmax)
    p.Δ[negmask] = max.(p.eta_neg.*p.Δ[negmask], p.dmin)

    p.gpre[:] = g
    p.gpre[negmask] = 0

    w[:] .-= sign.(p.gpre).*p.Δ
end

end


"""
Abstract basis

  T: data type
  N: number of nodes
"""
abstract type Basis{T<:AbstractFloat, N} end

nbasis(b::Basis{T,N}) where {T,N} = error("Not implemented!")
nnodes(b::Basis{T,N}) where {T,N} = error("Not implemented!")
degree(b::Basis{T,N}) where {T,N} = error("Not implemented!")
nodes(b::Basis{T,N})  where {T,N} = error("Not implemented!")

eval_basis(b::Basis, x, y)  = error("Not implemented!")
deriv_basis(b::Basis, x, y) = error("Not implemented!")
int_basis(b::Basis, x, y)   = error("Not implemented!")

"""
Abstract polynomial basis.
"""
abstract type PolynomialBasis{T,N} <: Basis{T,N} end

nbasis(b::PolynomialBasis{T,N}) where {T,N} = N
nnodes(b::PolynomialBasis{T,N}) where {T,N} = N
degree(b::PolynomialBasis{T,N}) where {T,N} = N-1


using XLSX, LinearAlgebra

data = XLSX.readxlsx("transfer_m.xlsx")

data = data["Sheet6"]
num = length(data["F"])
R_matrix = zeros(2, num)
I_matrix = zeros(2, num)

R_matrix[1, :] = data["F"]
R_matrix[2, :] = data["H"]

I_matrix[1, :] = data["B"]
I_matrix[2, :] = data["D"]

M = I_matrix * pinv(R_matrix)

@show(M)


function part1(lines)
    departure = parse(Int, first(lines))
    ids = split(last(lines), ',')
    inservice = parse.(Int, filter(x -> x != "x", ids))

    wait = inservice .* ceil.(Int, departure ./ inservice) .- departure
    _, i = findmin(wait)

    wait[i] * inservice[i]
end

function part2(lines)
    fields = split(last(lines), ",")

    ranges = map(filter(!=("x") ∘ last, collect(enumerate(fields)))) do elem
        δ = first(elem) - 1
        c = parse(Int, last(elem))

        # BigInt is necessary to prevent integer overflow inside the
        # `intersect` to come
        c .* (one(BigInt):typemax(Int)) .- δ
    end

    first(intersect(ranges...))
end

function main()
    lines = readlines("input.txt")
    @info "Solution" part1(lines) part2(lines)
end

main()


using StaticArrays

const σz = SA[1.0+0.0im 0.0+0.0im; 0.0+0.0im -1.0+0.0im]
const σx = SA[0.0+0.0im 1.0+0.0im; 1.0+0.0im  0.0+0.0im]
const σy = SA[0.0+0.0im 0.0-1.0im; 0.0+1.0im  0.0+0.0im]
const zm = SA[0.0+0.0im 0.0+0.0im; 0.0+0.0im  0.0+0.0im]
const eye = SA[1.0+0.0im 0.0+0.0im; 0.0+0.0im  1.0+0.0im]

@inline function comm(A,B)
    A*B - B*A
end

@inline function acomm(A,B)
    A*B + B*A
end


function mixing_length(param_set, ml_model::MinDisspLen{FT}) where {FT}
    c_m::FT = CPEDMF.c_m(param_set)
    c_d::FT = CPEDMF.c_d(param_set)
    smin_ub::FT = CPEDMF.smin_ub(param_set)
    smin_rm::FT = CPEDMF.smin_rm(param_set)
    l_max::FT = ICP.l_max(param_set)
    c_b::FT = ICP.static_stab_coeff(param_set)
    g::FT = CPP.grav(param_set)
    molmass_ratio::FT = CPP.molmass_ratio(param_set)
    vkc::FT = CPSGS.von_karman_const(param_set)
    ustar = ml_model.ustar
    z = ml_model.z
    tke_surf = ml_model.tke_surf
    ∂b∂z = ml_model.∇b.∂b∂z
    tke = ml_model.tke

    # kz scale (surface layer)
    if ml_model.obukhov_length < 0.0 #unstable
        l_W =
            vkc * z / (sqrt(tke_surf / ustar / ustar) * c_m) *
            min((1 - 100.0 * z / ml_model.obukhov_length)^0.2, 1 / vkc)
    else # neutral or stable
        l_W = vkc * z / (sqrt(tke_surf / ustar / ustar) * c_m)
    end

    # compute l_TKE - the production-dissipation balanced length scale
    a_pd = c_m * (ml_model.Shear² - ∂b∂z / ml_model.Pr) * sqrt(tke)
    # Dissipation term
    c_neg = c_d * tke * sqrt(tke)
    # Subdomain exchange term
    b_exch = ml_model.b_exch

    if abs(a_pd) > eps(FT) && 4 * a_pd * c_neg > -b_exch * b_exch
        l_TKE = max(-b_exch / 2 / a_pd + sqrt(b_exch * b_exch + 4 * a_pd * c_neg) / 2 / a_pd, 0)
    elseif abs(a_pd) < eps(FT) && abs(b_exch) > eps(FT)
        l_TKE = c_neg / b_exch
    else
        l_TKE = FT(0)
    end

    # compute l_N - the effective static stability length scale.
    N_eff = sqrt(max(∂b∂z, 0))
    if N_eff > 0.0
        l_N = min(sqrt(max(c_b * tke, 0)) / N_eff, l_max)
    else
        l_N = l_max
    end

    # add limiters
    l = SA.SVector(
        (l_N < eps(FT) || l_N > l_max) ? l_max : l_N,
        (l_TKE < eps(FT) || l_TKE > l_max) ? l_max : l_TKE,
        (l_W < eps(FT) || l_W > l_max) ? l_max : l_W,
    )

    # get soft minimum
    min_len, min_len_ind = findmin(l)
    mix_len = lamb_smooth_minimum(l, smin_ub, smin_rm)
    ml_ratio = mix_len / min_len
    return MixLen(min_len_ind, mix_len, ml_ratio)
end


#Script to plot Rydberg radial wave functions
#23/07/2017

using Plots, JLD, LaTeXStrings
pyplot()
include("functions.jl")
PyPlot.close("all")

#Input information
atom = "87Rb"
nn = 50
ll = 0
jj = 0.5

#Calculate wave function
normY_sol, rr = numerovfunc(atom,nn,ll,jj)

#Rescale for plotting
plotscale = sqrt(rr)
probamp = (normY_sol.*plotscale).^2

alpha_c = getalpha(atom)

PyPlot.figure()
if nn>20
  plot(plotscale[plotscale .> sqrt(alpha_c^(1/3))],normY_sol[plotscale .> sqrt(alpha_c^(1/3))],linewidth=2)
else
  plot(plotscale,normY_sol,linewidth=2)
end
plot!(xlabel=L"(r/a_0)^{1/2}")
plot!(ylabel=L"r^{1/2}R(r) \,(a_0^{-1})")
plot!(title=string(atom," radial wavefunction |n,l,j⟩ = |",string(nn),",",string(ll),",",string(jj),"⟩"))
plot!(leg=false)
gui()

PyPlot.figure()
if nn>20
  plot(rr[rr .> alpha_c^(1/3)],probamp[rr .> alpha_c^(1/3)],linewidth=2)
else
  plot(rr,probamp,linewidth=2)
end
plot!(xlabel=L"r/a_0")
plot!(ylabel=L"|rR(r)|^2 \,(a_0^{-1})")
plot!(title=string(atom," radial probability density |n,l,j⟩ = |",string(nn),",",string(ll),",",string(jj),"⟩"))
plot!(leg=false)
gui()


"""
    LeisenReimer()

[Leisen-Reimer binomial model](http://www.goddardconsulting.ca/option-pricing-binomial-alts.html#lr).
"""
struct LeisenReimer <: Model end

function h(z::T, n::Int64) where {T<:Number}
    return 0.5 + sign(z) * 0.5 * sqrt(1 - exp(-((z / (n + 1.0 / 3.0))^2.0) * (n + 1.0 / 6.0)))
end

"""
    evaluate(O, LeisenReimer(), N = 1001)

Evaluate option `O` using `LeisenReimer` binomial model.

# Arguments
- `N`: number of paths to simulate, must be odd
"""
function evaluate(O::Option, m::LeisenReimer, N::Int64 = 1001)
    Δt = 0.01 # O.t / N
    R = exp(O.r * Δt)
    d1 = (log(O.s / O.k) + (O.r + O.σ * O.σ / 2) * O.t) / (O.σ * √O.t)
    d2 = d1 - O.σ * √O.t
    p = h(d2, N)
    q = 1 - p

    if O.call == -1
        Z = [max(0, O.k - O.s * exp((2 * i - N) * O.σ * √Δt)) for i = 0:N]
    elseif O.call == 1
        Z = [max(0, O.s * exp((2 * i - N) * O.σ * √Δt) - O.k) for i = 0:N]
    end

    for n = N-1:-1:0, i = 0:n
        if O.call == -1
            x = O.k - O.s * exp((2 * i - n) * O.σ * √Δt)
        elseif O.call == 1
            x = O.s * exp((2 * i - n) * O.σ * √Δt) - O.k
        end
        y = (q * Z[i+1] + p * Z[i+2]) / exp(O.r * Δt)
        Z[i+1] = max(x, y)
    end

    return Z[1]
end


#module DynamicCorporateModels

using PyPlot
using Optim
#using BlackBoxOptim
using LinearAlgebra, Statistics
using Parameters, QuantEcon, DataFrames, Plots, Random
using Interpolations
using BenchmarkTools

#Here we start putting the Neoclassical models and add extensions
function NeoClassical(;
#Flavours
    cash::Bool=false,
    costly_equity::Bool=true,
    asymmetric_investment::Bool=false,
    quadratic_investment::Bool=true,
    risk_neutral::Bool=true,
    systematic::Bool=true,
    idiosyncratic::Bool=false,
#Parameters
    α::Float64=0.3, #Elasticity of capital
    r::Float64=0.01, #Risk free rate
    f::Float64=0.0, #Fixed cost
    τ::Float64=0.3, # Tax rate
    δ::Float64=0.01, # Depreciation rate
    x_bar::Float64=2.0, #Unconditional mean of the systematic shock
    ρ_x::Float64=0.9, #Persistence of Systematic Shock
    σ_x::Float64=0.1, #Volatility of systematic shock innovations
    ρ_z::Float64=0.99, #Persistence of idiosyncratic shock
    σ_z::Float64=0.1, #Volatility of idiosyncratic shock innovations
    λ::Float64=0.001, #Equity issuance cost

# Sizes
    n_k::Int64=30,
    n_x::Int64=10,
#Adjustment costs
    η::Float64=1.0, # Map i×k -> real)
    ) # End of arguments
# Here we create the matrices with the value function and policy function
    V::Matrix=zeros(n_k, n_x)
    i_opt::Matrix=zeros(n_k, n_x)
    mc = tauchen(n_x, ρ_x, σ_x, x_bar);
    Π_x=mc.p;
    x_grid = collect(range(x_bar-3*σ_x/sqrt(1-ρ_x^2), x_bar+3*σ_x/sqrt(1-ρ_x^2), length = n_x));
    k_grid = collect(range(1.0, 400.0, length=n_k))

    # Dividend payout in the simplest case
    Ψ(i,k)=k*0.5*η*(i/k)^2
    e(x, k, i)=(1-τ)*(exp(x)*k^α-f-δ*k)-i-Ψ(i,k)
    function d(x, k, i)
        earnings=e(x,k,i)
        return earnings+λ*abs(earnings)*(earnings<0)
    end

    function TV!(V, i_opt)
        V_func=interpolate((k_grid, x_grid), V, Gridded(Linear()));
        V_func=extrapolate(V_func, (Interpolations.Flat(),  Interpolations.Flat()));
        for (i_k, k) in enumerate(k_grid)
            for (i_x, x) in enumerate(x_grid)
                obj(i)=-d(x, k, i)-(1.0/(1+r))*sum(Π_x[i_x, j]*V_func(k*(1-δ)+i, x_grid[j]) for j in 1:n_x)
                # Here we get the optimal one, we can use one dimensional optimization
                res=Optim.optimize(obj, -maximum(k_grid), maximum(k_grid))
                V[i_k, i_x]=-res.minimum
                i_opt[i_k, i_x]=res.minimizer
            end
        end
    end

    function solve_VFI(V, i_opt; max_iter::Int64=1000, max_tol::Float64=1e-6)
        it=1
        tol=Inf
        while tol>max_tol && it<=max_iter
            V_old=copy(V)
            TV!(V, i_opt)
            tol=maximum(abs(x - y) for (x, y) in zip(V_old, V))
            it=it+1
        end
        return V, i_opt
    end

    function simulate_model(V_opt::Matrix, i_opt::Matrix, T::Int64=100)
        #Solves the model
        V_func=interpolate((k_grid, x_grid), V, Gridded(Linear()));
        V_func=extrapolate(V_func, (Interpolations.Flat(),  Interpolations.Flat()));

        I_func=interpolate((k_grid, x_grid), i_opt, Gridded(Linear()));
        I_func=extrapolate(I_func, (Interpolations.Flat(),  Interpolations.Flat()));

        x_init = mean(x_grid) # Initial x
        k_init = mean(k_grid)
        x_init_ind = searchsortedfirst(x_grid, x_init)

        k_sim=zeros(T+1,1)
        k_sim[1]=k_init
        i_sim=zeros(T+1,1)
        d_sim=zeros(T+1,1)
        mc_x = MarkovChain(Π_x)
        x_sim_indices = simulate(mc_x, T+1; init = x_init_ind)
        x_sim_val = zeros(T)
        #Pre return
        r_sim=ones(T+1,1)

        for t in 1:T
            current_x=x_grid[x_sim_indices[t]]
            current_k=k_sim[t]
            k_sim[t+1]=(1-δ)*current_k+I_func(current_k, current_x)
            i_sim[t]=I_func(current_k, current_x)
            d_sim[t]=d(current_x, current_k, i_sim[t])
            r_sim[t+1]=V_func(k_sim[t+1], x_grid[x_sim_indices[t+1]])/(V_func(k_sim[t], x_grid[x_sim_indices[t]])+d_sim[t])
        end

        # Here we plot the outcome of the model

        pygui(true)
        subplot(511)
        PyPlot.plot(1:T, x_grid[x_sim_indices][1:T])
        PyPlot.ylim(x_grid[1],x_grid[n_x])
        xlabel("Time")
        ylabel("X")
        subplot(512)
        PyPlot.plot(1:T, k_sim[1:T])
        PyPlot.ylim(minimum(k_sim),maximum(k_sim))
        xlabel("Time")
        ylabel("K")
        subplot(513)
        PyPlot.plot(1:T, i_sim[1:T])
        PyPlot.ylim(minimum(i_sim),maximum(i_sim))
        xlabel("Time")
        ylabel("I")
        subplot(514)
        PyPlot.plot(1:T, d_sim[1:T])
        PyPlot.ylim(minimum(d_sim),maximum(d_sim))
        xlabel("Time")
        ylabel("D")
        subplot(515)
        PyPlot.plot(1:T, r_sim[1:T])
        PyPlot.ylim(minimum(r_sim),maximum(r_sim))
        xlabel("Time")
        ylabel("R")
        show()

    end

    return (solve = solve_VFI, simulate_model=simulate_model, V=V, i_opt=i_opt)

end

neoclassical=NeoClassical()

solve_model=neoclassical.solve
simulate_model=neoclassical.simulate_model
V=neoclassical.V
i_opt=neoclassical.i_opt

V_opt, i_opt=solve_model(V, i_opt)

simulate_model(V, i_opt)

#end # module


using LaserTypes
using LaserTypes: pochhammer, immutable_cache
using StaticArrays
using UnPack
using Test

x₀ = SVector{3}(0,0,0)
t₀ = 0

@testset "(:y, :x) orientation" begin
    s = setup_laser(LaguerreGaussLaser, :SI, m = 1, p = 1, profile=ConstantProfile, orientation=(:y,:x))
    Ex, Ey, Ez = E(x₀, t₀, s)
    @test iszero(Ex)
    @test iszero(Ey)
    @test iszero(Ez)

    @unpack E₀, Nₚₘ, z_R = immutable_cache(s)
    c = s.constants.c
    Bx, By, Bz = B(x₀, t₀, s)
    @test Bx ≈ - factorial(s.p)/pochhammer(abs(s.m)+1,s.p)*E₀*Nₚₘ*(√2*s.w₀)/(c*z_R)
    @test iszero(By)
    @test iszero(Bz)
end


import Base: sqrt, exp, log, sin, tanh, -, abs, abs2, sign

cuda1 = [
"sqrt",
# "rsqrt",
# "cbrt",
# "rcbrt",
"exp",
# "exp2",
# "exp10",
# "expm1",
"log",
# "log2",
# "log10",
# "log1p",
"sin",
# "cos",
# "tan",
# "sinpi",
# "cospi",
# "asin",
# "acos",
# "atan",
# "sinh",
# "cosh",
"tanh",
# "asinh",
# "acosh",
# "atanh",
# "erf",
# "erfc",
# "erfinv",
# "erfcinv",
# "erfcx",
# "normcdf",
# "normcdfinv",
# "lgamma",
# "tgamma",
# "logb",
# "ilogb",
# "j0",
# "j1",
# "y0",
# "y1",
# "cyl_bessel_i0",
# "cyl_bessel_i1",
# "trunc",
# "round",
# "rint",
# "nearbyint",
# "ceil",
# "floor",
# "lrint",
# "lround",
# "llrint",
# "llround",
("neg", "-", "-xi"),
("invx", "invx", "1/xi"),
("relu", "relu", "(xi>0?xi:0)"),
("sigm", "sigm", "1/(1+exp(-xi))"),
("abs", "abs", "(xi<0?-xi:xi)"),
("abs2", "abs2", "(xi*xi)"),
("sign", "sign", "(xi>0?1:xi<0?-1:0)"),
]

function cuda1def(f, j=f, o...)
    J=Symbol(j)
    for S in (32,64)
        T = Symbol("Float$S")
        F = "$(f)_$S"
        @eval begin
            function $J(x::DexArray{$T})
                y = similar(x)
                ccall(($F,$libcudex),Void,(Cint,Ptr{$T},Ptr{$T}),length(y),x,y)
                return y
            end
        end
    end
end

#if isdefined(:libcudex)
    for f in cuda1
        isa(f,Tuple) || (f=(f,))
        cuda1def(f...)
    end
#end

# Define some common operations as primitives for efficiency:
# 1. Avoid creating intermediate arrays
# 2. Avoid taking derivatives of intermediate operations

for (f,g,y,dx) in ((:invx, :invxback, :(one(T)/x[i]), :(-y[i]*y[i]*dy[i])),
                   (:relu, :reluback, :(max(zero(T),x[i])), :(ifelse(y[i]>0,dy[i],zero(T)))),
                   (:sigm, :sigmback, :(one(T)/(one(T)+exp(-x[i]))), :(dy[i]*y[i]*(one(T)-y[i]))),
                   (:tanx, :tanhback, :(tanh(x[i])), :(dy[i]*(one(T)-y[i]*y[i]))),
                   )
    @eval begin
        function $f{T<:AbstractFloat}(x::Array{T})
            y = similar(x)
            @inbounds for i=1:length(y)
                y[i] = $y
            end
            return y
        end
        function $g{T<:AbstractFloat}(dy::Array{T},y::Array{T})
            dx = similar(dy)
            @inbounds for i=1:length(dx)
                dx[i] = $dx
            end
            return dx
        end
        # @primitive $f(x),dy,y $g(dy,y)
    end
end

# To avoid conflict with AutoGrad:
# @primitive tanh(x::Array),dy,y     tanhback(dy,y)
# @primitive tanh(x::DexArray),dy,y tanhback(dy,y)
# @primitive tanhback(dy,y),ddx  ddx.*(1.-y.*y)  ddx.*(-2.*dy.*y)

# Math for the cross-entropy loss: x is unnormalized input, p is
# target probabilities, q is estimated probabilities. Read left column
# down, right column (loss gradients) back up.

# x			dx = -p + qz/z = -p + exp(logq)
# xmax  = max(x,1)	-sum(db)=0
# logqz = x .- xmax	-p + qz/z
# qz    = exp(logqz)	rep(1/z)
# z     = sum(qz,1)	1/z
# logz  = log(z)	sum(p)=1
# logq  = logqz.-logz	-p
# plogq = p .* logq	-1
# loss  = -sum(plogq)	1

"""

logp(x,[dims]) treats entries in x as as unnormalized logp and returns
normalized logp.  If dims is not specified the normalization is over
the whole x, otherwise the normalization is performed over the given
dimensions.  In particular dims=1 normalizes columns of x and dims=2
normalizes rows of x.

"""
function logp(x,d...)
    x = x .- maximum(x,d...)
    x = x .- log(sum(exp(x),d...))
end

# dy should be -p and y=logq so this should give us -p+q
# @primitive  logp(x,d...),dy,y  (dy - exp(y).*sum(dy,d...))


const TPQ = 960

const THIRTY_SECOND = (TPQ//8) // TPQ
const SIXTEENTH = (TPQ//4) // TPQ
const TRIPLET = (TPQ//3) // TPQ
const EIGHTH = (TPQ//2) // TPQ
const QUARTER = TPQ // TPQ
const HALF = (TPQ*2) // TPQ
const WHOLE = (TPQ*4) // TPQ


function lateral_attenuation(settings::PyObject, beta, x_obs)
    # Depression angle: phi_d = beta (elevation angle) + epsilon (aircraft bank angle = 0)
    # Note: beta is in degrees
    T = eltype(beta)
    phi_d = beta

    # Lateral side distance
    l = x_obs[2]

    # Engine installation term [dB]
    if settings.engine_mounting == "underwing"
        E_eng = 10 * log10.((0.0039 * cos.(phi_d*pi/180).^2 .+ sin.(phi_d*pi/180).^2).^0.062./(0.8786 * sin.(2 * phi_d*pi/180).^2 .+ cos.(2*phi_d*pi/180).^2))
    elseif settings.engine_mounting == "fuselage"
        E_eng = 10 * log10.((0.1225 * cos.(phi_d*pi/180).^2 .+ sin.(phi_d*pi/180).^2).^0.329)
    elseif settings.engine_mounting == "propeller"
        E_eng = zeros(size(beta))
    elseif settings.engine_mounting == "none"
        E_eng = zeros(size(beta))
    else
        throw(DomainError("Invalid engine_mounting specified. Specify: underwing/fuselage/propeller/none."))
    end

    # Attenuation caused by ground and refracting-scattering effects [dB]
    # Note: beta is in degrees
    A_grs = zeros(T, size(beta))
    A_grs[findall(beta.<=50.)] = (1.137 .- 0.0229*beta .+ 9.72 * exp.(-0.142 * beta))[findall(beta.<=50.)]

    # Over-ground attenuation [dB]
    if 0. <= l <= 914
        g = 11.83 * (1 - exp(-0.00274 * l))
    elseif l > 914
        g = 10.86  # 11.83*(1-exp(-0.00274*914))
    else
        throw(DomainError("Lateral sideline distance negative."))
    end

    # Overall lateral attenuation
    Lambda = 10 .^ ((E_eng .- g * A_grs ./ 10.86) ./ 10.)

    return Lambda
end


# Routines and types to support Helmholtz decomposition

struct ScalarPotentialCache{DVT,VNT,FT} <: AbstractExtraILMCache
   dv :: DVT
   dvn :: VNT
   ftemp :: FT
   divv_temp :: FT
end

struct VectorPotentialCache{DVT,VNT,ST} <: AbstractExtraILMCache
   dv :: DVT
   dvn :: VNT
   stemp :: ST
   curlv_temp :: ST
end

struct VectorFieldCache{ST,VST,FT,VFT,VPT,SPT} <: AbstractExtraILMCache
   ψtemp :: ST
   vψ :: VST
   ϕtemp :: FT
   vϕ :: VFT
   wcache :: VPT
   dcache :: SPT
end

"""
    ScalarPotentialCache(base_cache::BasicILMCache)

Create a cache for calculations involving the scalar potential field
that induces a vector field on the grid. The base cache must be
in support of vector field data.
"""
function ScalarPotentialCache(base_cache::AbstractBasicCache{N,GCT}) where {N,GCT<:Edges{Primal}}
    dv = zeros_surface(base_cache)
    ftemp = zeros_griddiv(base_cache)
    divv_temp = zeros_griddiv(base_cache)
    dvn = ScalarData(dv)
    ScalarPotentialCache(dv,dvn,ftemp,divv_temp)
end

"""
    VectorPotentialCache(base_cache::BasicILMCache)

Create a cache for calculations involving the vector potential field (i.e. streamfunction)
that induces a vector field on the grid. The base cache must be
in support of vector field data.
"""
function VectorPotentialCache(base_cache::BasicILMCache)
    dv = zeros_surface(base_cache)
    stemp = zeros_gridcurl(base_cache)
    curlv_temp = zeros_gridcurl(base_cache)
    dvn = ScalarData(dv)
    VectorPotentialCache(dv,dvn,stemp,curlv_temp)
end

"""
    VectorFieldCache(base_cache::BasicILMCache)

Create a cache for calculations involving the vector field
derived from the vector and scalar potentials.
The base cache must be in support of vector field data.
"""
function VectorFieldCache(base_cache::BasicILMCache)
    ψtemp = zeros_gridcurl(base_cache)
    vψ = zeros_grid(base_cache)
    ϕtemp = zeros_griddiv(base_cache)
    vϕ = zeros_grid(base_cache)
    wfield = VectorPotentialCache(base_cache)
    dfield = ScalarPotentialCache(base_cache)
    VectorFieldCache(ψtemp,vψ,ϕtemp,vϕ,wfield,dfield)
end

"""
    vectorpotential_from_masked_curlv!(ψ::Nodes{Dual},masked_curlv::Nodes{Dual},dv::VectorData,base_cache::BasicILMCache,wcache::VectorPotentialCache)

Return the vector potential field `ψ` from the masked curl of the vector field, `masked_curlv` (``\\overline{\\nabla\\times\\mathbf{v}}``),
the jump in the vector field across immersed surface `dv`. It solves

``L_n\\psi = -\\overline{\\nabla\\times\\mathbf{v}} - R_n\\mathbf{n}\\times[\\mathbf{v}]``

and returns ``\\psi``.
"""
function vectorpotential_from_masked_curlv!(ψ::Nodes{Dual},curlv::Nodes{Dual},dv::VectorData,base_cache::BasicILMCache,wcache::VectorPotentialCache)
    @unpack nrm = base_cache
    @unpack stemp = wcache

    fill!(stemp,0.0)
    _curlv_from_surface_jump!(stemp,dv,base_cache)
    stemp .+= curlv

    vectorpotential_from_curlv!(ψ,stemp,base_cache)

    return ψ

end

function _curlv_from_surface_jump!(curlv::Nodes{Dual},dv::VectorData{N},base_cache::BasicILMCache) where {N}
    regularize_normal_cross!(curlv,dv,base_cache)
end

function _curlv_from_surface_jump!(curlv::Nodes{Dual},dv::VectorData{0},base_cache::BasicILMCache)
    return curlv
end

"""
    vectorpotential_from_curlv!(ψ::Nodes{Dual},curlv::Nodes{Dual},base_cache::BasicILMCache)

Return the vector potential field `ψ` from the curl of the masked vector field, `curlv` (``\\nabla\\times\\overline{\\mathbf{v}}``),
the jump in the vector field across immersed surface `dv`. It solves

``L_n\\psi = -\\nabla\\times\\overline{\\mathbf{v}}``

and returns ``\\psi``.
"""
function vectorpotential_from_curlv!(ψ::Nodes{Dual},curlv::Nodes{Dual},base_cache::BasicILMCache)
    inverse_laplacian!(ψ,curlv,base_cache)
    ψ .*= -1.0
end

"""
    vecfield_from_vectorpotential!(v::Edges{Primal},ψ::Nodes{Dual},base_cache::BasicILMCache)

Return the vector field `v` associated with vector potential field `ψ` (in 2-d, a scalar
streamfunction), via the curl

``\\mathbf{v} = \\nabla\\times\\psi \\mathbf{e}_z``
"""
function vecfield_from_vectorpotential!(v::Edges{Primal},ψ::Nodes{Dual},base_cache::BasicILMCache)
    fill!(v,0.0)
    curl!(v,ψ,base_cache)
    return v
end

"""
    masked_curlv_from_curlv_masked!(masked_curlv::Nodes{Dual},curlv::Nodes{Dual},dv::VectorData,base_cache::BasicILMCache,wcache::VectorPotentialCache)

Return the masked curl of the vector field ``\\overline{\\nabla\\times\\mathbf{v}}`` (`masked_curlv`) from
the curl of the masked vector field ``\\nabla\\times\\overline{\\mathbf{v}}`` (`curlv`). It obtains
this from

``\\overline{\\nabla\\times\\mathbf{v}} = \\nabla\\times\\overline{\\mathbf{v}} - R_n\\mathbf{n}\\times[\\mathbf{v}]``
"""
function masked_curlv_from_curlv_masked!(masked_curlv::Nodes{Dual},curlv::Nodes{Dual},dv::VectorData{N},base_cache::BasicILMCache,wcache::VectorPotentialCache) where {N}
    @unpack nrm = base_cache

    fill!(masked_curlv,0.0)
    #pointwise_cross!(dvn,nrm,dv)
    #regularize!(masked_curlv,dvn,Rn)
    regularize_normal_cross!(masked_curlv,dv,base_cache)
    masked_curlv .*= -1.0
    masked_curlv .+= curlv
    return masked_curlv
end

function masked_curlv_from_curlv_masked!(masked_curlv::Nodes{Dual},curlv::Nodes{Dual},dv::VectorData{0},base_cache::BasicILMCache,wcache::VectorPotentialCache)
    masked_curlv .= curlv
end

"""
    curlv_masked_from_masked_curlv!(curlv::Nodes{Dual},masked_curlv::Nodes{Dual},dv::VectorData,base_cache::BasicILMCache,wcache::VectorPotentialCache)

Return the curl of the masked vector field ``\\nabla\\times\\overline{\\mathbf{v}}`` (`curlv`) from
the masked curl of the vector field ``\\overline{\\nabla\\times\\mathbf{v}}`` (`masked_curlv`). It obtains
this from

``\\nabla\\times\\overline{\\mathbf{v}} = \\overline{\\nabla\\times\\mathbf{v}} + R_n\\mathbf{n}\\times[\\mathbf{v}]``
"""
function curlv_masked_from_masked_curlv!(curlv::Nodes{Dual},masked_curlv::Nodes{Dual},dv::VectorData{N},base_cache::BasicILMCache,wcache::VectorPotentialCache) where {N}
    @unpack nrm = base_cache
    #@unpack dvn, Rn = wcache

    fill!(curlv,0.0)
    #pointwise_cross!(dvn,nrm,dv)
    #regularize!(curlv,dvn,Rn)
    regularize_normal_cross!(curlv,dv,base_cache)
    curlv .+= masked_curlv
    return curlv
end

function curlv_masked_from_masked_curlv!(curlv::Nodes{Dual},masked_curlv::Nodes{Dual},dv::VectorData{0},base_cache::BasicILMCache,wcache::VectorPotentialCache)
    curlv .= masked_curlv
end

"""
    scalarpotential_from_masked_divv!(ϕ::Nodes{Primal},masked_divv::Nodes{Primal},dv::VectorData,base_cache::BasicILMCache,dcache::ScalarPotentialCache)

Return the scalar potential field `ϕ` from the masked divergence of the vector field, `masked_divv` (``\\overline{\\nabla\\cdot\\mathbf{v}}``)
the jump in the vector field across immersed surface `dv`. It solves

``L_c\\phi = \\overline{\\nabla\\cdot\\mathbf{v}} + R_c\\mathbf{n}\\cdot[\\mathbf{v}]``

and returns ``\\phi``.
"""
function scalarpotential_from_masked_divv!(ϕ::Nodes{Primal},divv::Nodes{Primal},dv::VectorData,base_cache::BasicILMCache,dcache::ScalarPotentialCache)
  @unpack nrm = base_cache
  @unpack dvn, ftemp = dcache

    fill!(ftemp,0.0)
    _divv_from_surface_jump!(ftemp,dv,base_cache)
    ftemp .+= divv

    scalarpotential_from_divv!(ϕ,ftemp,base_cache)
    return ϕ

end

function _divv_from_surface_jump!(divv::Nodes{Primal},dv::VectorData{N},base_cache::BasicILMCache) where N
  regularize_normal_dot!(divv,dv,base_cache)
end

function _divv_from_surface_jump!(divv::Nodes{Primal},dv::VectorData{0},base_cache::BasicILMCache)
    divv
end

"""
    scalarpotential_from_divv!(ϕ::Nodes{Primal},divv::Nodes{Primal},base_cache::BasicILMCache,dcache::ScalarPotentialCache)

Return the scalar potential field `ϕ` from the divergence of the masked vector field, `divv` (``\\nabla\\cdot\\overline{\\mathbf{v}}``)
the jump in the vector field across immersed surface `dv`. It solves

``L_c\\phi = \\nabla\\cdot\\overline{\\mathbf{v}}``

and returns ``\\phi``.
"""
function scalarpotential_from_divv!(ϕ::Nodes{Primal},divv::Nodes{Primal},base_cache::BasicILMCache)

    inverse_laplacian!(ϕ,divv,base_cache)
    return ϕ

end

"""
    vecfield_from_scalarpotential!(v::Edges{Primal},ϕ::Nodes{Primal},base_cache::BasicILMCache)

Return the vector field `v` associated with scalar potential field `ϕ`, via the gradient

``\\mathbf{v} = \\nabla\\phi``
"""
@inline function vecfield_from_scalarpotential!(v::Edges{Primal},ϕ::Nodes{Primal},base_cache::BasicILMCache)
    fill!(v,0.0)
    grad!(v,ϕ,base_cache)
    return v
end

"""
    masked_divv_from_divv_masked!(masked_divv::Nodes{Primal},divv::Nodes{Primal},dv::VectorData,base_cache::BasicILMCache,dcache::ScalarPotentialCache)

Return the masked divergence of the vector field ``\\overline{\\nabla\\cdot\\mathbf{v}}`` (`masked_divv`) from
the divergence of the masked vector field ``\\nabla\\cdot\\overline{\\mathbf{v}}`` (`divv`). It obtains
this from

``\\overline{\\nabla\\cdot\\mathbf{v}} = \\nabla\\cdot\\overline{\\mathbf{v}} - R_c\\mathbf{n}\\cdot[\\mathbf{v}]``
"""
function masked_divv_from_divv_masked!(masked_divv::Nodes{Primal},divv::Nodes{Primal},dv::VectorData{N},base_cache::BasicILMCache,dcache::ScalarPotentialCache) where N
    @unpack nrm = base_cache
    #@unpack dvn, Rc = dcache

    fill!(masked_divv,0.0)
    regularize_normal_dot!(masked_divv,dv,base_cache)
    #pointwise_dot!(dvn,nrm,dv)
    #regularize!(masked_divv,dvn,Rc)
    masked_divv .*= -1.0
    masked_divv .+= divv
    return masked_divv
end

function masked_divv_from_divv_masked!(masked_divv::Nodes{Primal},divv::Nodes{Primal},dv::VectorData{0},base_cache::BasicILMCache,dcache::ScalarPotentialCache)
    masked_divv .= divv
end

"""
    divv_masked_from_masked_divv!(divv::Nodes{Primal},masked_divv::Nodes{Primal},dv::VectorData,base_cache::BasicILMCache,dcache::ScalarPotentialCache)

Return the divergence of the masked vector field ``\\nabla\\cdot\\overline{\\mathbf{v}}`` (`divv`) from the masked divergence
of the vector field ``\\overline{\\nabla\\cdot\\mathbf{v}}`` (`masked_divv`). It obtains
this from

``\\nabla\\cdot\\overline{\\mathbf{v}} = \\overline{\\nabla\\cdot\\mathbf{v}} + R_c\\mathbf{n}\\cdot[\\mathbf{v}]``
"""
function divv_masked_from_masked_divv!(divv::Nodes{Primal},masked_divv::Nodes{Primal},dv::VectorData{N},base_cache::BasicILMCache,dcache::ScalarPotentialCache) where N
    @unpack nrm = base_cache
    #@unpack dvn, Rc = dcache

    fill!(divv,0.0)
    #pointwise_dot!(dvn,nrm,dv)
    #regularize!(divv,dvn,Rc)
    regularize_normal_dot!(divv,dv,base_cache)
    divv .+= masked_divv
    return divv
end

function divv_masked_from_masked_divv!(divv::Nodes{Primal},masked_divv::Nodes{Primal},dv::VectorData{0},base_cache::BasicILMCache,dcache::ScalarPotentialCache)

    divv .= masked_divv
end

"""
    vecfield_helmholtz!(v::Edges{Primal},curlv::Nodes{Dual},divv::Nodes{Primal},dv::VectorData,vp::Union{Edges{Primal},Nothing},base_cache::BasicILMCache,veccache::VectorFieldCache)

Recover the vector field `v` from the masked curl field `curlv` (``\\overline{\\nabla\\times\\mathbf{v}}``),
divergence field `divv` (``\\overline{\\nabla\\cdot\\mathbf{v}}``), surface
vector jump `dv` (``[\\mathbf{v}]``), and additional irrotational, divergence-free vector field `vp`. It obtains this
from the Helmholtz decomposition,

``\\mathbf{v} = \\nabla\\times\\psi + \\nabla\\phi + \\mathbf{v}_p``

in which ``\\psi`` is the solution of

``L_n\\overline{\\psi} = -\\overline{\\nabla\\times\\mathbf{v}} - R_n\\mathbf{n}\\times[\\mathbf{v}]``

in which ``\\phi`` is the solution of

``L_c\\overline{\\phi} = \\overline{\\nabla\\cdot\\mathbf{v}} + R_c\\mathbf{n}\\cdot[\\mathbf{v}]``

It is important to stress that `curlv` is the masked curl of the vector field, not the
curl of the masked vector field. To get this the former from the latter, use [`masked_curlv_from_curlv_masked!`](@ref).
Similarly for `divv`, which is the masked divergence of the vector field, not the divergence
of the masked vector field. One can use [`masked_divv_from_divv_masked!`](@ref) for this.

To specify the irrotational, divergence-free vector field `vp`, one can also simply provide
a tuple, e.g., `(1.0,0.0)`, to specify a uniform vector field.
"""
function vecfield_helmholtz!(v::Edges{Primal},curlv::Nodes{Dual},divv::Nodes{Primal},dv::VectorData,vp::Union{Edges{Primal},Nothing},base_cache::BasicILMCache,veccache::VectorFieldCache)
    @unpack wcache, dcache, ψtemp, vψ, ϕtemp, vϕ = veccache

    fill!(ψtemp,0.0)
    vectorpotential_from_masked_curlv!(ψtemp,curlv,dv,base_cache,wcache)
    vecfield_from_vectorpotential!(vψ,ψtemp,base_cache)

    fill!(ϕtemp,0.0)
    scalarpotential_from_masked_divv!(ϕtemp,divv,dv,base_cache,dcache)
    vecfield_from_scalarpotential!(vϕ,ϕtemp,base_cache)

    v .= vψ .+ vϕ
    if !isnothing(vp)
        v .+= vp
    end
    return v
end

vecfield_helmholtz!(v,curlv,divv,dv,V::Tuple,base_cache,veccache) =
            vecfield_helmholtz!(v,curlv,divv,dv,vecfield_uniformvecfield!(base_cache.gdata_cache,V...,base_cache),base_cache,veccache)

"""
    vectorpotential_uniformvecfield!(ψ::Nodes{Dual},Vx::Real,Vy::Real,base_cache::BasicILMCache)

Return a vector potential field `ψ` associated with a uniform vector field with the components `Vx` and `Vy`.
"""
function vectorpotential_uniformvecfield!(ψ::Nodes{Dual},Vx::Real,Vy::Real,base_cache::BasicILMCache)
    @unpack gcurl_cache = base_cache
    gcurl_cache .= y_gridcurl(base_cache)
    ψ .= Vx*gcurl_cache
    gcurl_cache .= x_gridcurl(base_cache)
    ψ .-= Vy*gcurl_cache
    return ψ
end

"""
    scalarpotential_uniformvecfield!(ϕ::Nodes{Primal},Vx::Real,Vy::Real,base_cache::BasicILMCache)

Return a vector potential field `ϕ` associated with a uniform vector field with the components `Vx` and `Vy`.
"""
function scalarpotential_uniformvecfield!(ϕ::Nodes{Primal},Vx::Real,Vy::Real,base_cache::BasicILMCache)
    @unpack gdiv_cache = base_cache
    gdiv_cache .= x_griddiv(base_cache)
    ϕ .= Vx*gdiv_cache
    gdiv_cache .= y_griddiv(base_cache)
    ϕ .+= Vy*gdiv_cache
    return ϕ
end

"""
    vecfield_uniformvecfield!(v::Edges{Primal},Vx::Real,Vy::Real,base_cache::BasicILMCache)

Return a vector field `v` filled with the uniform components `Vx` and `Vy`, respectively.
"""
function vecfield_uniformvecfield!(v::Edges{Primal},Vx::Real,Vy::Real,base_cache::BasicILMCache)
    v.u .= Vx
    v.v .= Vy
    return v
end


abstract KernelFilter
type AverageFilter <: KernelFilter
  xband :: Int64
  yband :: Int64
end

function AverageFilter()
  return AverageFilter(1,1)
end

type FourierFilter <: KernelFilter
  xband :: Float64
  yband :: Float64
end
function FourierFilter()
  return FourierFilter(2.0,2.0)
end

function xysmooth( signal :: Array{Float64,3}, filter :: KernelFilter)
  res = zeros(size(signal))
  for iz = 1:size(signal,3)
    res[:,:,iz] = xysmooth(signal[:,:,iz], filter)
  end
  return res
end

function xysmooth( x:: Array{Float64,2}, filter :: AverageFilter)
  y = zeros(size(x))
  xband = filter.xband
  yband = filter.yband
  for ix = 1:size(x,1)
    for iy = 1:size(x,2)
      px :: UnitRange{Int64} = max(1,ix-xband):min(size(x,1),ix + xband)
      py :: UnitRange{Int64} = max(1,iy-yband):min(size(x,2),iy + yband)
      y[ix,iy] = mean(x[px,py])
    end
  end
  return y
end

function xysmooth( ss :: Array{Float64,2}, filter :: FourierFilter)

  xband = filter.xband
  yband = filter.yband
  signal =ss

  kx = collect(1:(size(signal,1)/xband)) / size(signal,1)
  ky = collect(1:(size(signal,2)/yband)) / size(signal,2)

  fresx = zeros(size(signal))
  for ix = kx
    psix = ix*pi*collect(1:size(signal,1))
    fsinx  = mean(sin(psix) .* signal,1).* sin(psix)/(0.5)
    fcosx  = mean(cos(psix) .* signal,1).* cos(psix)/(0.5)
    fresx += fsinx + fcosx
  end
  signal = fresx

  fresy = zeros(size(signal))
  for iy = ky
    psiy = iy*pi*collect(1:size(signal,2))
    psiy = psiy'
    fsiny  = mean(sin(psiy) .* signal,2).* sin(psiy)/(0.5)
    fcosy  = mean(cos(psiy) .* signal,2).* cos(psiy)/(0.5)
    fresy += fsiny + fcosy
  end

  return fresy
end

export xysmooth
export AverageFilter, FourierFilter
export KernelFilter


module Graphene

using Interpolations
using StaticArrays
using Base.Iterators
using LinearAlgebra

SV = SVector{2, Float64}

const ccd = 0.142 # carbon carbon distance in nanometers
const a0 = √3 * ccd # in nanometers, lattice constant
const a1 = SV(a0, 0)  # bravais vector 1
const a2 = a0 .* SV(1/2, sqrt(3)/2) # bravais vector 2
const VALLEYS = # K₁, K₂, K₃, K₁', K₂', K₃'
@SVector [SV(4π/(3a0), 0), SV(-2π/(3a0), 2*√3*π/(3a0)), SV(-2π/(3a0), -2*√3*π/(3a0)),
 SV(-4π/(3a0), 0), SV(2π/(3a0), 2*√3*π/(3a0)), SV(2π/(3a0), -2*√3*π/(3a0)),
 SV(0, 0)]
const ħvF = 0.6582119514 #in units of eV*nm
const vF = 1e15 # in nm/sec
const K1 = SV(-2π/(3a0), 2*√3*π/(3a0))
const K2 = SV(2π/(3a0), 2*√3*π/(3a0))
const ħ = 6.582119514e-16  # in eV * seconds
const t = 2.8 # hoppping constant in eV

function dirac_sigma(E, Δk_over_k) #this σ gives **exact** position uncertainty
    k = abs(E/ħvF)
    # Divide by 2 due to uncertainty principle being ΔxΔk = 1/2 (for gaussian)
    σ = 1/(2Δk_over_k*k)
end
# E in eV, B in Tesla, result in nm
cyclotron_radius(E, B) = E*1000/B

##########################################
# energy dispersion and contour
##########################################
"""
    populate_contour(E, φs, ξ; kwargs...) → ks
Populate the energy contour of graphene at energy `E` at wavevectors oriented
at angles `φs` with respect to the center of the given valley `ξ`
(graphene has six valleys).

Notice that if `E > t` then the "valley" is always the center of the B.Z.
"""
function populate_contour(E, φs, xi; kwargs...)
    E ≥ t && (xi = 7)
    if abs(E) ≤ 1.0 # 2nd order expansion is accurate here
        kDiracs = wavevector_from_2ndorder.(E, φs; ξ = xi < 4 ? 1 : -1, kwargs...)
        k0s = [kD + VALLEYS[xi] for kD in kDiracs]
    else
        k0s = angle2wavevector.(φs, E; ξ = xi < 4 ? 1 : -1, kwargs...)
    end
    return k0s
end

dipsersion(k::AbstractArray; kwargs...) = dispersion(k[1], k[2]; kwargs...)
function dispersion(kx, ky; tprimeval = 0, λ=1, tval =t)
    a = ccd
    f = 2cos(√3*kx*a) + 4cos(√3*kx*a/2)*cos(3ky*a/2)
    ε = λ*tval*sqrt(3 + f) - tprimeval*f
end

function wavevector_from_2ndorder(E, φ; ξ=1, tprimeval = 0.0, tval = t)
    λ = sign(E)
    E = abs(E)
    α = (1/tval)*( (λ*tprimeval/tval) - ξ*cos(3φ)/6 )
    k = 2*(E/ħvF) / (1 + sqrt(1 + 4α*E))
    return SV(k*cos(φ), k*sin(φ))
end

"""
    groupvelocity(kx, ky; λ = 1, tprimeval = 0.0)
Return the group velocity at point `(kx, ky)` of momentum space.
"""
function groupvelocity(kx, ky; λ = 1, tprimeval = 0.0)
    # wavenumbers assumed to be in 1/nm
    t = t
    a = ccd
    s3 = √3
    f = 2cos(s3*kx*a) + 4cos(s3*kx*a/2)*cos(3*ky*a/2)
    sf3 = √(f + 3)
    vx = (s3*a/sf3) * (-λ*t + 2*tprimeval*sf3) *
         (   sin(s3*kx*a/2)*cos(3*ky*a/2) + sin(s3*kx*a)   )
    vy = (3a*cos(s3*kx*a/2)/ sf3) * (-λ*t + 2tprimeval*sf3) * sin(3*ky*a/2)
    return vx, vy
end
groupvelocity(k::Union{<:Array, <:Tuple}) = groupvelocity(k[1], k[2])

##########################################
# Convertions
##########################################
"""
    angle2wavevector(φ, E; ξ=1) → k
Create a wavevector `k` with angle `φ` at the energy contour `E`.

Choose the contour around the `ξ` valley (if E>t, thats the BZ center).
"""
function angle2wavevector(φ, E; ξ = 1, dE = 0.01, dk = 0.001)

    start = E > t ? 12.0 : 8.0
    E > t && (ξ = 7)
    c = VALLEYS[ξ]
    k = start
    ss, cs = sincos(φ)
    dir = SV(cs, ss)
    kx, ky = k*dir + c
    V = dispersion(kx, ky; λ = sign(E))
    while V ≥ E + dE
        k -= dk
        k ≤ 0 && error("Instability, k ≤ 0")
        kx, ky = k*dir + c
        V = dispersion(kx, ky; λ = sign(E))
    end
    return SV(kx, ky)
end

"""
    wavevector2angle(k0; kwargs...)
Transform a wavevector from an arbitrary location, to an angle.
Automatically decide the nearest valley to give a correct angle.
"""
function wavevector2angle(k0; kwargs...)
    E = dispersion(k0; kwargs...)
    kx, ky = k0
    if E < t
        xi = valley_index(kx, ky)
        kx, ky = bring_to_valley(kx, ky, xi)
        valley = xi ≤ 3 ? K1 : K2
        φ = round(atan(ky - valley[2], kx - valley[1]); digits = 8)
    else
        kx, ky = bring_to_center(kx, ky)
        φ = round(atan(ky, kx); digits = 8)
    end
    return φ
end

##########################################################################################
# Identify valleys / move from/to valleys
##########################################################################################
"""
    bring_to_valley(kx, ky) -> kx, ky
Bring wavevectors to a single valley (instead of the three equivalent).
The valley is the top one, either K_2 or K_2'.
"""
function bring_to_valley(kx, ky)
    xi = valley_index(kx, ky)
    a = ccd
    a1 = 4π/(3a) .* (-(√3) / 2, 1/2)
    a2 = 4π/(3a) .* (0.0, 1.0)
    b1 = 4π/(3a) .* ((√3) / 2, 1/2)
    if xi ≤ 3
        if kx > 0
            kx, ky = (kx, ky) .+ a1
        elseif ky < 0
            kx, ky = (kx, ky) .+ a2
        end
    else # valley K1', K2', K3'
        if kx < 0
            (kx, ky) = (kx, ky) .+ b1
        elseif ky < 0
            (kx, ky) = (kx, ky) .+ a2
        end
    end
    return (kx, ky)
end

valley_index(k0) = valley_index(k0[1], k0[2])
function valley_index(kx, ky) # 1 to 6
    φ = atan(ky, kx)
    if -π/6 ≤ φ < π/6;                   1
    elseif π/2 ≤ φ < π - π/6;            2
    elseif -π + π/6 ≤ φ < -π/2;          3
    elseif φ ≥ π - π/6 || φ < -π + π/6;  4
    elseif π/6 ≤ φ < π/2;                5
    elseif -π/2 ≤ φ < - π/6;             6
    end
end

function bring_to_center(kx, ky)
    a = 0.142
    a1 = 4π/(3a) .* ((√3) / 2, +1/2)
    a2 = 4π/(3a) .* (0.0, 1.0)

    if sqrt(kx^2 + ky^2) < 13.5
        return kx, ky
    end
    xsign = kx > 0 ? +1 : -1
    ysign = ky > 0 ? +1 : -1

    if abs(kx) < 12 # I need to transpose only up or down
        return kx, ky - a2[2]*ysign
    else
        return (kx, ky) .- (xsign, ysign) .* a1
    end
end

# This is useful when plotting:
function centralize(kx, ky, E)
    if abs(E) ≥ t
        kx, ky = bring_to_center(kx, ky)
    else
        kx, ky = bring_to_valley(kx, ky)
        z = 2*√3*π/(3a0)
        if ky < z
            ky += 2(z-ky)
        end
    end
    return kx, abs(ky)
end

end#module


λ_params = Dict(
    "A" => (a=-11.4, b=0.10),
    "B" => (a=-26.0, b=0.17),
    "C" => (a=-123.0, b=0.30),
    #"D" => Monin-Obhukov length is infinite
    "E" => (a=123.0, b=0.30),
    "F" => (a=26.0, b=0.17)
)

"""
    monin_obukhov(stability::String, roughness::Number)
returns the Monin-Obukhov length for a given Pasquill-Gifford stability class
and surface roughness (in meters)
Curve fit from
    Pasquill, F., *Atmospheric Diffusion, 2nd Ed.*, Halstead Press, New York, 1974.
"""
function monin_obukhov(stability::String, roughness::Number)
    if stability ∈ Set(["A","B","C","E","F"])
        a, b = λ_params[stability]
        λ = a*roughness^b
    elseif stability == "D"
        λ = Inf
    else
        err = "$stability is not a valid Pasquill-Gifford stability class"
        error(err)
    end

    return λ
end


using Plots
using Measures

include("helper_functions.jl")

begin
    λ1 = 1
    λ2 = 1.5
    α = 1
    β = 1
    μ(α) = 1 / (1.5 * (α))
    ν(α) = 1 / (0.7 * (α))
    parameters = (λ1, λ2, α, μ, ν)
    he_parameters = (λ1, λ2, α, μ, ν, β)
    # opponent_parameters = (λ2, λ1, α, μ, ν)
    # opponent_he_parameters = (λ2, λ1, α, μ, ν, β)
    # qs = 0:0.001:1
end

itr = 0.1:0.05:2.1

poa(1.5, 1.5, α, μ, ν)
heatmap(itr, itr, (x, y) -> poa(x, y, α, μ, ν))
surface(itr, itr, (x, y) -> poa(x, y, α, μ, ν))
# heatmap(itr, itr, (x, y) -> total_cost(equilibrium_probability(x, y, α, μ, ν)..., x, y, α, μ, ν))
# heatmap(itr, itr, (x, y) -> total_cost(he_equilibrium_probability(x, y, α, μ, ν, 1)..., x, y, α, μ, ν))
# plot(0.666:0.00001:0.667, 2.095:0.00001:2.0953, (x, y) -> poa(x, y, α, μ, ν), st = :contourf)
poa(0.66655, 2.09522, 1, μ, ν)
poa(0.666, 2.095, 1, μ, ν)
poa(0.66, 2.0, 1, μ, ν)
poac(x, y) = poa(x, y, 1, μ, ν)
poac(0.1, 2.095)

plot!(
    xlabel = "Player 1 arrival rate",
    ylabel = "Player 2 arrival rate",
    title = "Price of Anarchy for Various Arrival Rates",
    xticks = 0:0.25:2,
    yticks = 0:0.25:2,
    clim = (1, Inf),
    margin = 6pt,
    size = (450, 400),
)
annotate!(1.45, 1.45, text("Infeasible \n Arrival Rates", 9, rotation = -45))

savefig("outputs/price_of_anarchy_heatmap.pdf")


using InteractionNetworkModels, Plots, Distributions, JLD2
using StatsPlots, Plots.Measures, StatsBase

cd("Z:/simulations/")
readdir()
chains, times = load(
    "iex_sim_50_match_dist_multi.jld2",
    "chains", "times"
)

plot([x.suff_stat_trace for x in chains])
plot([length.(x.S_sample) for x in chains])

plot(chains, E)

posterior = chains[1].posterior
data = posterior.data
d = posterior.dist

Ê, ind = sample_frechet_mean(data, d)
sum(x->d(x,Ê), data)


using Wavelets
using PyPlot

J = 11
n = 2^J
x = testfunction(n,"Bumps")
y = dwt(x, wavelet(WT.cdf97, WT.Filter))
d,l = wplotdots(y, 0.1, n)
A = wplotim(y)

f, ax = plt.subplots(3, 1, sharex=true)
ax[1][:plot](x, "k")
ax[1][:set_xlim]([0,n])
ax[1][:set_ylabel](L"signal $x$")
ax[2][:scatter](d,l)
ax[2][:margins](0.05)
ax[2][:set_ylim](ax[2][:get_ylim]()[2:-1:1])
ax[2][:set_ylabel](L"level $j$")
ax[3][:imshow](A,aspect="auto",interpolation="none")
ax[3][:set_ylim]([J-0.5,-0.5])
ax[3][:set_ylabel](L"level $j$")

f[:set_size_inches](6,5)

savefig("transform1d_bumps.png")


using Plots

eff = 0.15
area = 30

teta_p = deg2rad(170)
alfa_p = deg2rad(35)

lat = deg2rad(59.3)

delta_s(n) = deg2rad(-23.44) * cos((2pi / 365) * n)

omega(t) = deg2rad((15 * t) - 180)

alfa_s(n, t) = asin(sin(lat) * sin(delta_s(n)) + cos(lat) * cos(delta_s(n)) * cos(omega(t)))

teta_s(n, t) = pi + (omega(t) / abs(omega(t))) * acos((sin(lat) * sin(alfa_s(n, t)) - sin(delta_s(n))) / (cos(lat) * cos(alfa_s(n, t))))

I_0 = 1360
I(n, t) = 1.1 * I_0 * 0.7^((1 / (abs(sin(alfa_s(n, t)))+sin(alfa_s(n, t)))/2)^0.678)  # underlig abs medelvärde för att få 0 om negativt värde
# Detta ser till att vi får reella talet 0 när solen inte lyser på våran platts på jorden.

I_p(n, t) = I(n, t) * (cos(teta_p - teta_s(n, t)) * cos(alfa_p - alfa_s(n, t)) + sin(alfa_s(n, t)) * sin(alfa_p) * (1 - cos(teta_p - teta_s(n, t))))

weather_loss = 1-(1821/(365*12))

P(x) = (*(((abs(I_p(floor(x/24), x))+I_p(floor(x/24), x))/2),weather_loss , eff, area))

s_date = 152
e_date = 181
step = 0.1

x = 24*s_date:step:24*(e_date)+24
x = filter(x->x%12!=0,x)
# samma som i sun, men nu behövs det faktiskt eftersom vi kollar på ett större spann och behöver mer presicion.
# vi kan inte häller använda plot funktionen eftersom vi integrerar nu.

kwh = 0

for i in x
    global kwh += i*P(i)
end

print(kwh)


# Julia code
using LinearAlgebra, Roots

function rotmat(axis, angle)
    cang = cos(pi*angle/180)
    sang = sin(pi*angle/180)
    if axis == 1
        mat = [1.0 0 0; 0 cang -sang; 0 sang cang]
    elseif axis == 2
        mat = [cang 0.0 sang; 0.0 1.0 0.0; -sang 0.0 cang]
    else
        mat = [cang -sang 0.0; sang cang 0.0; 0.0 0.0 1.0]
    end
    if angle ≈ 0.0
        mat = diagm(ones(3))
    end
    return mat
end

# vec function is replaced with vcat
function vec(x,y=0.0,z=0.0)
    return vcat(x,y,z)
end

Gmat(phi) = Gmat0 * rotmat(3, phi)
norm_Evec2 = norm(Evec)^2

function get_xyz(h,k,l)
    phis = Array{Float64,1}(undef,2)
    if !((h==0)&&(k==0)&&(l==0))
        v5 = UBmat*vec(h,k,l)
        function ewald_condition(phi)
            return (norm_Evec2 - norm((Gmat(phi)*v5 + Evec))^2)
        end
        phis = unique(round.(filter(x-> (x!=nothing)&&(x<360.0)&&(x>=0.0), map(x-> try find_zero(ewald_condition,(x,x+30),Bisection()); catch; nothing end,2.5:30:332.5)),digits=4))

        function get_ij(phi)
            p = Gmat(phi)*v5 + Evec
            normalize!(p)
            v3 = -(Dvec[1,1]/p[1,1]) *p
            v2 = Dmat*(v3+Dvec)
            v1 = Omat*v2+Cvec
            return (v1[1], v1[2])
        end
        peaks = Tuple{Float64,Float64,Float64,Int64,Int64,Int64}[]
        for phi in phis
            x,y = get_ij(phi)
            z = ((phi+5.0)/0.1)%3600
            z += (z<25.0)*3600.0 .+ (z>3625.0)*(-3600.0)
            if (x > 0)&&(x < shape[2])&&(y > 0)&&(y < shape[1])
                push!(peaks,(x, y, z, h, k, l))
            end
        end
        return peaks
    else
        skip;
    end
end

function get_xyzs(Qh,Qk,Ql)
    return mapreduce(i->get_xyz(i...),vcat,Iterators.product(-Qh:Qh,-Qk:Qk,-Ql:Ql))[:]
end


function splitinvariants!(W::Vector{Float64},Q::Vector{Float64},A::Vector{Float64},
    Qo::Vector{Float64},Ao::Vector{Float64},beta::Vector{Float64},c0::Vector{Float64},
    rho::Float64,k::Vector{Float64},h::Float64,diff::Float64,mu::Float64,children::Vector{Int64})
    lf = Float64;
    lb = Float64;
    W1 = Vector{Float64}[];
    dW1 = Float64;
    W2 = Vector{Float64}[];
    dW2 = Float64;

    # forward wave speed for parent's distal end
    lf = (Q[1]./A[1] .+ sqrt.(0.5.*beta[1]./rho).*A[1].^0.25);
    # parent's right running invariant at current time
    W1 = ([Qo[1]./Ao[1] .+ 4.*(sqrt.(0.5.*beta[1]./rho).*Ao[1]^0.25 .- c0[1])]);
    push!(W1,Q[1]./A[1] .+ 4.*(sqrt.(0.5.*beta[1]./rho).*A[1].^0.25 .- c0[1]));
    dW1 = (W1[2] - W1[1])/k[1];
    dQ = (Q[1] .- Qo[1])./k[1];
    dA = (A[1] .- Ao[1])./k[1];
    # advance parent's distal boundary W1 by linear interp.
    Qint = Q[1] .- dQ.*lf.*h;
    Aint = A[1] .- dA.*lf.*h;
    W[1] = W1[2] .- dW1.*lf.*h .- diff.*pi.*mu./rho.*Qint./Aint.^2.*h;
    for j = 2:(length(children)+1)
        lb = (Q[j]./A[j] .- sqrt.(0.5.*beta[j]./rho).*A[j].^0.25);
        W2 = ([Q[j]./A[j] .- 4.*(sqrt.(0.5.*beta[j]./rho).*A[j].^0.25 .- c0[j])]);
        push!(W2,Qo[j]./Ao[j] .- 4.*(sqrt.(0.5.*beta[j]./rho).*Ao[j].^0.25 .- c0[j]))
        dW2 = (W2[2] .- W2[1])./k[j];
        dQ = (Qo[j] .- Q[j])./k[j];
        dA = (Ao[j] .- A[j])./k[j];
        # advance child's proximal boundary invariant
        Qint = Q[j] .- dQ.*lb.*h;
        Aint = A[j] .- dA.*lb.*h;
        W[j] = W2[1] .- dW2.*lb.*h .- diff.*pi.*mu./rho.*Qint./Aint.^2.*h;
    end
end


export gauss_line_fit

function gaussclossure(X::AbstractVector)
    function gaussloss(params)
        a, σ = params[1], params[2]
        x = a * [exp(-(t)^2 / (2 * σ^2)) for t in axes(X, 1)]
        return sum(abs2, x - X)
    end
    return gaussloss
end

function gauss_line_fit(X::AbstractVector; kwargs...)
    f = gaussclossure(X)
    sX = sum(X)
    σ² = sum(i^2 * X[i] for i in axes(X, 1)) / sX
    σ = sqrt(max(σ², oftype(σ², 1e-4)))  # σ >= 0.01 since we only have integer-scale resolution
    params = [sX / (σ * sqrt(2π)), σ]

    #result = optimize(f, params, Newton(), Optim.Options(; kwargs...); autodiff = :forward)
    result = optimize(f, params, BFGS(), Optim.Options(; kwargs...))
    Optim.converged(result) || @warn "Optimization failed to converge"
    return result
    #return Optim.minimizer(result)
end


"""
Operation - Google Code Jam 2017 Finals, Problem B
(https://code.google.com/codejam/contest/6314486/dashboard#s=p1&a=0)

Run the script:

    julia --project src/Operation.jl <in.txt >out.txt
"""

module Operation

using Combinatorics: permutations

p(s::AbstractString) = parse(Int, s)

OP_MAP = Dict(
    "+" => +,
    "-" => -,
    "*" => *,
    "/" => //,
)

function solve!(start::BigInt, card_map::Dict{String, Vector{BigInt}})::Rational{BigInt}
    cards = Tuple{Function, BigInt}[]

    for op in ["+", "-"]
        push!(cards, (OP_MAP[op], sum(card_map[op])))
    end

    z_ind = findall(iszero, card_map["*"])
    if !isempty(z_ind)
        push!(cards, (OP_MAP["*"], 0))
        deleteat!(card_map["*"], z_ind)
    end

    for op in ["*", "/"]
        pd = prod(card_map[op])
        neg_list = filter(x -> x < 0, card_map[op])

        if length(neg_list) == 1
            m = neg_list[1]
            pd //= m
            push!(cards, (OP_MAP[op], m))
        elseif length(neg_list) >= 2
            mi, ma = minimum(neg_list), maximum(neg_list)
            pd //= mi * ma
            push!(cards, (OP_MAP[op], mi))
            push!(cards, (OP_MAP[op], ma))
        end

        push!(cards, (OP_MAP[op], pd))
    end

    reduce(max, (reduce((acc, (op, x)) -> op(acc, x), cs; init = start // big(1))
        for cs in permutations(cards)))
end

function main()
    n_tests = p(readline())
    for t in 1:n_tests
        (start, n_cards) = p.(split(readline()))

        card_map = Dict(op => BigInt[] for (op, _) in OP_MAP)
        for _ in 1:n_cards
            (op, x) = begin
                (op, x) = split(readline())
                (String(op), p(x))
            end
            if op == "+" && x < 0
                (op, x) = ("-", -x)
            elseif op == "-" && x < 0
                (op, x) = ("+", -x)
            end
            push!(card_map[op], x)
        end

        res = solve!(big(start), card_map)
        println("Case #$(t): $(res.num) $(res.den)")
        flush(stdout)
    end
end

main()

end # module