#
# Computes the total potential energy of the system using r2
#

function utotal3!(ft, p, data)
    first_atom = zeros(Int64, data.nc, data.nc)
    next_atom = zeros(Int64, data.N)
    makell!(first_atom, next_atom, p, data)
    ut = 0.
    for i = 1:length(ft)
        ft[i] .= 0
    end
    for ip = 1:data.N
        icell = trunc(Int64, p[ip][1]/data.cutoff) + 1
        jcell = trunc(Int64, p[ip][2]/data.cutoff) + 1
        for ic = icell-1:icell+1
            iw = wrap_cell(data.nc, ic)
            for jc = jcell-1:jcell+1
                jw = wrap_cell(data.nc, jc)
                jp = first_atom[iw,jw]
                while jp > 0
                    if ip < jp
                        r2 = (p[ip][1]-p[jp][1])^2 + (p[ip][2]-p[jp][2])^2 # r2pbc(p[ip], p[jp], data.bdim)
                        if r2 ≤ data.cutoff2
                            up, fp = upair3(r2, p[ip], p[jp], data)
                            ut += up
                            ft[ip] .+= fp
                            ft[jp] .-= fp
                        end
                    end
                    jp = next_atom[jp]
                end
            end
        end
    end
    return ut
end

function utotal3(p, data)
    ft = [ Array{Float64}(undef, 2) for i=1:length(p) ]
    return utotal3!(ft, p, data)
end


# A stochastic extension to the SIR and SEIR model

"""
We consider the 2 dimensional system of SDEs

dX_t = F(X_t) dt + G(X_t) dW_t

where X_t = (S_t; I_t) and F:R^2 -> R^2 and G(⋅) ∈ R^4.

The component R for the standard SIR model need not be considered.
"""

using DifferentialEquations
###########################################
#First some testing
f(du,u,p,t) = du .= 1.01u
function g(du,u,p,t)
  du[1,1] = 0.3u[1]
  du[1,2] = 0.6u[1]
  du[2,1] = 1.2u[2]
  du[2,2] = 0.2u[2]
end
prob = SDEProblem(f,g,ones(2),(0.0,1.0),noise_rate_prototype=zeros(2,2))
sol = solve(prob,LambaEM())
plot(sol)

############################################################
beta = 0.25; gamma = 0.07; N = 7000;
function f(du,u,p,t)
    du[1] = -beta * u[2]*u[1]/N;
    du[2] = beta*u[1]*u[2]/N - gamma*u[2]
end

function g(du,u,p,t)
    du[1,1] = -sqrt(beta*u[1]*u[2]/N)
    du[1,2] = 0
    du[2,1] = sqrt(beta*u[1]*u[2]/N)
    du[2,2] = -sqrt(gamma*u[2])
  end

prob = SDEProblem(f,g,[N-15;15],(0.0,200.0),noise_rate_prototype=zeros(2,2))
sol = solve(prob,LambaEM())

plot(sol)


# using PyPlot

###############################################################################
# This code section is translated directly from Joe's matlab script. It uses
# the bent-cylinder equation from Stanton 1998 (II).
###############################################################################

freq = 10.0.^range(3., stop=6., length=1000);
# define contants
Theta = 0;
g = 1.0357; # rho2/rho1
rho1 = 1.025; # density of water
rho2 = g*rho1; # density of shrimp
h = 1.0279; # c2 / c1
c1 = 1500; # spped of sound in  seawater
c2 = h*c1; # speed of sound in shrimp

a = 1.9E-3; # in m
rho_c_div_L = 2.2;
L = 29E-3; #in m
Ldiva = 16;
a = L./Ldiva;

R_12 = ((rho2*c2)/(rho1*c1) - 1) ./ (rho2*c2/(rho2*c1) + 1);

k1 = 2*pi.* freq ./ c1;
k2 =  2*pi.* freq ./ c2;

T_12 = 2*(rho2*c2./(rho1*c1)) / (1 + (rho2*c2./(rho1*c1)));
T_21 = 2*(rho1*c1./(rho2*c2)) / (1 + (rho1*c1./(rho2*c2)));
rho_c = rho_c_div_L * L;

alpha_B = 0.8;
mu_term = -pi/2*k1*a ./ (k1*a .+ 0.4);

I_o = 1 .- T_12 * T_21 .* exp.(im*4*k2*a) .* exp.(im*mu_term);

f_shrimp = 1/2*sqrt(rho_c*a) * R_12 .* exp.(-im*2*k1*a) .* I_o .*
	exp.(-alpha_B*(2*Theta*rho_c / L)^2);

TS_s = 10*log10.(abs.(f_shrimp).^2);
TS_shrimp = TS_s;

# plot(freq,TS_s);
# axis([350E3 800E3 -100 -40]);

###############################################################################
# recreating above using DWBA
###############################################################################
n_segments = 20

phi = 1 / rho_c_div_L
curvature_angle = range(-phi / 2, stop=phi / 2, length=n_segments)
origin = (rho_c - a) * [sin(phi / 2), cos(phi / 2)]
x = (rho_c - a) .* sin.(curvature_angle) .- origin[1]
z = (rho_c - a) .* cos.(curvature_angle) .- origin[2]
# plot(x, z)

bent_cylinder = Scatterer(
	[x'; zeros(1, n_segments); z'],
	a * ones(n_segments),
	h * ones(n_segments),
	g * ones(n_segments),
)

dwba = freq_spectrum(bent_cylinder, freq[1], freq[end], c1, length(k1))
# plot(freq, TS_s)
# plot(dwba["freq"], dwba["TS"])

@test dwba["TS"][1:10] == [-146.4384727146246,-134.3981674296699,-127.35600801470818,
	-122.36054597020149,-118.48682943594616,-115.3228602668385,-112.64886618846771,
	-110.33366282447088,-108.29263365106235,-106.46800319058687]


# -*- coding: utf-8 -*-
# ---
# jupyter:
#   jupytext:
#     formats: ipynb,jl:hydrogen
#     text_representation:
#       extension: .jl
#       format_name: hydrogen
#       format_version: '1.3'
#       jupytext_version: 1.11.2
#   kernelspec:
#     display_name: Julia 1.8.0-DEV
#     language: julia
#     name: julia-1.8
# ---

# %%
using Plots

function randunitdisk()
    y, x = sincospi(2rand())
    r = √rand()
    r*x, r*y
end

randunitdisk(n) = [randunitdisk() for _ in 1:n]

# %%
scatter(randunitdisk(2^12); legend=false, msw=0, ms=3, alpha=0.5, size=(500, 500))

# %%
scatter(randunitdisk(2^14); legend=false, msw=0, ms=1.5, alpha=0.5, size=(500, 500))

# %%

0012/uniform distribution on the unit disk.jl


function mergesort_iter!(a)
    k = 1
    while k < length(a)
        for i in 1:2k:length(a)
            if (i + k - 1 < length(a) < i + 2k -1)
                b = merge(a[i:i+k-1], a[i+k : lastindex(a)])
                a[i:lastindex(a)] = b
            elseif ( i + 2k -1 <= length(a))
                b = merge(a[i:i+k-1], a[i+k : i+2k - 1])
                a[i:i+2k-1] = b
            end
        end
        k *= 2
    end

    return a
end

function merge1!(a, b, c)
    d = merge(a,b)
    for i in 1:length(c)
        c[i] = d[i]
    end
    return c
end

function mergesort_iter2!(a)
    k = 1
    b = similar(a)
    while k < length(a)
        for i in 1:2k:length(a)
            if (i + k - 1 < length(a) < i + 2k -1)
                b = merge(a[i:i+k-1], a[i+k : lastindex(a)])
                a[i:lastindex(a)] = b
            elseif ( i + 2k -1 <= length(a))
                b = merge(a[i:i+k-1], a[i+k : i+2k - 1])
                a[i:i+2k-1] = b
            end
        end
        k *= 2
    end

    a = b
    return a
end


"""
    splitobs(n::Int, [at = 0.7]) -> Tuple

Pre-compute the indices for two disjoint subsets and return them
as a tuple of two ranges. The first range will span the first
`at` fraction of possible indices, while the second range will
cover the rest. These indices are applicable to any data
container of size `n`.

```julia
julia> splitobs(100, at = 0.7)
# (1:70,71:100)
```
"""
splitobs(n::Int; at = 0.7) = splitobs(n, at)

# partition into 2 sets
function splitobs(n::Int, at::AbstractFloat)
    0 < at < 1 || throw(ArgumentError("the parameter \"at\" must be in interval (0, 1)"))
    n1 = clamp(round(Int, at*n), 1, n)
    (1:n1, n1+1:n)
end

# has to be outside the generated function
_ispos(x) = x > 0
# partition into length(at)+1 sets
# we use @generated because we compute "N+1"
@generated function splitobs(n::Int, at::NTuple{N,AbstractFloat}) where N
    quote
        (all(map(_ispos, at)) && sum(at) < 1) || throw(ArgumentError("all elements in \"at\" must be positive and their sum must be smaller than 1"))
        nleft = n
        lst = UnitRange{Int}[]
        for (i, sz) in enumerate(at)
            ni = clamp(round(Int, sz*n), 0, nleft)
            push!(lst, n-nleft+1:n-nleft+ni)
            nleft -= ni
        end
        push!(lst, n-nleft+1:n)
        $(Expr(:tuple, (:(lst[$i]) for i in 1:N+1)...))
    end
end

"""
    splitobs(data, [at = 0.7], [obsdim]) -> Tuple

Split the `data` into multiple subsets proportional to the
value(s) of `at`.

Note that this function will perform the splits statically and
thus not perform any randomization. The function creates a
`NTuple` of data subsets in which the first N-1 elements/subsets
contain the fraction of observations of `data` that is specified
by `at`.

For example, if `at` is a `Float64` then the return-value will be
a tuple with two elements (i.e. subsets), in which the first
element contains the fracion of observations specified by `at`
and the second element contains the rest. In the following code
the first subset `train` will contain the first 70% of the
observations and the second subset `test` the rest.

```julia
train, test = splitobs(X, at = 0.7)
```

If `at` is a tuple of `Float64` then additional subsets will be
created. In this example `train` will have the first 50% of the
observations, `val` will have next 30%, and `test` the last 20%

```julia
train, val, test = splitobs(X, at = (0.5, 0.3))
```

It is also possible to call `splitobs` with multiple data
arguments as tuple, which all must have the same number of total
observations. This is useful for labeled data.

```julia
train, test = splitobs((X, y), at = 0.7)
(x_train,y_train), (x_test,y_test) = splitobs((X, y), at = 0.7)
```

If the observations should be randomly assigned to a subset,
then you can combine the function with `shuffleobs`

```julia
# This time observations are randomly assigned.
train, test = splitobs(shuffleobs((X,y)), at = 0.7)
```

When working with arrays one may want to choose which dimension
represents the observations. By default the last dimension is
assumed, but this can be overwritten.

```julia
# Here we say each row represents an observation
train, test = splitobs(X, obsdim = 1)
```

The functions also provide a type-stable API

```julia
# By avoiding keyword arguments, the compiler can infer the return type
train, test = splitobs((X,y), 0.7)
train, test = splitobs((X,y), 0.7, ObsDim.First())
```

see [`DataSubset`](@ref) for more information on data subsets.

see [`stratifiedobs`](@ref) for a related function that preserves
the target distribution.
"""
splitobs(data; at = 0.7, obsdim = default_obsdim(data)) =
    splitobs(data, at, convert(LearnBase.ObsDimension,obsdim))

# partition into 2 sets
function splitobs(data, at::AbstractFloat, obsdim=default_obsdim(data))
    allowcontainer(splitobs, data) || throw(MethodError(splitobs, (data,at,obsdim)))
    n = nobs(data, obsdim)
    idx1, idx2 = splitobs(n, at)
    datasubset(data, idx1, obsdim), datasubset(data, idx2, obsdim)
end

# partition into length(at)+1 sets
function splitobs(data, at::NTuple{N,AbstractFloat}, obsdim=default_obsdim(data)) where N
    allowcontainer(splitobs, data) || throw(MethodError(splitobs, (data,at,obsdim)))
    n = nobs(data, obsdim)
    map(idx->datasubset(data, idx, obsdim), splitobs(n, at))
end


# In this tutorial, we present the use of Stopping to checkpointing.

# When using an optimizer for high-scale problems, the resolution process
# might be extremly long. In order to analyze the progress of the algorithm or save ongoing results,
# an idea is to introduce checkpointing, i.e. we save the output result in the file
# every `n`-steps. Using `Stopping` this operation is now very simple.

using ADNLPModels, FileIO, JLD2, LinearAlgebra, NLPModels, Printf, Random, Stopping

# In this tutorial, we will use the steepest descent method with a fixed stepsize.

function fixed_step_steepest_descent(stp :: NLPStopping; t = 1e-5)

  xk = stp.current_state.x

  OK = update_and_start!(stp, gx = grad(stp.pb, xk))

  @printf "%2s %7s\n" "k" "||∇f(x)||"
  @printf "%2d %7.1e\n" stp.meta.nb_of_stop norm(stp.current_state.current_score)
  while !OK
    xk -= t * stp.current_state.gx

    OK = update_and_stop!(stp, x = xk, gx = grad(stp.pb, xk))

    @printf "%2d %7.1e\n" stp.meta.nb_of_stop norm(stp.current_state.current_score)
  end
  return stp
end

# We now generate a regularized least squares problem using `ADNLPModels.jl`.

Random.seed!(1234)
m, n = 10_000, 10
A  = rand(m, n)
b  = A * ones(n)
f(x, A, b, λ) = norm(A * x - b)^2 + λ * norm(x)^2
pb = ADNLPModel(x -> f(x, A, b, 1e-2), zeros(n))

# The final step is now to initialize the `Stopping` and specify user-defined structures
# to store the parameter `n_save` set to 50 so that every 50 iterations the current
# stopping is saved using the package `JLD2.jl`.

save_check(stp, b) = begin
  if stp.meta.nb_of_stop % stp.stopping_user_struct[:n_save] == 0
    @save "checkpoint_stopping_$(stp.meta.nb_of_stop).jld2" stp
  end
end
n_save = 50
stp = NLPStopping(pb, user_struct = Dict(:n_save => n_save),
                      user_check_func! = save_check, max_iter = 99)
# Let's go
fixed_step_steepest_descent(stp)

# The algorithm has now generated two files `checkpoint_stopping_0.jld2` and `checkpoint_stopping_50.jld2` that
# can be analyzed.

stp0 = load("checkpoint_stopping_0.jld2")["stp"]
stp50 = load("checkpoint_stopping_50.jld2")["stp"]


### A Pluto.jl notebook ###
# v0.16.1

using Markdown
using InteractiveUtils

# ╔═╡ 458f9f6e-2c17-11ec-08fe-d92011537eff
begin
	using PlutoUI
end

# ╔═╡ 56ff08ee-dcba-4aae-971a-5eec10a5d8c2
md"""
#### Solution: Coffee Maker Problem
"""

# ╔═╡ 28054bbb-cf38-403f-a563-c8a79551c950
md"""$(PlutoUI.LocalResource("./figs/Fig-CoffeeMaker-Problem-P1.png"))"""

# ╔═╡ 3e6a3f82-70fd-4692-8779-9d43f41947a2
begin

	# setup problem -
	m_3_dot = 0.5 	# units: kg/min
	m_4_dot = 0.36 	# units: kg/min
	α = 0.25 	 	# units: dimensionless
	H₁ = 104.5 		# units: kJ/kg
	H₂ = 283.5 		# units: kJ/kg
	H₃ = 168.5 		# units: kJ/kg
	H₄ = 722.4 		# units: kJ/kg
	m_coffee = 0.5 	# units: kg

	# show -
	nothing
end

# ╔═╡ cfad92dc-25e1-4d45-b94d-9c83888e3695
md"""
a) Determine the flow rate of coffee beans ($\dot{m}_{2}$) and water ($\dot{m}_{1}$) required to make a good cup of coffee (requirement: $\dot{m}_2$ = 1/4$\dot{m}_{1}$).

Start by applying the total mass balance at steady state:

$$\sum_{s=1}^{\mathcal{S}}v_{s}\dot{m}_{s} = 0$$

to the mixer grinder unit:

$$\dot{m}_{1}+\dot{m}_{2} = \dot{m}_{3}$$

In addition to the total mass balance, we also know that $\dot{m}_2$ = 1/4$\dot{m}_{1}$, thus we have two equations and two unknowns which we can solve for $\dot{m}_{1}$ and $\dot{m}_{2}$. You can do this via subsutitution, or by setting a a matrix inversion.

_Substitution approach_: Substitute $\dot{m}_{2}$ = $\alpha\dot{m}_{1}$ into the total mass balance and solve for $\dot{m}_{1}$:

$$\dot{m}_{1} = \frac{\dot{m}_{3}}{1+\alpha}$$

_Matrix vector approach_: We can solve for $\dot{m}_{1}$ and $\dot{m}_{2}$ by setting up the mass balance and coffee requirement as a 2$\times$2 system of equations:

$$\begin{pmatrix}
1 & 1 \\
-\alpha & 1
\end{pmatrix}
\begin{pmatrix}
\dot{m}_{1} \\
\dot{m}_{2}
\end{pmatrix} =
\begin{pmatrix}
\dot{m}_{3} \\
0
\end{pmatrix}$$

which is in the form $\mathbf{A}\mathbf{x}$=$\mathbf{b}$. We can compute the matrix
inverse $\mathbf{A}^{-1}$ to estimate $\mathbf{x}.

"""

# ╔═╡ 36a54cd9-d496-4066-872f-311fb1eba987
begin

	# subsitution method -
	m_1_dot = (m_3_dot)/(1+α)
	m_2_dot = α * m_1_dot

	# show -
	with_terminal() do
		println("m_1_dot = $(m_1_dot) kg/min and m_2_dot = $(m_2_dot) kg/min")
	end
end

# ╔═╡ dbe9f642-feca-4d4e-8cb9-1c851066863d
begin

	# matrix vector solution for m1 and m2 -
	A = [1 1 ; -α 1];
	b = [m_3_dot ; 0];

	# compute -
	x = inv(A)*b
end

# ╔═╡ ea96f2e5-1e4d-43e8-a9ef-a5f0af703349
md"""
b) Because he can’t plug the coffee maker in, he has to generate power to mix/grind the beans and water. What is the rate of work $\dot{W}_{sh}$ that he must supply to the mixer/grinder if this unit is well insulated?

To estimate the rate of shaft work $\dot{W}_{sh}$, we need to use the open energy balance equation at steady-state:

$$\dot{Q}+\dot{W}_{sh}+\sum_{s=1}^{\mathcal{S}}v_{s}\dot{m}_{s}H_{s} = 0$$

We know from the problem that $\dot{Q}$ = 0, thus we can solve the open energy balance to give an expression for the shaft work:

$$\dot{W}_{sh} = \dot{m}_{3}H_{3} - \left(\dot{m}_{1}H_{1} + \dot{m}_{2}H_{2}\right)$$

You already solved for the mass flow rates $\dot{m}_{i}$, and the enthalpy of each stream is given in the problem, thus, we know everything we need to solve for the work required to grind the beans.

"""

# ╔═╡ a3882553-e45f-49a9-8399-9adaaf02a9e7
begin

	W_dot_sh = m_3_dot*H₃ - (m_1_dot*H₁ + m_2_dot*H₂)

	with_terminal() do
		println("Rate of work for grinder = $(W_dot_sh) kJ/min")
	end

end

# ╔═╡ ff263509-d5a8-4227-898b-67491b99bf51
md"""
c) Prof. V loves high enthalpy coffee! In fact, he refuses to drink coffee unless the enthalpy content is between 475 kJ/kg $\leq{H_{5}}\leq$ 500 kJ/kg. What is the minimum and maximum rate of heat input to the brewer/separator that must be supplied to meet this completely reasonable criteria?

To estimate the heat input into the brewer/separator, we need to estimate $\dot{m}_{5}$ (using a total mass balance around the brewer) and then solve for $\dot{Q}$ of the brewer unit using an open energy balance.

Again, we could do solve using algebra and substitution (first find $\dot{m}_{5}$ using the total mass balance, then find $\dot{Q}$ using the open energy balance). However, I'm lazy (and I'm running out of time to post these solutions!) so I'm going to setup the solution as a matrix vector problem, and then invert the matrix:

$$\begin{pmatrix}
-1 & 0 \\
-H_{5} & 1
\end{pmatrix}
\begin{pmatrix}
\dot{m}_{5}\\
\dot{Q}
\end{pmatrix} =
\begin{pmatrix}
\dot{m}_{4} - \dot{m}_{3} \\
\dot{m}_{4}H_{4} - \dot{m}_{3}H_{3}
\end{pmatrix}$$

We can solve the problem twice: once for $H_{5}$ = 475 kJ/kg and again for $H_{5}$ = 500 kJ/kg, this will give us the $\dot{Q}$ range (I'll show the calcultion for the lower bound).

"""

# ╔═╡ 6fff5dba-e6e0-42e8-a06d-519fa582695e
begin

	# Setup -
	H₅ = 475.0 	# units: kJ/kg

	# setup matrix vector form Ax = b, the find x = A^{-1}b
	Ac = [-1 0; -H₅ 1];
	bc = [m_4_dot - m_3_dot ; m_4_dot*H₄ - m_3_dot*H₃];

	# solve -
	xc = inv(Ac)*bc

	# split -
	m_5_dot = xc[1]
	Qdot = xc[2]

	# show -
	with_terminal() do
		println("m_5_dot = $(m_5_dot) kg/min and Q_dot = $(Qdot) kJ/min")
	end
end

# ╔═╡ 2387358c-ee90-4594-9023-b03a51867745
md"""
d) Prof. V has five minutes to make coffee before class starts, did he make it in time (1 cup of coffee is $\simeq$ 0.5 kg)?

To estimate the time, we need to write a total mass balance around the coffee cup, however, the coffee cup will __not__ be at steady-state, this we have:

$$\frac{dm}{dt} = \dot{m}_{5}$$

where $m$ denotes the mass of coffee in the cup, and $t$ denotes time. Let's treat the derivative as an infinitesimal, and rearrange the mass balance as:

$$dm = \dot{m}_{5}dt$$

Next, we can integrate both sides (given that $\dot{m}_{5}$ is constant) between an initial point $t_{1}$ (when the cup is empty, define $t_{1}$ = 0) and someother point in time $t_{2}$:

$$m_{2} = m_{1} + \dot{m}_{5}\left(t_{2} - t_{1}\right)$$

However, at $t_{1}$ = 0, we know that the cup is empty, so $m_{1}$ = 0. Thus, we can rearrange and solve for $t_{2}$:

$$t_{2} = \frac{m_{2}}{\dot{m}_{5}}$$

"""

# ╔═╡ cf7f585a-29a0-4df6-a2a1-dd40dd8a9cf0
begin

	t₂ = m_coffee/(m_5_dot) # units: minutes

	with_terminal() do
		println("Time for coffee to fill cup: $(t₂) min")
	end
end

# ╔═╡ 00000000-0000-0000-0000-000000000001
PLUTO_PROJECT_TOML_CONTENTS = """
[deps]
PlutoUI = "7f904dfe-b85e-4ff6-b463-dae2292396a8"

[compat]
PlutoUI = "~0.7.16"
"""

# ╔═╡ 00000000-0000-0000-0000-000000000002
PLUTO_MANIFEST_TOML_CONTENTS = """
# This file is machine-generated - editing it directly is not advised

[[Base64]]
uuid = "2a0f44e3-6c83-55bd-87e4-b1978d98bd5f"

[[Dates]]
deps = ["Printf"]
uuid = "ade2ca70-3891-5945-98fb-dc099432e06a"

[[Hyperscript]]
deps = ["Test"]
git-tree-sha1 = "8d511d5b81240fc8e6802386302675bdf47737b9"
uuid = "47d2ed2b-36de-50cf-bf87-49c2cf4b8b91"
version = "0.0.4"

[[HypertextLiteral]]
git-tree-sha1 = "f6532909bf3d40b308a0f360b6a0e626c0e263a8"
uuid = "ac1192a8-f4b3-4bfe-ba22-af5b92cd3ab2"
version = "0.9.1"

[[IOCapture]]
deps = ["Logging", "Random"]
git-tree-sha1 = "f7be53659ab06ddc986428d3a9dcc95f6fa6705a"
uuid = "b5f81e59-6552-4d32-b1f0-c071b021bf89"
version = "0.2.2"

[[InteractiveUtils]]
deps = ["Markdown"]
uuid = "b77e0a4c-d291-57a0-90e8-8db25a27a240"

[[JSON]]
deps = ["Dates", "Mmap", "Parsers", "Unicode"]
git-tree-sha1 = "8076680b162ada2a031f707ac7b4953e30667a37"
uuid = "682c06a0-de6a-54ab-a142-c8b1cf79cde6"
version = "0.21.2"

[[Logging]]
uuid = "56ddb016-857b-54e1-b83d-db4d58db5568"

[[Markdown]]
deps = ["Base64"]
uuid = "d6f4376e-aef5-505a-96c1-9c027394607a"

[[Mmap]]
uuid = "a63ad114-7e13-5084-954f-fe012c677804"

[[Parsers]]
deps = ["Dates"]
git-tree-sha1 = "a8709b968a1ea6abc2dc1967cb1db6ac9a00dfb6"
uuid = "69de0a69-1ddd-5017-9359-2bf0b02dc9f0"
version = "2.0.5"

[[PlutoUI]]
deps = ["Base64", "Dates", "Hyperscript", "HypertextLiteral", "IOCapture", "InteractiveUtils", "JSON", "Logging", "Markdown", "Random", "Reexport", "UUIDs"]
git-tree-sha1 = "4c8a7d080daca18545c56f1cac28710c362478f3"
uuid = "7f904dfe-b85e-4ff6-b463-dae2292396a8"
version = "0.7.16"

[[Printf]]
deps = ["Unicode"]
uuid = "de0858da-6303-5e67-8744-51eddeeeb8d7"

[[Random]]
deps = ["Serialization"]
uuid = "9a3f8284-a2c9-5f02-9a11-845980a1fd5c"

[[Reexport]]
git-tree-sha1 = "45e428421666073eab6f2da5c9d310d99bb12f9b"
uuid = "189a3867-3050-52da-a836-e630ba90ab69"
version = "1.2.2"

[[SHA]]
uuid = "ea8e919c-243c-51af-8825-aaa63cd721ce"

[[Serialization]]
uuid = "9e88b42a-f829-5b0c-bbe9-9e923198166b"

[[Test]]
deps = ["InteractiveUtils", "Logging", "Random", "Serialization"]
uuid = "8dfed614-e22c-5e08-85e1-65c5234f0b40"

[[UUIDs]]
deps = ["Random", "SHA"]
uuid = "cf7118a7-6976-5b1a-9a39-7adc72f591a4"

[[Unicode]]
uuid = "4ec0a83e-493e-50e2-b9ac-8f72acf5a8f5"
"""

# ╔═╡ Cell order:
# ╟─56ff08ee-dcba-4aae-971a-5eec10a5d8c2
# ╟─28054bbb-cf38-403f-a563-c8a79551c950
# ╠═3e6a3f82-70fd-4692-8779-9d43f41947a2
# ╟─cfad92dc-25e1-4d45-b94d-9c83888e3695
# ╠═36a54cd9-d496-4066-872f-311fb1eba987
# ╠═dbe9f642-feca-4d4e-8cb9-1c851066863d
# ╟─ea96f2e5-1e4d-43e8-a9ef-a5f0af703349
# ╠═a3882553-e45f-49a9-8399-9adaaf02a9e7
# ╟─ff263509-d5a8-4227-898b-67491b99bf51
# ╠═6fff5dba-e6e0-42e8-a06d-519fa582695e
# ╟─2387358c-ee90-4594-9023-b03a51867745
# ╠═cf7f585a-29a0-4df6-a2a1-dd40dd8a9cf0
# ╠═458f9f6e-2c17-11ec-08fe-d92011537eff
# ╟─00000000-0000-0000-0000-000000000001
# ╟─00000000-0000-0000-0000-000000000002


module _Iterative

include("pi_iterative_gauss-legendre.jl")
include("pi_iterative_sine.jl")

end


tol = 1e-7
################################################
# Uncertain values represented by KDE estimates
################################################

# Create a random sample
d = Normal()
some_sample = rand(d, 2000)

# Create an uncertain value from the random sample by kernel density
# estimation.
uv = UncertainValue(some_sample)

@test resample(uv) isa Float64
@test resample(uv, 10) isa Vector{Float64}

@test resample(uv, NoConstraint()) isa Float64
@test resample(uv, NoConstraint(), 10) isa Vector{Float64}

r1a = resample(uv, TruncateLowerQuantile(0.2))
r1b = resample(uv, TruncateLowerQuantile(0.2), 10)
@test r1a isa Float64
@test r1b isa Vector{Float64}
#@test quantile(uv, 0.2) <= r1a + tol
##@test all(quantile(uv, 0.2) .<= r1b .+ tol)

r1a = resample(uv, TruncateUpperQuantile(0.8))
r1b = resample(uv, TruncateUpperQuantile(0.8), 10)
@test r1a isa Float64
@test r1b isa Vector{Float64}
#@test r1a <= quantile(uv, 0.8) + tol
#@test all(r1b .<= quantile(uv, 0.8) + tol)

r1a = resample(uv, TruncateQuantiles(0.2, 0.8))
r1b = resample(uv, TruncateQuantiles(0.2, 0.8), 10)
@test r1a isa Float64
@test r1b isa Vector{Float64}
#@test quantile(uv, 0.2) - tol <= r1a <= quantile(uv, .8) + tol
#@test all(quantile(uv, 0.2) - tol .<= r1b .<= quantile(uv, .8) + tol)

r1a = resample(uv, TruncateMinimum(-0.5))
r1b = resample(uv, TruncateMinimum(-0.5), 10)
@test r1a isa Float64
@test r1b isa Vector{Float64}
#@test -0.5 <= r1a + tol
#@test all(-0.5 .<= r1b .+ tol)

r1a = resample(uv, TruncateMaximum(0.5))
r1b = resample(uv, TruncateMaximum(0.5), 10)
@test r1a isa Float64
@test r1b isa Vector{Float64}
#@test r1a <= 0.5 + tol
#@test all(r1b .<= 0.5 + tol)

r1a = resample(uv, TruncateRange(-0.5, 0.5))
r1b = resample(uv, TruncateRange(-0.5, 0.5), 10)
@test r1a isa Float64
@test r1b isa Vector{Float64}
#@test -0.5 - tol <= r1a <= 0.5 + tol
#@test all(-0.5 - tol .<= r1b .<= 0.5 + tol)

# TruncateStd will not work, but with the default fallback we should still be able
# to resample.
r1a = resample(uv, TruncateStd(1))
r1b = resample(uv, TruncateStd(1), 10)
@test r1a isa Float64
@test r1b isa Vector{Float64}


using Test
using LatticeTools

@testset "Hypercube" begin

    @testset "failures" begin
        @test_throws DimensionMismatch Hypercube([1 2 3; 4 5 6])
        @test_throws ArgumentError Hypercube([1 1; 1 1])
    end

    @testset "orthogonal" begin
        hypercube = Hypercube([3 0; 0 3])
        @test dimension(hypercube) == 2
        @test isequiv(hypercube, Hypercube([3 0; 3 3]))
        @test !isequiv(hypercube, Hypercube([3 0; 1 3]))
        @test !isequiv(hypercube, Hypercube([6 0; 0 6]))
        @test !isequiv(Hypercube([6 0; 0 6]), hypercube)

        for i in 0:6, j in 0:6
            @test hypercube.wrap([i, j]) == ([i÷3, j÷3], [i%3, j%3])
        end
        let r = hcat([[i,j] for i in 0:6 for j in 0:6]...),
            R = hcat([[i÷3,j÷3] for i in 0:6 for j in 0:6]...),
            ρ = hcat([[i%3,j%3] for i in 0:6 for j in 0:6]...)
            @test hypercube.wrap(r) == (R, ρ)
        end
    end

    @testset "nonorthogonal" begin
        hypercube = Hypercube([2 1; 1 2])
        @test hypercube.inverse_shape_matrix == [2//3 -1//3; -1//3 2//3]
        @test dimension(hypercube) == 2
        @test hypercube.wrap([0,0]) == ([0,  0], [0, 0])
        @test hypercube.wrap([1,0]) == ([0, -1], [2, 2])
        @test hypercube.wrap([2,0]) == ([1, -1], [1, 1])
        @test hypercube.wrap([3,0]) == ([2, -1], [0, 0])
    end

    @testset "generator" begin
        cube = Hypercube([4 0; 0 3])
        generate_coordinates(cube, [ 1 4; -1 -3])
        generate_coordinates(cube, [ 1 0; 0 1])
        @test_throws DimensionMismatch generate_coordinates(cube, [ 1 0 0; 0 1 0]) # not square
        @test_throws ArgumentError generate_coordinates(cube, [ 2 0; 0 1]) # unimodular
        @test_throws ArgumentError generate_coordinates(cube, [ 1 0; 1 1])
        # c2 = generate_coordinates(cube, [1 0; 0 1])
    end

end


# struct SgramData <: JAXTAMData
#     mission::Symbol
#     instrument::Symbol
#     obsid::String
#     bin_time::Real
#     bin_size::Int
#     gti_index::Int
#     gti_start_time::Real
#     amps::Array
#     freqs::Array
# end

function _sgram(lc::JAXTAM.BinnedData, stft_intervals=round(Int, 1024/(lc.bin_time)))
    gti_only_counts = Array{Int64,1}()
    gti_only_times  = Array{Float64,1}()

    for gti in DataFrames.eachrow(lc.gtis)
        gti_start = gti[:start]
        gti_stop  = gti[:stop]

        if gti_stop - gti_start >= 16
            start = findfirst(lc.times .>= gti_start)
            stop  = findfirst(lc.times .>= gti_stop) - 1

            append!(gti_only_counts, lc.counts[start:stop])
            append!(gti_only_times, lc.times[start:stop])
        end
    end

    sgrm = spectrogram(gti_only_counts, stft_intervals, 0; fs=1/lc.bin_time)

    sgram_pseudo_time = 1:size(sgrm.power, 2)
    sgram_freqs  = collect(sgrm.freq)
    sgram_powers = sgrm.power
    sgram_powers[1, :] .= NaN
    sgram_powers[sgram_powers .== -Inf] .= NaN

    heatmap(sgram_freqs, sgram_pseudo_time, sgram_powers', legend=true, size=(1140,400))
end

function _stft(lc::JAXTAM.BinnedData, stft_intervals=round(Int, 1024/(lc.bin_time)))
    gti_only_counts = Array{Int64,1}()
    gti_only_times  = Array{Float64,1}()

    for gti in DataFrames.eachrow(lc.gtis)
        gti_start = gti[:start]
        gti_stop  = gti[:stop]

        if gti_stop - gti_start >= 16
            start = findfirst(lc.times .>= gti_start)
            stop  = findfirst(lc.times .>= gti_stop) - 1

            append!(gti_only_counts, lc.counts[start:stop])
            append!(gti_only_times, lc.times[start:stop])
        end
    end

    dsp_stft      = abs.(stft(gti_only_counts, stft_intervals; fs=1/lc.bin_time))
    #dsp_stft      = dsp_stft.^2
    dsp_stft_time = collect(1:size(dsp_stft, 2)).*(lc.bin_time*stft_intervals/2)
    dsp_stft_freq = linspace(0, 0.5*(1/lc.bin_time), size(dsp_stft, 1))

    dsp_stft[1, :] .= NaN # NaN 0 Hz

    return heatmap(dsp_stft_freq, dsp_stft_time, dsp_stft', legend=true, size=(1140,400))
end


@testset "970.powerful-integers.jl" begin
    @test powerful_integers(2, 3, 10) == Set([2, 3, 4, 5, 7, 9, 10])
    @test powerful_integers(3, 5, 15) == Set([2, 4, 6, 8, 10, 14])
    @test powerful_integers(1, 5, 15) == Set([2, 6])
    @test powerful_integers(1, 1, 5) == Set([2])
end


function spdiags(B,d,m,n)
  d = d[:]
  p = length(d)

  len = zeros(p+1,1)
  for k = 1:p
      len[k+1] = int(len[k]+length(Base.max(1,1-d[k]): Base.min(m,n-d[k])))
  end
  a = zeros(int(len[p+1]),3)
  for k = 1:p
      # Append new d[k]-th diagonal to compact form
      i = Base.max(1,1-d[k]):Base.min(m,n-d[k])
      a[(int(len[k])+1):int(len[k+1]),:] = [i i+d[k] B[i+(m>=n)*d[k],k]]
  end

  A = sparse(int(a[:,1]),int(a[:,2]),a[:,3],m,n)

  return A
end

y = readcsv("snp_500.txt")
n = length(y)

e = ones(n, 1)
D = spdiags([e -2*e e], 0:2, n-2, n)

lambda = 50
start = time()
D*x
println(time() - start)

x = Variable(n)
p = minimize(lambda*sum(D*x))
println(time() - start)
solve!(p)
p.optval
println(time() - start)


# This file contains all objects created and
# used in Liga

"""
```
GAAbstractType (Liga type)
```
GAAbstractType is the most primitive type in Liga.
This type is used to identify others types like BasisBlade
MultiVectors and Blade.
"""
abstract type GAAbstractType end

"""
```
BasisBlade (Liga type)
```
Basis Blade is an imutable type.
It is important to define several table operations in Liga.

## Example (in layout(3,0,"GA")=𝔾₃)

```julia-repl
julia> BasisBlade(1) #
```
returns e1.

```julia-repl
julia> e1.index #in layout(3,0,"GA")
```
returns 1 (::Int).
"""
struct BasisBlade <: GAAbstractType
	index::Int
end

"""
```
MultiVector (Liga type)
```
MultiVector is a mutable subtype of GAAbstract type. It is the
most used object of Liga. It is defined by BasisBlade operations
like bbscalar (*). Essentially, this object is a SparseVector.

## Example
```julia-repl
julia> v=1.0*e1  #in layout(3,0,"GA")
```
returns 1.0e1 (MultiVector not BasisBlade).
```julia-repl
julia> v.comp
```
returns
```
8-element SparseVector{Float64,Int64} with 1 stored entry:
[2]  =  1.0
```
A more directed way to create a MultiVector is to define
a vector. But in this case, an order must be respected.
```
julia>MultiVector([1.0,0.0,2.0]) #in layout(3,0,"GA")
```
returns 1.0id+2.0e2
"""
mutable struct MultiVector <: GAAbstractType
	comp::SparseVector{Float64}
#	function MultiVector(u)
#		for i=1:length(u)
#			if abs(u[i])<LigaPrecision
#				u[i]=0.0
#			end
#		end
#		new(u)
#	end
end

"""
```
Blade (Liga type)
```
A Blade is a mutable subtype of GAAbstractType.
In order to define a Blade, you need to setup tree arguments:
vectors, grade and checked. The first one is related to the set
of linear independent vectors wich define the Blade. The grade
is the number of vectors in this set. If you trust that the vectors
are L. I. so you can set the argument `checked` with `true` value.
If not, you can setup like `false`. In last case, is tested if the
vector's set is really a L. I. set.

```
julia> Blade([[1.0,2.0,4.0]],1,false)
```

returns [1.0,2.0,3.0].

```
julia> Blade([[1.0,0.0,0.0],[0.0,1.0,0.0]],2,true)
```
returns [1.0, 0.0, 0.0]∧[0.0, 1.0, 0.0].

"""
mutable struct Blade <: GAAbstractType
	scalar::Float64
	vectors::Array{Array{Float64,1},1}
	grade::Int
	checked::Bool
	function Blade(s,v,gr,li)
		if li == false
			lenv=length(v)
			lenvi=length(v[1])
			A=zeros(lenvi,lenv)
			for i=1:lenv
				if lenvi!=length(v[i])
					error("Some vector component with wrong dimension")
				else
					A[:,i]=v[i]
				end
			end
			if rank(A)<lenv
				error("Not LI vectors components, impossible to define a Blade")
			else
				new(s,v,lenv,true)
			end
		else
			new(s,v,gr,true)
		end
	end
end


export sgiter

"""
    λ,v = sgiter([eltype],nep::NEP,j::Integer;[λ_min,][λ_max,][λ,][errmeasure,][tol,][maxit,][logger,][eigsolvertype::Type,])

Finds the `j`-th eigenvalue of the NEP using safeguarded iteration, with eigenvalue numbering
according to min-max theory. The method only works for Hermitian problems, and the eigenvalues
are assumed to be real. If an interval [`λ_min`,`λ_max`] is given, then the Rayleigh functional
is assumed to be unique on the interval. If no interval is given, then the minimum solution is
always taken. The method requires the computation of (all) eigenvalues of a matrix. The `eigsolvertype`
is a `Type` that specifies which eigevalue solver is used inside the algorithm.

See [`augnewton`](@ref) for other parameters.

# Example
```julia-repl
julia> nep = nep_gallery("real_quadratic");
julia> λ,v = sgiter(nep, 1, λ_min = -10, λ_max = 0,  λ = -10, maxit = 100);
julia> minimum(svdvals(compute_Mder(nep,λ)))
0.0
julia> norm(v)
1.0
```

# References
* V. Mehrmann and H. Voss, Nonlinear eigenvalue problems: a challenge for modern eigenvalue methods, GAMM‐Mitteilungen 27.2 (2004): 121-152.
* H. Voss and B. Werner, Solving sparse nonlinear eigenvalue problems. Technical Report 82/4, Inst. f. Angew. Mathematik, Universität Hamburg, 1982.
* B. Werner. Das Spektrum von Operatorenscharen mit verallgemeinerten Rayleighquotienten. PhD thesis, Fachbereich Mathematik, Universität Hamburg, 1970

"""
sgiter(nep::NEP, j::Integer; params...) = sgiter(ComplexF64, nep, j; params...)
function sgiter(::Type{T},
                   nep::NEP,
                   j::Integer;
                   λ_min::Real = NaN,
                   λ_max::Real = NaN,
                   λ::Number = zero(real(T)),
                   errmeasure::ErrmeasureType = DefaultErrmeasure(nep),
                   tol::Real = eps(real(T)) * 100,
                   maxit::Integer = 100,
                   inner_solver=@default_compute_rf_inner_solver(nep),
                   logger = 0,
                   eigsolvertype::Type = DefaultEigSolver
                   ) where {T<:Number}

    @parse_logger_param!(logger)

    n = size(nep,1)
    if (j > n) || (j <= 0)
       error("j must be a number between 1 and size(nep) = ", n, ". You have selected j = ", j, ".")
    end
    real_T = real(T)
    λ_min::real_T = real_T(λ_min)
    λ_max::real_T = real_T(λ_max)
    if !( (isnan(λ_min) && isnan(λ_max)) || (!isnan(λ_min) && !isnan(λ_max)) )
        error("A proper interval is not chosen.") #Both should be either given or not given
    end
    if (λ_max < λ_min)
       error("The interval cannot be empty, λ_max >= λ_min is required. Supplied interval [λ_min,λ_max] = [", λ_min, ",", λ_max, "].")
    end
    λ::real_T = real_T(λ)
    if ( (λ < λ_min) || (λ > λ_max) )
       error("The starting guess is outside the interval.")
    end

    v::Array{T,1} = zeros(T,n)
    err = 0

    for k = 1:maxit
       eig_solver = eigsolvertype(compute_Mder(nep, λ, 0))
       v[:] = compute_jth_eigenvector(eig_solver, nep, λ, j)
       λ_vec = compute_rf(real_T, nep, v, inner_solver, TOL = tol/10)
       push_info!(logger,2,"compute_rf: $λ_vec")
       λ = choose_correct_eigenvalue_from_rf(λ_vec, λ_min, λ_max)
       err = estimate_error(errmeasure,λ, v)
       push_iteration_info!(logger,k,err=err,λ=λ);
       if (err < tol)
           return (λ, v)
       end
    end

    msg = "Number of iterations exceeded. maxit = $(maxit)."
    throw(NoConvergenceException(λ, v, err ,msg))
end

function compute_jth_eigenvector(eig_solver, nep::NEP, λ, j)
    n = size(nep,1)
    Λ, V = eig_solve(eig_solver, nev = n)
    if (n>1)
        p = sortperm(Λ)
        return vec(V[:,p[j]])
    else
        return V
    end
end

function choose_correct_eigenvalue_from_rf(λ_vec, λ_min, λ_max)
    if ( isnan(λ_min) && isnan(λ_max) )
        return minimum(λ_vec)
    else
        idxes = (λ_vec .<= λ_max) .& (λ_vec .>= λ_min)
        if sum(idxes) > 1
            error("Multiple values of λ found in the interval.")
        end
        if sum(idxes) == 0
            error("No λ found in the prescribed interval.")
        end
        idx = findall(idxes)[1]::Int #This vector is only 1 element
        return λ_vec[idx]
    end
end


using Statistics
using PyPlot
pygui(true)

function biased(x::Array{Float64,1})
    N = length(x)
    Nsub = Int(round(0.05*N))    # index to disregard the first 5% of the array
    # calculate a cumulative mean, disregard the first 5%
    cummean = (cumsum(x)./Array(1:N))[Nsub:end]
    # for a bias free x, the mean shouldn't be much larger than the std
    bias = abs(mean(cummean)) / std(cummean)
end

function biassample(::Type{T},          # number format to test for bias
                    op=+,               # arithmetic operation
                    rng=rand,          # random number generator, e.g. rand,randn
                    N=1000000) where T

    x = fill(0.0,N)   # preallocate

    for i in 1:N
        a,b = T.(8*rng(2))
        truth = op(Float64(a),Float64(b))
        approx = Float64(op(a,b))
        x[i] = approx-truth
    end

    return x
end

function cummean(x::AbstractArray)
    N = length(x)
    return cumsum(x)./Array(1:N)
end


using ManifoldsBase
using Test

struct ErrorTestManifold <: Manifold{ℝ} end

function ManifoldsBase.check_manifold_point(::ErrorTestManifold, x)
    if any(u -> u < 0, x)
        return DomainError(x, "<0")
    end
    return nothing
end
function ManifoldsBase.check_tangent_vector(M::ErrorTestManifold, x, v)
    mpe = check_manifold_point(M, x)
    mpe === nothing || return mpe
    if any(u -> u < 0, v)
        return DomainError(v, "<0")
    end
    return nothing
end

@testset "Domain errors" begin
    M = ErrorTestManifold()
    @test isa(check_manifold_point(M, [-1, 1]), DomainError)
    @test check_manifold_point(M, [1, 1]) === nothing
    @test !is_manifold_point(M, [-1, 1])
    @test is_manifold_point(M, [1, 1])
    @test_throws DomainError is_manifold_point(M, [-1, 1], true)

    @test isa(check_tangent_vector(M, [1, 1], [-1, 1]), DomainError)
    @test check_tangent_vector(M, [1, 1], [1, 1]) === nothing
    @test !is_tangent_vector(M, [1, 1], [-1, 1])
    @test is_tangent_vector(M, [1, 1], [1, 1])
    @test_throws DomainError is_tangent_vector(M, [1, 1], [-1, 1], true)
end


using TravelingSalesmanHeuristics
using Base.Test
using Distances

###
# helpers
###

# generate a Euclidean distance matrix for n points in the unit square
function generate_planar_distmat(n)
	pts = rand(2, n)
	return pairwise(Euclidean(), pts, pts)
end

# test that a path is acceptable:
# - at most one city appears twice, and if so must be first and last
# - all values 1:n are present where n is the maximum city index, and no other values
function testpathvalidity(path, iscycle)
	if iscycle
		@test path[1] == path[end]
		pop!(path)
	end
	path = sort(path)
	for i in 1:length(path)
		@test path[i] == i
	end
end

###
# main tests
###

function test_nearest_neighbor()
	distmats = cell(2)
	distmats[1] = generate_planar_distmat(10)
	distmats[2] = generate_planar_distmat(2)

	for dm in distmats
		n = size(dm, 1)
		randstartcity = rand(1:n)
		# standard
		path, cost = nearest_neighbor(dm)
		@test cost > 0
		testpathvalidity(path, true)
		# repetitive
		path, cost = nearest_neighbor(dm, repetitive = true)
		@test cost > 0
		testpathvalidity(path, true)
		# no loop, 2 opt
		path, cost = nearest_neighbor(dm, closepath = false)
		@test cost > 0
		testpathvalidity(path, false)
		# no loop, no 2 opt
		path, cost = nearest_neighbor(dm, closepath = false, do2opt = false)
		@test cost > 0
		testpathvalidity(path, false)
		# fixed start, no loop
		path, cost = nearest_neighbor(dm, closepath = false, firstcity = Nullable(randstartcity))
		@test cost > 0
		testpathvalidity(path, false)
	end

	dm_bad = rand(3,2)
	@test_throws ErrorException nearest_neighbor(dm_bad)
end

function test_cheapest_insertion()
	# default random start
	dm = generate_planar_distmat(8)
	path, cost = cheapest_insertion(dm)
	@test cost > 0
	testpathvalidity(path, true) # should be a closed path
	# repetitive start
	path, cost = cheapest_insertion(dm, repetitive = true)
	@test cost > 0
	testpathvalidity(path, true)

	# bad input
	dmbad = rand(2,3)
	@test_throws ErrorException cheapest_insertion(dmbad)
end

function test_simulated_annealing()
	dm = generate_planar_distmat(14)
	# single start
	path, cost = simulated_annealing(dm)
	@test cost > 0
	testpathvalidity(path, true) # closed path
	# multi-start
	dm = generate_planar_distmat(8)
	path, cost = simulated_annealing(dm, num_starts = 3)
	@test cost > 0
	testpathvalidity(path, true) # also closed
	# given init path
	init_path = collect(1:8)
	push!(init_path, 1)
	reverse!(init_path, 2, 6)
	path, cost = simulated_annealing(dm; init_path = Nullable(init_path))
	@test cost > lowerbound(dm)
	testpathvalidity(path, true) # still closed
end

function test_bounds()
	dm = generate_planar_distmat(2)
	path, cost = solve_tsp(dm)
	lb = lowerbound(dm)
	@test lb <= cost
	dm = generate_planar_distmat(30)
	path, cost = solve_tsp(dm)
	lb = lowerbound(dm)
	@test lb <= cost
end

function test_path_costs()
	dm = [1 2 3; 4 5 6; 7 8 9]
	p1 = [1, 2, 3, 1]
	@test TravelingSalesmanHeuristics.pathcost(dm, p1) == 2 + 6 + 7
	# various reverse indices to test
	revs = [(1,4), (2,3), (2,4), (3,3)]
	for rev in revs
		path_rev = reverse(p1, rev[1], rev[2])
		@test TravelingSalesmanHeuristics.pathcost(dm, path_rev) ==
			  TravelingSalesmanHeuristics.pathcost_rev(dm, p1, rev[1], rev[2])
	end
end

###
# run
###
srand(47)
test_nearest_neighbor()
test_cheapest_insertion()
test_simulated_annealing()
test_bounds()
test_path_costs()
println("Done testing.")


using Statistics, LinearAlgebra

"""
    sqrt_svd(A)

Returns the square root matrix by SVD
"""
function sqrt_svd(A::AbstractMatrix)
    F = svd(A)
    F.U * diagm(sqrt.(F.S)) * F.Vt
end

export RMSE

"""
    RMSE(e)

Returns the Root Mean Squared Error
"""
RMSE(e) = sqrt.(mean(e .^ 2))

"""
    normalise!( w )

Normalize the entries of a multidimensional array sum to 1.
"""
function normalise!(w)

    c = sum(w)
    # Set any zeros to one before dividing
    c += c == 0
    w ./= c

end

"""
    mk_stochastic!(w)

Ensure the matrix is stochastic, i.e.,
the sum over the last dimension is 1.
"""
function mk_stochastic!(w::Array{Float64,2})

    if first(size(w)) == 1
        normalise!(w)
    else
        # Copy the normaliser plane for each i.
        normaliser = sum(w, dims = 2)
        # Set zeros to 1 before dividing
        # This is valid since normaliser(i) = 0 iff T(i) = 0
        normaliser .+= normaliser .== 0
        w ./= normaliser
    end

end

"""
    sample_discrete(prob, r, c)

Sampling from a non-uniform distribution.
"""
function sample_discrete(prob, r, c)

    # this speedup is due to Peter Acklam
    cumprob = cumsum(prob)
    R = rand(r, c)
    M = zeros(Int64, (r, c))
    N = length(cumprob)
    for i = 1:N-1
        M .+= R .> cumprob[i]
    end
    M

end

"""
    inv_using_SVD(Mat, eigvalMax)

SVD decomposition of Matrix.
"""
function inv_using_SVD(Mat, eigvalMax)

    F = svd(Mat; full = true)
    eigval = cumsum(F.S) ./ sum(F.S)
    # search the optimal number of eigen values
    icut = findfirst(eigval .>= eigvalMax)

    U_1 = @view F.U[1:icut, 1:icut]
    V_1 = @view F.Vt'[1:icut, 1:icut]
    tmp1 = (V_1 ./ F.S[1:icut]') * U_1'

    if icut + 1 > length(eigval)
        tmp1
    else
        U_3 = @view F.U[icut+1:end, 1:icut]
        V_3 = @view F.Vt'[icut+1:end, 1:icut]
        tmp2 = (V_1 ./ F.S[1:icut]') * U_3'
        tmp3 = (V_3 ./ F.S[1:icut]') * U_1'
        tmp4 = (V_3 ./ F.S[1:icut]') * U_3'
        vcat(hcat(tmp1, tmp2), hcat(tmp3, tmp4))
    end

end

"""
    resample_multinomial( w )

Multinomial resampler.
"""
function resample_multinomial(w::Vector{Float64})

    m = length(w)
    q = cumsum(w)
    q[end] = 1.0 # Just in case...
    i = 1
    indx = Int64[]
    while i <= m
        sampl = rand()
        j = 1
        while q[j] < sampl
            j = j + 1
        end
        push!(indx, j)
        i = i + 1
    end
    indx
end

"""
    resample!( indx, w )

Multinomial resampler.
"""
function resample!(indx::Vector{Int64}, w::Vector{Float64})

    m = length(w)
    q = cumsum(w)
    i = 1
    while i <= m
        sampl = rand()
        j = 1
        while q[j] < sampl
            j = j + 1
        end
        indx[i] = j
        i = i + 1
    end
end

"""
    ensure_pos_sym(M; ϵ= 1e-8)

Ensure that matrix `M` is positive and symmetric to avoid numerical errors when numbers are small by doing `(M + M')/2 + ϵ*I`

reference : [StateSpaceModels.jl](https://github.com/LAMPSPUC/StateSpaceModels.jl)
"""
function ensure_pos_sym(M::Matrix{T}; ϵ::T = 1e-8) where {T<:AbstractFloat}
    return (M + M') ./ 2 + ϵ * I
end

function ensure_pos_sym!(M::Matrix{T}; ϵ::T = 1e-8) where {T<:AbstractFloat}
    M .= (M + M') ./ 2 + ϵ * I
end


# 1D regression.

import Random
import PyPlot

using LinearAlgebra
#import Interpolations
#using Revise

import RKHSRegularization # https://github.com/RoyCCWang/RKHSRegularization

PyPlot.matplotlib["rcParams"][:update](["font.size" => 22, "font.family" => "serif"])

PyPlot.close("all")

Random.seed!(25)

fig_num = 1

##### regression
θ = RKHSRegularization.Spline34KernelType(0.04)

# user inputs.
σ² = 1e-3
N = 15

x_range = LinRange(-24.0, 45.0, N)
Nq = 1000
#xq_range = LinRange(-30.0, 50.0, Nq)
Nq = 1000
xq_range = LinRange(x_range[1], x_range[end], Nq)

#DelimitedFiles.writedlm( "query.txt",  xq_range, '\n')

# if 0 and 1 are included, we have posdef error, or rank = N - 2.
X = collect( [x_range[n]] for n = 1:N )

f = xx->sinc(4*xx)*xx^3
y = f.(x_range)

# check posdef.
K = RKHSRegularization.constructkernelmatrix(X, θ)
println("rank(K) = ", rank(K))

println("isposdef = ", isposdef(K))

# fit RKHS.
η = RKHSRegularization.RKHSProblemType( zeros(Float64,length(X)),
                     X,
                     θ,
                     σ²)
RKHSRegularization.fitRKHS!(η, y)

# query.
xq = collect( [xq_range[n]] for n = 1:Nq )

f_xq = f.(xq_range) # generating function.

yq = Vector{Float64}(undef, Nq)
RKHSRegularization.query!(yq,xq,η)

# Visualize regression result.
PyPlot.figure(fig_num)
fig_num += 1

PyPlot.plot(X, y, ".", label = "observed")
PyPlot.plot(xq, yq, label = "GP mean function")

title_string = "1D GP regression"
PyPlot.title(title_string)
PyPlot.legend()
##### end of regression


using SMC, Base.Test

include("_legacy_.jl")

srand(125)

## Linear Gaussian

dx, dy = 5, 3
A = randn(dx,dx); A /= 0.9norm(A)
B = randn(dy,dx); B /= 1.1norm(B)
Q = randn(dx,dx); Q *= Q'; Q += 0.5*eye(dx); Q += Q'; Q /= 5
R = randn(dy,dy); R *= R'; R += 0.5*eye(dy); R += R'; R /= 20

lg  = LinearGaussian(A,B,Q,R)
hmm = HMM(lg)
x0  = randn(dx)

K = 100
(states, observations) = generate(lg, x0, K)

x00 = x0+randn(dx)/5

srand(12)
kf = kalmanfilter(lg, observations, x00, eye(dx))
srand(12)
(kfm_leg, kfc_leg, kfm__leg, kfc__leg) = kf_legacy(observations, A, B, Q,
                                                    R, K, x00, eye(dx))

@test isapprox(kf.means, kfm_leg)
@test isapprox(kf.covs, kfc_leg)
@test isapprox(kf.means_, kfm__leg)
@test isapprox(kf.covs_, kfc__leg)

# fragile test
@test norm(kf.means-states)/norm(states) < 0.25

srand(32)
ks = kalmansmoother(lg, observations, kf)
srand(32)
(ksm_leg, ksc_leg) = ks_legacy(observations, A, B, Q, R, K,
                                kfm__leg, kfc__leg)

@test isapprox(ks.means, ksm_leg)
@test isapprox(ks.covs, ksc_leg)

# fragile test
@test norm(ks.means-states)/norm(states) < 0.2


using Turing
using AbstractGPs, Random

using LinearAlgebra
using VegaLite, DataFrames, StatsPlots, StatsBase
using RDatasets

Random.seed!(1789);

data = dataset("datasets", "iris")
species = data[!, "Species"]
index = shuffle(1:150)
# we extract the four measured quantities,
# so the dimension of the data is only d=4 for this toy example
dat = Matrix(data[index, 1:4])
labels = data[index, "Species"]

# non-linearize data to demonstrate ability of GPs to deal with non-linearity
dat[:, 1] = 0.5 * dat[:, 1] .^ 2 + 0.1 * dat[:, 1] .^ 3
dat[:, 2] = dat[:, 2] .^ 3 + 0.2 * dat[:, 2] .^ 4
dat[:, 3] = 0.1 * exp.(dat[:, 3]) - 0.2 * dat[:, 3] .^ 2
dat[:, 4] = 0.5 * log.(dat[:, 4]) .^ 2 + 0.01 * dat[:, 3] .^ 5

# normalize data
dt = fit(ZScoreTransform, dat; dims=1);
StatsBase.transform!(dt, dat);

@model function pPCA(x, ::Type{TV}=Array{Float64}) where {TV}
    # Dimensionality of the problem.
    N, D = size(x)
    # latent variable z
    z ~ filldist(Normal(), D, N)
    # weights/loadings W
    w ~ filldist(Normal(), D, D)
    mu = (w * z)'
    for d in 1:D
        x[:, d] ~ MvNormal(mu[:, d], I)
    end
end;

linear_kernel(α) = LinearKernel() ∘ ARDTransform(α)
sekernel(α, σ) = σ * SqExponentialKernel() ∘ ARDTransform(α);

@model function GPLVM_linear(Y, K=4, ::Type{T}=Float64) where {T}

    # Dimensionality of the problem.
    N, D = size(Y)
    # K is the dimension of the latent space
    @assert K <= D
    noise = 1e-3

    # Priors
    α ~ MvLogNormal(MvNormal(zeros(K), I))
    Z ~ filldist(Normal(), K, N)
    mu ~ filldist(Normal(), N)

    kernel = linear_kernel(α)

    gp = GP(mu, kernel)
    cv = cov(gp(ColVecs(Z), noise))
    return Y ~ filldist(MvNormal(mu, cv), D)
end;

@model function GPLVM(Y, K=4, ::Type{T}=Float64) where {T}

    # Dimensionality of the problem.
    N, D = size(Y)
    # K is the dimension of the latent space
    @assert K <= D
    noise = 1e-3

    # Priors
    α ~ MvLogNormal(MvNormal(zeros(K), I))
    σ ~ LogNormal(0.0, 1.0)
    Z ~ filldist(Normal(), K, N)
    mu ~ filldist(Normal(), N)

    kernel = sekernel(α, σ)

    gp = GP(mu, kernel)
    cv = cov(gp(ColVecs(Z), noise))
    return Y ~ filldist(MvNormal(mu, cv), D)
end;

# Standard GPs don't scale very well in n, so we use a small subsample for the purpose of this tutorial
n_data = 40
# number of features to use from dataset
n_features = 4
# latent dimension for GP case
ndim = 4;

ppca = pPCA(dat[1:n_data, 1:n_features])
chain_ppca = sample(ppca, NUTS(), 1000);

# we extract the posterior mean estimates of the parameters from the chain
w = reshape(mean(group(chain_ppca, :w))[:, 2], (n_features, n_features))
z = reshape(mean(group(chain_ppca, :z))[:, 2], (n_features, n_data))
X = w * z

df_pre = DataFrame(z', :auto)
rename!(df_pre, Symbol.(["z" * string(i) for i in collect(1:n_features)]))
df_pre[!, :type] = labels[1:n_data]
p_ppca = @vlplot(:point, x = :z1, y = :z2, color = "type:n")(df_pre)

gplvm_linear = GPLVM_linear(dat[1:n_data, 1:n_features], ndim)

chain_linear = sample(gplvm_linear, NUTS(), 500)
# we extract the posterior mean estimates of the parameters from the chain
z_mean = reshape(mean(group(chain_linear, :Z))[:, 2], (n_features, n_data))
alpha_mean = mean(group(chain_linear, :α))[:, 2]

df_gplvm_linear = DataFrame(z_mean', :auto)
rename!(df_gplvm_linear, Symbol.(["z" * string(i) for i in collect(1:ndim)]))
df_gplvm_linear[!, :sample] = 1:n_data
df_gplvm_linear[!, :labels] = labels[1:n_data]
alpha_indices = sortperm(alpha_mean; rev=true)[1:2]
println(alpha_indices)
df_gplvm_linear[!, :ard1] = z_mean[alpha_indices[1], :]
df_gplvm_linear[!, :ard2] = z_mean[alpha_indices[2], :]

p_linear = @vlplot(:point, x = :ard1, y = :ard2, color = "labels:n")(df_gplvm_linear)
p_linear

gplvm = GPLVM(dat[1:n_data, 1:n_features], ndim)

chain_gplvm = sample(gplvm, NUTS(), 500)
# we extract the posterior mean estimates of the parameters from the chain
z_mean = reshape(mean(group(chain_gplvm, :Z))[:, 2], (ndim, n_data))
alpha_mean = mean(group(chain_gplvm, :α))[:, 2]

df_gplvm = DataFrame(z_mean', :auto)
rename!(df_gplvm, Symbol.(["z" * string(i) for i in collect(1:ndim)]))
df_gplvm[!, :sample] = 1:n_data
df_gplvm[!, :labels] = labels[1:n_data]
alpha_indices = sortperm(alpha_mean; rev=true)[1:2]
println(alpha_indices)
df_gplvm[!, :ard1] = z_mean[alpha_indices[1], :]
df_gplvm[!, :ard2] = z_mean[alpha_indices[2], :]

p_gplvm = @vlplot(:point, x = :ard1, y = :ard2, color = "labels:n")(df_gplvm)
p_gplvm

let
    @assert abs(
        mean(z_mean[alpha_indices[1], labels[1:n_data] .== "setosa"]) -
        mean(z_mean[alpha_indices[1], labels[1:n_data] .!= "setosa"]),
    ) > 1.4
end

using Stheno
@model function GPLVM_sparse(Y, K, ::Type{T}=Float64) where {T}

    # Dimensionality of the problem.
    N, D = size(Y)
    # dimension of latent space
    @assert K <= D
    # number of inducing points
    n_inducing = 25
    noise = 1e-3

    # Priors
    α ~ MvLogNormal(MvNormal(zeros(K), I))
    σ ~ LogNormal(1.0, 1.0)
    Z ~ filldist(Normal(), K, N)
    mu ~ filldist(Normal(), N)

    kernel = σ * SqExponentialKernel() ∘ ARDTransform(α)

    ## Standard
    # gpc = GPC()
    # f = atomic(GP(kernel), gpc)
    # gp = f(ColVecs(Z), noise)
    # Y ~ filldist(gp, D)

    ## SPARSE GP
    #  xu = reshape(repeat(locations, K), :, K) # inducing points
    #  xu = reshape(repeat(collect(range(-2.0, 2.0; length=20)), K), :, K) # inducing points
    lbound = minimum(Y) + 1e-6
    ubound = maximum(Y) - 1e-6
    #  locations ~ filldist(Uniform(lbound, ubound), n_inducing)
    #  locations = [-2., -1.5 -1., -0.5, -0.25, 0.25, 0.5, 1., 2.]
    #  locations = collect(LinRange(lbound, ubound, n_inducing))
    locations = quantile(vec(Y), LinRange(0.01, 0.99, n_inducing))
    xu = reshape(locations, 1, :)
    gp = atomic(GP(kernel), GPC())
    fobs = gp(ColVecs(Z), noise)
    finducing = gp(xu, 1e-12)
    sfgp = SparseFiniteGP(fobs, finducing)
    cv = cov(sfgp.fobs)
    return Y ~ filldist(MvNormal(mu, cv), D)
end

n_data = 50
gplvm_sparse = GPLVM_sparse(dat[1:n_data, :], ndim)

chain_gplvm_sparse = sample(gplvm_sparse, NUTS(), 500)
# we extract the posterior mean estimates of the parameters from the chain
z_mean = reshape(mean(group(chain_gplvm_sparse, :Z))[:, 2], (ndim, n_data))
alpha_mean = mean(group(chain_gplvm_sparse, :α))[:, 2]

df_gplvm_sparse = DataFrame(z_mean', :auto)
rename!(df_gplvm_sparse, Symbol.(["z" * string(i) for i in collect(1:ndim)]))
df_gplvm_sparse[!, :sample] = 1:n_data
df_gplvm_sparse[!, :labels] = labels[1:n_data]
alpha_indices = sortperm(alpha_mean; rev=true)[1:2]
df_gplvm_sparse[!, :ard1] = z_mean[alpha_indices[1], :]
df_gplvm_sparse[!, :ard2] = z_mean[alpha_indices[2], :]
p_sparse = @vlplot(:point, x = :ard1, y = :ard2, color = "labels:n")(df_gplvm_sparse)
p_sparse


# export ckepler, ckepler!, pkepler, pkepler!
export vdp, vdp!, basic!, basic

m = 1;
k = 1.0168951928e3;
kh = k/m;

function ckepler!(dx, x, p, t)
    dx[1] = x[2]
    dx[2] = -kh/(norm([x[1], x[3]])^3) * x[1]
    dx[3] = x[4]
    dx[4] = -kh/(norm([x[1], x[3]])^3) * x[3]
end
function ckepler(x, p, t)
    dx = similar(x)
    ckepler!(dx, x, p, t)
    return dx
end

function pkepler!(dx, x, p, t)
    dx[1] = x[2]
    dx[2] = x[1]*x[4]^2 - kh/(x[1]^2)
    dx[3] = x[4]
    dx[4] = -2*x[2]*x[4]/x[1]
end
function pkepler(x, p, t)
    dx = similar(x)
    pkepler!(dx, x, p, t)
    return dx
end

function vdp!(dx, x, p, t)
    dx[1] = x[2]
    dx[2] = (1-x[1]^2)*x[2] - x[1]
end
function vdp(x, p, t)
    dx = similar(x)
    vdp!(dx, x, p, t)
    return dx
end

function basic!(dx, x, p, t)
    A = [0. -0.95; 0.95 0.]
    dx[:] = A * x
end

function basic(x, p, t)
    dx = similar(x)
    basic!(dx, x, p, t)
    return dx
end


using DelimitedFiles, SharedArrays, Random, LinearAlgebra, Clustering, Base
using DataFrames, CSV
using Statistics, ArgParse

nmats = 100

function max_eigv(gene) #returns largest eigenvalue of a randomized matrix generated from data.
    gran = zeros(size(gene))
    for i = 1:size(gene,2)
        gran[:,i] = gene[shuffle(1:end),i]
    end
 #    @time mr = corr_mat(gran)
    # println(mr[[end-1],end])
    mr = cor(transpose(gran))
    # println(mr[[end-1],end])
    return eigvals(mr)
end

parsed_args = Dict([("c", true), ("expfile", "Data/clean/Basal-clean.tsv")])

##============ 0.5 - Input
#! gname s1 s2 ... gstart chromname
df = CSV.read(parsed_args["expfile"], delim='\t')

df = df[df.chromname .== "17", :]
gene = convert(Array{Float64,2},df[:, 2:end-2])

evs = SharedArray{Float64, 2}((nmats,size(gene)[1]))
@time for i in 1:nmats
    @time evs[i,:] = max_eigv(gene) # return array of ngenes size
end
# All randomized eigenvalues
new_evs = vec(evs)
new_evs = filter(x->x>0.0000001, new_evs) #Drop Zeros

# Original eigenvalues
mr = cor(transpose(gene))
mr_e = eigvals(mr)
mr_e = filter(x->x>0.0000001, mr_e) #Drop Zeros

using PyPlot

#Figura 2a
plt.clf()
# color = '0.75'
# color = '#eeefff'
plt.hist(new_evs,normed=true,bins=100,label="Randomized",color = [:C1])
# grid("on")
xlabel("eigenvalue")
ylabel("Probability density")
legend()
ax = gca()
ax.spines["top"].set_visible(false)
ax.spines["right"].set_visible(false)
plot!(size=(1200,800))
savefig("Basal-ch17-eigendist.png")

#Figura 2b
plt.clf()
plt.hist(mr_e,normed=true,bins=100,label="Empirical data")
plt.hist(new_evs,normed=true,bins=100,alpha=0.7,label="Randomized")
# grid("on")
xlabel("eigenvalue")
ylabel("Probability density")
legend()
ax = gca()
ax.spines["top"].set_visible(false)
ax.spines["right"].set_visible(false)

# using GR
# histogram(evs)
# readline()

mr = cor(transpose(gene))
mr_e = eigvals(mr)


# Run mixture c-posterior on real-data examples
module RealData
using Distributions
using PyPlot
draw() = (pause(0.001); get_current_fig_manager()[:window][:raise_]())

# Settings
alphas = [20,100,500,1000,Inf]  # robustification params to use
Ns = [10^5,10^5,10^5,10^5,10^6]  # number of MCMC iterations for each alpha
nburns = [10^4,10^4,10^4,10^4,2.5*10^5]  # number of iterations to discard as burn-in for each alpha
data_ID = "Shapley"  # which dataset to use
m = 20 # maximum number of components

# Load data
if data_ID=="birth"
    dataset = readdlm("datasets/gestday.dat",' ')
    xmin,xmax = 180,380
elseif data_ID=="Shapley"
    dataset = readdlm("datasets/Shapley_galaxy.dat",' ')
    dataset = convert(Array{Float64,1}, dataset[:,4])/1000
    xmin,xmax = -5,50
elseif data_ID=="height"
    #D = readdlm("datasets/height_DEFG.dat",' ')
    D = readdlm("datasets/height_FG.dat",' ')
    subset = (18 .<= D[:,1])  # ages 18+
    dataset = vec(D[subset, 2])
    dataset = 0.393701*dataset  # convert from centimeters to inches
    xmin,xmax = minimum(dataset),maximum(dataset)
end
x = convert(Array{Float64,1}, dataset[:])
n = length(x)

# Stuff for mixture weights
a = 1/m # Gamma parameters
G = Gamma(a,1/1) # prior on weights (before conditioning on s>0)
lambda = 1.0  # Poisson parameter of the limiting prior distribution on k as m -> Inf
c = quantile(G,1-lambda/m)  # cutoff (Note: This choice makes p(k) \propto Binomial(k|m,lambda/m)I(k>0).)
sigma = 0.25 # scale for weight proposals
log_v_prior(v_i) = logpdf(G,v_i) # prior on latent weights v_i

# Stuff for mixture components
m0m,s0m = mean(x),std(x)  # data-dependent choices here
m0l = log(4/var(x))  # expect stddev of components to be around 1/2 the stddev of the overall data
s0l = 2.0

# Inference stuff
normpdf(x,m,l) = sqrt(l/(2*pi))*exp(-0.5*l*(x-m).*(x-m))
normlogpdf(x,m,s) = -0.5*log(2*pi) - log(s) - 0.5*(x-m)*(x-m)/(s*s)
likelihood(x,t) = normpdf(x,t[1],t[2])  # t=[mean,precision]
log_theta_prior(t) = normlogpdf(t[1],m0m,s0m) + normlogpdf(log(t[2]),m0l,s0l)
sm,sl = 0.2*s0m, 0.2
theta_prop(t) = Float64[t[1]+sm*randn(), t[2]*exp(randn()*sl)]
log_theta_prop(t,tp) = normlogpdf(tp[1],t[1],sm) + normlogpdf(log(tp[2]),log(t[2]),sl)
new_thetas(m) = [Float64[m0m,exp(m0l)] for i=1:m]

# Sampler code
include("core.jl")

# Run sampler
n_alphas = length(alphas)
k_posteriors = zeros(m,n_alphas)
for (i_a,alpha) in enumerate(alphas)

    # Run sampler
    n_samples = Ns[i_a]
    nburn = nburns[i_a]
    zeta = (1/n)/(1/n + 1/alpha)
    p,theta,k_r,v_r,art,arv,m_r,s_r = sampler(x,n_samples,m,c,sigma,zeta)

    # Compute posterior on k
    edges,counts = hist(k_r[nburn+1:end],[0:m])
    k_posteriors[:,i_a] = counts/(n_samples-nburn)

    # Plot traceplot of k
    figure(10,figsize=(8,3.2)); clf(); hold(true)
    plot(k_r,"k.",markersize=2)
    savefig("real-traceplot-alpha=$alpha.png",dpi=150)
    #draw()

    # Plot typical sample density
    figure(i_a,figsize=(8,3.2)); clf(); hold(true)
    subplots_adjust(bottom=0.2)
    xs = linspace(xmin,xmax,1000)
    density = zeros(length(xs))
    for i = 1:m
        if p[i]>0
            pixs = p[i]*normpdf(xs,theta[i][1],theta[i][2])
            density += pixs
            plot(xs,pixs)
        end
    end
    plot(xs,density,"k--",linewidth=3)
    xlim(xmin,xmax)
    if alpha<Inf; title("\$\\alpha = $(repr(int(alpha)))\$",fontsize=17)
    else; title("\$\\alpha = \\infty\$",fontsize=17)
    end
    xlabel("\$x\$",fontsize=16)
    ylabel("\$\\mathrm{density}\$",fontsize=15)
    #draw()
    savefig("real-density-alpha=$alpha.png",dpi=150)
end

# Plot posterior on k
figure(11,figsize=(8,3.2)); clf(); hold(true)
subplots_adjust(bottom=0.2)
colors = "bgyrm"
shapes = "ds^v*"
for (i_a,alpha) in enumerate(alphas)
    lab = "\$\\alpha = " * (alpha<Inf ? "$(repr(int(alpha)))\$" : "\\infty\$")
    plot(1:m,vec(k_posteriors[:,i_a]),"$(colors[i_a])$(shapes[i_a])-",label=lab,markersize=8,linewidth=2)
end
xlim(0,m)
xticks(1:m)
xlabel("\$k \\,\\,\\mathrm{(\\#\\,of\\,components)}\$",fontsize=17)
ylabel("\$\\Pi(k|\\mathrm{data})\$",fontsize=15)
legend(loc="upper right",numpoints=1)
draw()
savefig("real-k-posteriors.png",dpi=150)

# Plot data distribution
figure(12,figsize=(8,3.2)); clf(); hold(true)
subplots_adjust(bottom=0.2)
edges,counts = hist(x,linspace(xmin,xmax,81))
dx = edges[2]-edges[1]
bar(edges[1:end-1],(counts/n)/dx,dx,color="w")
xlim(xmin,xmax)
title("\$\\mathrm{Data\\,distribution}\$",fontsize=17)
xlabel("\$x\$",fontsize=16)
ylabel("\$\\mathrm{density}\$",fontsize=15)
draw()
savefig("real-data.png",dpi=150)

end # module


@testset "7.6 Inverse hyperbolic cosecant" begin
    include("7.6.1 u (a+b arccsch(c x))^n.jl")
    include("7.6.2 Inverse hyperbolic cosecant functions.jl")
end

test/7 Inverse hyperbolic functions/7.6 Inverse hyperbolic cosecant/.jl


export blockCG

function blockCG{T1,T2}(A::SparseMatrixCSC{T1,Int},B::Array{T2,2}; kwargs...)
	X = zeros(promote_type(T1,T2),size(A,2),size(B,2)) # pre-allocate
	return blockCG(V -> At_mul_B!(1.0,A,V,0.0,X),B;kwargs...) # multiply with transpose of A for efficiency
end

blockCG(A,B::Array;kwargs...) = blockCG(X -> A*X,B;kwargs...)

"""
blockCG(A,B,X)

Preconditioned Conjugate Gradient Method for solving

A * X = B

Input:

	A           - matrix or function computing A*x
	B           - array of right hand sides
	tol         - error tolerance
	maxIter     - maximum number of iterations
	M           - preconditioner, a function that computes M\\x
	X           - array of starting guesses (will be overwritten)
	out         - flag for output (-1: no output, 0: only errors, 1: final status, 2: residual norm at each iteration)
	ortho       - flag for re-orthogonalization (default: false)
	pinvTol     - tolerance for pseudoinverse (default: eps(T)*size(B,1))
	storeInterm - flag for storing iterates (default: false)

Output:

  X       - approximate solutions (2D array if !storeInterm, 3D array else)
  flag    - exit flag ( 0 : desired tolerance achieved,
  					 -1 : maxIter reached without converging
  					 -9 : right hand side was zero)
  err     - norm of relative residual, i.e., norm(A*X-B)/norm(B)
  iter    - number of iterations
  resvec  - norm of relative residual at each iteration

Reference:

	O'Leary, D. P. (1980). The block conjugate gradient algorithm and related methods.
	Linear Algebra and Its Applications, 29, 293–322. http://doi.org/10.1016/0024-3795(80)90247-5

"""
function blockCG{T<:AbstractFloat}(A::Function,B::Array{T};X=zeros(T,size(B)),M::Function=identity,maxIter=20,tol=1e-2,ortho::Bool=false,pinvTol =eps(T)*size(B,1),out::Int=0,storeInterm::Bool=false)

if norm(B)==0; return zeros(eltype(B),size(B)),-9,0.0,0,[0.0]; end

R = copy(B)
Z = M(R)
if ortho
    P, = mgs(Z);
else
	P = copy(Z); #P must be different memory than Z and R.
end

n, nrhs = size(X)
nB      = computeNorm(B)
resmat  = zeros(maxIter,nrhs)

if storeInterm
    Xout = zeros(n,nrhs,maxIter)	# allocate space for intermediates
end

if out==2
	println("=== blockcg ===")
	println(@sprintf("%4s\t%7s","iter","max(relres)"))
end

# pre-allocate
PTQ = zeros(T,nrhs,nrhs)
PTR = zeros(T,nrhs,nrhs)
QTZ = zeros(T,nrhs,nrhs)

flag = -1;
iter = 0
for iter=1:maxIter
	Q = A(P)
    # PTQ   = P'*Q
	BLAS.gemm!('T','N',1.0,P,Q,0.0,PTQ)

    # Alpha = (PTQ)\(P'*R);
    BLAS.gemm!('T','N',1.0,P,R,0.0,PTR)
	pinvPTQ = getPinv!(PTQ,pinvTol)
    Alpha = pinvPTQ*PTR

    # X     += P*Alpha
    BLAS.gemm!( 'N','N',1.0, P, Alpha, 1.0, X)
	if storeInterm; Xout[:,:,iter] = X; end
    # R     -= Q*Alpha
    BLAS.gemm!('N','N',-1.0,Q,Alpha,1.0,R)

    resmat[iter,:] = computeNorm(R)./nB
    if out==2
		println(@sprintf("%3d\t%1.2e",iter,maximum(resmat[iter,:])))
	end
    if maximum(resmat[iter,:]) < tol
	    flag = 0;
        break;
    end

    Z     = M(R)
    #Beta  = -(PTQ)\(Q'*Z);
	BLAS.gemm!('T','N',1.0,Q,Z,0.0,QTZ)
    Beta  = -pinvPTQ*QTZ

	# Z might be just R here - don't overwite it! Q is not needed.
	Q[:] = Z;
	BLAS.gemm!('N','N',1.0,P,Beta,1.0,Q);
	P[:] = Q;

    if ortho
        P,     =  mgs!(P)
 	end
end
if out>=0
	if flag==-1
		println(@sprintf("blockCG iterated maxIter (=%d) times but reached only residual norm %1.2e instead of tol=%1.2e.",																							maxIter,maximum(resmat[iter,:]),tol))
	elseif flag==0 && out>=1
		println(@sprintf("blockCG achieved desired tolerance at iteration %d. Residual norm is %1.2e.",iter,maximum(resmat[iter,:])))
	end
end
if storeInterm
    return Xout[:,:,1:iter],flag,resmat[iter,:],iter,resmat[1:iter,:]
else
    return X,flag,resmat[iter,:],iter,resmat[1:iter,:]
end
end

function computeNorm(R)
	n,nrhs = size(R)
	res    = zeros(nrhs)
	for k=1:nrhs
		for i=1:n
			res[k]+=R[i,k]*R[i,k]
		end
	end
	return sqrt(res)
end

function getPinv!(A,pinvTol)
	SVD         = svdfact!(A)
	Sinv        = zeros(length(SVD.S))
    index       = SVD.S .> pinvTol*maximum(SVD.S)
    Sinv[index] = 1.0./ SVD.S[index]
    Sinv[find(!isfinite(Sinv))] = 0.0
    return SVD.Vt'scale(Sinv, SVD.U')
end


using Test
using AbstractLattices

function (∨)(a::Number,b::Number)
    return max(a,b)
end

function (∧)(a::Number,b::Number)
    return min(a,b)
end

@test 5 ∧ 10 == 5
@test 5 ∨ 10 == 10


module Dec3Part1
slope = [3,1]

function get_level(pattern, level)
    return pattern[level]
end

function get_x_value(level, index)
    n_l = length(level)
    if index <= n_l
        return index
    else
        return index - n_l
    end
end
function get_next_point(current_point, slope, pattern)
    x_ref, y_ref = current_point + slope
    n_level = y_ref
    level = get_level(pattern, n_level)
    x = get_x_value(level, x_ref)
    y = y_ref
    return [x,y]
end

function get_marker_point(pattern, current_point)
    row, col = current_point
    return pattern[col][row]
end

function get_n_trees(pattern)
    global slope
    initial_point = [1,1]
    n_max = length(pattern)
    current_point = initial_point
    counter = 0
    while current_point[2] < n_max
        current_point = get_next_point(current_point, slope, pattern)
        marker = get_marker_point(pattern, current_point)
        if marker == '#'
            counter += 1
        end
    end
    return counter
end
function main(file_path::String)
    lines = readlines(file_path)
    return get_n_trees(lines)
end

end


"""

Calculate the parent probabilites of each event.

    parent_probs(events, bg, kappa, kern)

# Arguments

* `events` Array of event times.
* `bg` Background intensity
* `kappa` Child excitation parameter.
* `kern` Kernel decay function

# Examples

```julia
kern_f(x) = pdf.(Distributions.Exponential(1/0.5), x)
parent_probs([1,2,3], 0.5, 0.5, )
```

"""
function parent_probs(events::Array{<:Number, 1}, bg, kappa::Float64, kern::Function)
    eventdifferencelist = event_difference_list(events)
    parent_probs(events, bg, kappa, kern, eventdifferencelist)
end

function parent_probs(events::Array{<:Number, 1}, bg, kappa::Float64, kern::Function, eventdifferencelist)
    parentprobs = Array{Array{Float64, 1}}(undef, length(events))
    for i in 1:length(events)
        parentprobs[i] = event_parent_prob(events[i], bg, kappa, kern, eventdifferencelist[i])
    end
    return parentprobs
end

function event_parent_prob(event::Number, bg::Number, kappa::Float64, kern::Function, eventdifference::Array{<:Number, 1})
#    probs = Array{Float64, 1}(undef, length(eventdifference) + 1)
#    probs[1] = bg
#    probs[2:end] .= kappa * kern.(eventdifference)
#    probs
    n = length(eventdifference)
    probs = Array{Float64, 1}(undef, n + 1)
    probs[1] = bg
    for i = 2:(n+1)
        probs[i] = kappa * kern(eventdifference[i-1])
    end
    probs
end

function event_parent_prob(event::Number, bg::Function, kappa::Float64, kern::Function, eventdifference::Array{<:Number, 1})
    event_parent_prob(event, bg(event), kappa, kern, eventdifference)
end


include("helpers.jl")

function laff_axpy(alpha::Number, x::T, y::T) where T <: Matrix{Number}

    my, ny = verifydimensions(x, y)

    # Redimensionar o x para o formato de y
    x = reshape(x, my, ny)

    x * alpha .+ y
end

## Testar ##
x = Matrix([1,2,3], 3, 1)
y = Matrix([4,5,6], 1, 3)

laff_axpy(0.5, x, y)


abstract type AbstractLifeRule <: AbstractTDRule end

struct Life{T,A,C} <: AbstractLifeRule
    born::T
    survive::A
    radius::C
end

"""
Life(life_description; radius=1)

Returns a ```Life``` object given a tuple of tuples that follows the Golly notation ((b), (s)), where b stands for birth and s for survival.
These values indicates the number of neighbouring cells needed to birth a new one in the following generation, or to make the current alive
one survive.
"""
function Life(life_description; radius=1)
    born, survive = life_description[1], life_description[2]
    Life(born, survive, radius)
end

function (life::Life)(starting_array)
    return nextgen = life_evolution(starting_array, life.born, life.survive, life.radius)
end

function virtual_expansion(starting_array, radius)

    height, width = size(starting_array)
    nh, nw = height-radius+1, width-radius+1
    left = vcat(starting_array[nh:end,nw:end], starting_array[:,nw:end], starting_array[1:radius, nw:end])
    right = vcat(starting_array[nh:end, 1:radius], starting_array[:,1:radius], starting_array[1:radius,1:radius])
    middle = vcat(starting_array[nh:end,:], starting_array, starting_array[1:radius,:])

    hcat(left, middle, right)
end

function life_application(state, born, survive)

    past_value = state[size(state, 1)÷2+1, size(state, 2)÷2+1] #save past cell value
    state[size(state, 1)÷2+1, size(state, 2)÷2+1] = 0 #past cell value set to zero
    alive = sum(state) #the sum is the number of cell alive in the neighborhood since the central value is equal to zero

    if past_value == 1 && alive in survive
        return 1
    elseif past_value == 0 && alive in born
        return 1
    else
        return 0
    end
end

function life_evolution(starting_array, born, survive, radius)

    height, width = size(starting_array)
    output = zeros(typeof(starting_array[2]), height, width)
    virtual_output = virtual_expansion(starting_array, radius)

    for i = 1:height-radius+1, j = 1:width-radius+1
        output[i, j] = life_application(virtual_output[i:i+radius+1, j:j+radius+1], born, survive)
    end
    output
end


module BenchPartitionBy

using Statistics
using BenchmarkTools
using Transducers

function manual_partition_by(xs)
    acc = zero(eltype(xs))
    buffer = similar(xs, 0)
    pval = xs[1] > 0
    push!(buffer, xs[1])
    for x in @view xs[2:end]
        val = x > 0
        if val == pval
            push!(buffer, x)
        else
            if mean(abs, buffer) < 1.0
                acc += prod(buffer)
            end
            empty!(buffer)
            push!(buffer, x)
            pval = val
        end
    end
    if mean(abs, buffer) < 1.0
        acc += prod(buffer)
    end
    return acc
end

suite = BenchmarkGroup()

xf = PartitionBy(x -> x > 0) |>
    Filter(xs -> mean(abs, xs) < 1.0) |>
    Map(prod)

n = 10^5

let xs = randn(100)
    @assert manual_partition_by(xs) == mapfoldl(xf, +, xs, init=0.0)
end

suite["xf"] = @benchmarkable(
    mapfoldl($xf, +,  xs, init=0.0),
    setup=(xs = randn($n)))
suite["man"] = @benchmarkable(
    manual_partition_by(xs),
    setup=(xs = randn($n)))

end  # module
BenchPartitionBy.suite


using InvariantMeasures
using ValidatedNumerics
using LinearAlgebra

@testset "Hat assembler" begin

D = Mod1Dynamic(x->2*x)
B = Hat(8)
P = assemble(B, D)

Ptrue = [
        0.5 0.25 0    0    0    0    0   0.25;
        0   0.25 0.5  0.25 0    0    0   0   ;
        0   0    0    0.25 0.5  0.25 0   0   ;
        0   0    0    0    0    0.25 0.5 0.25;
        0.5 0.25 0    0    0    0    0   0.25;
        0   0.25 0.5  0.25 0    0    0   0   ;
        0   0    0    0.25 0.5  0.25 0   0   ;
        0   0    0    0    0    0.25 0.5 0.25;
		]
Ptrue = Ptrue'

@test all(contains_zero.(P-Ptrue))

@test opnormbound(L1, DiscretizedOperator(B, D)) == 1
@test opnormbound(Linf, DiscretizedOperator(B, D)) == 1

Q = DiscretizedOperator(B, D)
@test size(Q) == (8,8)

n = size(Q)[1]
e = randn(n)
x = similar(e)
x[:] .= 2.

mQ = mid(Q)
@test mul!(x, mQ, e, 1, 0) == mQ*e

fQ = mQ.L + mQ.e * mQ.w
@test fQ * e ≈ mQ * e

end


function erdos_renyi_test()
    @test_throws DomainError erdos_renyi_undirected(10,11.)
    @test_throws DomainError erdos_renyi_directed(10,11.)

    n = 100
    avgdegs = linspace(1.,2*log(n),100)
    compsizes = map( (dbar) ->
            maximum(scomponents(erdos_renyi_undirected(n,dbar)).sizes),
        avgdegs )

    @test is_undirected(erdos_renyi_undirected(10,2.))

    erdos_renyi_undirected(5, 0.)
    erdos_renyi_undirected(5, 1.)
    @test all(diag(sparse_transpose(erdos_renyi_undirected(10, 0.5))) .== 0.)
    erdos_renyi_directed(5, 0.)
    erdos_renyi_directed(5, 1.)
    @test all(diag(sparse_transpose(erdos_renyi_directed(10, 0.5))) .== 0.)

end

function chung_lu_test()
    @test_throws ArgumentError chung_lu_undirected([1,2],2)
    @test_throws ArgumentError chung_lu_undirected([1,3])
    @test size(chung_lu_undirected([1,2]),1) == 2
end

function havel_hakimi_test()
    @test is_graphical_sequence([1,1]) == true
    @test is_graphical_sequence([0,1,1]) == true
    @test is_graphical_sequence([2,2,2]) == true
    @test is_graphical_sequence([1,1,2]) == true
    @test is_graphical_sequence([1,2,2]) == false
    @test is_graphical_sequence([1,1,3]) == false
    @test is_graphical_sequence([0]) == true
    @test is_graphical_sequence(Int[]) == true
    @test is_graphical_sequence([1,1,1,1]) == true

    @test full(sparse_transpose(havel_hakimi_graph(Int[]))) == zeros(Bool,0,0)
    @test full(sparse_transpose(havel_hakimi_graph([0]))) == zeros(Bool,1,1)
    @test full(sparse_transpose(havel_hakimi_graph([1,1]))) == [0 1; 1 0]
    @test full(sparse_transpose(havel_hakimi_graph([2,2,2]))) == [0 1 1; 1 0 1; 1 1 0]
    six_star = [0 1 1 1 1 1; 1 0 0 0 0 0; 1 0 0 0 0 0; 1 0 0 0 0 0; 1 0 0 0 0 0; 1 0 0 0 0 0]
    @test full(sparse_transpose(havel_hakimi_graph([5,1,1,1,1,1]))) == six_star

   @test_throws ArgumentError is_graphical_sequence([-1])
   @test_throws ArgumentError havel_hakimi_graph([1,2,2])

end

function generators_test()

erdos_renyi_test()
chung_lu_test()
havel_hakimi_test()
return true

end


let
	bn = BayesNet()
	@test length(bn) == 0

	push!(bn, StaticCPD(:a, Normal(0.0,1.0)))

	@test length(bn) == 1
	@test name(get(bn, :a)) == :a
	@test name(get(bn, 1)) == :a
	@test names(bn) == [:a]
	@test parents(bn, :a) == NodeName[]
	@test children(bn, :a) == NodeName[]
	@test neighbors(bn, :a) == NodeName[]
	@test descendants(bn, :a) == NodeName[]
	@test pdf(bn, Assignment(:a=>0.0)) == pdf(Normal(0.0,1.0), 0.0)
	@test logpdf(bn, Assignment(:a=>0.0)) == logpdf(Normal(0.0,1.0), 0.0)
	@test isapprox(pdf(bn, DataFrame(a=[0.0, 1.0])), pdf(Normal(0.0,1.0), 0.0) * pdf(Normal(0.0,1.0), 1.0))
	@test isapprox(logpdf(bn, DataFrame(a=[0.0, 1.0])), logpdf(Normal(0.0,1.0), 0.0) + logpdf(Normal(0.0,1.0), 1.0))

	# b = 2a + 1
	push!(bn, LinearGaussianCPD(:b, [:a], [2.0], 1.0, 1.0))
	@test length(bn) == 2
	@test name(get(bn, :b)) == :b
	@test name(get(bn, 2)) == :b
	@test has_edge(bn, :a, :b)
	@test names(bn) == [:a, :b]
	@test parents(bn, :a) == NodeName[]
	@test children(bn, :a) == [:b]
	@test neighbors(bn, :a) == [:b]
	@test descendants(bn, :a) == [:b]
	@test parents(bn, :b) == [:a]
	@test children(bn, :b) == NodeName[]
	@test neighbors(bn, :b) == [:a]
	@test descendants(bn, :b) == NodeName[]
	@test pdf(bn, Assignment(:a=>1.0, :b=>2.0)) == pdf(Normal(0.0,1.0), 1.0) * pdf(Normal(3.0, 1.0), 2.0)
	@test logpdf(bn, Assignment(:a=>1.0, :b=>2.0)) == logpdf(Normal(0.0,1.0), 1.0) + logpdf(Normal(3.0, 1.0), 2.0)

	# test removal
	delete!(bn, :b)
	@test length(bn) == 1
	@test name(get(bn, :a)) == :a
	@test name(get(bn, 1)) == :a
	@test !has_edge(bn, :a, :b)
	@test names(bn) == [:a]
	@test parents(bn, :a) == NodeName[]
	@test children(bn, :a) == NodeName[]
end

let
	bn = BayesNet()
	push!(bn, StaticCPD(:A, Normal(0.0,1.0)))
	push!(bn, StaticCPD(:B, [:A], Normal(0.0,1.0)))
	push!(bn, StaticCPD(:D, [:A], Normal(0.0,1.0)))
	push!(bn, StaticCPD(:C, [:A, :B, :D], Normal(0.0, 1.0)))
	push!(bn, StaticCPD(:F, Normal(0.0,1.0)))
	push!(bn, StaticCPD(:E, [:F], Normal(0.0,1.0)))
	@test is_independent(bn, [:B], [:D], [:A])
	@test !is_independent(bn, [:B], [:D], [:C])
	@test !is_independent(bn, [:B], [:D], [:E])
	@test is_independent(bn, [:B], [:C], [:A])
end

let
	bn = BayesNet()
	push!(bn, StaticCPD(:B, Normal(0.0,1.0)))
	push!(bn, StaticCPD(:S, Normal(0.0,1.0)))
	push!(bn, StaticCPD(:E, [:B, :S], Normal(0.0,1.0)))
	push!(bn, StaticCPD(:D, [:E], Normal(0.0,1.0)))
	push!(bn, StaticCPD(:C, [:E], Normal(0.0,1.0)))
	@test !is_independent(bn, [:B], [:S], [:E, :D])
	@test !is_independent(bn, [:B], [:S], [:C])
end


# This file is part of IntegerSequences.
# Copyright Peter Luschny. License is MIT.

(@__DIR__) ∉ LOAD_PATH && push!(LOAD_PATH, (@__DIR__))

module StirlingNumbers

using Nemo, IterTools, Triangles

export ModuleStirlingNumbers
export I132393, L132393, V132393, M132393
export I048993, L048993, V048993, M048993
export I271703, L271703, V271703, M271703
export I094587, L094587, V094587, M094587
export I008279, L008279, V008279, M008279
# export Lah, StirlingSet, StirlingCycle

"""
* I132393, L132393, V132393, M132393, I048993, L048993, V048993, M048993, I271703, L271703, V271703, M271703, I094587, L094587, V094587, M094587, I008279, L008279, V008279, M008279
"""
const ModuleStirlingNumbers = ""

"""
Recurrence for A132393, StirlingCycle numbers.
"""
function R132393(n::Int, k::Int, prevrow::Function)
    (k == 0 && n == 0) && return ZZ(1)
    (n - 1)*prevrow(k) + prevrow(k - 1)
end
"""
Recurrence for A048993, StirlingSet numbers.
"""
function R048993(n::Int, k::Int, prevrow::Function)
    (k == 0 && n == 0) && return ZZ(1)
    k*prevrow(k) + prevrow(k - 1)
end
"""
Recurrence for A271703, Lah numbers.
"""
function R271703(n::Int, k::Int, prevrow::Function)
    (k == 0 && n == 0) && return ZZ(1)
    (k - 1 + n)*prevrow(k) + prevrow(k - 1)
end
"""
Recurrence for A094587.
"""
function R094587(n::Int, k::Int, prevrow::Function)
    (k == 0 && n == 0) && return ZZ(1)
    (n - k)*prevrow(k) + prevrow(k - 1)
end
"""
Recurrence for A008279. Number of permutations of n things k at a time.
"""
function R008279(n::Int, k::Int, prevrow::Function)
    (k == 0 && n == 0) && return ZZ(1)
    prevrow(k) + k*prevrow(k - 1)
end
"""
Iterates over the first n rows of `A132393`.
"""
I132393(n) = RecTriangle(n, R132393)
"""
Iterates over the first n rows of `A048993`.
"""
I048993(n) = RecTriangle(n, R048993)
"""
Iterates over the first n rows of `A271703`.
"""
I271703(n) = RecTriangle(n, R271703)
"""
Iterates over the first n rows of `A094587`.
"""
I094587(n) = RecTriangle(n, R094587)
"""
Iterates over the first n rows of `A008279`.
"""
I008279(n) = RecTriangle(n, R008279)
"""
Lists the first n rows of `A132393` by concatenating.
"""
L132393(n) = vcat(I132393(n)...)
"""
Lists the first n rows of `A048993` by concatenating.
"""
L048993(n) = vcat(I048993(n)...)
"""
Lists the first n rows of `A271703` by concatenating.
"""
L271703(n) = vcat(I271703(n)...)
"""
Lists the first n rows of `A094587` by concatenating.
"""
L094587(n) = vcat(I094587(n)...)
"""
Lists the first n rows of `A008279` by concatenating.
"""
L008279(n) = vcat(I008279(n)...)
"""
Return the triangular array as a square matrix with dim rows.
"""
M132393(dim) = fromΔ(I132393(dim))
"""
Return the triangular array as a square matrix with dim rows.
"""
M048993(dim) = fromΔ(I048993(dim))
"""
Return the triangular array as a square matrix with dim rows.
"""
M271703(dim) = fromΔ(I271703(dim))
"""
Return the triangular array as a square matrix with dim rows.
"""
M094587(dim) = fromΔ(I094587(dim))
"""
Return the triangular array as a square matrix with dim rows.
"""
M008279(dim) = fromΔ(I008279(dim))
"""
Return row n of A132393 based on the iteration `I132393`(n).
"""
V132393(n) = nth(I132393(n+1), n+1)
"""
Return row n of A048993 based on the iteration `I048993`(n).
"""
V048993(n) = nth(I048993(n+1), n+1)
"""
Return row n of A271703 based on the iteration `I271703`(n).
"""
V271703(n) = nth(I271703(n+1), n+1)
"""
Return row n of A094587 based on the iteration `I094587`(n).
"""
V094587(n) = nth(I094587(n+1), n+1)
"""
Return row n of A008279 based on the iteration `I008279`(n).
"""
V008279(n) = nth(I008279(n+1), n+1)

#START-TEST-########################################################

using Test, Combinatorics

function test()

    @testset "Stirling1" begin

        for (n, S1) in enumerate(I132393(10))
            SC = [stirlings1(n-1, k) for k in 0:n-1]
            @test S1 == SC
        end
    end

    @testset "Stirling2" begin

        for (n, S2) in enumerate(I048993(10))
            SC = [stirlings2(n-1, k) for k in 0:n-1]
            @test S2 == SC
        end
    end
end # test

function demo()
    for row in I132393(9) println(row) end
    println()

    for n in 0:8 println(n, ": ", V132393(n)) end
    println()

    ShowAsΔ(I132393(9), "\t")

    println(L132393(9))
    println()

    ShowAsMatrix(I132393(9))
    println()

    println(M132393(9))
    println()

    println(V132393(8))
    println()

    # ===================================

    for row in I048993(9) println(row) end
    println()

    for n in 0:8 println(n, ": ", V048993(n)) end
    println()

    ShowAsΔ(I048993(9), "\t")

    println(L048993(9))
    println()

    ShowAsMatrix(I048993(9))
    println()

    println(M048993(9))
    println()

    println(V048993(8))
    println()

    # ===================================

    for row in I008279(9) println(row) end
    println()

    for n in 0:8 println(n, ": ", V008279(n)) end
    println()

    ShowAsΔ(I008279(9), "\t")

    println(L008279(9))
    println()

    ShowAsMatrix(I008279(9))
    println()

    println(M008279(9))
    println()

    println(V008279(8))
    println()
end

"""
(for row in I132393(500) end)
    0.246288 seconds (379.25 k allocations: 8.748 MiB)
"""
function perf()
    GC.gc()
    @time (for row in I132393(500) end)
    GC.gc()
    @time (for row in I048993(500) end)
    GC.gc()
    @time (for row in I271703(500) end)
end

function main()
    test()
    demo()
    perf()
end

main()

end # module

#=
1;
0,    1;
0,    1,     1;
0,    2,     3,     1;
0,    6,    11,     6,    1;
0,   24,    50,    35,   10,    1;
0,  120,   274,   225,   85,   15,   1;
0,  720,  1764,  1624,  735,  175,  21,  1;
0, 5040, 13068, 13132, 6769, 1960, 322, 28, 1;

1
0  1
0  1   1
0  1   3     1
0  1   7     6     1
0  1   15    25    10     1
0  1   31    90    65     15     1
0  1   63    301   350    140    21    1
0  1   127   966   1701   1050   266   28   1

1
0  1
0  2       1
0  6       6       1
0  24      36      12      1
0  120     240     120     20      1
0  720     1800    1200    300     30      1
0  5040    15120   12600   4200    630     42    1
0  40320   141120  141120  58800   11760   1176  56   1

1
1       1
2       2       1
6       6       3       1
24      24      12      4       1
120     120     60      20      5      1
720     720     360     120     30     6     1
5040    5040    2520    840     210    42    7    1
40320   40320   20160   6720    1680   336   56   8    1

1
1  1
1  2  2
1  3  6   6
1  4  12  24   24
1  5  20  60   120  120
1  6  30  120  360  720   720
1  7  42  210  840  2520  5040  5040
1  8  56  336  1680 6720  20160 40320  40320
=#


"""
# Usage

    solve(FDDE::FDDEProblem, h, DelayPI())

Use explicit rectangular product integration algorithm to solve an FDDE problem.

### References

```tex
@article{2020,
   title={On initial conditions for fractional delay differential equations},
   ISSN={1007-5704},
   url={http://dx.doi.org/10.1016/j.cnsns.2020.105359},
   DOI={10.1016/j.cnsns.2020.105359},
   journal={Communications in Nonlinear Science and Numerical Simulation},
   author={Garrappa, Roberto and Kaslik, Eva},
   year={2020},
}
```
"""
struct DelayPI <: FractionalDiffEqAlgorithm end

function solve(FDDE::FDDEProblem, h, ::DelayPI)
    @unpack f, ϕ, α, τ, tspan = FDDE
    t0 = tspan[1]; T = tspan[2]
    N = ceil(Int, (T-t0)/h)
    t = t0 .+ h*collect(0:N)

    nn_al = collect(0:N).^α
    b = [0; nn_al[2:end].-nn_al[1:end-1]]/gamma(α+1)
    h_al = h^α

    y0 = ϕ(t0)
    y = zeros(N+1)

    g = zeros(N+1)
    y[1] = y0

    for n = 1:N
        tnm1 = t[n]
        if tnm1 <= τ
            y_nm1_tau = ϕ(tnm1-τ)
        else
            nm1_tau1 = floor(Int, (tnm1-τ)/h)
            nm1_tau2 = ceil(Int, (tnm1-τ)/h)
            if nm1_tau1 == nm1_tau2
                y_nm1_tau = y[nm1_tau1+1]
            else
                tt0 = t[nm1_tau1+1]
                tt1 = t[nm1_tau1+2]
                yy0 = y[nm1_tau1+1]
                yy1 = y[nm1_tau1+2]
                y_nm1_tau = ((tnm1-τ)-tt0)/(tt1-tt0)*yy1 + ((tnm1-τ)-tt1)/(tt0-tt1)*yy0
            end
        end

        g[n] = f(tnm1, y[n], y_nm1_tau)
        f_mem = 0
        for j = 0:n-1
            f_mem = f_mem + g[j+1]*b[n-j+1]
        end
        y[n+1] = y0 + h_al*f_mem
    end
    return y
end


# # Point group 4mm (C₄ᵥ)

# ## Preamble
using LatticeTools
using Formatting: printfmt

function display_matrix(io::IO, matrix::AbstractMatrix; prefix::AbstractString="")
    width = ceil(Int, maximum(length("$item")+1 for item in matrix)/4)*4
    for row in eachrow(matrix)
        for (icol, col) in enumerate(row)
            icol == 1 && print(io, prefix)
            printfmt(io, "{:>$(width)s}", "$col")
        end
        println(io)
    end
end

# ## Irreps of 4mm
point_symmetry = project(PointSymmetryDatabase.get(13), [1 0 0; 0 1 0])
println("symmetry: ", point_symmetry.hermann_mauguin)
println("order: ", group_order(point_symmetry))
println("irreducible_representations:")
for irrep_index in 1:num_irreps(point_symmetry)
    println("- name: GM($irrep_index)")
    println("  elements:")
    for (ename, mat) in zip(element_names(point_symmetry), irrep(point_symmetry, irrep_index))
        println("  - name: \"$ename\"")
        m = Int.(real.(mat))
        if size(m, 1) == 1
            println("    matrix: $(first(m))")
        else
            println("    matrix: |-")
            display_matrix(stdout, m; prefix="      ")
        end
    end
end


"""
Photovoltaic models. Single-diode model (SDM) and double-diode model (DDM).
"""
module PV
using CUDA

export sdm, ddm, sse, sse_sdm, sse_ddm

const k = 1.3806503e-23     # Boltzmann constant
const q = 1.60217646e-19    # electron charge

const k32 = 1.3806503f-23     # Boltzmann constant
const q32 = 1.60217646f-19    # electron charge

"""
    sdm(V, I, T, Iph, I0, n, Rs, Rp)

Single diode model with five parameters
- V, I are the input voltage and current.
- T: temperature in Calvin (K)
- Iph:  photocurrent generated by the solar cell (A)
- I0: the reverse saturation current of the diode (μA)
- n: diode ideality factor
- Rs: series resistance (Ω)
- Rp: parallel resistance (Ω)
"""
function sdm(V, I, T, Iph, I0, n, Rs, Rp)
    tmp = V + I * Rs
    s = 1e-6
    return Iph - I0 * s * (exp(q * tmp / (n * k * T)) - 1) - tmp / Rp
end

function sdm(V::Float32, I::Float32, T::Float32, Iph::Float32, I0::Float32, n::Float32, Rs::Float32, Rp::Float32)
    tmp = V + I * Rs
    s = 1f-6
    return Iph - I0 * s * (exp(q32 * tmp / (n * k32 * T)) - 1) - tmp / Rp
end

"""
    ddm(V, I, T, Iph, I01, I02, n1, n2, Rs, Rp)

Double diode model with seven parameters
- V, I are the input voltage and current.
- T: temperature in Calvin (K)
- Iph:  photocurrent generated by the solar cell (A)
- I01, I02: the reverse saturation current of the diode (μA)
- n1, n2: diode ideality factor
- Rs: series resistance (Ω)
- Rp: parallel resistance (Ω)
"""
function ddm(V, I, T, Iph, I01, I02, n1, n2, Rs, Rp)
    tmp = V + I * Rs
    c = k * T / q
    s = 1e-6
    return Iph - I01 * s * (exp(tmp / (n1 * c)) - 1) - I02 * s * (exp(tmp / (n2 * c)) - 1) - tmp / Rp
end

function ddm(V::Float32, I, T, Iph, I01, I02, n1, n2, Rs, Rp)
    tmp = V + I * Rs
    c = k32 * T / q32
    s = 1f-6
    return Iph - I01 * s * (exp(tmp / (n1 * c)) - 1) - I02 * s * (exp(tmp / (n2 * c)) - 1) - tmp / Rp
end

"""
    sse(data::AbstractMatrix, T::AbstractFloat, model::Function, x::AbstractVector)

For a given parameter vector `x`, compute the SSE (sum of squared error) using the `model` and
experimental voltage-current points in `data`.

- data: each column is a (V, I) point
- T: temperature in Calvin (K)
- model: either `sdm` or `ddm`
- x: a parameter vector
"""
function sse(data::AbstractMatrix{TF}, T::TF, model::Function,
            x::AbstractVector{TF}) where TF <: AbstractFloat
    res = zero(eltype(x))
    for (V, I) in eachcol(data)
        Ic = model(V, I, T, x...)  # calculated current
        res += (Ic - I)^2
    end
    return res
end

"""
    sse_sdm(data::AbstractMatrix{TF}, T::TF, x::AbstractVector{TF}) where TF <: AbstractFloat

Compute SSE for `data` using SDM. This function is intended to be used in a CUDA kernel.
See [`sdm`](@ref) and [`sse`](@ref) for more details.
"""
function sse_sdm(data::AbstractMatrix{TF}, T::TF, x::AbstractVector{TF}) where TF <: AbstractFloat
    res = zero(TF)
    @inbounds for (V, I) in eachcol(data)
        Ic = sdm(V, I, T, x[1], x[2], x[3], x[4], x[5])  # calculated current
        res += (Ic - I)^2
    end
    return res
end

"""
    sse_ddm(data::AbstractMatrix{TF}, T::TF, x::AbstractVector{TF}) where TF <: AbstractFloat

Compute SSE for `data` using dDM. This function is intended to be used in a CUDA kernel.
See [`ddm`](@ref) and [`sse`](@ref) for more details.
"""
function sse_ddm(data::AbstractMatrix{TF}, T::TF, x::AbstractVector{TF}) where TF <: AbstractFloat
    res = zero(TF)
    @inbounds for (V, I) in eachcol(data)
        Ic = ddm(V, I, T, x[1], x[2], x[3], x[4], x[5], x[6], x[7])  # calculated current
        res += (Ic - I)^2
    end
    return res
end

end # module


"""
    NormalizedKernel(k::Kernel)

A normalized kernel derived from `k`.

# Definition

For inputs ``x, x'``, the normalized kernel ``\\widetilde{k}`` derived from
kernel ``k`` is defined as
```math
\\widetilde{k}(x, x'; k) = \\frac{k(x, x')}{\\sqrt{k(x, x) k(x', x')}}.
```
"""
struct NormalizedKernel{Tk<:Kernel} <: Kernel
    kernel::Tk
end

@functor NormalizedKernel

(κ::NormalizedKernel)(x, y) = κ.kernel(x, y) / sqrt(κ.kernel(x, x) * κ.kernel(y, y))

function kernelmatrix(κ::NormalizedKernel, x::AbstractVector, y::AbstractVector)
    return kernelmatrix(κ.kernel, x, y) ./
           sqrt.(
        kernelmatrix_diag(κ.kernel, x) .* permutedims(kernelmatrix_diag(κ.kernel, y))
    )
end

function kernelmatrix(κ::NormalizedKernel, x::AbstractVector)
    x_diag = kernelmatrix_diag(κ.kernel, x)
    return kernelmatrix(κ.kernel, x) ./ sqrt.(x_diag .* permutedims(x_diag))
end

function kernelmatrix_diag(κ::NormalizedKernel, x::AbstractVector)
    first_x = first(x)
    return Ones{typeof(κ(first_x, first_x))}(length(x))
end

function kernelmatrix_diag(κ::NormalizedKernel, x::AbstractVector, y::AbstractVector)
    return kernelmatrix_diag(κ.kernel, x, y) ./
           sqrt.(kernelmatrix_diag(κ.kernel, x) .* kernelmatrix_diag(κ.kernel, y))
end

function kernelmatrix!(
    K::AbstractMatrix, κ::NormalizedKernel, x::AbstractVector, y::AbstractVector
)
    kernelmatrix!(K, κ.kernel, x, y)
    K ./=
        sqrt.(kernelmatrix_diag(κ.kernel, x) .* permutedims(kernelmatrix_diag(κ.kernel, y)))
    return K
end

function kernelmatrix!(K::AbstractMatrix, κ::NormalizedKernel, x::AbstractVector)
    kernelmatrix!(K, κ.kernel, x)
    x_diag = kernelmatrix_diag(κ.kernel, x)
    K ./= sqrt.(x_diag .* permutedims(x_diag))
    return K
end

function kernelmatrix_diag!(
    K::AbstractVector, κ::NormalizedKernel, x::AbstractVector, y::AbstractVector
)
    kernelmatrix_diag!(K, κ.kernel, x, y)
    K ./= sqrt.(kernelmatrix_diag(κ.kernel, x) .* kernelmatrix_diag(κ.kernel, y))
    return K
end

function kernelmatrix_diag!(K::AbstractVector, κ::NormalizedKernel, x::AbstractVector)
    first_x = first(x)
    return fill!(K, κ(first_x, first_x))
end

Base.show(io::IO, κ::NormalizedKernel) = printshifted(io, κ, 0)

function printshifted(io::IO, κ::NormalizedKernel, shift::Int)
    println(io, "Normalized Kernel:")
    for _ in 1:(shift + 1)
        print(io, "\t")
    end
    return printshifted(io, κ.kernel, shift + 1)
end


using Base.LinAlg.BLAS: trmv, trmm, trsv, trsm

@unionise begin

"""
    DiagAffine{T<:AbstractVector} <: Invertible

DiagAffine transform with parametes `α` and `log(β)`. Defines function `f(z) = α + diag(β) * z`.
"""
struct DiagAffine{T<:AbstractVector} <: Invertible
    α::T
    logβ::T
    function DiagAffine(α::T, logβ::T) where {T}
        @assert length(α) == length(logβ)
        return new{T}(α, logβ)
    end
end
DiagAffine(α::T, logβ::T) where T<:Real = DiagAffine([α], [logβ])
function DiagAffine(θ::Vector, D::Int)
    @assert length(θ) == nparams(DiagAffine, D)
    return DiagAffine(θ[1:D], θ[D+1:2D])
end
dim(t::DiagAffine) = length(t.α)

"""
    apply(f::DiagAffine, z)

Compute the affine transform of `z` specified by `f`.
"""
function apply(f::DiagAffine, z::AbstractVecOrMat{<:Real})
    @assert length(f.α) == size(z, 1)
    return f.α .+ exp.(f.logβ) .* z
end

"""
    invert(f::DiagAffine, y)

Apply the inverse transform of `f` to `y` such that `invert(f, apply(f, z)) ≈ z` and
`apply(f, invert(f, z)) ≈ z`.
"""
function invert(f::DiagAffine, y::AbstractVecOrMat{<:Real})
    @assert dim(f) == size(y, 1)
    return (y .- f.α) ./ exp.(f.logβ)
end

function logdetJ(f::DiagAffine, z::AbstractVector{<:Real})
    @assert dim(f) == length(z)
    return sum(f.logβ)
end
function logdetJ(f::DiagAffine, z::AbstractMatrix{<:Real})
    @assert dim(f) == size(z, 1)
    return sum(f.logβ) .* ones(size(z, 2))
end

end # @unionise

params(f::DiagAffine) = [f.α, f.logβ]
nparams(::Type{<:DiagAffine}, D::Int) = 2D

function identity_init!(θ::AbstractVector{<:Real}, ::Type{<:DiagAffine}, ::AbstractRNG, D::Int)
    @assert length(θ) == nparams(DiagAffine, D)
    return fill!(θ, zero(eltype(θ)))
end

@unionise begin

"""
    Affine{TL<:RealMat, TU<:RealMat, Td′<:RealVec, Tb<:RealVec} <: Invertible

A dense affine transform. Parameterised in terms of the LDU decomposition, yielding
O(D^2) application and inversion, and O(D) Jacobian log determinant computation.
"""
struct Affine{TL<:RealMat, TU<:RealMat, Td<:RealVec, Tb<:RealVec} <: Invertible
    L::TL
    U::TU
    logd::Td
    b::Tb
    function Affine(L::TL, U::TU, logd::Td, b::Tb) where {TL, TU, Td, Tb}
        @assert size(L, 1) == size(L, 2)
        @assert size(L) == size(U)
        @assert length(logd) == length(b)
        return new{TL, TU, Td, Tb}(L, U, logd, b)
    end
end

dim(a::Affine) = length(a.b)
nparams(::Type{<:Affine}, D::Int) = 2D * (D + 1)
function Affine(θ::AbstractVector, D::Int)
    @assert length(θ) == nparams(Affine, D)
    L = reshape(θ[1:D^2], D, D)
    U = reshape(θ[D^2+1:2 * D^2], D, D)
    logd = θ[2 * D^2 + 1:2 * D^2 + D]
    b = θ[2 * D^2 + D + 1:nparams(Affine, D)]
    return Affine(L, U, logd, b)
end

function apply(f::Affine, z::AbstractVector{<:Real})
    @assert dim(f) == length(z)
    D = Diagonal(exp.(f.logd))
    return trmv('L', 'N', 'U', f.L, D * trmv('U', 'N', 'U', f.U, z)) .+ f.b
end

function apply(f::Affine, Z::AbstractMatrix{<:Real})
    @assert dim(f) == size(Z, 1)
    D = Diagonal(exp.(f.logd))
    DUδ = D * trmm('L', 'U', 'N', 'U', one(eltype(Z)), f.U, Z)
    return trmm('L', 'L', 'N', 'U', one(eltype(Z)), f.L, DUδ) .+ f.b
end

function invert(f::Affine, z::AbstractVector{<:Real})
    @assert dim(f) == length(z)
    D, δ = Diagonal(exp.(f.logd)), z - f.b
    return trsv('U', 'N', 'U', f.U, D \ trsv('L', 'N', 'U', f.L, δ))
end

function invert(f::Affine, Z::AbstractMatrix{<:Real})
    @assert dim(f) == size(Z, 1)
    D, δ = Diagonal(exp.(f.logd)), Z .- f.b
    tmp = D \ trsm('L', 'L', 'N', 'U', one(eltype(Z)), f.L, δ)
    return trsm('L', 'U', 'N', 'U', one(eltype(Z)), f.U, tmp)
end

function logdetJ(f::Affine, z::AbstractVector{<:Real})
    @assert dim(f) == length(z)
    return sum(f.logd)
end

function logdetJ(f::Affine, Z::AbstractMatrix{<:Real})
    @assert dim(f) == size(Z, 1)
    return sum(f.logd) .* ones(size(Z, 2))
end

end # @unionise

identity_init!(θ::AbstractVector{<:Real}, ::Type{<:Affine}, ::AbstractRNG, D::Int) =
    fill!(θ, zero(eltype(θ)))