#=

  From BLOG examples/healtiness.blog:
  """
  healthiness model
  This BLOG program describes the model for healthiness in
  a person.
  See exercise 3.5 in:
  Probabilistic Graphical Models: Principles and Techniques
  Daphne Koller, Nir Friedman, MIT 2009
  Each person is described by whether they are health
  conscious, have free time, exercise, have a good diet,
  have normal weight, have high cholesterol, and whether
  they tested positive for high cholesterol.
  @author: jnieh
  @date: 2012-09-13
  """

  For evidence TestedHighCholesterol == false:
  Summary Statistics
             parameters      mean       std   naive_se      mcse          ess      rhat   ess_per_sec
                 Symbol   Float64   Float64    Float64   Float64      Float64   Float64       Float64

        HealthConscious    0.5614    0.4962     0.0050    0.0080    4159.9755    0.9999      405.4952
         LittleFreeTime    0.4870    0.4999     0.0050    0.0081    4242.7390    0.9999      413.5626
               Exercise    0.5430    0.4982     0.0050    0.0075    4070.4820    1.0002      396.7718
               GoodDiet    0.6641    0.4723     0.0047    0.0074    4392.4673    1.0001      428.1575
           NormalWeight    0.5620    0.4962     0.0050    0.0069    4156.5796    1.0010      405.1642
        HighCholesterol    0.0987    0.2983     0.0030    0.0043    4344.1532    0.9999      423.4480
  TestedHighCholesterol    0.0001    0.0100     0.0001    0.0001   10000.0008    1.0000      974.7540

  For evidence exercise = true
  Summary Statistics
             parameters      mean       std   naive_se      mcse         ess      rhat   ess_per_sec
                 Symbol   Float64   Float64    Float64   Float64     Float64   Float64       Float64

        HealthConscious    0.6805    0.4663     0.0047    0.0071   4317.9635    1.0006      425.9607
         LittleFreeTime    0.2970    0.4570     0.0046    0.0070   4361.5047    1.0000      430.2560
               Exercise    1.0000    0.0000     0.0000    0.0000         NaN       NaN           NaN
               GoodDiet    0.5774    0.4940     0.0049    0.0072   4022.1381    1.0000      396.7780
           NormalWeight    0.6787    0.4670     0.0047    0.0074   3908.1858    0.9999      385.5367
        HighCholesterol    0.4715    0.4992     0.0050    0.0078   4117.2828    1.0004      406.1638
  TestedHighCholesterol    0.4780    0.4995     0.0050    0.0078   4117.0265    0.9999      406.1386

  Cf ~/webppl/healthiness.wppl

=#

using Turing, StatsPlots, DataFrames
include("jl_utils.jl")

@model function healthiness()

    HealthConscious ~ flip(0.5)
    LittleFreeTime ~ flip(0.5)

    Exercise ~
            (HealthConscious==true && LittleFreeTime==true) ? flip(0.5) :
            (HealthConscious==true && LittleFreeTime== false) ? flip(0.9) :
            (HealthConscious==false && LittleFreeTime== true) ? flip(0.1) :
            (HealthConscious==false && LittleFreeTime== false) ? flip(0.5) : Dirac(false)

    GoodDiet ~ HealthConscious ? flip(0.7) : flip(0.3)

    NormalWeight ~ (GoodDiet==true && Exercise==true) ? flip(0.8) :
                   (GoodDiet==true && Exercise== false) ? flip(0.5) :
                   (GoodDiet==false && Exercise== true) ? flip(0.5) :
                   (GoodDiet==false && Exercise== false) ? flip(0.2) : Dirac(false)

    HighCholesterol ~ GoodDiet ? flip(0.3) : flip(0.7)

    TestedHighCholesterol ~ HighCholesterol ? flip(0.9) : flip(0.1)

    # Evidence
    true ~ Dirac(TestedHighCholesterol == false) #  Original evidence
    # true ~ Dirac(TestedHighCholesterol == true)
    # true ~ Dirac(Exercise == true)
    # true ~ Dirac(LittleFreeTime == true)

end

model = healthiness()

num_chains = 4

# chns = sample(model, Prior(), 10_000)
# chns = sample(model, MH(), 10_000)
chns = sample(model, PG(5), 10_000)
# chns = sample(model, SMC(), 10_000)
# chns = sample(model, IS(), 10_000)

display(chns)
# display(plot(chns))


# ## Ridge
# Ridge回帰の関数は少し煩雑ですが、標準化処理(データの平均値をひいて、標準偏差で割る)
# を行った後に、回帰分析を行っています。 全てが1の値を持つ列を説明変数に入れてしまうと
# 標準偏差(=0)で割るとエラーを吐くので、こういうことをしているのだと思います。
# ```julia
# using StatsBase
# function ridge(X::Matrix,y::Vector,λ=0)
#     @assert λ ≥ 0
#     n,p = size(X)
#     X̄, σ = mean_and_std(X,1) #説明変数の平均と標準偏差を記録
#     ȳ = mean(y) #目的変数については平均値を取得
#     zscore!(X,X̄,σ) #標準化処理
#     y .-= ȳ
#     β = (X'X + n*λ*I(p)) \ X'*y
#     β ./= vec(σ) #X'Xで割っているので、βをσで割っておく
#     β₀ = ȳ .- X̄*β
#     Dict("β" => β, "β₀" => β₀)
# end
# ```
# ### 例48
# 米国犯罪データをRidge回帰します。

using DelimitedFiles, Plots
using Joe:ridge
using Plots.PlotMeasures # hide
Plots.reset_defaults() # hide
default( # hide
    titlefont  = font("JuliaMono", default(:titlefontsize),  ),  # hide
    guidefont  = font("JuliaMono",  default(:guidefontsize),  ), # hide
    tickfont   = font("JuliaMono", default(:tickfontsize),   ), # hide
    legendfont = font("JuliaMono",  default(:legendfontsize), ), # hide
    left_margin = 30px, # hide
    bottom_margin = 30px # hide
) # hide
df = readdlm(joinpath("_assets","data","crime.txt"))
X = df[:,3:7]; y =df[:,1]
λ = 0:0.5:50

_,p = size(X)
β = hcat([ridge(X,y,l)["β"] for l in λ]...)
p48 = plot(size=(500,500),xlims=(0,50),ylims=(-7.5,15),
           xlabel="λ",ylabel="β")
labels=["警察への年間資金                                                 ",
        "25歳以上で高校を卒業した人の割合",
        "16-19歳で高校に通っていない人の割合",
        "18-24歳で大学生の割合",
        "25歳以上で４年制大学を卒業した人の割合"]
for j in 1:p
    global p48, λ, β, labels # hide
    plot!(p48, λ,β[j,:],label=labels[j])
end
savefig(p48,joinpath(@OUTPUT,"fig5-1.svg")) # hide
# \fig{fig5-1}

# ### 例49
# 絶対値を含む関数のプロット
using Plots
x_seq = -2:0.05:2;
p49_1 = plot(x_seq, x -> x^2-3x+abs(x),title = "y=x^2-3x+|x|")
scatter!(p49_1,[1],[-1],color=:red,legend=false)
p49_2 = plot(x_seq,x -> x^2 + x +2abs(x),title="y=x^2+x+2|x|")
scatter!(p49_2,[0],[-0],color=:red, legend=false)
p49 = plot(p49_1,p49_2,layout=(1,2),size= (500,200))
savefig(p49,joinpath(@OUTPUT,"fig5-2.svg")) # hide
# \fig{fig5-2}
# ## Lasso
# lassoのプログラムは標準化処理はridgeと共通しています。
# ### 例50
using Joe:lasso
df = readdlm(joinpath("_assets","data","crime.txt"))
X = df[:,3:7]; y =df[:,1]
λ = 0:0.5:200
_,p = size(X)
β = hcat([lasso(X,y,l)["β"] for l in λ]...)
p50 = plot(size=(500,500),xlims=(0,200),ylims=(-7.5,15),
           xlabel="λ",ylabel="β")
labels=["警察への年間資金                                                 ",
        "25歳以上で高校を卒業した人の割合",
        "16-19歳で高校に通っていない人の割合",
        "18-24歳で大学生の割合",
        "25歳以上で４年制大学を卒業した人の割合"]
for j in 1:p
    global p50, λ, β, labels # hide
    plot!(p50, λ,β[j,:],label=labels[j])
end
savefig(p50,joinpath(@OUTPUT,"fig5-3.svg")) # hide
# \fig{fig5-3}

# ### 例51
# CRANパッケージのglmnetはjuliaにも移植されています。
# ここでは[GLMNet.jl](https://github.com/JuliaStats/GLMNet.jl)を使って、クロスバリデーションによる
# λの最適化を検討します。
using GLMNet
df = readdlm(joinpath("_assets","data","crime.txt"))
X = df[:,3:7]; y =df[:,1]
cv  = glmnetcv(X,y)
# λは20くらいが最適のようです。その時の係数は以下のコードで求められます。
GLMNet.coef(cv)
# 後半の二つは係数が0になって、前半の三つの変数が選択されました。
# Python版の初版では係数の値が違うのですが、
# R版の原著ではこれらの値であまり問題はなさそうです。
# (これはPython版のScikitLearnのLassoCVでは0にされていないですね。
# ScikitLearnが悪いのか？)
# 横軸をlog λ, 縦軸を平均二乗誤差（リボンはσ）にして可視化します。
p51 = plot(xlabel="log λ", ylabel="mean squared error")
plot!(p51,log.(cv.lambda),cv.meanloss,ribbon=cv.stdloss,legend=false)
savefig(p51,joinpath(@OUTPUT,"fig5-8.svg")) # hide
# \fig{fig5-8}
# ### 問55
# 例48のcrime.txtで、今度はScikitLearnのLassoを利用してみることにします。
using ScikitLearn, DelimitedFiles
@sk_import linear_model: Lasso
@sk_import linear_model: LassoCV
df = readdlm(joinpath("_assets","data","crime.txt"))
X = df[:,3:7]; y =df[:,1];
# まずはLassoのインスタンスを作ります。
Las = Lasso(alpha=20)
# キーワード引数のalphaは上の例でいう正則化パラメータλのことです。
# Lasにデータを入力して、$\lambda=20$の時の係数を求めます。
Las.fit(X,y);
Las.coef_
# LassoCVの場合は、複数の$\lambda$の値を与えることができます。
Lcv = LassoCV(alphas=0.1:0.1:30,cv=10)
Lcv.fit(X,y)
Lcv.alpha_
# なんと$\lambda = 30$が最適のようです。
# 係数はどうでしょうか？
Lcv.coef_
# こちらも係数が0に近づいて変数選択されているようには見えません。
# これではめでたしめでたしにはならないので、設定を見直すことにします。
# Lassoオブジェクトのインスタンスを生成する際の、キーワード引数はnormalize=falseとなっています。
# scikit-learnのドキュメントによれば、normalize=trueにすると、
# 平均値をひいたあとに、二乗ノルムで割ることになっているので標準化には使えません。
# StandardScalerを使えと書いてありますが、大した処理ではないので自前で標準化したデータを投げてみます。
using DelimitedFiles, StatsBase
df = readdlm(joinpath("_assets","data","crime.txt"))
X = df[:,3:7]; y =df[:,1];
X̄,σ = mean_and_std(X,1)
XX = zscore(X,X̄,σ)
ȳ = mean(y)
yy = y .- ȳ
Lcv2 = LassoCV(alphas=0.1:0.1:30,cv=10)
Lcv2.fit(XX,yy)
@show Lcv2.alpha_;
Lcv2.coef_
# 正則化パラメータについては、それらしい値が得られました。
# 係数については、変数選択されていることは確認できましたが、値が大きいので$\sigma$で割って確かめることにします。
Lcv2.coef_ ./ σ'
# 混乱しましたが、[例50](#例50)と大体同じ係数になることを確認できました。


using Printf
using Statistics
using Oceananigans
using Oceananigans.ImmersedBoundaries: ImmersedBoundaryGrid, GridFittedBottom, mask_immersed_field!

@inline bottom_drag_func(x, y, t, u, Cᴰ) = - Cᴰ * u^2

function hilly_simulation(;
                          Nx = 64,
                          Nz = Nx,
                          ϵ = 0.1,
                          Re = 1e4,
                          N² = 1e-2,
                          bottom_drag = false,
                          stop_time = 1,
                          save_interval = 0.1,
                          architecture = CPU(),
                          name = "flow_over_hills")

    underlying_grid = RectilinearGrid(architecture,
                                      size = (Nx, Nz),
                                      halo = (3, 3),
                                      x = (0, 2π),
                                      z = (0, 2π),
                                      topology = (Periodic, Flat, Bounded))

    if ϵ > 0
        x, y, z = nodes((Center, Center, Center), underlying_grid, reshape=true)
        hills = @. ϵ * (1 + sin(x)) / 2
        grid = ImmersedBoundaryGrid(underlying_grid, GridFittedBottom(hills))
    else # no hills
        grid = underlying_grid
    end

    closure = isfinite(Re) ? ScalarDiffusivity(ν=1/Re, κ=1/Re) : nothing

    if bottom_drag
        Δz = 2π / Nz
        z₀ = 1e-4
        κ = 0.4
        Cᴰ = (κ / log(Δz / 2z₀))^2
        u_bottom_bc = FluxBoundaryCondition(bottom_drag_func, field_dependencies=:u, parameters=Cᴰ)
        u_bcs = FieldBoundaryConditions(bottom=u_bottom_bc)
        boundary_conditions = (; u = u_bcs)
        @info string("Using a bottom drag with coefficient ", Cᴰ)
    else
        boundary_conditions = NamedTuple()
    end

    model = NonhydrostaticModel(; grid, closure, boundary_conditions,
                                advection = WENO5(),
                                timestepper = :RungeKutta3,
                                tracers = :b,
                                buoyancy = BuoyancyTracer())

    bᵢ(x, y, z) = N² * z + 1e-9 * rand()
    set!(model, b=bᵢ, u=1)

    Δx = 2π / Nx
    Δt = 0.1 * Δx
    simulation = Simulation(model; Δt, stop_time)

    u, v, w = model.velocities
    Uᵢ = mean(u)

    wall_clock = Ref(time_ns())

    function progress(sim)
        δU = mean(u) / Uᵢ
        elapsed = 1e-9 * (time_ns() - wall_clock[])
        @info @sprintf("Iter: %d, time: %.2e, δU: %.2e, wall time: %s",
                       iteration(sim), time(sim), δU, prettytime(elapsed))
        wall_clock[] = time_ns()
        return nothing
    end

    simulation.callbacks[:progress] = Callback(progress, IterationInterval(100))

    U = Average(u, dims=(1, 2, 3))
    ξ = ∂z(u) - ∂x(w)

    simulation.output_writers[:fields] =
        JLD2OutputWriter(model, merge(model.velocities, model.tracers, (; ξ, U)),
                         schedule = TimeInterval(save_interval),
                         prefix = name,
                         force = true)

    @info "Make a simulation:"
    @show simulation

    return simulation
end

function momentum_time_series(filepath)
    U = FieldTimeSeries(filepath, "U")
    t = U.times
    δU = [U[1, 1, 1, n] / U[1, 1, 1, 1] for n=1:length(t)]
    return δU, t
end

Nx = 256
stop_time = 100.0
reference_name = "bottom_drag_reference"
#=
reference_sim = hilly_simulation(; stop_time, Nx, ϵ=0, name=reference_name, bottom_drag=true)
run!(reference_sim)
δU_ref, t_ref = momentum_time_series(name * ".jld2")
=#

experiments = []
for ϵ = [0.02, 0.05, 0.1]
    name = string("flow_over_hills_height_", ϵ)
    push!(experiments, (; ϵ, name))
    simulation = hilly_simulation(; stop_time, Nx, ϵ, name)
    run!(simulation)
end

using GLMakie

fig = Figure()
ax = Axis(fig[1, 1])
lines!(ax, t_ref, δU_ref, label="Reference with bottom drag")

for experiment in experiments
    name = experiment.name
    ϵ = experiment.ϵ
    δU, t = momentum_time_series(name * ".jld2")
    lines!(ax, t, δU, label=string("ϵ = ", ϵ))
end

axislegend(ax)

display(fig)

#=
# Animate vorticity if you like
filepath = experiments[end].name * ".jld2"
ξ = FieldTimeSeries(filepath, "ξ")
Nt = length(ξ.times)

fig = Figure()
slider = Slider(fig[2, 1], range=1:Nt, startvalue=1)
n = slider.value

title = @lift @sprintf("Vorticity in flow over hills at t = %.2e", ξ.times[$n])
ax = Axis(fig[1, 1]; title)
ξn = @lift interior(ξ[$n], :, 1, :)

#=
masked_ξn = @lift begin
    ξn = ξ[$n]
    mask_immersed_field!(ξn, NaN)
    interior(ξn, :, 1, :)
end
=#

heatmap!(ax, ξn)

display(fig)
=#


# ---
# title: 1617. Count Subtrees With Max Distance Between Cities
# id: problem1617
# author: Tian Jun
# date: 2020-10-31
# difficulty: Hard
# categories: Backtracking
# link: <https://leetcode.com/problems/count-subtrees-with-max-distance-between-cities/description/>
# hidden: true
# ---
#
# There are `n` cities numbered from `1` to `n`. You are given an array `edges`
# of size `n-1`, where `edges[i] = [ui, vi]` represents a bidirectional edge
# between cities `ui` and `vi`. There exists a unique path between each pair of
# cities. In other words, the cities form a **tree**.
#
# A **subtree** is a subset of cities where every city is reachable from every
# other city in the subset, where the path between each pair passes through only
# the cities from the subset. Two subtrees are different if there is a city in
# one subtree that is not present in the other.
#
# For each `d` from `1` to `n-1`, find the number of subtrees in which the
# **maximum distance** between any two cities in the subtree is equal to `d`.
#
# Return _an array of size_ `n-1` _where the_`dth` ___element **(1-indexed)** is
# the number of subtrees in which the **maximum distance** between any two
# cities is equal to _`d`.
#
# **Notice**  that the **distance** between the two cities is the number of
# edges in the path between them.
#
#
#
# **Example 1:**
#
# **![](https://assets.leetcode.com/uploads/2020/09/21/p1.png)**
#
#
#
#     Input: n = 4, edges = [[1,2],[2,3],[2,4]]
#     Output: [3,4,0]
#     Explanation: The subtrees with subsets {1,2}, {2,3} and {2,4} have a max distance of 1.
#     The subtrees with subsets {1,2,3}, {1,2,4}, {2,3,4} and {1,2,3,4} have a max distance of 2.
#     No subtree has two nodes where the max distance between them is 3.
#
#
# **Example 2:**
#
#
#
#     Input: n = 2, edges = [[1,2]]
#     Output: [1]
#
#
# **Example 3:**
#
#
#
#     Input: n = 3, edges = [[1,2],[2,3]]
#     Output: [2,1]
#
#
#
#
# **Constraints:**
#
#   * `2 <= n <= 15`
#   * `edges.length == n-1`
#   * `edges[i].length == 2`
#   * `1 <= ui, vi <= n`
#   * All pairs `(ui, vi)` are distinct.
#
#
## @lc code=start
using LeetCode

## add your code here:
## @lc code=end


@inline function Flux(Cell,s,z,Def,j_tf,D,res0)
"""
Flux Transfer Function

Flux(Cell,s,z,Def)

"""

if Def == "Pos"
   Electrode = Cell.Pos #Electrode Length
else
   Electrode = Cell.Neg #Electrode Length
end

κ_eff = Cell.Const.κ*Electrode.ϵ_e^Electrode.κ_brug #Effective Electrolyte Conductivity
σ_eff = Electrode.σ*Electrode.ϵ_s^Electrode.σ_brug #Effective Electrode Conductivity

#Defining SOC
θ = Cell.Const.SOC * (Electrode.θ_100-Electrode.θ_0) + Electrode.θ_0

#Prepare for j0
ce0 = Cell.Const.ce0
cs_max = Electrode.cs_max
cs0 = cs_max*θ
α = Electrode.α

#Current Flux Density
if Cell.Const.CellTyp == "Doyle_94"
    κ = Electrode.k_norm/Electrode.cs_max/ce0^(1-α)
    j0 = κ*(ce0*(cs_max-cs0))^(1-α)*cs0^α
else
   j0 = Electrode.k_norm*(Cell.Const.ce0*cs0*(Electrode.cs_max-cs0))^(1-Electrode.α)
end

#Resistances
Rtot = R*Cell.Const.T /(j0*F^2) + Electrode.RFilm
#Rtot = R*Cell.Const.T/(j0*Cell.Const.CC_A*F) + Electrode.RFilm

#∂Uocp_Def
∂Uocp_elc = Cell.Const.∂Uocp(Def,θ)/cs_max

#Condensing Variable
ν = @. Electrode.L*sqrt((Electrode.as/σ_eff+Electrode.as/κ_eff)/(Rtot+∂Uocp_elc*(Electrode.Rs/(F*Electrode.Ds))*(tanh(Electrode.β)/(tanh(Electrode.β)-Electrode.β))))
ν_∞ = @. Electrode.L*sqrt(Electrode.as*((1/κ_eff)+(1/σ_eff))/(Rtot))

#Transfer Function
j_tf .= @. ν*(σ_eff*cosh(ν*z)+κ_eff*cosh(ν*(z-1)))/(Electrode.as*F*Electrode.L*Cell.Const.CC_A*(κ_eff+σ_eff)*sinh(ν))
D .= @. ν_∞*(σ_eff*cosh(ν_∞*z)+κ_eff*cosh(ν_∞*(z-1)))/(Electrode.as*F*Electrode.L*Cell.Const.CC_A*(κ_eff+σ_eff)*sinh(ν_∞))
zero_tf =ones(size(z,1))*1/(Cell.Const.CC_A*Electrode.as*F*Electrode.L)
j_tf[:,findall(s.==0)] .= zero_tf[:,findall(s.==0)]
res0 .= zeros(length(z))

if Def == "Pos"
   j_tf .= -j_tf
   D .= -D
end

end


using RNLA, Test, LinearAlgebra

@testset "rnmf_test1" begin
    m = 50
    k = 10
    n = 30
    W = rand(1:100, (m, k))
    H = rand(1:100, (k, n))
    X = W * H

    rW, rH = rnmf(X, k)
    @test norm(rW - W) <= sqrt(eps())
    @test norm(rH - H) <= sqrt(eps())
end

@testset "rnmf_test2" begin
    m = 100
    k = 5
    n = 15
    W = rand(1:100, (m, k))
    H = rand(1:100, (k, n))
    X = W*H

    rW, rH = rnmf(X, k)
    @test norm(rW - W) <= sqrt(eps())
    @test norm(rH - H) <= sqrt(eps())
end


using OptimalEstimationProject
using LinearAlgebra
using AstroTime
using MATLAB

function main()
    # Spacecraft simulator
    t0      = TAIEpoch("2021-08-08T12:00:00")
    scsim   = SpacecraftSim(t0.second)

    # Initial state error variance
    σr      = 0.1   # [km]
    σv      = 0.01  # [km / s]
    σm      = 1e-6  # [kg]
    sqrtP0  = Diagonal([σr, σr, σr, σv, σv, σv, σm])
    P0      = sqrtP0.^2

    # Initial state estimate
    (y0true, us) = OptimalEstimationProject.GetStateAndControl(scsim, t0.second)
    xhat0   = y0true[1:7] + sqrtP0*randn(7)

    # Measurement time stamps
    Δt      = 30        # [sec]
    gpsΔt   = 15*60     # [sec]
    ts      = range(t0.second + Δt; step = Δt, stop = t0.second + scsim.ts[2]*86400)

    # Measurement statistics
    σρ      = 10.0e-3 # [km]     Pseudorange noise standard deviation
    σr      = 5.0e-3 # [km]     GPS broadcast ephemeris standard deviation
    σa      = 1.0e-6 # [km/s^2] Accelerometer noise standard deviation

    # Process noise covariance
    R       = Diagonal((σρ^2 + 3*σr^2)*ones(32))
    Q       = Diagonal([0.0, 0.0, 0.0, 1e-12, 1e-12, 1e-12, 1e-3])

    # GPS Simulation Span
    startWeek   = 2170
    startDay    = 0
    endWeek     = 2174
    endDay      = 6

    # Create GPS simulation object
    gpssim = GPSSim(startWeek, startDay, endWeek, endDay; σρ = σρ, σr = σr)

    # Create IMU simulation object
    imusim = IMUSim(σa)

    # Create EKF
    ekf = EKF(xhat0, P0, Q, (σρ^2 + 3*σr^2), σa^2, ts, gpsΔt, gpssim, imusim, scsim; steps2save = 2, lunaPerts = true);

    # Run filter
    runFilter!(ekf)

    # Get true trajectory for plotting
    xtrue = zeros(length(ekf.txp), 3)
    for i in 1:length(ekf.txp)
        (y0l, us) = OptimalEstimationProject.GetStateAndControl(scsim, ekf.txp[i])
        xtrue[i, :] .= y0l[1:3]
    end

    # Plotting
    plot(ekf, xtrue, length(ekf.txp))
end

main()


function calculateTransfiniteNodes(c1::Function, c2::Function, c3::Function, c4::Function, P12::Vector{Float64},
    P14::Vector{Float64}, P34::Vector{Float64}, P32::Vector{Float64}, us::Vector{Float64}, vs::Vector{Float64})

    S = Array{Union{Nothing, Tuple}}(nothing, size(us,1), size(vs,1))

    for (i, u) in enumerate(us)
        for (j, v) in enumerate(vs)
            tmp = (1-v)*c1(u) + v*c3(u) + (1-u)*c2(v) + u*c4(v) - ( (1-u)*(1-v)*P12 + u*v*P34 + u*(1-v)*P14 + (1-u)*v*P32 )
            S[i,j] = tuple(tmp...)
        end
    end

    return S

end


### A Pluto.jl notebook ###
# v0.11.10

using Markdown
using InteractiveUtils

# ╔═╡ 1ca14906-eca1-11ea-23f6-472ed97d75aa
begin
	using Statistics
	using Images
	using FFTW
	using Plots
	using DSP
	using ImageFiltering
	using PlutoUI
end

# ╔═╡ 42ed52ba-ed34-11ea-26b5-05379824cbc0
md"""
# Convolutions with various kernels
"""

# ╔═╡ 4c13d558-ee15-11ea-2ed9-c5fb90d93881
kernel = Kernel.gaussian((2, 2))

# ╔═╡ 673f7ac0-ee16-11ea-35d0-cf3da430b843
sum(kernel)

# ╔═╡ 9c90feb8-ec79-11ea-2870-31be5cedff43
md"""
# Function definitions
"""

# ╔═╡ 84e6a57c-edfc-11ea-01a0-157f1df77518
function show_colored_kernel(kernel)
	to_rgb(x) = RGB(max(-x, 0), max(x, 0), 0)
	to_rgb.(kernel) / maximum(abs.(kernel))
end

# ╔═╡ 9424b46a-ee16-11ea-1819-f17ce53e9997
show_colored_kernel(kernel)

# ╔═╡ 68f2afec-eca2-11ea-0758-2f22c7afdd94
function decimate(arr, ratio=5)
	return arr[1:ratio:end, 1:ratio:end]
end

# ╔═╡ aa3b9bd6-ed35-11ea-1bdc-33861bdbd29a
function shrink_image(image, ratio=5)
	(height, width) = size(image)
	new_height = height ÷ ratio - 1
	new_width = width ÷ ratio - 1
	list = [
		mean(image[
			ratio * i:ratio * (i + 1),
			ratio * j:ratio * (j + 1),
		])
		for j in 1:new_width
		for i in 1:new_height
	]
	reshape(list, new_height, new_width)
end

# ╔═╡ 6d39fea8-ed3c-11ea-3d7c-3f62ca91ce23
begin
	large_image = load("tom_in_bowtie.jpg")
	image = shrink_image(large_image, 7)
end

# ╔═╡ 2f446dcc-ee15-11ea-0e78-931ff507b5e5
size(image)

# ╔═╡ 14d5b144-ee18-11ea-0080-c187f068c168
image

# ╔═╡ 160eb236-eca1-11ea-1dbe-47ad61cc9397
function rgb_to_float(color)
    return mean([color.r, color.g, color.b])
end

# ╔═╡ fa3c5074-eca0-11ea-2d2d-bb6bcdeb834c
function fourier_spectrum_magnitudes(img)
    grey_values = rgb_to_float.(img)
    spectrum = fftshift(fft(grey_values))
	return abs.(spectrum)
end

# ╔═╡ e40d807e-ed3a-11ea-2340-7f98bd5d04a2
function plot_1d_fourier_spectrum(img, dims=1)
	spectrum = fourier_spectrum_magnitudes(img)
	plot(centered(mean(spectrum, dims=1)[1:end]))
end

# ╔═╡ beb6b4b0-eca1-11ea-1ece-e3c9931c9c13
function heatmap_2d_fourier_spectrum(img)
	heatmap(log.(fourier_spectrum_magnitudes(img)))
end

# ╔═╡ 18045956-ee18-11ea-3e34-612133e2e39c
heatmap_2d_fourier_spectrum(image)

# ╔═╡ 58f4754e-ed31-11ea-0464-5bfccf397966
function clamp_at_boundary(M, i, j)
	return M[
		clamp(i, 1, size(M, 1)),
		clamp(j, 1, size(M, 2)),
	]
end

# ╔═╡ f28af11e-ed31-11ea-2b46-7dff147ccb48
function rolloff_boundary(M, i, j)
	if (1 ≤ i ≤ size(M, 1)) && (1 ≤ j ≤ size(M, 2))
		return M[i, j]
	else
		return 0 * M[1, 1]
	end
end

# ╔═╡ 572cf620-ecb2-11ea-0019-21666a30d9d2
function convolve(M, kernel, M_index_func=clamp_at_boundary)
    height = size(kernel, 1)
    width = size(kernel, 2)

    half_height = height ÷ 2
    half_width = width ÷ 2

    new_image = similar(M)

    # (i, j) loop over the original image
    @inbounds for i in 1:size(M, 1)
        for j in 1:size(M, 2)
            # (k, l) loop over the neighbouring pixels
			new_image[i, j] = sum([
				kernel[k, l] * M_index_func(M, i - k, j - l)
				for k in -half_height:-half_height + height - 1
				for l in -half_width:-half_width + width - 1
			])
        end
    end

    return new_image
end

# ╔═╡ 5afed4ea-ee18-11ea-1aa4-abca154b3793
conv_image = convolve(image, kernel)

# ╔═╡ 6340c0f8-ee18-11ea-1765-45f4bc140670
heatmap_2d_fourier_spectrum(conv_image)

# ╔═╡ 587092e4-ecb2-11ea-18fc-ad5e9778fb30
box_blur(n) = centered(ones(n, n) ./ (n^2))

# ╔═╡ 991cb9b8-ecb8-11ea-3f80-5d95b2200259
function gauss_blur(n, sigma=0.25)
	kern = gaussian((n, n), sigma)
	return kern / sum(kern)
end

# ╔═╡ Cell order:
# ╟─42ed52ba-ed34-11ea-26b5-05379824cbc0
# ╠═6d39fea8-ed3c-11ea-3d7c-3f62ca91ce23
# ╠═2f446dcc-ee15-11ea-0e78-931ff507b5e5
# ╠═4c13d558-ee15-11ea-2ed9-c5fb90d93881
# ╠═9424b46a-ee16-11ea-1819-f17ce53e9997
# ╠═673f7ac0-ee16-11ea-35d0-cf3da430b843
# ╠═14d5b144-ee18-11ea-0080-c187f068c168
# ╠═18045956-ee18-11ea-3e34-612133e2e39c
# ╠═5afed4ea-ee18-11ea-1aa4-abca154b3793
# ╠═6340c0f8-ee18-11ea-1765-45f4bc140670
# ╟─9c90feb8-ec79-11ea-2870-31be5cedff43
# ╟─1ca14906-eca1-11ea-23f6-472ed97d75aa
# ╟─84e6a57c-edfc-11ea-01a0-157f1df77518
# ╟─68f2afec-eca2-11ea-0758-2f22c7afdd94
# ╟─aa3b9bd6-ed35-11ea-1bdc-33861bdbd29a
# ╟─160eb236-eca1-11ea-1dbe-47ad61cc9397
# ╟─fa3c5074-eca0-11ea-2d2d-bb6bcdeb834c
# ╟─e40d807e-ed3a-11ea-2340-7f98bd5d04a2
# ╟─beb6b4b0-eca1-11ea-1ece-e3c9931c9c13
# ╟─58f4754e-ed31-11ea-0464-5bfccf397966
# ╟─f28af11e-ed31-11ea-2b46-7dff147ccb48
# ╟─572cf620-ecb2-11ea-0019-21666a30d9d2
# ╟─587092e4-ecb2-11ea-18fc-ad5e9778fb30
# ╟─991cb9b8-ecb8-11ea-3f80-5d95b2200259


using StanSample

ProjDir = @__DIR__

gq = "
  data {
    int<lower=0> N;
    int<lower=0> y[N];
  }
  parameters {
    real<lower=0.00001> Theta;
  }
  model {
    Theta ~ gamma(4, 1000);
    for (n in 1:N)
      y[n] ~ exponential(Theta);
  }
  generated quantities{
    real y_pred;
    y_pred = exponential_rng(Theta);
  }
";

data = Dict(
  "N" => 3,
  "y" => [100, 950, 450]
);

stanmodel = SampleModel("Generate_quantities", gq);

rc = stan_sample(stanmodel; data,
  use_cpp_chains=true, check_num_chains=false,
  num_cpp_chains=2, num_julia_chains=2)

if success(rc)
  # Convert to an MCMCChains.Chains object
  chns = read_samples(stanmodel)

  # Show the same output in DataFrame format
  df = StanSample.read_summary(stanmodel)

  available_chains(stanmodel) |> display
  println()

  gq_df = stan_generate_quantities(stanmodel, 1, "2_3")
end


using ReachabilityBenchmarks, MathematicalSystems, Reachability, Plots

include(@relpath "lorenz_model.jl")
include(@relpath "lorenz_specifications.jl")

S = lorenz_model()
X0, options = lorenz_specification()
options = Options(options)

# initial value problem
problem = InitialValueProblem(S, X0)

# reachability algorithm
algorithm_reach = TMJets(:abs_tol=>1e-27, :orderT=>10, :orderQ=>2,
                         :max_steps=>50_000)

# compute flowpipe
options[:mode] = "reach"
solution = solve(problem, options; op=algorithm_reach)

# project flowpipe to x₁ and x₃
solution.options[:plot_vars] = [1, 3]
solution_proj = project(solution)

# plot projection
plot(solution_proj)
savefig("lorenz")


using Regression
import Regression: solve, Options

# Data

k = 3
d = 5
n = 1000

W = randn(k, d+1)
X = randn(d, n)
U = (W[:,1:d] * X .+ W[:,d+1]) + 0.2 * randn(k, n)
y = vec(mapslices(indmax, U, 1))

# Solve

ret = solve(mlogisticreg(X, y, k; bias=1.0);
            reg=SqrL2Reg(1.0e-3),
            options=Options(verbosity=:iter, ftol=1.0e-5 * n))

println()

# Print results

W_est = ret.sol

@show size(W_est)
U_r = predict(MvAffinePred(d, k), W_est, X)
y_r = vec(mapslices(indmax, U_r, 1))

@printf("correct rate = %.4f\n", countnz(y .== y_r) / n)


using FdeSolver
using Test
using SpecialFunctions

@testset "FdeSolver.jl" begin

    tSpan = [0, 5]
    y0 = [1, 0.5, 0.3]
    β = [0.5, 0.2, 0.6]

    function F(t, n, β, y)

        F1 = 1 / sqrt(pi) * (((y[n, 2] - 0.5) * (y[n, 3] - 0.3))^(1 / 6) + t[n]^(1 / 2))
        F2 = gamma(2.2) * (y[n, 1] - 1)
        F3 = gamma(2.8) / gamma(2.2) * (y[n, 2] - 0.5)

        return [F1, F2, F3]

    end

    t, Yapp = FDEsolver(F, tSpan, y0, β, nothing)

    @test @isdefined(t)
    @test @isdefined(Yapp)

end


using Plots
using FFTW
using NonlinearSchrodinger

function T1A_K(ψᵢ, b)
    γ = 1
    p = 1
    q = 1

    n = 1
    μ = 0

    #ψ = ifft(im*b.ω.*fftshift(fft(ψᵢ)))
    #ψₒ = exp.(-γ*ψᵢ.^(n-1).*b.dx.*ψ).*ψᵢ
    #ψₒ = fftshift(fft(ψₒ))
    #ψₒ = ifft(exp.(-b.dx*(im*b.ω*μ .+ im*b.ω.^3*p .+ im*b.ω.^5*q)).*ψₒ)

    ψₒ = fftshift(fft(ψᵢ))
    ψₒ = ifft(exp.(-b.dx*(im*b.ω*μ .+ im*b.ω.^3*p .+ im*b.ω.^5*q)).*ψₒ)
    ψₒ = exp.(-γ*ψᵢ.^(n-1).*b.dx.*ifft(+im*b.ω.*fftshift(fft(ψₒ)))).*ψₒ
end

xᵣ = 0=>50
box = Box(xᵣ, 200, dx=5e-2, Nₜ = 512, n_periods =3)

ψ = zeros(box.Nₓ, box.Nₜ) .+ im

ψ[1, :] = 105/169*sech.(1/2/sqrt(13)*box.t).^4
for i = 1:box.Nₓ-1
   ψ[i+1, :] .=  T1A_K(ψ[i,:], box)
end
heatmap(abs.(ψ))


export G, C, M_P, K_B, SIGMA_T, SIGMA_SB,
M_SUN, YEAR_TO_SEC, SIGMA_E, H_PLANCK, ERG_TO_KEV, HZ_TO_KEV, UV_LOW_KEV, UV_HIGH_KEV

# all in cgs
const G = 6.6743e-8
const C = 29979245800.
const M_P = 1.67262192369e-24
const K_B = 1.380649e-16
const H_PLANCK = 6.62607015e-27
const SIGMA_T = 6.6524587321000005e-25
const SIGMA_E = SIGMA_T / M_P
const SIGMA_SB = 5.6703744191844314e-05
const M_SUN = 1.988409870698051e+33
const YEAR_TO_SEC = 31557600.0
const ERG_TO_KEV = 6.24150907e+08
const HZ_TO_KEV = 4.1356677e-18
const UV_LOW_KEV = 0.00387
const UV_HIGH_KEV = 0.06


using QuasiHamiltonianRTN
using QuasiHamiltonianRTN.Lattice1D
using Combinatorics
using SparseArrays
using LinearAlgebra

include("../src/SpecialUnitary.jl")
include("../src/Utilities.jl")
using Test

@testset "Utilities" begin
    @testset "random_density_matrix" for i = 1:10
        ρ = random_density_matrix(4)
        @test tr(ρ) ≈ 1
        @test ρ ≈ ρ'
        @test real(tr(ρ^2)) <= 1.
        @test all(eigvals(ρ) .>= 0)
    end
end

@testset "SU(N) generators" begin
    @testset "N = $N" for N = 20:21
        let λ = sun_generators(N)
            @testset "Traceless" begin
                @test mapreduce(A -> isapprox(tr(A), 0., atol=eps(Float64) * N), &, λ)
            end
            # Hermitian
            @testset "Hermitian" begin
                @test mapreduce(A -> A ≈ A', &, λ)
            end
            # Orthogonal
            @testset "Orthogonal" begin
                @test [tr(λ[i] * λ[j]) for i = 1:length(λ), j = 1:length(λ)] ≈ 2*I
             end
        end
    end
end

@testset "SU(N) structure constants" begin
    @testset "SU(2) gives the Levi-Civita tensor" begin
        @test reduce(&, [structure_constants(sun_generators(2))[i][j,k] == levicivita([i,j,k]) for i=1:3,j=1:3,k=1:3])
    end

    @testset "[λ[i], λ[j]] = i ∑ f[i][j,k] λ[k] for SU($n)" for n = 7:9
        let λ = sun_generators(n), f = structure_constants(λ)
            @test reduce(&,[commutator(λ[i], λ[j]) ≈ 2*im*sum([f[i][j,k]*λ[k] for k = 1:n^2-1])  for i = 1 : n^2-1, j = 1:n^2-1])
        end
    end
end

@testset "Bloch vector" begin
    @testset "Back and forth" begin
        for n = 2:10
            ρ = random_density_matrix(n)
            @test Matrix(density_operator(bloch_vector(ρ))) ≈ ρ
        end
    end
end

@testset "Unitary evolution check" begin
    let n = 4
        ρ = random_density_matrix(n)
        ρ1 = unitary_op(Lattice1D.H0(n), 1.)*ρ*unitary_op(Lattice1D.H0(n), 1.)'
        ρ2 = Matrix(density_operator(exp(- Matrix(igen(Lattice1D.H0(n)))) * bloch_vector(ρ)))
        @test ρ1 ≈ ρ2
    end
end

@testset "Noiseless evolution check" begin
    let tv = range(0, stop=5, length=10), n = 5, ρ0 = localized_state(n, 3)
        rtU = [bloch_vector(unitary_op(Lattice1D.H0(n), t) * ρ0 * unitary_op(Lattice1D.H0(n), t)') for t in tv]
        rtE = evolution(Lattice1D.Hamiltonian(n, .0), bloch_vector(ρ0), tv, γ=0.)
        @test hcat(rtU...) ≈ rtE

        Hq = quasiHamiltonian(Lattice1D.Hamiltonian(n, .0,), 0.)
        rtEHq = evolution(Hq, bloch_vector(ρ0), tv)

        @test rtEHq ≈ rtE

        rtEHqEK = evolution(Hq, bloch_vector(ρ0), tv; expmpkg=:Expokit)
        @test rtEHq ≈ rtEHqEK
    end
end

@testset "Noisy evolution" begin
    let t = 1., longt = 80, n = 5, ρ0 = localized_state(n, 3)
        H = Lattice1D.Hamiltonian(n, 1.)
        Hq = quasiHamiltonian(H, 1.)

        # Check that the two expm give similar results
        rtEHq = evolution(Hq, bloch_vector(ρ0), t)
        rtEHqEK = evolution(Hq, bloch_vector(ρ0), t; expmpkg=:Expokit)

        @test rtEHq ≈ rtEHqEK

        # Check that the stationary state is the maximally mixed one
        # (i.e. Bloch vector is zero)

        rtEHq = evolution(Hq, bloch_vector(ρ0), longt)
        rtEHqEK = evolution(Hq, bloch_vector(ρ0), longt; expmpkg=:Expokit)
        @test rtEHq ≈ zero(rtEHq) atol=1e-16
    end
end


include("../src/SFFM.jl")
using LinearAlgebra, Plots, JLD2, SparseArrays

T = [-1.0 1.0 0; 0.5 -1.0 0.5; 0.5 0.5 -1]
C = [2.0; -2.0; -1.0]

r = (
    r = function (x)
        [-2*(x.>-1) 2*(x.>-1) -1*(x.>-1)] # [ones(size(x)) ones(size(x))]#
    end,
    R = function (x)
        [-2*(x.>-1).*x 2*(x.>-1).*x -1*(x.>-1).*x] # [x x]
    end,
)

Bounds = [0 6; -Inf Inf]
model = SFFM.Model(T = T, C = C, r = r, Bounds = Bounds)

Δ = 2
Nodes = collect(Bounds[1,1]:Δ:Bounds[1,2])
NBases = 3
Basis = "lagrange"
mesh = SFFM.MakeMesh(model = model, Nodes = Nodes, NBases = NBases, Basis=Basis)

V = SFFM.vandermonde(NBases=NBases)
Matrices2 = SFFM.MakeMatrices2(model=model, mesh=mesh)
MatricesR = SFFM.MakeMatricesR(model=model, mesh=mesh)
MR = SparseArrays.spzeros(Float64, model.NPhases * mesh.TotalNBases, model.NPhases * mesh.TotalNBases)
Minv =
    SparseArrays.spzeros(Float64, model.NPhases * mesh.TotalNBases, model.NPhases * mesh.TotalNBases)
FGR = SparseArrays.spzeros(Float64, model.NPhases * mesh.TotalNBases, model.NPhases * mesh.TotalNBases)
for i in 1:model.NPhases
    idx = (1:mesh.TotalNBases) .+ (i-1)*mesh.TotalNBases
    MR[idx,idx] = MatricesR.Global.M[i]
    Minv[idx,idx] = Matrices2.Global.MInv
    FGR[idx,idx] = model.C[i]*(MatricesR.Global.F[i]+MatricesR.Global.G[i])
end

T = SparseArrays.kron(model.T, SparseArrays.sparse(LinearAlgebra.I,mesh.TotalNBases,mesh.TotalNBases))
DR = MR * T * Minv + FGR * Minv

All = SFFM.MakeAll(model = model, mesh = mesh, approxType = "interpolation")
Matrices = SFFM.MakeMatrices(model=model,mesh=mesh,probTransform=false)
MatricesR = SFFM.MakeMatricesR(model=model,mesh=mesh)
B = SFFM.MakeB(model=model,mesh=mesh,Matrices=Matrices,probTransform=false)
Dr = SFFM.MakeDR(
    Matrices=Matrices,
    MatricesR=MatricesR,
    model=model,
    mesh=mesh,
    B=B,
)

Db = All.R.R[3:end-1,3:end-1]*B.B[3:end-1,3:end-1]


########################################################################################################
## Details
# This file implements the MAP estimation proceedure for UQ inference problems.
#       Note, we convert the named tuple p.x into a vector for use in GalacticOptim.
#       For the conversion, we use TransformVariables.
#########################################################################################################

function MAP(pdist :: ArbitraryDistribution; solver = LBFGS(), verbose = false)

    # Define a Hamiltonian system
    x0 = inverse(pdist.t, pdist.x)
    n = length(x0)
    @model function demo()
        x ~ pdist.prior
        Turing.@addlogprob! pdist.distribution(x)
    end

    model = demo()
    map_estimate = optimize(model, Turing.MAP(), x0, solver)
    pdist.x = transform(pdist.t, map_estimate.values.array)
    if verbose
        println("Map Estimate ")
        show(stdout, "text/plain", map_estimate)
    end

    return pdist

end


# Tests for Radial DEA Models
@testset "AdditiveDEAModel" begin

    ## Test Additive DEA Models with FLS Book data
    # Test against results in R
    X = [5 13; 16 12; 16 26; 17 15; 18 14; 23 6; 25 10; 27 22; 37 14; 42 25; 5 17]
    Y = [12; 14; 25; 26; 8; 9; 27; 30; 31; 26; 12]

    # Additive CRS
    deaaddcrs1 = deaadd(X, Y, :Custom, rhoX = ones(size(X)), rhoY = ones(size(Y)), rts = :CRS)

    @test typeof(deaaddcrs1) == AdditiveDEAModel

    @test nobs(deaaddcrs1) == 11
    @test ninputs(deaaddcrs1) == 2
    @test noutputs(deaaddcrs1) == 1
    @test efficiency(deaaddcrs1) ≈ [0.0000000000;
                               10.76923077;
                               10.83783784;
                               0.0000000000;
                               22.15384615;
                               17.92307692;
                               0.0000000000;
                               12.07692308;
                               11.61379310;
                               35.000000000;
                               4.000000000]

    @test convert(Matrix, peers(deaaddcrs1)) ≈
    [ 1.0000000000  0  0 0.0000000000  0  0 0.0000000000  0  0   0   0
      0.0000000000  0  0 0.5384615385  0  0 0.0000000000  0  0   0   0
      0.1216216216  0  0 0.9054054054  0  0 0.0000000000  0  0   0   0
      0.0000000000  0  0 1.0000000000  0  0 0.0000000000  0  0   0   0
      0.0000000000  0  0 0.3076923077  0  0 0.0000000000  0  0   0   0
      0.0000000000  0  0 0.3461538462  0  0 0.0000000000  0  0   0   0
      0.0000000000  0  0 0.0000000000  0  0 1.0000000000  0  0   0   0
      0.0000000000  0  0 1.1538461538  0  0 0.0000000000  0  0   0   0
      0.0000000000  0  0 0.4689655172  0  0 0.6965517241  0  0   0   0
      0.0000000000  0  0 1.0000000000  0  0 0.0000000000  0  0   0   0
      1.0000000000  0  0 0.0000000000  0  0 0.0000000000  0  0   0   0]

    # Additive VRS model
    deaaddvrs1 = deaadd(X, Y, :Custom, rhoX = ones(size(X)), rhoY = ones(size(Y)), rts = :VRS)

    @test nobs(deaaddvrs1) == 11
    @test ninputs(deaaddvrs1) == 2
    @test noutputs(deaaddvrs1) == 1
    @test efficiency(deaaddvrs1) ≈ [0.0000000000;
                               7.333333333;
                               0.0000000000;
                               0.0000000000;
                               18.000000000;
                               0.0000000000;
                               0.0000000000;
                               0.0000000000;
                               0.0000000000;
                               35.000000000;
                               4.000000000]

    @test convert(Matrix, peers(deaaddvrs1)) ≈
    [1.000000000  0  0  0  0  0 0.00000000000  0  0   0   0;
     0.6666666667 0  0  0  0  0 0.3333333333   0  0   0   0;
     0.000000000  0  1  0  0  0 0.00000000000  0  0   0   0;
     0.000000000  0  0  1  0  0 0.00000000000  0  0   0   0;
     1.000000000  0  0  0  0  0 0.00000000000  0  0   0   0;
     0.000000000  0  0  0  0  1 0.00000000000  0  0   0   0;
     0.000000000  0  0  0  0  0 1.00000000000  0  0   0   0;
     0.000000000  0  0  0  0  0 0.00000000000  1  0   0   0;
     0.000000000  0  0  0  0  0 0.00000000000  0  1   0   0;
     0.000000000  0  0  1  0  0 0.00000000000  0  0   0   0;
     1.000000000  0  0  0  0  0 0.00000000000  0  0   0   0]

    # Check weights equal to one equals default model
    deaadddefaultcrs = deaadd(X, Y, rts = :CRS)
    @test efficiency(deaaddcrs1) ≈ efficiency(deaadddefaultcrs)

    deaadddefaultvrs = deaadd(X, Y)
    @test efficiency(deaaddvrs1) ≈ efficiency(deaadddefaultvrs)

    # Test model :Ones equals model with all weights equal to 1
    deaaddonescrs = deaadd(X, Y, :Ones, rts = :CRS)
    @test deaaddonescrs.weights == :Ones
    @test efficiency(deaaddonescrs) ≈ efficiency(deaadddefaultcrs)

    @test efficiency(deaadd(targets(deaaddonescrs, :X), targets(deaaddonescrs, :Y), :Ones)) ≈ zeros(11) atol=1e-13

    deaaddonesvrs = deaadd(X, Y, :Ones)
    @test deaaddonesvrs.weights == :Ones
    @test efficiency(deaaddonesvrs) ≈ efficiency(deaadddefaultvrs)

    @test efficiency(deaadd(targets(deaaddonesvrs, :X), targets(deaaddonesvrs, :Y), :Ones, rts = :VRS)) ≈ zeros(11) atol=1e-12

    # MIP CRS
    deaaddmipcrs = deaadd(X, Y, :MIP, rts = :CRS)
    @test deaaddmipcrs.weights == :MIP
    @test efficiency(deaaddmipcrs) ≈ [0.0000000000;
                               0.7577160494;
                               0.4168399168;
                               0.0000000000;
                               2.2219512195;
                               1.1478260870;
                               0.0000000000;
                               0.4867909868;
                               0.4041184041;
                               1.0726153846;
                               0.2352941176]
    @test deaaddmipcrs.slackX ≈ [0 0;
                                 3.037037037 6.814814815;
                                 0 10.837837838;
                                 0 0;
                                 0 0;
                                 8.000000000  0;
                                 0 0;
                                 7.384615385 4.692307692;
                                 8.296296296 2.518518519;
                                 0.000000000  8.200000000;
                                 0 4.000000000]

    @test deaaddmipcrs.slackY ≈ [0;
                                  0;
                                  0;
                                  0;
                                  17.77560976 ;
                                  7.20000000;
                                  0;
                                  0;
                                  0;
                                  19.36000000;
                                  0]

    # MIP VRS
    deaaddmipvrs = deaadd(X, Y, :MIP, rts = :VRS)
    @test deaaddmipvrs.weights == :MIP
    @test efficiency(deaaddmipvrs) ≈ [0.0000000000;
                               0.5075187970;
                               0.0000000000;
                               0.0000000000;
                               2.2039473684;
                               0.0000000000;
                               0.0000000000;
                               0.0000000000;
                               0.0000000000;
                               1.0432234432;
                               0.2352941176]
    @test deaaddmipvrs.slackX ≈ [0 0;
                                 0 0;
                                 0 0;
                                 0 0;
                                 0 0;
                                 0 0;
                                 0 0;
                                 0 0;
                                 0 0;
                                 17 15;
                                 0 4]
    @test deaaddmipvrs.slackY ≈ [0;
                                 7.105263158;
                                 0;
                                 0;
                                 17.631578947;
                                 0;
                                 0;
                                 0;
                                 0;
                                 1.000000000;
                                 0]

    # Normalized CRS
    deaaddnormalizedcrs = deaadd(X, Y, :Normalized, rts = :CRS)
    @test deaaddnormalizedcrs.weights == :Normalized
    @test efficiency(deaaddnormalizedcrs) ≈ [0.0000000000;
                                 1.3569256615;
                                 1.7407259078;
                                 0.0000000000;
                                 2.6877810368;
                                 1.6953153107;
                                 0.0000000000;
                                 1.6540884810;
                                 1.1212042237;
                                 4.0172855145;
                                 0.6424624298]

    @test deaaddnormalizedcrs.slackX ≈ [0.000000000  0.000000000;
                                 3.037037037  6.814814815;
                                 0.000000000 10.837837838;
                                 0.000000000  0.000000000;
                                 10.592592593 11.037037037;
                                 14.666666667  2.666666667;
                                 0.000000000  0.000000000;
                                 0.000000000 10.298429319;
                                 8.296296296  2.518518519;
                                 17.925925926 15.370370370;
                                 0.000000000  4.000000000]

    @test deaaddnormalizedcrs.slackY ≈ [0;
                                  0;
                                  0;
                                  0;
                                  0 ;
                                  0;
                                  0;
                                  0;
                                  0;
                                  0;
                                  0]

    # Normalized VRS
    deaaddnormalizedvrs = deaadd(X, Y, :Normalized, rts = :VRS)
    @test deaaddnormalizedvrs.weights == :Normalized
    @test efficiency(deaaddnormalizedvrs) ≈ [0.0000000000;
                                 0.8049248943;
                                 0.0000000000;
                                 0.0000000000;
                                 2.0149704908;
                                 0.0000000000;
                                 0.0000000000;
                                 0.0000000000;
                                 0.0000000000;
                                 3.9898943952;
                                 0.6424624298]

    @test deaaddnormalizedvrs.slackX ≈ [0  0.00;
                                 0  0.65;
                                 0  0.00;
                                 0  0.00;
                                 0  2.95;
                                 0  0.00;
                                 0  0.00;
                                 0  0.00;
                                 0  0.00;
                                 17 15.00;
                                 0  4.00]

    @test deaaddnormalizedvrs.slackY ≈ [0.00;
                                6.25;
                                0.00;
                                0.00;
                                13.75;
                                0.00;
                                0.00;
                                0.00;
                                0.00;
                                1.00;
                                0.00]

    # RAM CRS
    deaaddramcrs = deaadd(X, Y, :RAM, rts = :CRS)
    @test deaaddramcrs.weights == :RAM
    @test efficiency(deaaddramcrs) ≈ [0.0000000000;
                                0.14094094094;
                                0.18063063063;
                                0.0000000000;
                                0.27937937938;
                                0.17657657658;
                                0.0000000000;
                                0.17164048866;
                                0.11671671672;
                                0.41766766767;
                                0.06666666667]

    @test deaaddramcrs.slackX ≈ [ 0.000000000  0.000000000;
                                3.037037037  6.814814815;
                                0.000000000  10.837837838;
                                0.000000000  0.000000000;
                                10.592592593 11.037037037;
                                14.666666667 2.666666667;
                                0.000000000  0.000000000;
                                0.000000000  10.298429319;
                                8.296296296  2.518518519;
                                17.925925926 15.370370370;
                                0.000000000  4.000000000]

    @test deaaddramcrs.slackY ≈ [0;
                                   0;
                                   0;
                                   0;
                                   0 ;
                                   0;
                                   0;
                                   0;
                                   0;
                                   0;
                                   0]

    # RAM VRS
    deaaddramvrs = deaadd(X, Y, :RAM, rts = :VRS)
    @test deaaddramvrs.weights == :RAM
    @test efficiency(deaaddramvrs) ≈ [0.0000000000;
                              0.10297482838;
                              0.0000000000;
                              0.0000000000;
                              0.25553012967;
                              0.0000000000;
                              0.0000000000;
                              0.0000000000;
                              0.0000000000;
                              0.41764590678 ;
                              0.06666666667]

    @test deaaddramvrs.slackX ≈ [0 0;
                                0 0;
                                0 0;
                                0 0;
                                0 0;
                                0 0;
                                0 0;
                                0 0;
                                0 0;
                                17 15;
                                0 4]
    @test deaaddramvrs.slackY ≈ [0;
                                7.105263158;
                                0;
                                0;
                                17.631578947;
                                0;
                                0;
                                0;
                                0;
                                1.000000000;
                                0]

    # BAM CRS
    deaaddbamcrs = deaadd(X, Y, :BAM, rts = :CRS)
    @test deaaddbamcrs.weights == :BAM
    @test efficiency(deaaddbamcrs) ≈ [0.0000000000;
                                0.4578892372;
                                0.3051750381;
                                0.0000000000;
                                0.6732181854;
                                0.3239568683;
                                0.0000000000;
                                0.5255235602;
                                0.1913580247;
                                0.6902867780;
                                0.1212121212]

    @test deaaddbamcrs.slackX ≈ [0.000000000  0.000000000;
                                4.110344828  6.000000000;
                                0.000000000  0.000000000;
                                0.000000000  0.000000000;
                                13.000000000  8.000000000;
                                17.493670886  0.000000000;
                                0.000000000  0.000000000;
                                0.000000000  9.225130890;
                                8.296296296  2.518518519;
                                13.296296296 13.518518519;
                                0.000000000  4.000000000]
    @test deaaddbamcrs.slackY ≈ [0;
                                0;
                                5.4931506849;
                                0;
                                0.4520547945 ;
                                0;
                                0;
                                1;
                                0;
                                5;
                                0]

    # BAM VRS
    deaaddbamvrs = deaadd(X, Y, :BAM, rts = :VRS)
    @test deaaddbamvrs.weights == :BAM
    @test efficiency(deaaddbamvrs) ≈ [0.0000000000;
                              0.1998936736;
                              0.0000000000;
                              0.0000000000;
                              0.4329710145;
                              0.0000000000;
                              0.0000000000;
                              0.0000000000;
                              0.0000000000;
                              0.5713608345;
                              0.1212121212]

    @test deaaddbamvrs.slackX ≈ [0 0;
                                6.596491228 0;
                                0 0;
                                0 0;
                                13.000000000 1;
                                0 0;
                                0 0;
                                0 0;
                                0 0;
                                5 11;
                                0 4]
    @test deaaddbamvrs.slackY ≈ [0;
                                0;
                                0;
                                0;
                                4;
                                0;
                                0;
                                0;
                                0;
                                5;
                                0]

    # Test model with Custom weights
    deaddcustomcrs = deaadd(X, Y, rhoX = 1 ./ X, rhoY = 1 ./ Y, rts = :CRS)
    @test deaddcustomcrs.weights == :Custom
    @test efficiency(deaddcustomcrs) ≈ efficiency(deaaddmipcrs)

    deaddcustomvrs = deaadd(X, Y, rhoX = 1 ./ X, rhoY = 1 ./ Y, rts = :VRS)
    @test deaddcustomvrs.weights == :Custom
    @test efficiency(deaddcustomvrs) ≈ efficiency(deaaddmipvrs)

    # Test that slacks are zero when weights are zero
    @test all(slacks(deaadd(X, Y, rhoX = zeros(size(X)), rhoY = ones(size(Y))) , :X) .== 0)
    @test all(slacks(deaadd(X, Y, rhoX = ones(size(X)) , rhoY = zeros(size(Y))), :Y) .== 0)

    ## Test if one-by-one DEA using evaluation and reference sets match initial results
    deaadddefaultcrs_ref_eff = zeros(size(X, 1))
    deaadddefaultvrs_ref_eff = zeros(size(X, 1))

    deaaddcustomcrs_ref_eff = zeros(size(X, 1))
    deaaddcustomvrs_ref_eff = zeros(size(X, 1))

    deaaddonescrs_ref_eff = zeros(size(X, 1))
    deaaddonesvrs_ref_eff = zeros(size(X, 1))

    deaaddmipcrs_ref_eff = zeros(size(X, 1))
    deaaddmipvrs_ref_eff = zeros(size(X, 1))

    Xref = X[:,:]
    Yref = Y[:,:]

    for i = 1:size(X, 1)
        Xeval = X[i:i,:]
        Xeval = Xeval[:,:]
        Yeval = Y[i:i,:]
        Yeval = Yeval[:,:]

        deaadddefaultcrs_ref_eff[i] = efficiency(deaadd(Xeval, Yeval, rts = :CRS, Xref = Xref, Yref = Yref))[1]
        deaadddefaultvrs_ref_eff[i] = efficiency(deaadd(Xeval, Yeval, rts = :VRS, Xref = Xref, Yref = Yref))[1]

        deaaddcustomcrs_ref_eff[i] = efficiency(deaadd(Xeval, Yeval, :Custom, rts = :CRS, rhoX = 1 ./ Xeval, rhoY = 1 ./ Yeval, Xref = Xref, Yref = Yref))[1]
        deaaddcustomvrs_ref_eff[i] = efficiency(deaadd(Xeval, Yeval, :Custom, rts = :VRS, rhoX = 1 ./ Xeval, rhoY = 1 ./ Yeval, Xref = Xref, Yref = Yref))[1]

        deaaddonescrs_ref_eff[i] = efficiency(deaadd(Xeval, Yeval, :Ones, rts = :CRS, Xref = Xref, Yref = Yref))[1]
        deaaddonesvrs_ref_eff[i] = efficiency(deaadd(Xeval, Yeval, :Ones, rts = :VRS, Xref = Xref, Yref = Yref))[1]

        deaaddmipcrs_ref_eff[i] = efficiency(deaadd(Xeval, Yeval, :MIP, rts = :CRS, Xref = Xref, Yref = Yref))[1]
        deaaddmipvrs_ref_eff[i] = efficiency(deaadd(Xeval, Yeval, :MIP, rts = :VRS, Xref = Xref, Yref = Yref))[1]

    end

    @test deaadddefaultcrs_ref_eff ≈ efficiency(deaadddefaultcrs)
    @test deaadddefaultvrs_ref_eff ≈ efficiency(deaadddefaultvrs)

    @test deaaddcustomcrs_ref_eff ≈ efficiency(deaaddmipcrs)
    @test deaaddcustomvrs_ref_eff ≈ efficiency(deaaddmipvrs)

    @test deaaddonescrs_ref_eff ≈ efficiency(deaaddcrs1)
    @test deaaddonesvrs_ref_eff ≈ efficiency(deaaddvrs1)

    @test deaaddmipcrs_ref_eff ≈ efficiency(deaaddmipcrs)
    @test deaaddmipvrs_ref_eff ≈ efficiency(deaaddmipvrs)

    # Print
    show(IOBuffer(), deaaddcrs1)

    # Test errors
    @test_throws DimensionMismatch deaadd([1; 2 ; 3], [4 ; 5], :Ones) #  Different number of observations
    @test_throws DimensionMismatch deaadd([1; 2], [4 ; 5], Xref = [1; 2; 3; 4], :Ones) # Different number of observations in reference sets
    @test_throws DimensionMismatch deaadd([1 1; 2 2], [4 4; 5 5], Xref = [1 1 1; 2 2 2], :Ones) # Different number of inputs
    @test_throws DimensionMismatch deaadd([1 1; 2 2], [4 4; 5 5], Yref = [4 4 4; 5 5 5], :Ones) # Different number of inputs
    @test_throws ArgumentError deaadd([1; 2; 3], [4; 5; 6], :Ones, rts = :Error) # Invalid returns to scale
    @test_throws ArgumentError deaadd([1; 2; 3], [4; 5; 6], :Error) # Invalid model
    @test_throws ArgumentError deaadd([1; 2; 3], [4; 5; 6], :Ones, orient = :Error) # Invalid orientation
    @test_throws ArgumentError deaadd([1; 2; 3], [4; 5; 6], :Custom, rhoX = [1; 1; 1], rhoY = [1; 1; 1], orient = :Error) # Invalid orientation with custom weights

    @test_throws DimensionMismatch deaadd([1; 2 ; 3], [4 ; 5]) #  Different number of observations
    @test_throws DimensionMismatch deaadd([1; 2], [4 ; 5], Xref = [1; 2; 3; 4]) # Different number of observations in reference sets
    @test_throws DimensionMismatch deaadd([1 1; 2 2], [4 4; 5 5], Xref = [1 1 1; 2 2 2]) # Different number of inputs
    @test_throws DimensionMismatch deaadd([1 1; 2 2], [4 4; 5 5], Yref = [4 4 4; 5 5 5]) # Different number of inputs
    @test_throws ArgumentError deaadd([1; 2; 3], [4; 5; 6], rts = :Error) # Invalid returns to scale
    @test_throws DimensionMismatch deaadd([1; 2; 3], [4; 5; 6], :Custom, rhoX = [1; 2; 3; 4], rhoY = [1; 1; 1]) # Different size of weights
    @test_throws DimensionMismatch deaadd([1; 2; 3], [4; 5; 6], :Custom, rhoX = [1; 1; 1], rhoY = [4; 5; 6; 7]) # Different size of weights

    @test_throws ArgumentError deaadd([1; 2; 3], [4; 5; 6], :Ones, rhoX = [1; 1; 1]) # Weights not allowed if model != :Custom

    # ------------------
    # Orientation Tests
    # ------------------

    X = [1; 2; 3; 2; 4]
    Y = [2; 3; 4; 1; 3]

    # Test default is graph
    @test efficiency(deaadd(X, Y)) == efficiency(deaadd(X, Y, orient = :Graph))

    # Graph orientaiton
    deaaddgraph = deaadd(X, Y, orient = :Graph)
    @test efficiency(deaaddgraph) ≈ [0; 0; 0; 2.0; 2.0]
    @test slacks(deaaddgraph, :X) ≈ [0; 0; 0; 0; 1.0]
    @test slacks(deaaddgraph, :Y) ≈ [0; 0; 0; 2.0; 1.0]

    # Input orientation
    deaaddinput = deaadd(X, Y, orient = :Input)
    @test efficiency(deaaddinput) ≈ [0; 0; 0; 1.0; 2.0]
    @test slacks(deaaddinput, :X) ≈ [0; 0; 0; 1.0; 2.0]
    @test slacks(deaaddinput, :Y) ≈ [0; 0; 0; 1.0; 0.0]

    # Output orientation
    deaaddoutput = deaadd(X, Y, orient = :Output)
    @test efficiency(deaaddoutput) ≈ [0; 0; 0; 2.0; 1.0]
    @test slacks(deaaddoutput, :X) ≈ [0; 0; 0; 0.0; 1.0]
    @test slacks(deaaddoutput, :Y) ≈ [0; 0; 0; 2.0; 1.0]

    # Input orientation with Weak disposability in outputs
    deaaddinputweak = deaadd(X, Y, orient = :Input, disposY = :Weak)
    @test efficiency(deaaddinputweak) ≈ [0; 0; 0; 0.0; 2.0]
    @test slacks(deaaddinputweak, :X) ≈ [0; 0; 0; 0.0; 2.0]
    @test slacks(deaaddinputweak, :Y) ≈ [0; 0; 0; 0.0; 0.0]

    # Output orientation with Weak disposability in inputs
    deaaddoutputweak = deaadd(X, Y, orient = :Output, disposX = :Weak)
    @test efficiency(deaaddoutputweak) ≈ [0; 0; 0; 2.0; 0.0]
    @test slacks(deaaddoutputweak, :X) ≈ [0; 0; 0; 0.0; 0.0]
    @test slacks(deaaddoutputweak, :Y) ≈ [0; 0; 0; 2.0; 0.0]

    # Test errors with orientation and disposability
    @test_throws ArgumentError deaadd([1; 2 ; 3], [4 ; 5; 6], orient = :Graph, disposX = :Error)  # Invalid inputs disposability
    @test_throws ArgumentError deaadd([1; 2 ; 3], [4 ; 5; 6], orient = :Graph, disposY = :Error)  # Invalid output disposability
    @test_throws ArgumentError deaadd([1; 2 ; 3], [4 ; 5; 6], orient = :Graph, disposX = :Weak)  # Weak disposability not possible in graph oriented model
    @test_throws ArgumentError deaadd([1; 2 ; 3], [4 ; 5; 6], orient = :Graph, disposX = :Weak)  # Weak disposability not possible in graph oriented model

    @test_throws ArgumentError deaadd([1; 2 ; 3], [4 ; 5; 6], orient = :Input, disposX = :Weak)  # Weak input disposability not possible in input oriented model

    @test_throws ArgumentError deaadd([1; 2 ; 3], [4 ; 5; 6], orient = :Output, disposY = :Weak)  # Weak output disposability not possible in output oriented model

    # ------------------
    # Test Vector and Matrix inputs and outputs
    # ------------------
    # Tests against results in R

    # Inputs is Matrix, Outputs is Vector
    X = [2 2; 1 4; 4 1; 4 3; 5 5; 6 1; 2 5; 1.6	8]
    Y = [1; 1; 1; 1; 1; 1; 1; 1]

    @test efficiency(deaadd(X, Y, :Ones)) ≈ [0.0; 0.0; 0.0; 3.0; 6.0; 2.0; 3.0; 5.2]

    # Inputs is Vector, Output is Matrix
    X = [1; 1; 1; 1; 1; 1; 1; 1]
    Y = [7 7; 4 8; 8 4; 3 5; 3 3; 8 2; 6 4; 1.5 5]

    @test efficiency(deaadd(X, Y, :Ones)) ≈ [0.0; 0.0; 0.0; 6.0; 8.0; 2.0; 4.0; 7.5]

    # Inputs is Vector, Output is Vector
    X = [2; 4; 8; 12; 6; 14; 14; 9.412]
    Y = [1; 5; 8; 9; 3; 7; 9; 2.353]

    @test efficiency(deaadd(X, Y, :Ones)) ≈ [0; 0; 0; 0;4; 7.333333333; 2; 8.059]

    # ------------------
    # RAM and BAM with orientation
    # ------------------

    X = [1; 2; 3; 2; 4]
    Y = [2; 3; 4; 1; 3]

    @test efficiency(deaadd(X, Y, :RAM, orient = :Graph)) ≈ [0.0; 0.0; 0.0; 1/3; 1/3]
    @test efficiency(deaadd(X, Y, :RAM, orient = :Input)) ≈ [0.0; 0.0; 0.0; 1/3; 2/3]
    @test efficiency(deaadd(X, Y, :RAM, orient = :Output)) ≈ [0.0; 0.0; 0.0; 2/3; 1/3]

    @test efficiency(deaadd(X, Y, :BAM, orient = :Graph)) ≈ [0.0; 0.0; 0.0; 2/3; 2/3]
    @test efficiency(deaadd(X, Y, :BAM, orient = :Input)) ≈ [0.0; 0.0; 0.0; 1; 2/3]
    @test efficiency(deaadd(X, Y, :BAM, orient = :Output)) ≈ [0.0; 0.0; 0.0; 2/3; 1]

end


# This file includes an example script to plot the basis functions of PSK modulator.

using DigitalCommunications
using Plots

# Simulation parameters
k = 2
M = 2^k
nsymbols = Int(1e6)
nbits = k * nsymbols
ebno = collect(0 : 10)          # Snr per bit
esno = ebno .+ 10 * log10(k)    # Snr ber symbol

# Communcation system components
gen = BitGenerator(nbits)
modulator = Modulator(FSK(M))

# Plot the basis of the modulator
t = 0 : modulator.tsample : modulator.pulse.duration
plt = plot(layout=(constelsize(modulator.scheme),1))
for (i, base) in enumerate(basis(modulator))
    plot!(t, base.(t), subplot=i)
end
display(plt)


#####
##### three-dimensional fluxes
#####

"function euler_fluxes_3D(rhoL,uL,vL,wL,betaL,rhologL,betalogL,
                         rhoR,uR,vR,wR,betaR,rhologR,betalogR)"
function euler_fluxes_3D(rhoL,uL,vL,wL,betaL,rhologL,betalogL,
                         rhoR,uR,vR,wR,betaR,rhologR,betalogR)

    rholog = logmean.(rhoL,rhoR,rhologL,rhologR)
    betalog = logmean.(betaL,betaR,betalogL,betalogR)

    # arithmetic avgs
    rhoavg = (@. .5*(rhoL+rhoR))
    uavg   = (@. .5*(uL+uR))
    vavg   = (@. .5*(vL+vR))
    wavg   = (@. .5*(wL+wR))

    unorm = (@. uL*uR + vL*vR + wL*wR)
    pa    = (@. rhoavg/(betaL+betaR))
    E_plus_p  = (@. rholog/(2*(γ-1)*betalog) + pa + .5*rholog*unorm)

    FxS1 = (@. rholog*uavg)
    FxS2 = (@. FxS1*uavg + pa)
    FxS3 = (@. FxS1*vavg) # rho * u * v
    FxS4 = (@. FxS1*wavg) # rho * u * w
    FxS5 = (@. E_plus_p*uavg)

    FyS1 = (@. rholog*vavg)
    FyS2 = (@. FxS3) # rho * u * v
    FyS3 = (@. FyS1*vavg + pa)
    FyS4 = (@. FyS1*wavg) # rho * v * w
    FyS5 = (@. E_plus_p*vavg)

    FzS1 = (@. rholog*wavg)
    FzS2 = (@. FxS4) # rho * u * w
    FzS3 = (@. FyS4) # rho * v * w
    FzS4 = (@. FzS1*wavg + pa) # rho * w^2 + p
    FzS5 = (@. E_plus_p*wavg)

    Fx = (FxS1,FxS2,FxS3,FxS4,FxS5)
    Fy = (FyS1,FyS2,FyS3,FyS4,FyS5)
    Fz = (FzS1,FzS2,FzS3,FzS4,FzS5)
    return Fx,Fy,Fz
end

#####
##### shim functions
#####

# dispatch to n-dimensional constitutive routines, with optional entropy scaling
function primitive_to_conservative(rho,u,v,w,p)
   rho,rhoU,E = primitive_to_conservative_nd(rho,(u,v,w),p)
   return rho,rhoU...,E
end
function v_ufun(rho,rhou,rhov,rhow,E)
    v1,vU,vE = v_ufun_nd(rho,(rhou,rhov,rhow),E)
    return scale_entropy_output(v1,vU...,vE)
end
function u_vfun(v1,vU1,vU2,vU3,vE)
    v1,vU1,vU2,vU3,vE = scale_entropy_input(v1,vU1,vU2,vU3,vE)
    rho,rhoU,E = u_vfun_nd(v1,(vU1,vU2,vU3),vE)
    return rho,rhoU...,E
end
function conservative_to_primitive_beta(rho,rhou,rhov,rhow,E)
    rho,U,beta = conservative_to_primitive_beta_nd(rho,(rhou,rhov,rhow),E)
    return rho,U...,beta
end

Sfun(rho,rhou,rhov,rhow,E) = Sfun_nd(rho,(rhou,rhov,rhow),E)
pfun(rho,rhou,rhov,rhow,E) = pfun_nd(rho,(rhou,rhov,rhow),E)
betafun(rho,rhou,rhov,rhow,E) = betafun_nd(rho,(rhou,rhov,rhow),E)

"function euler_fluxes_3D(rhoL,uL,vL,wL,betaL,rhoR,uR,vR,wR,betaR)"
function euler_fluxes(rhoL,uL,vL,wL,betaL,rhoR,uR,vR,wR,betaR)
    rhologL,betalogL,rhologR,betalogR = map(x->log.(x),(rhoL,betaL,rhoR,betaR))
    return euler_fluxes_3D(rhoL,uL,vL,wL,betaL,rhologL,betalogL,
                           rhoR,uR,vR,wR,betaR,rhologR,betalogR)
end
euler_fluxes(rhoL,uL,vL,wL,betaL,rhologL,betalogL,
             rhoR,uR,vR,wR,betaR,rhologR,betalogR) =
     euler_fluxes_3D(rhoL,uL,vL,wL,betaL,rhologL,betalogL,
                     rhoR,uR,vR,wR,betaR,rhologR,betalogR)


# Benders Algorithm for MILP with Sub and Ray Problems
# Version: 5.0
# Author: Edward J. Xu, edxu96@outlook.com
# Date: March 27th, 2019
module BendersMilp_EDXU
    export BendersMilp
    using JuMP
    using GLPKMathProgInterface
    using PrettyTables
    function BendersMilp(; n_x, n_y, vec_min_y, vec_max_y, vec_c, vec_f, vec_b,
        mat_a, mat_b, epsilon, timesIterationMax)
        println("-------------------------------------------------------------------------\n",
                "------------------------ 1/4. Begin Optimization ------------------------\n",
                "-------------------------------------------------------------------------\n")
        # Define Master problem
        n_constraint = length(mat_a[:, 1])
        model_mas = Model(solver = GLPKSolverMIP())
        @variable(model_mas, q)
        @variable(model_mas, vec_y[1: n_y], Int)
        @objective(model_mas, Min, (transpose(vec_f) * vec_y + q)[1])
        @constraint(model_mas, vec_y[1: n_y] .<= vec_max_y)
        @constraint(model_mas, vec_y[1: n_y] .>= vec_min_y)

        function solve_master(vec_uBar, opt_cut::Bool)
            if opt_cut
                @constraint(model_mas, (transpose(vec_uBar) * (vec_b - mat_b * vec_y))[1] <= q)
            else  # Add feasible cut Constraints
                @constraint(model_mas, (transpose(vec_uBar) * (vec_b - mat_b * vec_y))[1] <= 0)
            end
            @constraint(model_mas, (transpose(vec_uBar) * (vec_b - mat_b * vec_y))[1] <= q)
            solve(model_mas)
            vec_result_y = getvalue(vec_y)
            return getobjectivevalue(model_mas)
        end

        function solve_sub(vec_uBar, vec_yBar, n_constraint, vec_b, mat_b, mat_a, vec_c)
            model_sub = Model(solver = GLPKSolverLP())
            @variable(model_sub, vec_u[1: n_constraint] >= 0)
            @objective(model_sub, Max, (transpose(vec_b - mat_b * vec_yBar) * vec_u)[1])
            constraintsForDual = @constraint(model_sub, transpose(mat_a) * vec_u .<= vec_c)
            solution_sub = solve(model_sub)
            print("------------------------------ Sub Problem ------------------------------\n")  # , model_sub)
            vec_uBar = getvalue(vec_u)
            if solution_sub == :Optimal
                vec_result_x = zeros(length(vec_c))
                vec_result_x = getdual(constraintsForDual)
                return (true, getobjectivevalue(model_sub), vec_uBar, vec_result_x)
            end
            if solution_sub == :Unbounded
                print("Not solved to optimality because feasible set is unbounded.\n")
                return (false, getobjectivevalue(model_sub), vec_uBar, repeat([NaN], length(vec_c)))
            end
        end

        function solve_ray(vec_uBar, vec_yBar, n_constraint, vec_b, mat_b, mat_a)
            # model_ray = Model(solver = GurobiSolver())
            model_ray = Model(solver = GLPKSolverLP())
            @variable(model_ray, vec_u[1: n_constraint] >= 0)
            @objective(model_ray, Max, 1)
            @constraint(model_ray, (transpose(vec_b - mat_b * vec_yBar) * vec_u)[1] == 1)
            @constraint(model_ray, transpose(mat_a) * vec_u .<= 0)
            solve(model_ray)
            print("------------------------------ Ray Problem ------------------------------\n")  # , model_ray)
            vec_uBar = getvalue(vec_u)
            obj_ray = getobjectivevalue(model_ray)
            return (obj_ray, vec_uBar)
        end

        # Begin Calculation --------------------------------------------------------------------------------------------
        let
            boundUp = Inf
            boundLow = - Inf
            epsilon = 0
            # initial value of master variables
            vec_uBar = zeros(n_constraint, 1)
            vec_yBar = zeros(n_y, 1)
            vec_result_x = length(n_x)
            dict_obj_mas = Dict()
            dict_q = Dict()
            dict_obj_sub = Dict()
            dict_obj_ray = Dict()
            dict_boundUp = Dict()
            dict_boundLow = Dict()
            obj_sub = 0
            timesIteration = 1
            # Must make sure "result_q == obj_sub" in the final iteration
            # while ((boundUp - boundLow > epsilon) && (timesIteration <= timesIterationMax))  !!!
            while ((!((boundUp - boundLow <= epsilon) && ((result_q == obj_sub)))) &&
                (timesIteration <= timesIterationMax))
                (bool_solutionSubModel, obj_sub, vec_uBar, vec_result_x) = solve_sub(vec_uBar, vec_yBar, n_constraint,
                                                                                vec_b, mat_b, mat_a, vec_c)
                if bool_solutionSubModel
                    boundUp = min(boundUp, obj_sub + (transpose(vec_f) * vec_yBar)[1])
                else
                    (obj_ray, vec_uBar) = solve_ray(vec_uBar, vec_yBar, n_constraint, vec_b, mat_b, mat_a)
                end
                obj_mas = solve_master(vec_uBar, bool_solutionSubModel)
                vec_yBar = getvalue(vec_y)
                boundLow = max(boundLow, obj_mas)
                dict_boundUp[timesIteration] = boundUp
                dict_boundLow[timesIteration] = boundLow
                if bool_solutionSubModel
                    dict_obj_mas[timesIteration] = obj_mas
                    dict_obj_sub[timesIteration] = obj_sub
                    result_q = getvalue(q)
                    dict_q[timesIteration] = result_q
                    println("------------------ Result in $(timesIteration)-th Iteration with Sub ",
                            "-------------------\n", "boundUp: $(round(boundUp, digits = 5)), ",
                            "boundLow: $(round(boundLow, digits = 5)), obj_mas: $(round(obj_mas, digits = 5)), ",
                            "q: $result_q, obj_sub: $(round(obj_sub, digits = 5)).")
                else
                    dict_obj_mas[timesIteration] = obj_mas
                    dict_obj_ray[timesIteration] = obj_ray
                    result_q = getvalue(q)
                    dict_q[timesIteration] = result_q
                    println("------------------ Result in $(timesIteration)-th Iteration with Ray ",
                            "-------------------\n", "boundUp: $(round(boundUp, digits = 5)), ",
                            "boundLow: $(round(boundLow, digits = 5)), obj_mas: $(round(obj_mas, digits = 5)), ",
                            "q: $result_q, obj_ray: $(round(obj_ray, digits = 5)).")
                end
                timesIteration += 1
            end  # -----------------------------------------------------------------------------------------------------
            println("obj_mas: $(getobjectivevalue(model_mas))")
            println("----------------------------- Master Problem ----------------------------\n")
            println(model_mas)
            println("-------------------------------------------------------------------------\n",
                    "------------------------------ 2/4. Result ------------------------------\n",
                    "-------------------------------------------------------------------------")
            println("boundUp: $(round(boundUp, digits = 5)), boundLow: $(round(boundLow, digits = 5)), ",
                    "difference: $(round(boundUp - boundLow, digits = 5))")
            println("vec_x: $vec_result_x")
            vec_result_y = getvalue(vec_y)
            result_q = getvalue(q)
            println("vec_y: $vec_result_y")
            println("result_q: $result_q")
            println("-------------------------------------------------------------------------\n",
                    "------------------------- 3/4. Iteration Result -------------------------\n",
                    "-------------------------------------------------------------------------")
            # Initialize
            seq_timesIteration = collect(1: (timesIteration - 1))
            vec_boundUp = zeros(timesIteration - 1)
            vec_boundLow = zeros(timesIteration - 1)
            vec_obj_subRay = zeros(timesIteration - 1)
            vec_obj_mas = zeros(timesIteration - 1)
            vec_q = zeros(timesIteration - 1)
            vec_type = repeat(["ray"], (timesIteration - 1))
            #
            for i = 1: (timesIteration - 1)
                vec_obj_mas[i] = round(dict_obj_mas[i], digits = 5)
                vec_boundUp[i] = round(dict_boundUp[i], digits = 5)
                vec_boundLow[i] = round(dict_boundLow[i], digits = 5)
                vec_q[i] = round(dict_q[i], digits = 5)
                if haskey(dict_obj_sub, i)
                    vec_type[i] = "sub"
                    vec_obj_subRay[i] = round(dict_obj_sub[i], digits = 5)
                else
                    vec_obj_subRay[i] = round(dict_obj_ray[i], digits = 5)
                end
            end
            table_iterationResult = hcat(seq_timesIteration, vec_boundUp, vec_boundLow,
                                         vec_obj_mas, vec_q, vec_type, vec_obj_subRay)
            pretty_table(table_iterationResult,
                         ["Seq", "boundUp", "boundLow", "obj_mas", "q", "sub/ray", "obj_sub/ray"],
                         compact; alignment=:l)
        end
        println("-------------------------------------------------------------------------\n",
                "-------------------------- 4/4. Nominal Ending --------------------------\n",
                "-------------------------------------------------------------------------\n")
    end
end


using SpecialFunctions
using PRAS # From added registry

function map_log_gamma(v::Vector{N}) where {N <: Number}
  return map(loggamma, v)
end


export Simplex

struct Simplex{T <: Real} <: ConvexSet
    r::T
end
Simplex() = Simplex(1.0)

function project!(s::Simplex{<:Real}, v::Vector{T}, y::Vector{T}) where {T <: Real}
    n = length(y)
    copyto!(v, y)
    sort!(v, rev = true)
    ss, λ = zero(T), zero(T)
    for i in 1:n
        ss += v[i]
        λ = (ss - s.r) / i
        if i < n && λ < v[i] && λ ≥ v[i + 1]
            break
        end
    end
    v .= max.(y .- λ, 0)
end

function project(s::Simplex{<:Real}, y::Vector{<:Real})
    project!(s, similar(y), y)
end


function fakedata(name::AbstractString, args...)

    # gaussians with separation
    name == "gaussian" && return fakedata_gaussian(args...)

    # no dataset found
    error("dataset name not recognized.")
end

function fakedata_gaussian(
        pts_per_clust::Int=10,
        nclust::Int=2,
        xmin=0.,
        xmax=nclust*7.,
        nx=round(Int,(xmax-xmin)*10),
        σ=1.0,
        amin=1.,
        amax=2.
    )
    x = linspace(xmin,xmax,nx)
    μ = linspace(xmin,xmax,nclust+2)[2:end-1]
    a = linspace(xmin,xmax,pts_per_clust+2)[2:end-1]

    npts = pts_per_clust*nclust
    data = zeros(nx,npts)
    labels = zeros(Int,npts)

    i = 1
    for c = 1:nclust
        for n = 1:pts_per_clust
            data[:,i] = gauss_func(a[n],μ[c],σ,x)
            labels[i] = c
            i += 1
        end
    end

    return data, labels
end

gauss_func(a,μ,σ,x) = a*exp( (-(x-μ).^2) / (2σ^2) )


"""
The normal form of transcritical bifurcations.

https://en.wikipedia.org/wiki/Transcritical_bifurcation
"""
module Transcritical

using DiffEqBase: ODEProblem
using Setfield: @lens

using ...Bifurcations: BifurcationProblem

f(u::Real, p, t) = p * u - u^2

u0 = 0.0
tspan = (0.0, 1.0)
p = -1.0
ode = ODEProblem(f, u0, tspan, p)

param_axis = @lens _
prob = BifurcationProblem(ode, param_axis, (-1.0, 1.0))

using StaticArrays: @SVector

function closest_analytic(u, p)
    analytic = @SVector [0, p]
    _, i = findmin(abs.(u .- analytic))
    return [analytic[i], p]
end

end  # module


using InverseIsing
using StatsBase:sample
using JSON

function estim_test1()
    samples = [1 -1 -1;-1 1 1]
    model = GBM(size(samples, 2))
    fit(model, samples)
    W_fit = infer(model)
    @test W_fit == [0 -1 -1;-1 0 1;-1 1 0]
    @test decode(W_fit) == Dict((1, 2) => -1, (1, 3) => -1, (2, 3) => 1)
end

function estim_test2()
    n_unit = 5 # Number of units
    rate = 0.9 # zero rate (in all interaction-pairs)
    h = Dict()
    J_idx = [(i,j) for i in 1:n_unit for j in i+1:n_unit]
    len = length(J_idx)
    J_val = Int.(ones(len))
    J_val[sample(1:len, Int(ceil(len * rate)), replace=false)] .= 0
    J = Dict(zip(J_idx, J_val))

    # annealing and create samples
    resp = anneal(h, J, n_read=1000)
    samples = hcat(resp.states...)'

    # inverse ising inference
    model = GBM(n_unit)
    fit(model, samples)
    pred = decode(infer(model))
    @test J == pred
end

@testset "Test estim" begin
    estim_test1()
    estim_test2()
end


using FractalGeometry

using StaticArrays
using GeometryBasics
using FileIO
import Makie

begin
    testp = Point3(0.6, 0.3, -0.4)
    const tau = 2pi
    const τ = tau
    const TAU = τ
    ori = Point3(-1.1, -1.1, -1.1)
    wid = Point3(2.2, 2.2, 2.2)
    orisv = SVector(ori)
    widsv = SVector(wid)

    outerbox(v) = FractalGeometry.boxdist(v, Point3(0.8, 0.8, 0.8))
    outerboxsdf = FractalGeometry.FunctionSDF(outerbox)
end

begin
    arc_length_sampling_freq = 0.01
    linecurveγ = FractalGeometry.FunctionCurve(FractalGeometry.linecurve, (-1., 1.))
    linecurvesdf = FractalGeometry.curve_to_kdtree_sdf(linecurveγ, 0.125, arc_length_sampling_freq)
    distancep = linecurvesdf(testp)

    linecurveboxsdf = FractalGeometry.IntersectSDF(linecurvesdf, outerboxsdf)
end

begin
    using FractalGeometry
    arc_length_sampling_freq = 0.01
    helixcurve1(t) = Point3(0.3*cos(tau * t), 0.3*sin(tau * t), t)
    helixcurve2(t) = Point3(-0.3*cos(tau * t), -0.3*sin(tau * t), t)
    helixcurveconn1(t) = helixcurve1(0.4) * t + helixcurve2(0.4) * (1 - t)
    helixcurveconn2(t) = helixcurve1(-0.4) * t + helixcurve2(-0.4) * (1 - t)

    curves = [helixcurve1, helixcurve2, helixcurveconn1, helixcurveconn2]
    tspans = [(-1., 1.), (-1., 1.), (0., 1.), (0., 1.)]
    extrusions = [0.125, 0.125, 0.05, 0.05]

    union_sdf = FractalGeometry.curves_to_union_sdf(curves, tspans, extrusions, arc_length_sampling_freq; boundary_sdf=outerboxsdf)

    distancep = union_sdf(testp)

end

begin
    curdist = union_sdf
    curh0 = 0.03

    result = FractalGeometry.distmesh(curdist, FractalGeometry.HUniform(), curh0, FractalGeometry.DistMeshSetup(); origin=ori, widths=wid, maxiters=100)
    resultouterbox = FractalGeometry.distmesh(outerbox, HUniform(), 0.5, DistMeshSetup(); origin=ori, widths=wid, maxiters=100)
    boundarytris, alltris = FractalGeometry.boundary_triangles(result.tetrahedra)
    boundarytrisouterbox, alltrisouterbox = FractalGeometry.boundary_triangles(resultouterbox.tetrahedra)

    boundarymesh = GeometryBasics.Mesh(result.points, boundarytris)
    boundarymeshouterbox = GeometryBasics.Mesh(resultouterbox.points, boundarytrisouterbox)
end
begin

    show_axis = true

    scene = Makie.Scene()
    Makie.mesh!(scene, boundarymesh, color=[norm(v[1:2]) for v in coordinates(boundarymesh)], show_axis=show_axis)

    Makie.wireframe!(scene, boundarymesh, show_axis=show_axis)
    Makie.wireframe!(scene, boundarymeshouterbox, show_axis=show_axis, color=RGB(0.8, 0.3, 0.8))
end

begin
    savefile = "meshes/generated/doublehelixclipped.stl"
    save(savefile, boundarymesh)

end

begin
    mesh = load(savefile)
    scene = Makie.Scene()
    Makie.mesh!(scene, mesh, color=[norm(v[1:2]) for v in coordinates(mesh)], show_axis=show_axis)

    Makie.wireframe!(scene, mesh, show_axis=show_axis)

end


using LinearAlgebra, StableRNGs, StatsModels, Test

import MixedModels: statsqr, statscholesky

xtx(X) = Symmetric(X'X, :U)  # creat the symmetric matrix X'X from X
LinearAlgebra.rank(F::LinearAlgebra.QRPivoted; tol=1e-8) = searchsortedlast(abs.(diag(F.R)), tol, rev=true);

const rng = StableRNG(4321234)

const simdat = (
    G = repeat('A':'T', inner=10),
    H = repeat('a':'e', inner=2, outer=20),
    U = repeat(0.:9, outer=20),
    V = repeat(-4.5:4.5, outer=20),
    Y = 0.1 * randn(rng, 200),
    Z = rand(rng, 200)
)

@testset "fullranknumeric" begin
    mm = modelmatrix(@formula(Y ~ 1 + U), simdat)
    st = statsqr(mm)
    @test st.p == 1:2
end

@testset "fullrankcategorical" begin
    mm = modelmatrix(@formula(Y ~ 1 + G*H), simdat)
    st = statsqr(mm)
    @test rank(st) == 100
    @test st.p == 1:100
end

@testset "dependentcolumn" begin
    mm = modelmatrix(@formula(Y ~ 1 + U + V + Z), simdat)
    st = statsqr(mm)
    perm = [1,2,4,3]
    @test rank(st) == 3
    @test st.p == perm
end

@testset "qr missing cells" begin
    mm = modelmatrix(@formula(Y ~ 1 + G*H), simdat)[5:end,:]
    st = statsqr(mm)
    @test rank(st) == 98
    # we no longer offer ordering guarantees besides preserving
    # relative order of linearly independent columns
    # and trying to keep the first column in the first position
    unpivoted = st.p[begin:rank(st)]
    @test unpivoted == sort(unpivoted)
end

@testset "cholesky missing cells" begin
    mm = modelmatrix(@formula(Y ~ 1 + G*H), simdat)
    # when a cell is missing, the indicator for it is always zero
    mm[:, 43] .= 0
    XtX = xtx(mm)
    ch = statscholesky(XtX)
    perm = [1:42; 44:100; 43]
    @test ch.rank == 99
    @test ch.piv == perm
    @test isapprox(xtx(ch.U), XtX[perm, perm], atol=0.00001)
end


# This is a simple seismic modeling algorithm in the
# 3D poststack domain. It just convolves a wavelet
# with normal incident reflection coefficients
# generated from a velocity model.
#
# author: GRAM 5/1/16

function model(v_p, v_n, v_o, nx, ny, nz, wav)

	# initialize
	r_p = zeros(Float32,nz)
	r_n = zeros(Float32,nz)
	r_o = zeros(Float32,nz)
	d1 = d2 = 1		# acoustic assumption

	# calculate reflection coeficients
	for kk = 1 : nz - 1

		r_p[kk] = ((d2 * v_p[kk + 1] - d1 * v_p[kk]) /
				   (d2 * v_p[kk + 1] + d1 * v_p[kk])) ^ 2

		r_n[kk] = ((d2 * v_n[kk + 1] - d1 * v_n[kk]) /
				   (d2 * v_n[kk + 1] + d1 * v_n[kk])) ^ 2

		r_o[kk] = ((d2 * v_o[kk + 1] - d1 * v_o[kk]) /
				   (d2 * v_o[kk + 1] + d1 * v_o[kk])) ^ 2

	end

	# increase trace length for convolved samples
	nz_new = size(wav)[1] + nz - 1

	m_p = zeros(Float32, nz_new, nx, ny)
	m_n = zeros(Float32, nz_new, nx, ny)
	m_o = zeros(Float32, nz_new, nx, ny)

	# model seismic with a convolution
	m_p = conv(r_p, wav)
	m_n = conv(r_n, wav)
	m_o = conv(r_o, wav)

	# clip off unused (non-physical) ends of convolution
	extra = size(m_p)[1] - nz

	if isodd(extra) == true
		s_p = m_p[ceil(extra / 2) : nz - 1 + ceil(extra / 2)]
		s_n = m_n[ceil(extra / 2) : nz - 1 + ceil(extra / 2)]
		s_o = m_o[ceil(extra / 2) : nz - 1 + ceil(extra / 2)]
	else
		s_p = m_p[div(extra, 2) : nz - 1 + div(extra, 2)]
		s_n = m_n[div(extra, 2) : nz - 1 + div(extra, 2)]
		s_o = m_o[div(extra, 2) : nz - 1 + div(extra, 2)]
	end

	return s_p, s_n, s_o

end


function PV_savings(consumer_input::Consumer, system::PVSystem; tariff_increase=true)

    consumer = deepcopy(consumer_input)

    netsavings = Array{Float64,2}(undef,12,consumer.decision_timeframe)

    for y in 1:consumer.decision_timeframe

        ebalance = annual_energy_balance(consumer, system, print_output=false);

        for m in 1:12

            bill = monthly_bill(ebalance[m], consumer, print_output=false)

            netsavings[m,y] = bill["total_savings"]

        end

        tariff_increase ? increase_tariff!(consumer) : true

    end

    return netsavings

end

function PV_cashflow(consumer::T, system::PVSystem, financial_terms::Financial; tariff_increase=true) where {T <: Consumer}

    #println(consumer.econsumption, " ", system.capacity)
    if system.capacity > 0.0
        PV = PVCost(system,1.0)
        payment = amortize(financial_terms.apr/12, financial_terms.term*12, PV*(1-financial_terms.downpayment))
    else
        #dirty way to have 0 payments, needs some fixing later
        PV = 0.0
		payment = amortize(financial_terms.apr/12, financial_terms.term*12, 0.0)
    end

    bill_savings = PV_savings(consumer, system; tariff_increase=tariff_increase)

    for y in 1:financial_terms.term

        for m in 1:12

            bill_savings[m,y] = bill_savings[m,y] - payment[4]*financial_terms.XhR

        end

    end

	bill_savings[1,1] -= PV*(financial_terms.downpayment)

    return bill_savings

end

function PV_cashflow(consumer::Residential, system::PVSystem, financial_terms::Financial; tariff_increase=true)

    #println(consumer.econsumption, " ", system.capacity)
    if system.capacity > 0.0
        PV = PVCost(system,financial_terms.XhR)
        payment = amortize(financial_terms.apr/12, financial_terms.term*12, PV*(1-financial_terms.downpayment))
    else
        #dirty way to have 0 payments, needs some fixing later
        payment = amortize(financial_terms.apr/12, financial_terms.term*12, 0.0)
    end

    bill_savings = PV_savings(consumer, system; tariff_increase=tariff_increase)

    for y in 1:financial_terms.term

        for m in 1:12

            bill_savings[m,y] = bill_savings[m,y] - payment[4]

        end

    end

    return bill_savings

end

function optimal_pv(consumer::Residential, system::PVSystem, capacity_range::StepRangeLen{Float64,Base.TwicePrecision{Float64},Base.TwicePrecision{Float64}}, financial_terms::Financial; tariff_increase=true)

    results = Array{Float64,2}(undef, length(capacity_range), 2)

    for (ix,cap) in enumerate(capacity_range)
            system.capacity = cap
            cash_flows = PV_cashflow(consumer, system, financial_terms, tariff_increase=tariff_increase)
            annuities = sum(cash_flows,dims=1)

			if sum(annuities[1:5]) < 100

				results[ix,2] = -99
				results[ix,1] = 0.0;

			else

				results[ix,2] = npv(annuities, consumer.rate_return)
				results[ix,1] = cap;

            end

    end

	fm = findmax(results[:,2])

	if fm[1] == -99
		return (0.0, 0.0), results
	else
		return (fm[1], capacity_range[fm[2]]), results
	end

end

function optimal_pv(consumer::T, system::PVSystem, capacity_range::StepRangeLen{Float64,Base.TwicePrecision{Float64},Base.TwicePrecision{Float64}}, financial_terms::Financial; tariff_increase=true) where {T <: Consumer}

    results = Array{Float64,2}(undef, length(capacity_range), 2)

    for (ix,cap) in enumerate(capacity_range)
            system.capacity = cap
            cash_flows = PV_cashflow(consumer, system, financial_terms, tariff_increase=tariff_increase)
            annuities = sum(cash_flows,dims=1)
            if sum(annuities) < 100

				results[ix,2] = -99
				results[ix,1] = 0.0;

			else

				results[ix,2] = npv(annuities, consumer.rate_return)
				results[ix,1] = cap;

            end

    end

	fm = findmax(results[:,2])

	if fm[1] == -99
		return (0.0, 0.0), results
	else
		return (fm[1], capacity_range[fm[2]]), results
	end

end

function plot_monte_carlo_model_prediction(company, tariff, random_consumption, mc_data; width=50)
    fig = plt.figure()
    ax1 = fig.add_subplot()
    ax1.boxplot([[mc_data[j][i] for j in 1:size(mc_data,1)] for i in 1:size(mc_data[1],1)], positions=random_consumption, manage_ticks=false, widths=width)
#     ax1.set_xticks(random_consumption)
#     ax1.set_xticklabels(random_consumption, rotation="vertical")
    ylabel("Optimal PV System Capacity [kW]")
    xlabel("Consumer Monthly Energy use [kWh]")
    grid("on");
    title(string("Optimal System Choice for ", company, " ", tariff, " Consumer (Various Locations)"))
end

# NOTE: be sure to set the appropriate tariff before calling this function
function retrieve_monte_carlo_model_prediction(company, num_samples, tariff, tariff_obj, random_consumption, cap_range)
    mc_filename = string("data/monte_carlo_data/", company, "_", num_samples, ".txt")
    if isfile(mc_filename)
        mc_pv_output = readdlm(mc_filename, '\t', Float64, '\n')
    else
        println("Can't find NSRDB+SAM output")
        return
    end

    mc_data = []

    for pv_output in eachcol(mc_pv_output)
        mcPV = -1 # Placeholder because Julia's "Nothing" is strange and I don't understand it yet
        if tariff == "R"
            mcPV = newPVRes(pv_output)
        elseif tariff == "CI"
            mcPV = newPVComInd(pv_output)
        else
            mcPV = newPVTMT(pv_output)
        end
        pv_data = []
        for (ix, co) in enumerate(random_consumption)
            tariff_obj.econsumption = co; get_pmax(tariff_obj);
            new_data = optimal_pv(tariff_obj, mcPV, cap_range, BAC1, tariff_increase = true)
            push!(pv_data, new_data[1][2])
        end
        push!(mc_data, pv_data)
    end
    return mc_data
end


function solve()
    n = parse(Int, readline())
    ret = 0
    for i=1:n
        a, b = [parse(Int, x) for x in split(readline())]
        ret += (b-a+1)*(a+b)÷2
    end
    ret
end

println(solve())


@system Density begin
    weather ~ hold

    PD0: initial_planting_density => 55 ~ preserve(u"m^-2", parameter)

    CDSF: cold_damage_shape_factor => 0.9 ~ preserve(u"K^-1", parameter)
    CDCT: cold_damage_critical_temperature => -15 ~ preserve(u"°C", parameter)

    enable_cold_damage => true ~ flag(parameter)

    "mortality due to cold damage (2019-08-09: KHM, KDY)"
    CDM(s=CDSF, Tc=CDCT, T=weather.T_air): cold_damage_mortality => begin
        x = exp(-s * (T - Tc))
        x / (1 + x)
    end ~ track(u"d^-1", when=enable_cold_damage)

    CDS(CDS, CDM): cold_damage_survival_rate => (CDS * -CDM) ~ accumulate(init=1, min=0)

    PD(PD0, CDS): planting_density => (PD0 * CDS) ~ track(u"m^-2")
end


@testset "Photometric" begin
    I₁ = [0,0,1.0]
    I₂ = [1.0,0.2,1.0]
    I₃ = [0.2,0.9,1.0]
    img1, img2, img3 = generate_photometric(SynthSphere(50), 1, I₁, I₂, I₃)
    Z, p, q = Photometric(-I₁, -I₂, -I₃, Horn())(img1, img2, img3)
    @test p[76,76] ≈ 0.25222893791413625
    @test p[1,1] ≈ 0.25222893791413625
    @test p[151,151] ≈ 0.25222893791413625
    @test q[90,90] ≈ 0.4603504763877097
    @test q[101,36] ≈ 0.930232558139535
    @test q[22, 136] ≈ 0.23815510035929432
    @test Z[42,42] ≈ 4.274056595786455
end


using DSP, Test

@testset "esprit" begin
    # create a sum of sinusoids in noise, and estimate their frequencies
    Fs = 10000.0           # sampling frequency in Hz
    duration = 1           # length of signal, in seconds
    n = Int(Fs * duration) # number of samples
    t = collect((1:n)/Fs)  # time vector
    frequencies = [1000.0 1250.0]
    amplitudes = [2.0 1.5]
    phases = [0.7 -1.0]
    x = exp.( 1im*2*π*t*frequencies .+ phases) * amplitudes'
    noise = randn(n, 2)*[1;1im]
    sigma = 0.1
    noise *= sigma
    x += noise
    M = 300
    p = 2                  # number of sinusoids to estimate
    frequencies_estimated = sort(esprit(x, M, p, Fs))
    @test isapprox(frequencies', frequencies_estimated; atol = 1e-2)
end


export Normal

import Distributions

"""
    mutable struct Normal <: Dist{Float64}

`Normal` defines an *sfunc* representing unconditional Gaussian distributions.  Its
has no input, its output type is `Float64`, and its parameters are `(mean,standard deviation)`.

# Additional supported operators
- `support`
- `support_quality`
- `sample`
- `logcpdf`
- `bounded_probs`
- `compute_pi`
"""
mutable struct Normal <: Dist{Float64}
    params :: Tuple{Float64, Float64}
    """
        Normal(m::Float64, sd::Float64)

    `Normal`'s constructor

    # Arguments
    - `m::Float`: the mean of the `Normal`
    - `sd::Float64`: the standard deviation of the `Normal`
    """
    Normal(m::Float64, sd::Float64) = new((m,sd))
end

"returns the mean of the `Normal`"
mean(n::Normal) = n.params[1]
"returns the standard deviation of the `Normal`"
sd(n::Normal) = n.params[2]

"returns a `Distributions.Normal` from a `Scruff.SFuncs.Normal`"
dist(n) = Distributions.Normal(mean(n), sd(n))

@impl begin
    struct NormalSupport end
    function support(
            sf::Normal,
            parranges::NTuple{N,Vector},
            size::Integer,
            curr::Vector{Float64}) where N

        if isempty(curr)
            oldl = mean(sf)
            oldu = mean(sf)
        else
            oldl = minimum(filter(x -> x > -Inf, curr))
            oldu = maximum(filter(x -> x < Inf, curr))
        end

        if oldl == oldu
            if size <= 1
                result = [oldl]
                return result
            else
                l = oldl - sd(sf)
                u = oldl + sd(sf)
                result = Vector{Float64}()
                gap = (u-l) / (size-1)
                push!(result, l)
                for i = 1:(size-1)
                    push!(result, l + i*gap)
                end
                return result
            end
        else
            len = length(curr)
            if size <= len
                return curr
            else # we need to extend and thicken the current range while keeping existing points
                olddiff = oldu - oldl
                oldgap = olddiff / (len-1)
                newl = oldl - olddiff
                newu = oldu + olddiff
                result = copy(curr)
                i = newl
                while i < oldl
                    push!(result, i)
                    i += oldgap
                end
                i = newu
                while i > oldu
                    push!(result, i)
                    i -= oldgap
                end
                # Now we have 3 * len points covering twice the range
                # If size is greater, we will add points in between
                numperinterval = ceil(size / (3*len))
                if numperinterval > 1
                    newgap = oldgap / numperinterval
                    for x in copy(result)
                        for j = 1:(numperinterval-1)
                            push!(result, x + newgap * j)
                        end
                    end
                end
                sort!(result)
                return result
            end
        end
    end
end

@impl begin
    struct NormalSupportQuality end
    function support_quality(::Normal, parranges)
        :IncrementalSupport
    end
end

@impl begin
    struct NormalSample end
    function sample(sf::Normal, x::Tuple{})::Float64
        rand(dist(sf))
    end
end

@impl begin
    struct NormalLogcpdf end
    function logcpdf(sf::Normal, i::Tuple{}, o::Float64)::AbstractFloat
        Distributions.logpdf(dist(sf), o)
    end
end

@impl begin
    struct NormalBoundedProbsBoundedProbs
        numpartitions::Int64 = 10
    end

    function bounded_probs(
            sf::Normal,
            range::__OptVec{Float64},
            parranges::NTuple{N,Vector})::Tuple{Vector{<:AbstractFloat}, Vector{<:AbstractFloat}} where {N}

        intervals = make_intervals(range)
        lower = Array{Float64}(undef, length(range))
        upper = Array{Float64}(undef, length(range))
        d = dist(sf)
        ls = []
        us = []
        for interval in intervals
            (l, u) = interval
            if l == -Inf || u == Inf
                push!(ls, 0.0)
                push!(us, 1.0)
            else
                diff = (u - l) / numpartitions
                mn = 0.0
                mx = 0.0
                for i = 1:numpartitions
                    il = l + (i-1) * diff
                    iu = l + i * diff
                    p1 = Distributions.pdf(d, il) * diff
                    p2 = Distributions.pdf(d, iu) * diff
                    mn += min(p1,p2)
                    mx += max(p1,p2)
                end
                push!(ls, mn)
                push!(us, mx)
            end
        end
        # We get better bounds by considering the bounds on other intervals.
        # For any point, the lower bound can be normalized using the upper bounds of every other point, and vice versa.
        # This is especially important for intervals with -Inf or Inf as an
        # endpoint.
        for i = 1:length(intervals)
            otherls = 0.0
            otherus = 0.0
            for j = 1:i-1
                otherls += ls[j]
                otherus += us[j]
            end
            for j = i+1:length(intervals)
                otherls += ls[j]
                otherus += us[j]
            end
            l = max(ls[i], 1 - otherus)
            u = min(us[i], 1 - otherls)
            lower[i] = l
            upper[i] = u
        end
        return(lower, upper)
    end
end

# TODO: Replace this with a lazy implicit representation that doesn't require enumerating until the last minute
@impl begin
    struct NormalComputePi end

    function compute_pi(sf::Normal, range::__OptVec{Float64}, parranges::NTuple{N,Vector},
            incoming_pis::Tuple)::Dist{Float64} where {N}
        Cat(range, collect(map(x -> Distributions.pdf(dist(sf), x), range)))
    end

end
#=
@impl begin
    struct NormalExpectedStats end

    function expected_stats(sf::Normal, range::__OptVec{Float64}, parranges::NTuple{N,Vector},
            pis::NTuple{M,Dist},
            child_lambda::Score{Float64}) where {N,M}

        pis = [Distributions.pdf(dist(sf), x) for x in range]
        ls = [get_score(child_lambda, r) for r in range]
        prob = pis .* ls
        let totalX = 0.0, totalX2 = 0.0, count = 0.0
            for (i, x) in enumerate(range)
                totalX += x * prob[i]
                totalX2 += x^2 * prob[i]
                count += prob[i]
            end
            return (count,totalX,totalX2)
        end
    end
end

@impl begin
    struct NormalAccumulateStats end

    function accumulate_stats(sf::Normal, existing_stats, new_stats)
        existing_stats .+ new_stats
    end
end

@impl begin
    struct NormalInitialStats end

    function initial_stats(sf::Normal)
        return (0.0,0.0,0.0)
    end
end

@impl begin
    struct NormalMaximizeStats end

    function maximize_stats(sf::Normal, stats)
        (count, totalX, totalX2) = stats
        mean = totalX/count
        std = sqrt(totalX2/count - mean^2)
        return (mean, std)
    end
end
=#

Base.hash(n::Normal, h::UInt) = hash(n.params[2], hash(n.params[1], hash(:Normal, h)))
Base.isequal(a::Normal, b::Normal) = Base.isequal(hash(a), hash(b))
Base.:(==)(a::Normal, b::Normal) = Base.isequal(hash(a), hash(b))
Base.isless(a::Normal, b::Normal) = a.params[1] < b.params[1]
Base.:<(a::Normal, b::Normal) = Base.isless(a, b)


module LightGraphsMatching

using LightGraphs

using SparseArrays: spzeros

using JuMP
using MathProgBase: AbstractMathProgSolver
import BlossomV # 'using BlossomV'  leads to naming conflicts with JuMP
using Hungarian

export MatchingResult, maximum_weight_matching, maximum_weight_maximal_matching, minimum_weight_perfect_matching, HungarianAlgorithm, LPAlgorithm

"""
    struct MatchingResult{U}
        weight::U
        mate::Vector{Int}
    end

A type representing the result of a matching algorithm.

    weight: total weight of the matching

    mate:    `mate[i] = j` if vertex `i` is matched to vertex `j`.
             `mate[i] = -1` for unmatched vertices.
"""
struct MatchingResult{U<:Real}
    weight::U
    mate::Vector{Int}
end

include("lp.jl")
include("maximum_weight_matching.jl")
include("blossomv.jl")
include("hungarian.jl")
include("maximum_weight_maximal_matching.jl")

end # module