function nlp_008_011(
    optimizer,
    objective_tol,
    primal_tol,
    dual_tol,
    termination_target = TERMINATION_TARGET_LOCAL,
    primal_target = PRIMAL_TARGET_LOCAL,
)
    # Test Goals:
    # - dual values

    model = Model(optimizer)

    @variable(model, x)
    @variable(model, y)
    @variable(model, z)

    @NLobjective(model, Min, x + y^2 + z^3)
    c1 = @NLconstraint(model, y >= exp(-x - 2) + exp(-z - 2) - 2)
    c2 = @constraint(model, x^2 <= y^2 + z^2)
    c3 = @constraint(model, y >= x / 2 + z)

    optimize!(model)

    check_status(model, FEASIBLE_PROBLEM, termination_target, primal_target)
    check_objective(model, -0.3755859312158738, tol = objective_tol)
    check_solution(
        [x, y, z],
        [-0.593158583913523, 0.2440479041672795, 0.5406271556211383],
        tol = primal_tol,
    )
    return check_dual(
        [c1, c2, c3],
        [0.0, -0.8697415278248679, 0.06357861274725801],
        tol = dual_tol,
    )
end


#=
Binary (or 0-1) knapsack problem

    Given a knapsack of some capacity C and n objects
       with object i having weight wi and profit pi,
       the goal is to choose some subset of the objects
       that can fit in the knapsack
       (i.e. the sum of their weights is no more than C)
       while maximizing profit.

    This can be formulated as a mixed-integer program as:
        maximize x' p subject to x∈{0,1}  and  w′x <= C
        x is a vector is size n, where x_i
        is 1 if we chose to keep the object in the knapsack,
           0 otherwise.

source: https://nbviewer.jupyter.org/github/JuliaOpt/Convex.jl/blob/master/
                                    examples/binary_knapsack.ipynb
=#

# Data taken from http://people.sc.fsu.edu/~jburkardt/datasets/knapsack_01/knapsack_01.html

w = [23; 31; 29; 44; 53; 38; 63; 85; 89; 82]
C = 165
p =  [92; 57; 49; 68; 60; 43; 67; 84; 87; 72];
n = length(w)

using Convex, GLPKMathProgInterface
x = Variable(n, :Bin)
problem = maximize(dot(p, x), dot(w, x) <= C)
solve!(problem, GLPKSolverMIP())


function newtonRaphson(f::Function, fprime::Function, xi::Number, ea::Number, maxIter::Int64)
  # Estimate roots of functions using the Newton-Raphson method
  for i = 1:maxIter
    xiprev  = xi;
    xi = xi - (f(xi) / fprime(xi));

    if abs(xi - xiprev) / xi < ea
      break;
    end;
  end
  return xi;
end


using Polynomials
using Plots

function main()

    xlin = range(-5, 5, length=200)

    # creating polynomials
    p = Polynomial([1, 3, 5])   # generating by coefficients
    q = fromroots([1, -4, 4])   # generating by roots

    # evaluating
    println(p)
    println(q)

    # math operations
    println(p(2))
    println(p + q)
    println(2*p - q)

    # derivatives and integrals
    println(derivative(p))
    println(integrate(p))

    # roots
    println(roots(q))

    # what are the roots and extrema of q?
    xq = roots(q)

    qprime = derivative(q)
    xqq = roots(qprime)

    plot(xlin, q.(xlin), label=string(q), legend=:bottomright, framestyle=:zerolines, dpi=150)
    plot!(xlin, qprime.(xlin), label=string(qprime))
    scatter!(xq, q.(xq), label="roots")
    scatter!(xqq, q.(xqq), label="extrema")

end

main()

    # xq = roots(q)
    # qprime = derivative(q)
    # xqq = roots(qprime)


# Univariate Gaussian Mixture Models

struct UnivariateGMM <: UnivariateMixture{Continuous,Normal}
    K::Int
    means::Vector{Float64}
    stds::Vector{Float64}
    prior::Categorical

    function UnivariateGMM(ms::Vector{Float64}, ss::Vector{Float64}, pri::Categorical)
        K = length(ms)
        length(ss) == K || throw(DimensionMismatch())
        ncategories(pri) == K ||
            error("The number of categories in pri should be equal to the number of components.")
        new(K, ms, ss, pri)
    end
end

@distr_support UnivariateGMM -Inf Inf

ncomponents(d::UnivariateGMM) = d.K

component(d::UnivariateGMM, k::Int) = Normal(d.means[k], d.stds[k])

probs(d::UnivariateGMM) = probs(d.prior)

mean(d::UnivariateGMM) = dot(d.means, probs(d))

rand(d::UnivariateGMM) = (k = rand(d.prior); d.means[k] + randn() * d.stds[k])

params(d::UnivariateGMM) = (d.means, d.stds, d.prior)

struct UnivariateGMMSampler <: Sampleable{Univariate,Continuous}
    means::Vector{Float64}
    stds::Vector{Float64}
    psampler::AliasTable
end

rand(s::UnivariateGMMSampler) = (k = rand(s.psampler); s.means[k] + randn() * s.stds[k])
sampler(d::UnivariateGMM) = UnivariateGMMSampler(d.means, d.stds, sampler(d.prior))


"""
    NormalInverseGamma{T<:Real} <: ContinuousUnivariateDistribution

Normal Inverse Gamma distribution. Adapted from the `NormalInverseGamma` in the
[ConjugatePriors.jl](https://github.com/JuliaStats/ConjugatePriors.jl/blob/master/src/normalinversegamma.jl)
package.
"""
struct NormalInverseGamma{T <: Real} <: ContinuousUnivariateDistribution
    μ::T
    v::T
    α::T # shape
    θ::T # scale
    function NormalInverseGamma(μ::Real, v::Real, α::Real, θ::Real)
    	v > zero(v) && α > zero(α) && θ > zero(θ) || error("v, α and θ must be positive")
        T = promote_type(typeof(μ), typeof(v), typeof(α), typeof(θ))
    	return new{T}(T(μ), T(v), T(α), T(θ))
    end
end

function rand(d::NormalInverseGamma, numsamples::Int)
    sig2_list = rand(InverseGamma(d.α, d.θ), numsamples)
    mu_list = [rand(Normal(d.μ, sqrt(sig2 * d.v))) for sig2 in sig2_list]
    return mu_list, sig2_list
end


using ReachabilityAnalysis

@taylorize function lotkavolterra!(dx, x, params, t)
    local α, β, γ, δ = 1.5, 1., 3., 1.
    dx[1] = α * x[1] - β * x[1] * x[2]
    dx[2] = δ * x[1] * x[2] - γ * x[2]
    return dx
end

X0 = Hyperrectangle(low=[4.8, 1.8], high=[5.2, 2.2]);
prob = @ivp(x' = lotkavolterra!(x), dim=2, x(0) ∈ X0)
sol = solve(prob, T=8.0, alg=TMJets());
solz = overapproximate(sol, Zonotope);

using Plots

plot(solz, vars=(1, 2), alpha=0.3,lw=0., xlab="x", ylab="y", label="Flowpipe", legend=:bottomright)
plot!(X0, label="X(0)")

@taylorize function f(du, u, p, t)
    du[1] = u[3] * u[1] - u[4] * (u[1] * u[2]) - u[7] * u[1]^2
    du[2] = -u[5] * u[2] + u[6] * (u[1] * u[2])

    #encode uncertain params
    du[3] = zero(u[1]) # p[1]
    du[4] = zero(u[1]) # p[2]
    du[5] = zero(u[1]) # p[3]
    du[6] = zero(u[1]) # p[4]
    du[7] = zero(u[1]) # p[5]
end

p_int = (0.99..1.01) × (0.99..1.01) × (2.99..3.01) × (0.99..1.01) × (0.099..0.101)
U0 = Singleton([1.0, 1.0]) × convert(Hyperrectangle, p_int)
prob = @ivp(u' = f(u), dim: 7, u(0) ∈ U0);

sol = solve(prob, tspan=(0.0, 10.0));
solz = overapproximate(sol, Zonotope);

plot(solz, vars=(1, 2), lw=0.3, title="Uncertain params", lab="abstol = 1e-15", xlab="u1", ylab="u2")

u0 = Singleton([1.0, 1.0])
□(ϵ) = BallInf(zeros(2), ϵ)
U0 = (u0 ⊕ □(0.05)) × convert(Hyperrectangle, p_int)

prob = @ivp(u' = f(u), dim: 7, u(0) ∈ U0)

sol = solve(prob, tspan=(0.0, 10.0), TMJets(abstol=1e-10))
solz = overapproximate(sol, Zonotope)
plot(solz, vars=(1, 2), color=:orange, lw=0.3,
     lab="eps = 0.05", title="Uncertain u0 and uncertain params",
     xlab="u1", ylab="u2")

U0 = (u0 ⊕ □(0.01)) × convert(Hyperrectangle, p_int)
prob = @ivp(u' = f(u), dim: 7, u(0) ∈ U0)

sol = solve(prob, tspan=(0.0, 10.0), TMJets(abstol=1e-10))
solz = overapproximate(sol, Zonotope)
plot!(solz, vars=(1, 2), color=:blue, lw=0.3,
  lab="eps = 0.01", title="Uncertain u0 and uncertain params",
  xlab="u1", ylab="u2")


"""
        wpm(decisionMat, weights, fns)

Apply WPM (Weighted Product Method) for a given matrix and weights.

# Arguments:
 - `decisionMat::DataFrame`: n × m matrix of objective values for n alterntives and m criteria
 - `weights::Array{Float64, 1}`: m-vector of weights that sum up to 1.0. If the sum of weights is not 1.0, it is automatically normalized.
 - `fns::Array{Function, 1}`: m-vector of functions to be applied on the columns.

# Description
wpm() applies the WPM method to rank n alterntives subject to m criteria which are supposed to be
either maximized or minimized.

# Output
- `::WPMResult`: WPMResult object that holds multiple outputs including scores, rankings, and best index.

# Examples
```julia-repl
julia> decmat = [3        12.5        2        120        14        3;
       5        15        3        110        38        4;
       3        13        2        120        19        3;
       4        14        2        100        31        4;
       3        15        1.5        125        40        4]
5×6 Array{Float64,2}:
 3.0  12.5  2.0  120.0  14.0  3.0
 5.0  15.0  3.0  110.0  38.0  4.0
 3.0  13.0  2.0  120.0  19.0  3.0
 4.0  14.0  2.0  100.0  31.0  4.0
 3.0  15.0  1.5  125.0  40.0  4.0

julia> df = makeDecisionMatrix(decmat)
5×6 DataFrame
 Row │ Crt1     Crt2     Crt3     Crt4     Crt5     Crt6
     │ Float64  Float64  Float64  Float64  Float64  Float64
─────┼──────────────────────────────────────────────────────
   1 │     3.0     12.5      2.0    120.0     14.0      3.0
   2 │     5.0     15.0      3.0    110.0     38.0      4.0
   3 │     3.0     13.0      2.0    120.0     19.0      3.0
   4 │     4.0     14.0      2.0    100.0     31.0      4.0
   5 │     3.0     15.0      1.5    125.0     40.0      4.0

julia> weights = [0.221, 0.159, 0.175, 0.127, 0.117, 0.201];

julia> fns = [maximum, minimum, minimum, maximum, minimum, maximum];

julia> result = wpm(df, weights, fns);

julia> result.scores
5-element Array{Float64,1}:
 0.7975224331331252
 0.7532541470584717
 0.7647463553356331
 0.7873956894790834
 0.7674278741781709

julia> result.bestIndex
1
```

# References
Zavadskas, E. K., Turskis, Z., Antucheviciene, J., & Zakarevicius, A. (2012). Optimization of Weighted Aggregated Sum Product Assessment. Elektronika Ir Elektrotechnika, 122(6), 3-6. https://doi.org/10.5755/j01.eee.122.6.1810
"""
function wpm(decisionMat::DataFrame, weights::Array{Float64,1}, fns::Array{Function,1})::WPMResult

    row, col = size(decisionMat)
    normalizedDecisionMat = similar(decisionMat)
    w = unitize(weights)
    colminmax = zeros(Float64, col)
    @inbounds for i in 1:col
        colminmax[i] = decisionMat[:, i] |> fns[i]
        if fns[i] == maximum
            normalizedDecisionMat[:, i] = decisionMat[:, i] ./ colminmax[i]
        elseif fns[i] == minimum
            normalizedDecisionMat[:, i] = colminmax[i] ./ decisionMat[:, i]
        end
    end
    scoreMat = similar(normalizedDecisionMat)
    for i in 1:col
        scoreMat[:, i] = normalizedDecisionMat[:, i].^w[i]
    end

    scores = zeros(Float64, row)
    for i in 1:row
        scores[i] = prod(scoreMat[i, :])
    end
    rankings = sortperm(scores)

    bestIndex = rankings |> last

    result = WPMResult(
        decisionMat,
        normalizedDecisionMat,
        w,
        scores,
        rankings,
        bestIndex
    )

    return result
end

"""
        wpm(setting)

Apply WPM (Weighted Product Method) for a given matrix and weights.

# Arguments:
 - `setting::MCDMSetting`: MCDMSetting object.

# Description
wpm() applies the WPM method to rank n alterntives subject to m criteria which are supposed to be
either maximized or minimized.

# Output
- `::WPMResult`: WPMResult object that holds multiple outputs including scores, rankings, and best index.
"""
function wpm(setting::MCDMSetting)::WPMResult
    wpm(
        setting.df,
        setting.weights,
        setting.fns
    )
end


@doc raw"""
    align(a⃗, b⃗, [w])

Solve [Wahba's problem](https://en.wikipedia.org/wiki/Wahba%27s_problem),
finding a rotation that aligns the set of points `a⃗` to a corresponding set of
points `b⃗` by minimizing the distance between the first set and the rotated
second set.

Here, `a⃗` and `b⃗` must be equally sized arrays of `QuatVec`s.  If present, `w`
must be an equally sized array of real numbers; if not, it is taken to be 1.
We define the loss function
```math
L(ℛ) ≔ Σᵢ wᵢ ‖a⃗ᵢ - ℛ b⃗ᵢ‖²
```
where ``ℛ`` is a rotation operator, and return the quaternion corresponding to
the optimal ``ℛ`` that minimizes this function.

Note that it is possible that the points do not uniquely determine a rotation —
as when one or both sets of points is rotationally symmetric.  In that case, the
loss function ``L(ℛ)`` will still be minimized and the points will still be
optimally aligned by the output quaternion, but that quaternion will not be
unique.

# Notes

In their book [_Fundamentals of Spacecraft Attitude Determination and Control_
(2014)](https://doi.org/10.1007/978-1-4939-0802-8), Markley and Crassidis say
that "Davenport’s method remains the best method for solving Wahba’s problem".
This method provides the optimal quaternion as the dominant eigenvector (the one
with the largest eigenvalue) of a certain matrix.  We start by defining the
supplementary matrix
```math
S ≔ Σᵢ wᵢ a⃗ᵢ b⃗ᵢᵀ
```
and vector
```math
s⃗ ≔ \begin{bmatrix}
S₂₃-S₃₂ \\
S₃₁-S₁₃ \\
S₁₂-S₂₁
\end{bmatrix}.
```
Then the key matrix is
```math
M ≔ \begin{bmatrix}
S + Sᵀ - (\mathrm{tr}S)\, I₃ & s⃗ \\
s⃗ᵀ & \mathrm{tr}S
\end{bmatrix}
```
It is possible for this matrix to have degenerate eigenvalues, corresponding to
cases where the points do not uniquely determine the rotation, as described
above.

"""
function align(a⃗::AbstractArray{<:QuatVec}, b⃗::AbstractArray{<:QuatVec}, w::AbstractArray{<:Real})
    # This is Eq. (5.11) from Markley and Crassidis
    S = sum(w[i] * a⃗[i].vec * b⃗[i].vec' for i in eachindex(a⃗, b⃗, w))
    return _align_Wahba(S)
end

function align(a⃗::AbstractArray{<:QuatVec}, b⃗::AbstractArray{<:QuatVec})
    # This is Eq. (5.11) from Markley and Crassidis
    S = sum(a⃗[i].vec * b⃗[i].vec' for i in eachindex(a⃗, b⃗))
    return _align_Wahba(S)
end

function _align_Wahba(S)
    # This is Eq. (5.17) from Markley and Crassidis, modified to suit our
    # conventions by flipping the sign of ``z``, and moving the final dimension
    # to the first dimension.
    M = Symmetric([
            S[1,1]+S[2,2]+S[3,3]      S[3,2]-S[2,3]         S[1,3]-S[3,1]           S[2,1]-S[1,2]
                S[3,2]-S[2,3]      S[1,1]-S[2,2]-S[3,3]     S[1,2]+S[2,1]           S[1,3]+S[3,1]
                S[1,3]-S[3,1]         S[1,2]+S[2,1]      -S[1,1]+S[2,2]-S[3,3]      S[2,3]+S[3,2]
                S[2,1]-S[1,2]         S[1,3]+S[3,1]         S[2,3]+S[3,2]       -S[1,1]-S[2,2]+S[3,3]
    ])
    # This extracts the dominant eigenvector, and interprets it as a Rotor.  In
    # particular, note that the _last_ eigenvector output by `eigen` (the 4th)
    # has the largest eigenvalue.
    return Rotor(eigen(M, 4:4).vectors[:, 1]...)
end

@doc raw"""
    align(A, B, [w])

Find a `Rotor` that aligns the set of rotors `A` to a corresponding set `B` by
minimizing the distance between the first set and the rotated second set.

Here, `A` and `B` must be equally sized arrays of `AbstractQuaternion`s.  If
present, `w` must be an equally sized array of real numbers; if not, it is
taken to be 1.  We define the loss function
```math
L(R) ≔ Σᵢ wᵢ |Aᵢ - R Bᵢ|²
```
where ``R`` is a `Rotor`, and return the quaternion corresponding to the optimal
``R`` that minimizes this function.

Note that it is possible that the input data do not uniquely determine a rotor,
which will happen when sum below is zero.  When this happens, the result will
contain `NaN`s, but no error will be raised.  When the sum is very close to —
but not exactly — zero, the accuracy of the result will be limited.  However,
the loss function will not depend strongly on the result in that case.

Be aware that this function _is_ sensitive to the signs of the input
quaternions.  See the [`unflip`](@ref) function for one way to avoid problems
related to signs.

## Notes

We can ensure that the loss function is minimized by multiplying ``R`` by an
exponential, differentiating with respect to the argument of the exponential,
and setting that argument to 0.  This derivative should be 0 at the minimum.  We
have
```math
∂ⱼ Σᵢ wᵢ |Aᵢ - \exp[vⱼ] R Bᵢ|²  →  2 ⟨ eⱼ R Σᵢ wᵢ Bᵢ Āᵢ ⟩₀
```
where → denotes taking ``vⱼ→0``, the symbol ``⟨⟩₀`` denotes taking the scalar
part, and ``eⱼ`` is the unit quaternionic vector in the ``j`` direction.  The
only way for this quantity to be zero for each choice of ``j`` is if
```math
R Σᵢ wᵢ Bᵢ Āᵢ
```
is itself a pure scalar.  This, in turn, can only happen if either (1) the sum
is 0 or (2) if ``R`` is proportional to the _conjugate_ of the sum:
```math
R ∝ Σᵢ wᵢ Aᵢ B̄ᵢ
```
Now, since we want ``R`` to be a rotor, we simply define it to be the normalized
sum.

"""
function align(A, B, w)
    Rotor(sum(w[i] * A[i] * conj(B[i]) for i in eachindex(A, B, w)))
end

function align(A, B)
    Rotor(sum(A[i] * conj(B[i]) for i in eachindex(A, B)))
end


#
# This example assumes below packages are available in your environment
#

using StatsModelComparisons, StanSample
using StatsFuns, CSV, Random

ProjDir = @__DIR__

df = CSV.read(joinpath(ProjDir, "..", "..", "data", "cars.csv"), DataFrame)

cars_stan = "
data {
    int N;
    vector[N] speed;
    vector[N] dist;
}
parameters {
    real a;
    real b;
    real sigma;
}
transformed parameters{
    vector[N] mu;
    mu = a + b * speed;
}
model {
    a ~ normal(0, 100);
    b ~ normal(0, 10);
    sigma ~ exponential(1);
    dist ~ normal(mu, sigma)    ;
}
generated quantities {
    vector[N] log_lik;
    for (i in 1:N)
        log_lik[i] = normal_lpdf(dist[i] | mu[i], sigma);
}
"

Random.seed!(1)
cars_stan_model = SampleModel("cars.model", cars_stan)
data = (N = size(df, 1), speed = df.Speed, dist = df.Dist)
rc = stan_sample(cars_stan_model; data)
println()

if success(rc)
    #stan_summary(cars_stan_model, true)
    nt_cars = read_samples(cars_stan_model);
end

log_lik = nt_cars.log_lik'
n_sam, n_obs = size(log_lik)
lppd = reshape(logsumexp(log_lik .- log(n_sam); dims=1), n_obs)

pwaic = [var(log_lik[:, i]) for i in 1:n_obs]
@show -2(sum(lppd) - sum(pwaic))
println()

@show waic(log_lik)
println()

loo, loos, pk = psisloo(log_lik)
@show -2loo


using LinearAlgebra
using ToeplitzMatrices

function chebdif(N, M)

    # The function x,DM = chebdif(N,M) computes the differentiation
    # matrices D1, D2, ..., DM on Chebyshev nodes.
    # It is adapted from the function with the same name in dmsuite
    # developed for Matlab by JAC Weideman and SC Reddy.
    #
    # Adapted by M. Beneitez at KTH Mechanics.
    # beneitez@mech.kth.se
    #
    # Input:
    # N:  Size of the differentiation matrix.
    # M:  Number of derivates required
    # Note: 0 < M <= N-1.
    #
    # Output:
    # DM: DM[1:N,1:N,ell] contains the ell-th derivative matrix, ell=1...M
    #

    eye = Matrix{Float64}(I, N, N);        # Identity matrix.

    n1 = Int(floor(N/2)); n2  = Int(ceil(N/2));     # Indices used for flipping trick.

    k = collect(0:N-1);                        # Compute theta vector.
    th = k.*pi./(N-1)

    x = sin.(pi.*collect(N-1:-2:1-N)./(2*(N-1))); # Compute Chebyshev points.

    T = repeat(th/2,1,N);
    DX = 2*sin.(T'+T).*sin.(T'-T);          # Trigonometric identity.
    DX = [DX[1:n1,:]; -reverse(reverse(DX[1:n2,:], dims = 2), dims=1)];   # Flipping trick.
    DX[eye.==true] = ones(N,1);                      # Put 1's on the main diagonal of DX.

    C = Matrix(Toeplitz((-1).^vec(k),(-1).^vec(k)));            # C is the matrix with
    C[1,:] = C[1,:].*2; C[N,:] = C[N,:].*2;     # entries c[k]/c[j]
    C[:,1] = C[:,1]/2; C[:,N] = C[:,N]/2

    Z = 1 ./DX;                              # Z contains entries 1/(x[k]-x[j])
    Z[eye.==true] = zeros(N,1);              # with zeros on the diagonal.

    D  = eye;                                 # D contains diff(). matrices.
    DM = zeros(N,N,M);

    for ell = 1:M
        D = ell*Z.*(C.*repeat(diag(D),1,N) - D); # Off-diagonals
        D[eye.==true] = -sum(D', dims = 1);                            # Correct main diagonal of D
        DM[:,:,ell] = D;                                   # Store current D in DM
    end

    return x,DM

end


using GalerkinSparseGrids
using Base.Test
using Cubature

#--------------------------------------
# Testing Differentiation
#--------------------------------------

print("Testing differentiation 1-D DG basis... ")

k=3
for l in 2:5
    frefVD = V2Dref(1, k, l);
    frefDV = D2Vref(1, k, l);
    D_op = D_matrix(1, k, l, frefVD, frefDV; scheme="full")
    vcoeffs = vcoeffs_DG(1, k, l, x->cos(2*pi*x[1]); scheme="full")
    dvcoeffs = *(D_op,vcoeffs)
    dict= V2D(1, k, l, vcoeffs; scheme="full")
    ddict = V2D(1, k, l, dvcoeffs; scheme="full")
    err = x->(reconstruct_DG(ddict,[x[1]])+2*pi*sin(2*pi*x[1]))^2
    @test hquadrature(err, 0, 1, abstol=1.0e-10, maxevals=500)[1] < 1/(1<<(k+l-2))
end

println("Test Passed.")

print("Testing differentiation 2-D full DG basis... ")

D = 2
k = 3
for l in 2:5
    D_op = D_matrix(D, 1, k, l; scheme="full")
    vcoeffs = vcoeffs_DG(D, k, l, x->cos(2*pi*x[1])*cos(2*pi*x[2]); scheme="full")
    dvcoeffs = *(D_op, vcoeffs)
    dict= V2D(D, k, l, vcoeffs; scheme="full")
    ddict = V2D(D, k, l, dvcoeffs; scheme="full")
    err = x->(reconstruct_DG(ddict,[x[1],x[2]])+2*pi*sin(2*pi*x[1])*cos(2*pi*x[2]))^2
    @test hcubature(err, [0,0], [1,1], abstol=1.0e-10, maxevals=500)[1] < 1/(1<<(k+l-2))
end

println("Test Passed.")


module Sudoku

# package code goes here

using JuMP, Cbc, ChooseOptimizer

export sudoku, sudoku_check

"""
`sudoku(A::Matrix)` solves the Sudoku puzzle given by the matrix `A`.
Here `A` is a 9-by-9 integer matrix whose nonzero entries are the
given values of a Sudoku puzzle and whose zero values are the blanks.
"""
function sudoku(A::Matrix{Int})::Matrix{Int}
    nn = size(A)[1] # number of items per row/column/block
    n = Int(sqrt(nn)) # size of the problem: normal sudoku n = 3
    # MOD = Model(JuMP.optimizer_with_attributes(Cbc.Optimizer, "logLevel" => 0))
    set_solver_verbose(false)
    MOD = Model(get_solver())

    # variable X[i,j,k] = 1 means there's a k in cell (i,j)
    @variable(MOD, X[1:nn, 1:nn, 1:nn], Bin)

    ########### model constraints ##################
    @constraints(
        MOD,
        begin
            oneKPerCell, sum(X[1:nn, 1:nn, k] for k ∈ 1:nn) .== 1
            oneKPerRow, sum(X[1:nn, j, 1:nn] for j ∈ 1:nn) .== 1
            oneKPerCol, sum(X[i, 1:nn, 1:nn] for i ∈ 1:nn) .== 1
            oneKPerBlock,
            sum.(X[i:i+n-1, j:j+n-1, k] for k ∈ 1:nn, i ∈ 1:n:nn, j ∈ 1:n:nn) .== 1
        end
    )

    # Process values in A
    nonZeroIndices = findall(i -> i != 0, A)
    for i in nonZeroIndices
        @constraint(MOD, X[i, A[i]] == 1)
    end

    # now solve and extract the solution
    optimize!(MOD)
    status = Int(termination_status(MOD))
    if status != 1
        error("No solution to this Sudoku puzzle")
    end

    return Int.(sum(value.(X[:, :, k]) * k for k = 1:nn))
end

"""
`sudoku_check(A)` checks if a 9x9 matrix is a valid solution to a Sudoku
puzzle. That is, we check that every row, column, and 3x3 submatrix contains
the values 1 through 9 exactly once each.
"""
function sudoku_check(A::Matrix{Int})::Bool
    nn = size(A)[1] # number of items per row/column/block
    n = Int(sqrt(nn)) # size of the problem: normal sudoku n = 3

    # All items in a row are 1:9
    if !all(mapslices(aux -> sort(aux) == 1:nn, A, dims = 1))
        return false
    end

    # All items in a column are 1:9
    if !all(mapslices(aux -> sort(aux) == 1:nn, A, dims = 2))
        return false
    end

    # Finally we check each block, if we get this far, the sucoku will be correct
    # or not decided by this condition
    return all(
        aux -> sort(aux) == 1:nn,
        vec.([A[i:i+n-1, j:j+n-1] for i ∈ 1:n:nn, j ∈ 1:n:nn]),
    )
end

include("sudoku_print.jl")
include("examples.jl")

end # module


using Test, DiscreteExteriorCalculus, DiscretePDEs
using UniqueVectors: UniqueVector
using LinearAlgebra: eigen

file_name = joinpath(@__DIR__, "sphere.geo")
geo_write!(file_name, characteristic_length_factor=.3,
    footer="""
    Sphere(1) = {0, 0, 0, 1, -Pi/2, Pi/2, 2*Pi};
    """)
initialize!()
gmsh_open!(file_name)
N, K = 3, 3
mesh!(K)

node_tags, points, tcomp = get_triangulated_complex(N, K)
@test typeof(tcomp) <: TriangulatedComplex{N, K}
comp = tcomp.complex
orient!(comp)
m = Metric(N)
mesh = Mesh(tcomp, circumcenter(m))

laplacian = differential_operator(m, mesh, "Δ", 1, true)

vals, vects = eigen(collect(laplacian))
inds = sortperm(vals)
vals, vects = vals[inds], vects[:, inds]

# solutions are spherical harmonics (or combinations of them since they
# are degenerate)
correct_modes = vcat([[l*(l+1) for _ in -l:l] for l in 0:3]...)

@testset "laplacian on a sphere" begin
    @test isapprox(vals[1:length(correct_modes)], correct_modes, rtol=1e-2)
end

if false # set to true to display plots with Gmsh
    comp_points = UniqueVector([c.points[1] for c in comp.cells[1]])
    ordering = [findfirst(isequal(p), comp_points) for p in points]
    for i in 1:length(correct_modes)
        v = vects[:,i]
        add_field!("Eigenvector", node_tags, v[ordering])
    end
    gui!()
end
rm(file_name)


function rat(x::AbstractFloat, tol::Real=eps(x))::Rational
    p, q, pp, qq  = copysign(1,x), 0, 0, 1
    x, y = abs(x), 1.0
    r, a = modf(x)
    nt, t, tt = tol, 0.0, tol

    while r > nt        # convergents of the continued fraction: np//nq = (p*a + pp) // (q*a + qq)
        np, nq = Int(a).*(p,q) .+ (pp,qq)
        p, pp, q, qq = np, p, nq, q

        x, y = y, r     # instead of the inexact 1/r...
        a, r = divrem(x,y)

        t, tt = nt, t   # maintain x = (p + (-1)^i * r) / q
        nt = a*t+tt
    end

    i = Int(cld(x-tt,y+t))   # find optimal semiconvergent: smallest i such that x-i*y < i*t+tt
    return (i*p+pp) // (i*q+qq)
end


using LinearAlgebra

"""
Go from `X'Q*X + U'R*U`
s.t. x+ = Ax+Bu
to
`U'F*U + 2U'*G*x(0) + x(0)'H*x(0)`
and M,N, where X = M*U + N*x(0)
"""
function complact_form(A::AbstractArray{T}, B, Q, R, n) where T
    nu = size(B,2)
    nx = size(A,1)
    M = zeros(T, n*nx, n*nu)
    for i = 0:(n-1)
        AiB = A^(i)*B
        for j = 1:(n-i)
            rows = (nx*(i+j-1)+1):(nx*(i+j))
            cols = (nu*(j-1)+1):(nu*j)
            M[rows, cols] .= AiB
        end
    end
    N = vcat([A^i for i in 1:n]...)
    # Blockdiag
    Qn = vcat([[zeros(T, size(Q,1), size(Q,1)*i) Q zeros(T, size(Q,1), size(Q,1)*(n-i-1))]  for i = 0:(n-1)]...)
    Rn = vcat([[zeros(T, size(R,1), size(R,1)*i) R zeros(T, size(R,1), size(R,1)*(n-i-1))]  for i = 0:(n-1)]...)
    F = M'Qn*M + Rn
    G = M'Qn*N
    H = N'Qn*N
    return F, G, H, M, N
end
T = Float64
A = T.([0.999  -3.008  -0.113  -1.608  ;
     0      0.986   0.048   0       ;
     0      2.083   1.009   0       ;
     0      0.053   0.05    1       ])
B = T.([-0.080 -0.635  ;
     -0.029 -0.014  ;
     -0.868 -0.092  ;
     -0.022 -0.002  ])
Q = Matrix{T}(I,4,4)
R = Matrix{T}(I,2,2)
n = 10
F, G, H, M, N = complact_form(A,B,Q,R,n)

x0 = 0.2.*ones(4)

nx = size(A, 1)
nu = size(B, 2)

xl = -0.2*ones(T, nx)
xu = 0.2*ones(T, nx)

Xl = vcat([xl for i in 1:n]...)
Xu = vcat([xu for i in 1:n]...)
Ul = Xl - N*x0
Uu = Xu - N*x0

UC = M

using QPDAS
nU = nu*n
z = G*x0
@time qp = QuadraticProgram(zeros(T, 0, nU), zeros(T, 0), [UC;-UC], [Uu; -Ul], z, F, ϵ=1e-6, smartstart=true)

@time sol, val = solve!(qp)
val = val + 1/2*x0'H*x0

X = M*sol + N*x0
maximum(abs, X) - 0.2
maximum([UC;-UC]*sol - [Uu; -Ul])
1/2*sum(abs2, X) + 1/2*sum(abs2, sol)


#!/usr/bin/env julia

module PE14

# collatz(n::Int) = iseven(n) ? collatz(n÷2)+1 : collatz(3n+1)+1

function collatz(n::Integer)
    local len=0;
    while n>1
        n = iseven(n) ? n÷2 : 3n+1;
        len += 1;
    end
    len
end

end

export pe14

pe14(n::Integer=10^6)=PE14.collatz.(1:n)|>findmax|>last

if !haskey(ENV,"PROJECT_EULER_WITHOUT_RESULT")
    pe14()|>print
end


"""
    Polynomial

one of functions mapping the set of real numbers is the algebraic polynomials, the set of functions of the form:
``p_n(x) = a_n x^n + a_{n-1} x^{n-1} + ... +a_{1}x + a_0``
- nth Larange interpolating polynomial
- Neville’s Iterated Interpolation
- Newton’s Divided-Difference Formula
- Natural Cubic Spline
- Clamped Cubic Spline
"""
module Polynomial
using SymPy
using LinearAlgebra
using Latexify

@inline function product(x::Vector)
    res = 1
    for i in 1:size(x)[1]
        res = res*x[i]
    end
    return res
end

"""
    Lagrange(X::Vector, Y::Vector; a::String="x")

use the ``(x_0, y_0), (x_1, y_1), ..., (x_n, y_n)``, to caculate a polynomial.
input X and Y, default use x to express functions.
output: function.
"""
@inline function Lagrange(X::Vector, Y::Vector; a::String="x")
    a = symbols(a)
    m = size(X)[1]
    A = ones(m) * X'
    An = A - A'
    An[An .== 0] .= 1
    up = a .- X
    upr = product(up)
    sum = 0
    for i in 1:m
        down = product(An[:,i])
        u = upr/up[i]
        sum = sum + Y[i] * (u/down)
    end
    return simplify(sum)
end

"""
    Neville(x::Vector, y::Vector, x₀::Real; tab::Bool=false)

input two vector, x and y. satisfy the function `y=f(x)`, then input x₀, which you want
to  approximate f(x₀). The default tab setting is false, just output the estimate. you can set true
to output a result table.
"""
@inline function Neville(x::Vector, y::Vector, x₀::Real; tab::Bool=false)
    n, = size(x)
    res = zeros(n, n)
    for i in 1:n
        res[i, 1] = y[i]
    end
    for i in 2:n
        for j in 2:i
            res[i,j] = ((x₀ - x[i-j+1])*res[i, j-1] - (x₀ - x[i])*res[i-1,j-1])/(x[i] - x[i-j+1])
        end
    end
    if tab
        return res
    else
        return res[n, n]
    end
end

"""
    NDDF(x::Vector, y::Vector; a::String="x", simple::Bool=true, tab::Bool=false, backward::Bool=false)

input ``x_1, x_2, x_3, ...x_n```,values ``f(x_1), f(x_2), ...,f(x_n)``, set Args `simpel = true`
output simplify ans, set tab to output a table. set `backward=true` use the Newton Backward–Difference Formula.
"""
@inline function NDDF(x::Vector, y::Vector; a::String="x", simple::Bool=true, tab=false, backward::Bool=false)
    a = symbols(a)
    n, = size(x)
    res = zeros(n, n)
    for i in 1:n
        res[i,1] = y[i]
    end
    for i in 2:n
        for j in 2:i
            res[i,j] = (res[i, j-1] - res[i-1, j-1])/(x[i] - x[i-j+1])
        end
    end
    sum = res[1,1]
    for i in 2:n
        p = 1
        for j in 1:i-1
            p = p*(a - x[j])
        end
        if backward
            sum = sum + res[n, i]*p
        else
            sum = sum + res[i, i]*p
        end
    end
    if tab
        return res
    else
        if simple
            return simplify(sum)
        else
            return sum
        end
    end
end

"""
    NCSpline( NCSpline(x::Vector, y::Vector; Latex::Bool=false, a::String="t")
input ``x_1, x_2, x_3, ...x_n```,values ``f(x_1), f(x_2), ...,f(x_n)``, set Args `Latex = true`  to
output information about the function.
"""
@inline function NCSpline(x::Vector, y::Vector; Latex::Bool=false, a::String="t")
    a = symbols(a)
    n, = size(x)
    h, α = zeros(n), zeros(n-1)

    for i in 1:n-1
        h[i] = x[i+1] - x[i]
    end
    for i in 2:n-1
        α[i] = 3/h[i] * (y[i+1] - y[i]) - 3/h[i-1] *(y[i] - y[i-1])
    end

    l = ones(n)
    μ = zeros(n)
    z = zeros(n)

    for i in 2:n-1
        l[i] = 2*(x[i+1] - x[i-1]) - h[i-1]*μ[i-1]
        μ[i] = h[i]/l[i]
        z[i] = (α[i] - h[i-1]*z[i-1])/l[i]
    end

    c, b, d = zeros(n), zeros(n), zeros(n)
    for j in reverse(1:n-1)
        c[j] = z[j] - μ[j]*c[j+1]
        b[j] = (y[j+1] - y[j])/h[j] - h[j]*(c[j+1]+2*c[j])/3
        d[j] = (c[j+1] - c[j])/(3*h[j])
    end
    A = [b[1:end-1] c[1:end-1] d[1:end-1]]
    X = []
    for i in 2:4
        for j in 1:3
            push!(X,(a - x[i-1])^(j))
        end
    end
    res = diag(A * reshape(X, (n-1,n-1))) + y[1:end-1]

    if Latex
        for i in 1:n-1
            print(res[i], ",","t ∈","[$(x[i]), $(x[i+1])]")
            print("\n")

        end
    else
        return res
    end
end

"""
    CCSpline(x::Vector, y::Vector, endpoint::Vector; Latex::Bool=false, a::String="t")

input ``x_1, x_2, x_3, ...x_n```,values ``f(x_1), f(x_2), ...,f(x_n)`` and ``f'(x_1), f'(x_n)``, set Args `Latex = true`  to
output information about the function.
"""
@inline function CCSpline(x::Vector, y::Vector, endpoint::Vector; Latex::Bool=false, a::String="t")
    n, = size(x)
    h = ones(n)
    α = ones(n)
    a = symbols(a)

    for i in 1:n-1
        h[i] = x[i+1] - x[i]
    end
    α[1] = 3(y[2] - y[1])/h[1] - 3*endpoint[1]
    α[n] = 3*endpoint[2] - 3*(y[n] - y[n-1])/h[n-1]
    for i in 2:n-1
        α[i] = 3/h[i] * (y[i+1] - y[i]) - 3/h[i-1] *(y[i] - y[i-1])
    end

    l = ones(n)
    l[1] = 2*h[1]
    μ = fill(0.5, n)
    z = fill(α[1]/l[1], n)
    for i in 2:n-1
        l[i] = 2*(x[i+1] - x[i-1]) - h[i-1]*μ[i-1]
        μ[i] = h[i]/l[i]
        z[i] = (α[i] - h[i-1]*z[i-1])/l[i]
    end
    l[n] = h[n-1]*(2 - μ[n-1])
    z[n] = (α[n] - h[n-1]*z[n-1])/l[n]
    c = fill(z[n], n)
    b, d = zeros(n), zeros(n)
    for j in reverse(1:n-1)
        c[j] = z[j] - μ[j]*c[j+1]
        b[j] = (y[j+1] - y[j])/h[j] - h[j]*(c[j+1]+2*c[j])/3
        d[j] = (c[j+1] - c[j])/(3*h[j])
    end
    A = [b[1:end-1] c[1:end-1] d[1:end-1]]
    X = []
    for i in 2:4
        for j in 1:3
            push!(X,(a - x[i-1])^(j))
        end
    end
    res = diag(A * reshape(X, (n-1,n-1))) + y[1:end-1]
    if Latex
        for i in 1:n-1
            print(res[i], ",","t ∈","[$(x[i]), $(x[i+1])]")
            print("\n")

        end
    else
        return res
    end

end

end


function Euler2()

	Fa = 1;
	Fb = 1;

	sum = 0;

	F = 0;

	while F<4e6
		F = Fa+Fb;
		Fa = Fb;
		Fb = F;

		if mod(F,2) == 0
			sum+=F
		end
	end

	return sum

end

@time Euler2()
println(Euler2())


using ITensors

# Text visualization, prints to screen
using ITensorUnicodePlots

# Visualization in an interactive window
using ITensorGLMakie

using LinearAlgebra

ITensorVisualizationBase.set_backend!("UnicodePlots")

println("
#######################################################
# Tutorial 1                                          #
#                                                     #
# 1-site state basics                                 #
#######################################################
")

i = Index(2)

#
# Making states
#

# Make an "up" state (+1 eigenvalue of `Z`),
# denoted as `|Z+⟩`.

# Define through setting elements
Zp = ITensor(i)
Zp[i => 1] = 1.0

# Alternative syntax, construct from a Vector
Zp = ITensor([1 0], i)

# Make an "down" state (-1 eigenvalue of `Z`),
# denoted as `|Z-⟩`.

# Define through setting elements
Zm = ITensor(i)
Zm[i => 2] = 1.0

# Alternative syntax, construct from a Vector
Zm = ITensor([0 1], i)

@visualize Zm

# Can do algebra, inner products, etc:
@show (Zp + Zm) / √2
@show dag(Zp) * Zm


### A Pluto.jl notebook ###
# v0.12.16

using Markdown
using InteractiveUtils

# ╔═╡ e077ab16-22e5-11eb-15ed-5f0654bf7328
using Pkg, DrWatson

# ╔═╡ e75a9a06-22e5-11eb-2e36-8d4f62b830ed
begin
	@quickactivate "StatisticsWithJuliaPlutoNotebooks"
	using Random, Distributions, Plots, LaTeXStrings
	Random.seed!(0)
end

# ╔═╡ ca5fbdc8-22e5-11eb-0a60-cb0a127a3ca5
md"## Listing5.18"

# ╔═╡ fa68607e-22e5-11eb-0558-c9a4d9f77426
begin
	prior(lam) = pdf(TriangularDist(0, 10, 3), lam)
	data = [2,1,0,0,1,0,2,2,5,2,4,0,3,2,5,0]

	like(lam) = *([pdf(Poisson(lam),x) for x in data]...)
	posteriorUpToK(lam) = like(lam)*prior(lam)
end

# ╔═╡ 2bd08a88-3afb-11eb-03e1-c3af32635947
begin
	delta = 10^-4.
	lamRange = 0:delta:10
	K = sum([posteriorUpToK(lam)*delta for lam in lamRange])
	posterior(lam) = posteriorUpToK(lam)/K
end

# ╔═╡ 2bd0c412-3afb-11eb-1e28-27323df7bbe6
begin
	bayesEstimate = sum([lam*posterior(lam)*delta for lam in lamRange])
	Text("Bayes estimate: $(bayesEstimate)")
end

# ╔═╡ 2bd13636-3afb-11eb-3c12-2d5a0c0e4b13
begin
	plot(lamRange, prior.(lamRange),
		c=:blue, label="Prior distribution")
	plot!(lamRange, posterior.(lamRange),
		c=:red, label="Posterior distribution",
		xlims=(0, 10), ylims=(0, 1.2),
		xlabel=L"\lambda",ylabel="Density")
end

# ╔═╡ 475ff888-22e6-11eb-2354-09f8f40a8e12
md"## End of listing5.18"

# ╔═╡ Cell order:
# ╟─ca5fbdc8-22e5-11eb-0a60-cb0a127a3ca5
# ╠═e077ab16-22e5-11eb-15ed-5f0654bf7328
# ╠═e75a9a06-22e5-11eb-2e36-8d4f62b830ed
# ╠═fa68607e-22e5-11eb-0558-c9a4d9f77426
# ╠═2bd08a88-3afb-11eb-03e1-c3af32635947
# ╠═2bd0c412-3afb-11eb-1e28-27323df7bbe6
# ╠═2bd13636-3afb-11eb-3c12-2d5a0c0e4b13
# ╟─475ff888-22e6-11eb-2354-09f8f40a8e12


"""
**RMSProp Optimizer**
```julia
RMSprop(; η::Real=0.001, γ::Real=0.01, ϵ::Real=1e-8)
```

Algorithm

```math
\\begin{align*}
    E[g^2]_t =& \\gamma E[g^2]_{t-1}+(1-\\gamma) g_t^2\\\\
    \\Delta x_t =& \\frac{\\eta}{\\sqrt{E[g^2]_t + \\epsilon}}g_t
\\end{align*}
```
    [Reference](http://www.cs.toronto.edu/~tijmen/csc321/slides/lecture_slides_lec6.pdf)
"""
mutable struct RMSprop <: Optimizer
    opt_type::String
    t::Int64
    ϵ::Float64
    η::Float64
    γ::Float64
    E_g²_t::AbstractArray
end

function RMSprop(; η::Real=0.001, γ::Real=0.01, ϵ::Real=1e-8)
    @assert η > 0.0 "η must be greater than 0"
    @assert γ > 0.0 "γ must be greater than 0"
    @assert ϵ > 0.0 "ϵ must be greater than 0"

    RMSprop("RMSprop", 0, ϵ, η, γ, [])
end

params(opt::RMSprop) = "ϵ=$(opt.ϵ), η=$(opt.η), γ=$(opt.γ)"

function update(opt::RMSprop, g_t::AbstractArray{T}) where {T<:Real}
    # resize accumulated and squared updates
    if opt.t == 0
        opt.E_g²_t = zero(g_t)
    end

    # accumulate gradient
    opt.E_g²_t = opt.γ * opt.E_g²_t + (one(T) - opt.γ) * (g_t .^ 2)

    # compute update
    RMS_g_t = sqrt.(opt.E_g²_t .+ opt.ϵ)
    δ = opt.η * g_t ./ RMS_g_t

    return δ
end


using Distributions
using Gadfly
using GSL
using PMTK4Stats

αSS    = [20, 20]
βSS    = [10, 10]
αPrior = [20, 30]
βPrior = [20, 10]
M = 2
mixprior = [0.5, 0.5]
αPost      = αPrior + αSS
βPost      = βPrior + βSS

function mixBetaPost_()
    logmarglik  = lbeta(αPrior+αSS, βPrior+βSS)-lbeta(αPrior, βPrior)
    normPost, L = normalizeLogspace(logmarglik .+ log(mixprior))
    mixpost     = exp(normPost)
end

function mixBetaDemo()
    mixpost = mixBetaPost_()
    grid = 0.0001:0.01:0.9999

    function evalpdf(θ, Z, α, β)
        sum([Z[i].*pdf(Beta(α[i], β[i]), θ) for i=1:length(Z)])
    end

    prior = evalpdf(grid, mixprior, αPrior, βPrior)
    post  = evalpdf(grid, mixpost, αPost, βPost)

    lprior = layer(x=grid, y=prior, Geom.line, Theme(default_color=colorant"red", line_width=3px, line_style=Gadfly.get_stroke_vector(:dash)))
    lpost  = layer(x=grid, y=post, Geom.line, Theme(default_color=colorant"blue", line_width=3px))
    [lprior lpost]
end

function mixBetaBiased()
    mixpost = mixBetaPost_()
    pbiased = sum(mixpost.*(1-[sf_beta_inc(αPost[i], βPost[i], .5) for i=1:length(αPost)]))
    pbiasedSimple = 1-sf_beta_inc(αPost[1], βPost[1], .5)
    [pbiased, pbiasedSimple]
end


using Test
using MatrixLM

using DataFrames
using Random
using LinearAlgebra
using GLM

@testset "GLM compatible" begin

    # Tolerance for tests
    tol = 10.0^(-7)

    # Dimensions of matrices
    n = 100
    m = 200
    p = 10
    q = 20

    # Generate some matrices.
    Random.seed!(4)
    X = rand(n,p)
    Z = rand(m,q)
    B = rand(1:20,p,q)
    E = randn(n,m)
    Y = X*B*transpose(Z)+E

    # Data frame to be passed into lm
    GLMData = DataFrame(hcat(vec(Y), kron(Z,X)), :auto)
    # lm estimate
    GLMEst = lm(Matrix(GLMData[:,2:end]), Vector(GLMData[:,1]))

    # Put together RawData object for MLM
    MLMData = RawData(Response(Y), Predictors(X, Z))
    # mlm estimate
    # MLMEst = mlm(MLMData, hasXIntercept = false, hasZIntercept = false)
    MLMEst = mlm(MLMData, hasXIntercept = false, hasZIntercept = false)

    @test isapprox(GLM.coef(GLMEst), vec(MatrixLM.coef(MLMEst)), atol=tol)
    @test isapprox(GLM.predict(GLMEst), vec(MatrixLM.predict(MLMEst).Y), atol=tol)
    @test LinearAlgebra.issymmetric(round.(MLMEst.sigma, digits=10)) # and also positive semi-definite?

end

# Tests for variance shrinkage, WLS
# Contrasts
# Permutations


"""
    HyperSphere{N, T}

A `HyperSphere` is a generalization of a sphere into N-dimensions.
A `center` and radius, `r`, must be specified.
"""
struct HyperSphere{N,T} <: GeometryPrimitive{N,T}
    center::Point{N,T}
    r::T
end

"""
    Circle{T}

An alias for a HyperSphere of dimension 2. (i.e. `HyperSphere{2, T}`)
"""
const Circle{T} = HyperSphere{2,T}

"""
    Sphere{T}

An alias for a HyperSphere of dimension 3. (i.e. `HyperSphere{3, T}`)
"""
const Sphere{T} = HyperSphere{3,T}

HyperSphere{N}(p::Point{N,T}, number) where {N,T} = HyperSphere{N,T}(p, convert(T, number))

widths(c::HyperSphere{N,T}) where {N,T} = Vec{N,T}(radius(c) * 2)
radius(c::HyperSphere) = c.r
origin(c::HyperSphere) = c.center

Base.minimum(c::HyperSphere{N,T}) where {N,T} = Vec{N,T}(origin(c)) - Vec{N,T}(radius(c))
Base.maximum(c::HyperSphere{N,T}) where {N,T} = Vec{N,T}(origin(c)) + Vec{N,T}(radius(c))

function Base.in(x::AbstractPoint, c::HyperSphere)
    return norm(origin(c) - x) ≤ radius(c)
end

centered(S::Type{HyperSphere{N,T}}) where {N,T} = S(Vec{N,T}(0), T(0.5))
function centered(::Type{T}) where {T<:HyperSphere}
    return centered(HyperSphere{ndims_or(T, 3),eltype_or(T, Float32)})
end

function coordinates(s::Circle, nvertices=64)
    rad = radius(s)
    inner(fi) = Point(rad * sin(fi + pi), rad * cos(fi + pi)) .+ origin(s)
    return (inner(fi) for fi in LinRange(0, 2pi, nvertices))
end

function texturecoordinates(s::Circle, nvertices=64)
    return coordinates(Circle(Point2f(0.5), 0.5f0), nvertices)
end

function coordinates(s::Sphere, nvertices=24)
    θ = LinRange(0, pi, nvertices)
    φ = LinRange(0, 2pi, nvertices)
    inner(θ, φ) = Point(cos(φ) * sin(θ), sin(φ) * sin(θ), cos(θ)) .* s.r .+ s.center
    return ivec((inner(θ, φ) for θ in θ, φ in φ))
end

function texturecoordinates(s::Sphere, nvertices=24)
    ux = LinRange(0, 1, nvertices)
    return ivec(((φ, θ) for θ in reverse(ux), φ in ux))
end

function faces(sphere::Sphere, nvertices=24)
    return faces(Rect(0, 0, 1, 1), (nvertices, nvertices))
end

function normals(s::Sphere{T}, nvertices=24) where {T}
    return coordinates(Sphere(Point{3,T}(0), 1), nvertices)
end


module RFIOD
export binarysearch, fixedpointiteration, staircasediagram

using Plots

"""
    binarysearch(f::Function, a::Real, b::Real, [ε::Real])

Use the binary search algorithm to find a root of `f` between `a` and `b` with maximum trucation error `ε`.

Also returns the number of iterations that the algorithm takes.

# Examples

```julia-repl
julia> binarysearch(sin,3,4)
(3.141590118408203, 17)

julia> abs(ans[1] - π) < 5e-6
true
```
"""
function binarysearch(f::Function, a::Real, b::Real, ε::Real = 5e-6)

    # Checks that the initial interval is valid
    fa, fb = f(a), f(b)
    iszero(fa) && return (a,0)
    iszero(fb) && return (b,0)
    fa * fb < 0 || error("cannot find a root, function must take opposite signs on each end of the interval")

    # Iteratively finds the halfway point of the interval, then keeps the half where the sign of f changes
    N = 0
    while abs(b-a) > 2ε
        N += 1
        m = (a+b)/2
        fm = f(m)
        iszero(fm) && return (m,N)
        (a, b, fa, fb) = ( fa * fm > 0 ? (m, b, fm, fb) : (a, m, fa, fm) )
    end
    # Returns the midpoint of the interval, which is within ε of the root
    return ((a+b)/2,N)

end

"""
    fixedpointiteration(f::Function, x₀::Real, Nₘₐₓ::Int, [ε::Real])

Use the fixed point iteration algorithm to converge on a fixed point of `f`, with starting value `x₀`.

Terminate if consecutive iterations differ by less than `ε`, or after `Nₘₐₓ` iterations.

Returns the sequence resulting from the iteration.

# Examples

```julia-repl
julia> xs = fixedpointiteration(cos, 0, 100)
92-element Vector{Float64}:
 0.0
 1.0
 [...]
 0.7390851332151607

julia> cos(xs[end])
0.7390851332151607
```
"""
function fixedpointiteration(f::Function, x₀::Real, Nₘₐₓ::Int, ε::Real = eps(Float64))

    # Sets up the initial values for the algorithm
    n = 1
    xₙ₋₁ = convert(Float64,x₀)
    xₙ = f(xₙ₋₁)
    xvalues = [xₙ₋₁, xₙ]

    # Iteratively advances the algorithm by evaluating f at the previous result and storing it
    while abs(xₙ₋₁ - xₙ) ≥ ε && n < Nₘₐₓ
        n += 1
        xₙ₋₁, xₙ = xₙ, f(xₙ)
        push!(xvalues,xₙ)
    end
    # Returns the sequence resulting from the algorithm
    return xvalues

end

"""
staircasediagram(f::Function, xvalues::Vector{Float64})

Create a staircase/cobweb diagram from `xvalues`, the results of the fixed point iteration algorithm on `f`.

# Examples

```julia-repl
julia> staircasediagram(cos, fixedpointiteration(cos, 0, 100))
```
"""
function staircasediagram(f::Function, xvalues::Vector{Float64})

    # Finds the scale at which to draw the diagram
    minx, maxx = min(xvalues...), max(xvalues...)
    δ = (maxx - minx)/100
    xticks = (minx - 10δ):δ:(maxx + 10δ)

    # Plots y = x and y = f(x), then adds in a marker for the starting point and the lines given by the sequence
    p = plot([x -> x, f], xticks, legend = false)
    scatter!(p, (xvalues[1], xvalues[1]), markercolor = :black)
    for n = 1:(length(xvalues)-1)
        plot!(p, [xvalues[n], xvalues[n], xvalues[n+1]],
            [xvalues[n], xvalues[n+1], xvalues[n+1]], linecolor = :black)
    end
    # Returns the plot
    return p

end

end


function linsolve_cr( LF::LF3dGrid, b::Array{Float64,1};
                      x0 = nothing,
                      NiterMax = 1000, TOL=5.e-10,
                      convmsg=false, showprogress=false )
  #
  Npoints = size(b)[1]
  #
  if x0 == nothing
    x = zeros(Float64,Npoints)
  else
    x = copy(x0)
  end
  #
  L_x = apply_Laplacian( LF, x )
  #
  r = b[:] - L_x[:]
  p = r[:]

  r_old = r[:]
  p_old = p[:]
  #
  L_p = apply_Laplacian( LF, p )
  x_old = x[:]
  #
  for iter = 1 : NiterMax
    #
    L_r = apply_Laplacian( LF, r )
    #
    rLr = dot( r, L_r )
    α = rLr / dot( L_p, L_p )  # ???
    #
    x_old = x[:]
    x[:] = x[:] + α * p[:]  # FIXME use x[:] to force x to be copied, not referenced
    #
    r_old = r[:]
    rLr_old = rLr
    r[:] = r[:] - α * L_p[:]
    #
    norm_res = sqrt( dot( r, r ) )
    norm_p   = sqrt( dot( p, p ) )
    Δx = abs(x-x_old)
    norm_Δx = sqrt( dot( Δx, Δx ) )
    if showprogress
      @printf("%8d %18.10e %18.10e %18.10e\n", iter, norm_res, norm_p, norm_Δx)
    end
    #
    if norm_res < TOL || norm_p < TOL || norm_Δx < TOL
      if convmsg
        @printf("#Convergence achieved in linsolve_cr: %8d iterations.\n", iter )
      end
      break
    end
    #
    L_r = apply_Laplacian( LF, r ) # calculate new L_r
    β = dot( r, L_r ) / rLr_old
    #
    p_old = p[:]
    p = r[:] + β * p[:]
    #
    L_p = L_r[:] + β * L_p[:]
  end
  #
  return x
  #
end # of function


function partition!(A, p, r)
	x = A[r]
	i = p-1
	for j in p:(r-1)
	    if A[j] <= x
	        i += 1
	        tmp = A[i]
	        A[i] = A[j]
	        A[j] = tmp
	    end
	end
	tmp = A[r]
	A[r] = A[i+1]
	A[i+1] = tmp
	return i+1
end

function quick_sort!(A, p, r)
    if p < r
        q = partition!(A, p, r)
        quick_sort!(A, p, q-1)
        quick_sort!(A, q+1, r)
    end
    return A
end

function algdat_sort!(A)
    return quick_sort!(A, 1, length(A))
end

function bisect_left(A, p, r, v)
    i = p
    if p < r
        q = floor(Int, (p+r)/2)
        if v <= A[q]
            i = bisect_left(A, p, q, v)
        else
            i = bisect_left(A, q+1, r, v)
        end
    end
    return i
end

function bisect_right(A, p, r, v)
    i = p
    if p < r
        q = floor(Int, (p+r)/2)
        if v < A[q]
            i = bisect_right(A, p, q, v)
       	else
            i = bisect_right(A, q+1, r, v)
        end
    end
    return i
end

function find_median(A, lower, upper)
    min_index = bisect_left(A, 1, length(A)+1, lower)
    max_index = bisect_right(A, 1, length(A)+1, upper) - 1

    if (max_index-min_index+1)%2 == 0
        i = floor(Int, (max_index-min_index)/2)
        i += min_index
        return (A[i] + A[i+1])/2
    else
        return A[min_index + floor(Int, (max_index - min_index)/2)]
    end
end


using JuMP
using GLPK
include("Data.jl")

# Int8: -128 -> 127
# Int16 : -32.768 -> 32.767
# Int32 : -2.147.483.648 -> 2.147.483.647
# Int64 : - 2^63 -> 2^63 - 1
# Float32, Float64, UINT8, UInt16, etccdddd

file = map(file->string(file), ARGS)

if(length(file) != 1)
  println("Wrong Pattern")
  println("julia main.jl instances/questao2.txt")
end

filepath = file[1]

function solver(filepath)
  capacity = Array{Int64,2}
  d1 = Main.DataReader.Data(0,0,0,0,[],[])

  capacity, d1 = Main.DataReader.readData(filepath)

  costs = zeros(Int64,(d1.N_vertex, d1.N_vertex))

  for i in 1:d1.N_vertex
    if i in d1.start_nodes
      costs[i,d1.end_node] = 1
    end
  end

  mcfp = Model(GLPK.Optimizer)

  n = d1.N_vertex

  @variable(mcfp, x[1:n,1:n] >= 0) #ok

  # capacity  --------- taok
  for i in 1:n
    for j in 1:n
      cap = capacity[i,j]
      @constraint(mcfp, x[i,j] - capacity[i,j] <= 0)
    end
  end

  #sum 0 // demand / offer
  for i in 1:n
    if i in d1.start_nodes
      @constraint(mcfp, sum(x[i,j] - x[j,i] for j in 1:n) == d1.max_possible_flow[i])
    elseif i == d1.end_node
      global max_end_flow = 0
      for node in d1.start_nodes
        max_end_flow += d1.max_possible_flow[node]
      end
      @constraint(mcfp, sum(x[i,j] - x[j,i] for j in 1:n) == max_end_flow * -1)
    else
      @constraint(mcfp, sum(x[i,j] - x[j,i] for j in 1:n) == 0)
    end
  end

  @objective(mcfp, Min, sum(costs[i,j] * x[i,j] for i in 1:n, j in 1:n))

  #println(mcfp)
  function solve_mcfp(mcfp)
    JuMP.optimize!(mcfp)
    @show JuMP.termination_status(mcfp)
    println("Max Flow = ", max_end_flow - objective_value(mcfp))

  end
  @time solve_mcfp(mcfp)
end

# run 3 times to compile the func in the first one
solver(filepath)
solver(filepath)
solver(filepath)


module MonteCarlo

using Random
using Statistics

# European Option Calculation from
# https://groups.google.com/forum/?hl=en&fromgroups=#!topic/julia-dev/ImhGsqX_IHc

function perf_euro_option_devec(npaths)
    steps = 250
    r = 0.05
    sigma = .4
    T = 1
    dt = T/steps
    K = 100
    S = fill(100.0, npaths)
    t1 = (r - 0.5*sigma^2)*dt
    t2 = sigma*sqrt(dt)

    for i=1:steps
        for j=1:npaths
            S[j] *= exp(t1 + t2 * randn())
        end
    end

    return mean(exp(-r*T) .* max.(K .- S, 0))
end

function perf_euro_option_vec(npaths)
    steps = 250
    r = 0.05
    sigma = .4
    T = 1
    dt = T/steps
    K = 100
    S = fill(100.0, npaths)
    t1 = (r - 0.5*sigma^2)*dt
    t2 = sigma*sqrt(dt)
    R = Array{Float64}(undef, npaths)

    for i=1:steps
        S .*= exp.(t2 .* randn!(R) .+ t1)
    end

    return mean(exp(-r*T) .* max.(K .- S, 0))
end

end # module


# # Two-Stage Stochastic Program

# ## Introduction
# First let's consider a standard two-stage stochastic program. Such problems
# consider 1st stage variables ``x \in X \subseteq \mathbb{R}^{n_x}`` which denote
# upfront (here-and-now) decisions made before any realization of the random
# parameters ``\xi \in \mathbb{R}^{n_\xi}`` is observed, and 2nd stage variables
# ``y(\xi) \in \mathbb{R}^{n_y}`` which denote recourse (wait-and-see) decisions
# that are made in response to realizations of ``\xi``. Moreover, the objective
# seeks to optimize 1st stage costs ``f_1(x)`` and second stage costs
# ``f_2(x, y(\xi))`` which are evaluated over the uncertain domain via a risk
# measure ``R_\xi[\cdot]`` (e.g., the expectation ``\mathbb{E}_\xi[\cdot]``).
# Putting this together, we obtain the two-stage stochastic program:
# ```math
# \begin{aligned}
#     &&\min_{x, y(\xi)} &&& f_1(x) + R_\xi[f_2(x, y(\xi))] \\
#     &&\text{s.t.} &&&  g_i(x, y(\xi)) = 0, && i \in I\\
#     &&&&& h_j(x, y(\xi)) \leq 0, && j \in J\\
#     &&&&&  x \in X\\
# \end{aligned}
# ```
# where ``g_i(x, y(\xi)), \ i \in I,`` denote 2nd stage equality constraints,
# ``h_j(x, y(\xi)), \ j \in J,`` are 2nd stage inequality constraints, and ``X``
# denotes the set of feasible 1st stage decisions.

# ## Formulation

# For an example, we consider the classic farmer problem. Here the farmer must
# allocate farmland ``x_c`` for each crop ``c \in C`` with random yields per acre
# ``\xi_c`` such that he minimizes expenses (i.e., maximizes profit) while fulfilling
# contractual demand ``d_c``. If needed he can purchase crops from other farmers
# to satisfy his contracts. He can also sell extra crop yield that exceeds his
# contractual obligations. Thus, here we have 1st stage variables ``x_c`` and
# 2nd stage variables of crops sold ``w_c(\xi)`` and crops purchased ``y_c(\xi)``.
# Putting this together using the expectation ``\mathbb{E}_\xi[\cdot]`` as our
# risk measure we obtain:
# ```math
# \begin{aligned}
#     &&\underset{x, y(\xi), w(\xi)}{\text{min}} &&& \sum_{c \in C} \alpha_c x_c + \mathbb{E}_{\xi}\left[\sum_{c \in C}\beta_c y_c(\xi) - \lambda_c w_c(\xi)\right] \\
#     &&\text{s.t.} &&&  \sum_{c \in C} x_c \leq \bar{x}\\
#     &&&&& \xi_c x_c + y_c(\xi) - w_c(\xi) \geq d_c, && c \in C \\
#     &&&&& 0 \leq x_c \leq \bar{x}, && c \in C \\
#     &&&&& 0 \leq y_c(\xi) \leq \bar{y}_c, && c \in C \\
#     &&&&& 0 \leq w_c(\xi) \leq \bar{w}_c, && c \in C \\
#     &&&&& \xi_c \in \Xi_c, && c \in C
# \end{aligned}
# ```
# where ``\alpha_c`` are production costs, ``\beta_c`` are the purchase prices,
# ``\lambda_c`` are the selling prices, ``\bar{x}`` is the total acreage,
# ``\bar{y}_c`` are purchases limits, ``\bar{w}_c`` are selling limits, and
# ``\Xi_c`` are the underlying distributions.

# ## Problem Setup

# First let's import the necessary packages:
using InfiniteOpt, Distributions, Ipopt

# Next let's specify the problem data:
num_scenarios = 10 # small amount for example
C = 1:3
α = [150, 230, 260] # land cost
β = [238, 210, 0]   # purchasing cost
λ = [170, 150, 36]  # selling price
d = [200, 240, 0]   # contract demand
xbar = 500          # total land
wbar3 = 6000        # no upper bound on the other crops
ybar3 = 0           # no upper bound on the other crops
Ξ = [Uniform(0, 5), Uniform(0, 5), Uniform(10, 30)]; # the distributions

# ## Problem Definition

# Let's start by setting up the infinite model that uses Ipopt as the optimizer
# that will ultimately be used to solve the transcribed variant:
model = InfiniteModel(Ipopt.Optimizer)
set_optimizer_attribute(model, "print_level", 0);

# Now let's define the infinite parameters using [`@infinite_parameter`](@ref):
@infinite_parameter(model, ξ[c in C] ~ Ξ[c], num_supports = num_scenarios)

# Now let's define all of the decision variables using `@variables`:
@variables(model, begin
    ## 1st stage variables
    0 <= x[C] <= xbar
    ## 2nd stage variables
    0 <= y[C], Infinite(ξ)
    0 <= w[C], Infinite(ξ)
end)

# Next, the objective is defined using `@objective` and [`𝔼`](@ref):
@objective(model, Min, sum(α[c] * x[c] for c in C) +
                       𝔼(sum(β[c] * y[c] - λ[c] * w[c] for c in C), ξ))

# Finally, all we need to do is define the constraints using `@constraints`:
@constraints(model, begin
    ## capacity constraint
    sum(x[c] for c in C) <= xbar
    ## balances
    [c in C], ξ[c] * x[c] + y[c] - w[c] >= d[c]
    ## crop limits
    w[3] <= wbar3
    y[3] <= ybar3
end)

# ## Problem Solution

# With the model defined, let's optimize and get the results
optimize!(model)
x_opt = value.(x)
profit = -objective_value(model)

println("Land Allocations: ", [round(x_opt[k], digits = 2) for k in keys(x_opt)])
println("Expected Profit: \$", round(profit, digits = 2))

# We did it!

# ## CVaR Objective

# An interesting modification to the above problem would be to use a ``CVaR``
# risk measure instead of an expectation. This also can be readily achieved via
# `InfiniteOpt`. The ``CVaR`` measure is defined:
# ```math
# CVaR_\epsilon(X) = \underset{t \in \mathbb{R}}{\text{inf}}\left\{t + \frac{1}{1-\epsilon} \mathbb{E}[\text{max}(0, X - t)] \right\}
# ```
# where ``\epsilon`` is the confidence level. Inserting this into the formulation,
# we now obtain:
# ```math
# \begin{aligned}
#     &&\underset{x, y(\xi), w(\xi), t, q(\xi)}{\text{min}} &&& \sum_{c \in C} \alpha_c x_c + t + \frac{1}{1-\epsilon} \mathbb{E}_{\xi}[q(\xi)] \\
#     &&\text{s.t.} &&& \sum_{c \in C} x_c \leq \bar{x}\\
#     &&&&& \xi_c x_c + y_c(\xi) - w_c(\xi) \geq d_c, && c \in C \\
#     &&&&& 0 \leq x_c \leq \bar{x}, && c \in C \\
#     &&&&& 0 \leq y_c(\xi) \leq \bar{y}_c, && c \in C \\
#     &&&&& 0 \leq w_c(\xi) \leq \bar{w}_c, && c \in C \\
#     &&&&& \xi_c \in \Xi_c, && c \in C \\
#     &&&&& q(\xi) \geq \sum_{c \in C}\beta_c y_c(\xi) - \lambda_c w_c(\xi) - t \\
#     &&&&& q(\xi) \geq 0
# \end{aligned}
# ```
# where ``q(\xi)`` is introduced to handle the max operator. Let's update and
# resolve our `InfiniteOpt` model using ``\epsilon = 0.95``:

# Define the additional variables:
@variables(model, begin
    t
    q >= 0, Infinite(ξ)
end)

# Redefine the objective:
@objective(model, Min, sum(α[c] * x[c] for c in C) + t + 1 / (1 - 0.95) * 𝔼(q, ξ))

# Add the max constraint:
@constraint(model, q >= sum(β[c] * y[c] - λ[c] * w[c] for c in C) - t)

# Optimize and get the results
optimize!(model)
x_opt = value.(x)
y_opt = value.(y)
w_opt = value.(w)
profit = -sum(α[c] * x_opt[c] for c in C) - 1 / num_scenarios *
            sum(β[c] * y_opt[c][k] - λ[c] * w_opt[c][k] for c in C, k in 1:num_scenarios)

println("Land Allocations: ", [round(x_opt[k], digits = 2) for k in keys(x_opt)])
println("Expected Profit: \$", round(profit, digits = 2))

# That's it!

# ### Maintenance Tests
# These are here to ensure this example stays up to date.
using Test
@test termination_status(model) == MOI.LOCALLY_SOLVED
@test x_opt isa JuMPC.DenseAxisArray{<:Real}
@test profit isa Real


using HypothesisTests, Distributions, Test, Random
using HypothesisTests: default_tail

@testset "Anderson-Darling" begin
@testset "One sample test" begin
    n = 1000
    Random.seed!(1984948)

    x = rand(Normal(), n)
    t = OneSampleADTest(x, Normal())
    @test t.A² ≈ 0.2013 atol=0.1^4
    @test pvalue(t) ≈ 0.99 atol=0.1^4
    @test default_tail(t) == :right

    x = rand(DoubleExponential(), n)
    t = OneSampleADTest(x, Normal())
    @test t.A² ≈ 10.7439 atol=0.1^4
    @test pvalue(t) ≈ 0.0 atol=0.1^4

    x = rand(Cauchy(), n)
    t = OneSampleADTest(x, Normal())
    @test pvalue(t) ≈ 0.0 atol=0.1^4

    x = rand(LogNormal(), n)
    t = OneSampleADTest(x, Normal())
    @test pvalue(t) ≈ 0.0 atol=0.1^4

    x = rand(Uniform(), n)
    t = OneSampleADTest(x, Uniform())
    @test pvalue(t) ≈ 0.145 atol=0.1^4

    x = rand(Uniform(0, 1.8), n)
    t = OneSampleADTest(x, Uniform())
    @test pvalue(t) ≈ 0.5112 atol=0.1^4

    x = rand(Exponential(), n)
    t = OneSampleADTest(x, Exponential())
    @test pvalue(t) ≈ 0.9427 atol=0.1^4
end

@testset "k-sample test" begin
    Random.seed!(948574875)
    samples = Any[
        [38.7, 41.5, 43.8, 44.5, 45.5, 46.0, 47.7, 58.0],
        [39.2, 39.3, 39.7, 41.4, 41.8, 42.9, 43.3, 45.8],
        [34.0, 35.0, 39.0, 40.0, 43.0, 43.0, 44.0, 45.0],
        [34.0, 34.8, 34.8, 35.4, 37.2, 37.8, 41.2, 42.8]
    ]

    t = KSampleADTest(samples...)
    @test t.A²k ≈ 8.3926 atol=0.1^4
    @test t.σ ≈ 1.2038 atol=0.1^4
    @test pvalue(t) ≈ 0.0022 atol=0.1^4
    @test default_tail(t) == :right

    ts = KSampleADTest(samples..., nsim = 20000);
    @test pvalue(ts) ≈ 0.00155 atol=0.1^3

    t = KSampleADTest(samples..., modified = false)
    @test t.A²k ≈ 8.3559 atol=0.1^4
    @test t.σ ≈ 1.2038 atol=0.1^4
    @test pvalue(t) ≈ 0.0023 atol=0.1^4

    ts = KSampleADTest(samples..., modified = false, nsim = 20000);
    @test pvalue(ts) ≈ 0.00150 atol=0.1^3
end

@testset "more tests" begin
    Random.seed!(31412455)
    samples = Any[rand(Normal(), 50), rand(Normal(0.5), 30)]
    t = KSampleADTest(samples...)
    @test pvalue(t) < 0.05

    samples = Any[rand(Normal(), 50), rand(Normal(), 30), rand(Normal(), 20)]
    t = KSampleADTest(samples...)
    @test pvalue(t) > 0.05

    @test pvalue(OneSampleADTest(vcat(rand(Normal(),500), rand(Beta(2,2),500)), Beta(2,2))) ≈ 0.0 atol=0.1^4

    n = 1000
    x = rand(Exponential(), n)
    @test pvalue(KSampleADTest(rand(1000),randn(1000))) ≈ 0.0 atol=eps()
    @test pvalue(KSampleADTest(x,x,x,x,x,x)) ≈ 1.0
end

end


#! /usr/bin/julia

# Rosetta Code, Digital root/Multiplicative digital root

const bs = 10
const excnt = 5

function digitalmultroot{S<:Integer,T<:Integer}(n::S, bs::T=10)
    -1 < n && 1 < bs || throw(DomainError())
    ds = n
    pers = 0
    while bs <= ds
        ds = prod(digits(ds, bs))
        pers += 1
    end
    return (pers, ds)
end

println("Testing Multiplicative Digital Root.\n")
for i in [123321, 7739, 893, 899998]
    (pers, ds) = digitalmultroot(i, bs)
    print(@sprintf("%8d", i))
    print(" has persistence ", pers)
    println(" and digital root ", ds)
end

dmr = zeros(Int, bs, excnt)
hasroom = trues(bs)
dex = ones(Int, bs)

i = 0
while any(hasroom)
    (pers, ds) = digitalmultroot(i, bs)
    ds += 1
    if hasroom[ds]
        dmr[ds, dex[ds]] = i
        dex[ds] += 1
        if dex[ds] > excnt
            hasroom[ds] = false
        end
    end
    i += 1
end

println("\n MDR:    First ", excnt, " numbers having this MDR")
for (i, d) in enumerate(0:(bs-1))
    print(@sprintf("%4d: ", d))
    println(join([@sprintf("%6d", dmr[i, j]) for j in 1:excnt], ","))
end


# This file calculates the third application in Y.-S. Chan, A. C. Fannjiang, and G. H. Paulino,
# Integral equations with hypersingular kernels -- theory and applications to fracture mechanics,
# Int. J. Eng. Sci., 41:683--720, 2003.

using ApproxFun, SingularIntegralEquations

x = Fun(identity)
w = 1/sqrt(1-x^2)
d = domain(x)
d2 = d^2
B = [Dirichlet(d);Neumann(d)]
H2 = Hilbert(d,2)
H4 = Hilbert(d,4)
ϵ = 0.2
L = -6ϵ^2*H4[w] + H2[w]
f = -Fun(one)

uSIE = [B;L]\[[0,0],[0,0],f]
@time uSIE = [B;L]\[[0,0],[0,0],f]
println("The length of uSIE is: ",ncoefficients(uSIE))
println("The extrema of uSIE are: ",extrema(uSIE))

test0 = uSIE/(1-x^2)^2
test = Fun(x->test0(x),Ultraspherical(1,d))
temp = 0.0
for i=1:ncoefficients(test)
    temp += i*test.coefficients[i]
end
println("The normalized generalized Stress Intensity Factors are: ",3ϵ*temp)


@doc raw"""
```
t = find_threshold(Yen(), histogram, edges)
```

Computes the threshold value using Yen's maximum correlation criterion for
bilevel thresholding.

# Output

Returns a real number `t` in `edges`. The `edges` parameter represents an
`AbstractRange` which specifies the intervals associated with the histogram bins.

# Details

This algorithm uses the concept of *entropic correlation* of a gray level histogram to produce a threshold
value.

Let ``f_1, f_2, \ldots, f_I`` be the frequencies in the various bins of the
histogram and ``I`` the number of bins. With ``N = \sum_{i=1}^{I}f_i``, let
``p_i = \frac{f_i}{N}`` (``i = 1, \ldots, I``) denote the probability
distribution of gray levels. From this distribution one derives two additional
distributions. The first defined for discrete values ``1`` to ``s`` and the
other, from ``s+1`` to ``I``. These distributions are

```math
A: \frac{p_1}{P_s}, \frac{p_2}{P_s}, \ldots, \frac{p_s}{P_s}
\quad \text{and} \quad
B: \frac{p_{s+1}}{1-P_s}, \ldots, \frac{p_n}{1-P_s}
\quad \text{where} \quad
P_s = \sum_{i=1}^{s}p_i.
```
The entropic correlations associated with each distribution are

```math
C(A) = -\ln \sum_{i=1}^{s} \left( \frac{p_i}{P_s} \right)^2 \quad \text{and} \quad C(B) = -\ln \sum_{i=s+1}^{I} \left( \frac{p_i}{1 - P_s} \right)^2.
```

Combining these two entropic correlation functions we have

```math
\psi(s) = -\ln \sum_{i=1}^{s} \left( \frac{p_i}{P_s} \right)^2 -\ln \sum_{i=s+1}^{I} \left( \frac{p_i}{1 - P_s} \right)^2.
```
Finding the discrete value ``s`` which maximises the function ``\psi(s)`` produces
the sought-after threshold value (i.e. the bin which determines the threshold).

# Arguments

The function arguments are described in more detail below.

##  `histogram`

An `AbstractArray` storing the frequency distribution.

##  `edges`

An `AbstractRange` specifying how the intervals for the frequency distribution
are divided.

# Example

Compute the threshold for the "cameraman" image in the `TestImages` package.

```julia

using TestImages, ImageContrastAdjustment, HistogramThresholding

img = testimage("cameraman")
edges, counts = build_histogram(img, 256)
#=
  The `counts` array stores at index 0 the frequencies that were below the
  first bin edge. Since we are seeking a threshold over the interval
  partitioned by `edges` we need to discard the first bin in `counts`
  so that the dimensions of `edges` and `counts` match.
=#
t = find_threshold(Yen(), counts[1:end], edges)
```

# Reference

1. Yen JC, Chang FJ, Chang S (1995), “A New Criterion for Automatic Multilevel Thresholding”, IEEE Trans. on Image Processing 4 (3): 370-378, [doi:10.1109/83.366472](https://doi.org/10.1109/83.366472)
"""
function find_threshold(algorithm::Yen, histogram::AbstractArray, edges::AbstractRange)
    total = sum(histogram)
    m = length(histogram)
    p = zeros(m)

    # Calulate probability.
    for i in eachindex(p)
        p[i] = histogram[i] / total
    end

    # Setup sums.
    Pₛ = 0
    Gₛ = 0
    G′ₛ = sum(p.^2)
    maxval = typemin(Float64)
    t = firstindex(edges)
    for s in eachindex(p[1:m-1])
        # Update sums.
        Pₛ += p[s]
        Gₛ += p[s]^2
        G′ₛ -= p[s]^2

        # Calculate total correlation.
        TC = -log(Gₛ * G′ₛ) + 2 * log(Pₛ * (1 - Pₛ))
        if TC > maxval
            maxval = TC
            t = s
        end
    end

    return edges[t]
end


@doc """

Calculates dfgls point optimal unit-root test statistic of Elliott, Rothenberg, and Stock (1996) value of the largest autorregressive coefficient under the alternative hypothesis given by ρ = 1+c/T, where c=-7 if trend="constant" and c=-13.5 if trend="linear")

"""->
function dfgls_test(y::Vector{Float64}; nar::Integer=5, trend::String="constant")
  #rewrite of mw's dfgls.prc
  t = length(y)
  dftau = NaN*zeros(1, 2)

  # @ -- GLS estimate of constant term -- @
  c = -7
  rho = (1+c/t)
  yrho = [y[1]; y[2:t]-rho*y[1:t-1]] #@ Quasi Differenced Y series @
  xcrho = [1.0; (1-rho)*ones(t-1)] #@ Quasi Difference Constant @
  mu = ((xcrho'*xcrho).^(-1)).*(xcrho'yrho)

  c = -13.5
  rho = (1+c/t)
  yrho = [y[1]; y[2:t]-rho*y[1:t-1]] #@ Quasi Differenced Y series @
  xcrho = [1.0; (1-rho)*ones(t-1)] #@ Quasi Difference Constant @
  trnd = collect(1:t)
  xtrho = [trnd[1]; trnd[2:t]-rho*trnd[1:t-1]]
  x = [xcrho xtrho]
  mut = inv(x'x)*(x'yrho)
  #  @ -- detrend y -- @
  y1 = y - mu.*ones(t)                #@ GLS Demeaned @
  y2 = y - [ones(t,1) trnd]*mut        #@ GLS Detrended @
  dy1 = diff(y1)
  dy2 = diff(y2)
  #compute adf
  x1 = ar_lagmatrix(dy1, nar)
  x2 = ar_lagmatrix(dy2, nar)

  z = [y1[1:size(x1,1)] x1]
  y = z[:, 1]
  x = z[:, 2:end]
  xxi = inv(x'x)
  xy = x'y
  bet = xxi*xy
  ssr = (y'y)-(xy)'bet
  s2 = ssr/(size(y,1)-size(x,2))
  b = bet[1]
  vb = s2.*xxi[1,1]
  dfmu = (b-1)./sqrt(vb)

  z = [y1[1:size(x1,1)] x1]
  y = z[:, 1]
  x = z[:, 2:end]
  xxi = inv(x'x)
  xy = x'y
  bet = xxi*xy
  ssr = (y'y)-(xy)'bet
  s2 = ssr/(size(y,1)-size(x,2))
  b = bet[1]
  vb = s2.*xxi[1,1]
  dftau = (b-1)./sqrt(vb)
  (trend=="constant") && return dfmu[1]
  (trend=="linear") && return dftau[1]
  (trend=="both") && return dfmu[1], dftau[1]
  end


# chebyshev points and chebyshev differentiation matrix
# Ref: Spectral methods in MATLAB by Trefethen, chapters 1, 5 and 6.
function cheb_pts(n)
	return cos.((0:n)*pi/n)
end
function cheb_diff_matrix(n)
	x = cheb_pts(n)
	np = n+1
	D = zeros(np,np)
	E = view(D, reverse(1:np*np))
	D[1] = (2*n*n + 1)/6.
	E[1] = -D[1]

	nhalf = fld(np, 2)
	xint = view(x,2:n)
	D[2:n] .= (-1).^(1:n-1)./(xint .- 1)./2.0
	E[2:n] .= -D[2:n]
	D[np] = (-1)^np/2.0
	E[np] = -D[np]

	if mod(np,2) != 0
		j = nhalf + 1
		A = view(D, (j-1)*np+1:j*np)
		A .=  1.0./(x .- x[j]).*
		((-1).^((1 + j):(np + j)))
		A[j] = -x[j]/2/(1. - x[j]*x[j])
		A[1] *= 2.0
		A[np] *= 2.0
	end

	for j=2:nhalf
		A = view(D, (j-1)*np+1:j*np)
		B = view(E, (j-1)*np+1:j*np)
		A .= 1.0./(x .- x[j]).*
		((-1).^((1 + j):(np + j)))
		B .= -A
		A[1] *= 2.0
		B[1] *= 2.0
		A[j] = -x[j]/2/(1. - x[j]*x[j])
		B[j] = -A[j]
		A[np] *= 2.0
		B[np] *= 2.0
	end

	return D
end


using LinearAlgebra
using SparseArrays
using Plots

"This routine solves the heat equation using Backwards Euler in time and finite differences
in space. The output is saved as a .gif file (which should play automatically when this file is run)."

m = 100 # number of points
dt = .01 # time-step
T = 2.0

u0(x) = 0.0 # initial condition
f(x) = 5*(Float64((x > -.5) && (x <= 0.0)) - Float64((x < .5) && (x >= 0.0)))
f(x,t) = f(x)*exp(-t)
α(t) = 1.0
β(t) = sin(2*pi*t) # du/dx

# define spatial grid
x = LinRange(-1,1,m+2)
xint = x[2:end-1]
h = x[2]-x[1]
A = (1/h^2) * spdiagm(0=>2*ones(m),-1=>-ones(m-1),1=>-ones(m-1))

function F(xint,t)
    b = f.(xint,t)
    b[1] += α(t)/h^2
    b[end] += β(t)/h^2
    return b
end

u = u0.(xint)
Nsteps = ceil(Int,T/dt)
dt = T / Nsteps

interval = 2
@gif for i = 1:Nsteps
    t = (i+1)*dt
    u .= (I + dt*A)\(u + dt*F(xint,t))
    if i % interval == 0 # % = mod function
        plot(xint,u,linewidth=2,label="Solution",ylims=(-1.0,3.0))
        println("on timestep $i out of $Nsteps.")
    end
end every interval


# Example for building JOLI operator having functions
# defining action for forward and transpose mapping
#
# we will use plain matrix to fake the actions

using JOLI

# forward function
function fwd(A::AbstractMatrix,vin::AbstractVector,RDT::DataType)
    vout=A*vin
    return jo_convert(RDT,vout)
end

# transpose function
function fwdT(A::AbstractMatrix,vin::AbstractVector,RDT::DataType)
    vout=A.'*vin
    return jo_convert(RDT,vout)
end

# define matrix for mapping functions
a=rand(ComplexF32,3,3)

# define JOLI operator with desired domain and range using
# using joLinearFunctionFwdT constructor
# note forced dropping imaginary part for transpose/adjoint operators
A=joLinearFunctionFwdT(3,3,
    v->fwd(a,v,ComplexF64),v->fwdT(a,v,Float32),
    Float32,ComplexF64;name="my_A")
show(A)

# disable warining for implicit inacurate conversions - use only after debugging your code
jo_convert_warn_set(false)

# display elements of the operator
println("forward: A")
show(A); display(elements(A)); println()
println("transpose: A.'")
show(A.'); display(elements(A.')); println()
println("adjoint: A'")
show(A'); display(elements(A')); println()
println("conjugate: conj(A)")
show(conj(A)); display(elements(conj(A))); println()

# multiply by vector
x=rand(Float32,3)
y=A*x
println("y: ",y)
xx=A'*y
println("xx: ",xx)


# src/secondorder.jl contains function for solving second order ODEs
# can use either numerical integration or closed for formulas
# secondorder(a,b,c) generates the function you pass to ODE.jl RK methods
# solution(a,b,c) is the closed form solver
# ivp solves the initial value problem.
using Optim
using NLsolve

""" secondorder(α,β,γ) yields a function f
    which calculates the ode rule

        αx'' + βx' + γx = 0

    using the substitution

        y = [x; x']

        y[1]' = y[2]

        y[2]' = -α^-1 (βy[2] + γy[1])

"""
function secondorder(α, β, γ)
        return (t, y) -> [y[2]; -(β*y[2] + γ*y[1])/α]
end

function secondorder(x::Vector)
    return secondorder(x...)
end

"""descriminant(a,b,c): quadtratic equation descriminant of ax^2 + bx + c = 0
"""
descriminant(a,b,c) = √(complex(b^2-4a*c))

"""roots(a,b,c): solve the quadratic equation ax^2 + bx + c = 0 """
function roots(a,b,c)
    d = descriminant(a,b,c);
    return (-b+d)/2a, (-b-d)/2a
end

doc"""solution(a,b,c,y₀,t₀) general solution of second order linear homogenous

Arguments:
   - a,b,c::Real the coeffients of the ODE

Returns:
   - func(t, c₁, c₂): the solution at t when the coefficients are c₁, c₂
   - deriv(t, c₁, c₂): the derivative at t when the coefficients are c₁, c₂

Notes: func is derived from the textbook definition and deriv is based on
 applying calculus rules to that (a symbolic algebra tool did this).
"""
function solution(a,b,c)
    @show d = descriminant(a,b,c)
    @show r₁, r₂ = roots(a,b,c)
    if real(d) == d
        info("characteristic polynomial has real roots")
        return (t, c₁, c₂) -> c₁ * exp(r₁*t) + c₂*exp(r₂*t),
               (t, c₁, c₂) -> c₁*r₁*exp(r₁*t) + c₂*r₂*exp(r₂*t)

    end
    if imag(d) != 0.0
        info("characteristic polynomial has complex roots")
        @show α, β = -b/2a, imag(d/2a)
        func(t, c₁, c) = begin
            return (c₁*exp(α*t)*cos(β*t) + c*exp(α*t)*sin(β*t))
        end
        deriv(t,c₁,c₂) = begin
            expα = exp(α*t)
            cosβ = cos(β*t)
            sinβ = sin(β*t)
            return (-β*c₁*expα*sinβ +
                    α*c₂*expα*sinβ +
                    α*c₁*expα*cosβ +
                    β*c₂*expα*cosβ
                    )
            end
        return func, deriv
    end
end

"""ivp(a,b,c,y₀,t₀) solves second order linear homogenous initial value problem

Arguments:
   - a,b,c::Real the coeffients of the ODE
   - y₀ the initial value as a vector
   - t₀ the initial time value

Returns:
   - result : the Optim.jl result object of solving the equations
   - general: the general form solution (see `solution`)
   - deriv:   the general form derivative (see `solution`)

Note: result.zero is the coefficients to plug into the general solution
to get the correct particular solution.
"""
function ivp(a,b,c, y₀, t₀)
    x₀ = y₀[1]
    v₀ = y₀[2]
    general, deriv = solution(a,b,c)

    function f!(params, fvec)
        fvec[1] = x₀ - general(t₀, params...)
        fvec[2] = v₀ - deriv(t₀, params...)
    end
    result = nlsolve(f!,
                     [0.0,0.0],
                     autodiff=true,
                     method=:trust_region,
                     store_trace=true)
    return result, general, deriv
end


using FFTW
export Fourier, FrequencySplit

# TODO: Allow width to the removed frequencies, ie. remove
# a Gaussian around the specified frequency
# This is possibly done via a combination of the
# DSP.Filters.Bandstop and probably some windowing function from DSP.

"""
    Fourier([s, ] frequencies, x=true) <: Decomposition
Decompose a timeseries `s` into a **sum** `x + r`, by identifying specific `frequencies`
at the Fourier space and removing them from the signal.
`x` is the removed periodic component while `r` is the residual.
If a given frequency is not exactly matching the Fourier frequencies, the closest one is
removed.

**Important**: periods/frequencies are defined with respect to the `t` axis length,
the actual `t` values are not used in this method. So, frequency `1/12` (a period of `12`)
means `12` data points (whose actual value depends on `t`).

If you provide `s` the method plans the forward and inverse
Fourier transforms (so that it is efficient to re-use it for `s` of same type and length).

This method works well when a periodic signal P is superimposed on fluctuations S,
and you have a good educated guess of what frequencies compose P.
This method works well if the given signal has length multiple of the periods given.

There is arbitrarity of which part of the signal `x, r`
gets the mean value of `s`, because it is deducted for a better fit.
The argument `x=true` attributes it to `x`, use `false` for `r`.
"""
struct Fourier{F, I} <: Decomposition
    fs::Vector{Float64}
    forward::F
    inverse::I
    x::Bool
end
Fourier(fs, x::Bool=true) = Fourier(fs, nothing, nothing, x)
function Fourier(s::AbstractVector, fs::AbstractVector, x::Bool=true)
    forward = plan_rfft(s)
    inverse = plan_irfft(forward*s, length(s))
    return Fourier(fs, forward, inverse, x)
end

function decompose(t, s, method::Fourier)
    isequispaced(t) || error("Input time axis must be equispaced for method Fourier.")
    if length(s) % round(Int, maximum(1/f for f in method.fs)) ≠ 0
        @warn "The signal length is not a multiple of largest period."
    end

    m = mean(s)
    𝓕 = isnothing(method.forward) ? rfft(s .- m) : method.forward*(s .- m)
    fs = rfftfreq(length(s))
    for f in method.fs
        i = findnearest(f, fs)
        𝓕[i] = 0.0
    end
    inv_𝓕 = isnothing(method.inverse) ? irfft(𝓕, length(s)) : method.inverse*𝓕
    residual = inv_𝓕
    periodic = s .- residual
    if !method.x
        periodic .-= m
        residual .+= m
    end
    return periodic, residual
end

"""
    FrequencySplit([s, ] f::Real) <: Decomposition
Similar to the [`Fourier`](@ref) method, but now the "residual" signal is the part
of `s` with frequencies higher than `f`, while the "seasonal" part has frequencies `≤ f`.
"""
struct FrequencySplit{F, I} <: Decomposition
    f::Float64
    forward::F
    inverse::I
end
FrequencySplit(fs) = FrequencySplit(fs, nothing, nothing)
function FrequencySplit(s, fs)
    forward = plan_rfft(s)
    inverse = plan_irfft(forward*s, length(s))
    return FrequencySplit(fs, forward, inverse)
end

function decompose(t, s, method::FrequencySplit)
    isequispaced(t) || error("Input time axis must be equispaced for method FrequencySplit.")

    m = mean(s)
    𝓕 = isnothing(method.forward) ? rfft(s .- m) : method.forward*(s .- m)
    fs = rfftfreq(length(s))
    i = findlast(f -> f ≤ method.f, fs)
    𝓕[1:i+1] .= 0.0
    inv_𝓕 = isnothing(method.inverse) ? irfft(𝓕, length(s)) : method.inverse*𝓕
    residual = inv_𝓕
    periodic = s .- residual
    return periodic, residual
end


module SecantMethod

export secant_method

function secant_method(f::Function, x0::Number, x1::Number, args::Tuple=();
                       tol::AbstractFloat=1e-5, maxiter::Integer=50)
    for _ in 1:maxiter
       y1 = f(x1, args...)
       y0 = f(x0, args...)
       x = x1 - y1* (x1 - x0)/(y1 - y0)
       if abs(x - x1) < tol
          return x
       end
       x0 = x1
       x1 = x
    end
    error("Max iteration exceeded")
end

end


doc"""
    FDist(ν1,ν2)

The *F distribution* has probability density function

$f(x; \nu_1, \nu_2) = \frac{1}{x B(\nu_1/2, \nu_2/2)}
\sqrt{\frac{(\nu_1 x)^{\nu_1} \cdot \nu_2^{\nu_2}}{(\nu_1 x + \nu_2)^{\nu_1 + \nu_2}}},
\quad x>0$

It is related to the [`Chisq`](:func:`Chisq`) distribution via the property that if $X_1
\sim \operatorname{Chisq}(\nu_1)$ and $X_2 \sim \operatorname{Chisq}(\nu_2)$, then
$(X_1/\nu_1) / (X_2 / \nu_2) \sim FDist(\nu_1, \nu_2)`.

```julia
FDist(d1, d2)     # F-Distribution with parameters d1 and d2

params(d)         # Get the parameters, i.e. (d1, d2)
```

External links

* [F distribution on Wikipedia](http://en.wikipedia.org/wiki/F-distribution)
    """
immutable FDist <: ContinuousUnivariateDistribution
    ν1::Float64
    ν2::Float64

    function FDist(ν1::Real, ν2::Real)
        @check_args(FDist, ν1 > zero(ν1) && ν2 > zero(ν2))
        new(ν1, ν2)
    end
end

@distr_support FDist 0.0 Inf

#### Parameters

params(d::FDist) = (d.ν1, d.ν2)

#### Statistics

mean(d::FDist) = (ν2 = d.ν2; ν2 > 2.0 ? ν2 / (ν2 - 2.0) : NaN)

mode(d::FDist) = ((ν1, ν2) = params(d); ν1 > 2.0 ? ((ν1 - 2.0)/ν1) * (ν2 / (ν2 + 2.0)) : 0.0)

function var(d::FDist)
    (ν1, ν2) = params(d)
    ν2 > 4.0 ? 2.0 * ν2^2 * (ν1 + ν2 - 2.0) / (ν1 * (ν2 - 2.0)^2 * (ν2 - 4.0)) : NaN
end

function skewness(d::FDist)
    (ν1, ν2) = params(d)
    if ν2 > 6.0
        return (2.0 * ν1 + ν2 - 2.0) * sqrt(8.0 * (ν2 - 4.0)) / ((ν2 - 6.0) * sqrt(ν1 * (ν1 + ν2 - 2.0)))
    else
        return NaN
    end
end

function kurtosis(d::FDist)
    (ν1, ν2) = params(d)
    if ν2 > 8.0
        a = ν1 * (5. * ν2 - 22.) * (ν1 + ν2 - 2.) + (ν2 - 4.) * (ν2 - 2.)^2
        b = ν1 * (ν2 - 6.) * (ν2 - 8.) * (ν2 - 2.)
        return 12. * a / b
    else
        return NaN
    end
end

function entropy(d::FDist)
    (ν1, ν2) = params(d)
    hν1 = ν1 * 0.5
    hν2 = ν2 * 0.5
    hs = (ν1 + ν2) * 0.5
    return log(ν2 / ν1) + lgamma(hν1) + lgamma(hν2) - lgamma(hs) +
        (1.0 - hν1) * digamma(hν1) + (-1.0 - hν2) * digamma(hν2) +
        hs * digamma(hs)
end

#### Evaluation & Sampling

@_delegate_statsfuns FDist fdist ν1 ν2

rand(d::FDist) = StatsFuns.Rmath.fdistrand(d.ν1, d.ν2)


export cg,bicgstab,Lanczos

function cg{T1<:Number,T2<:Number}(A::AbstractMatrix{T1},b::Vector{T2})
	n=length(b)
	n1,n2=size(A)
	n == n1 == n2 || throw(DimensionMismatch(""))
	T=promote_type(T1,T2)
	x=zeros(T,n)
	r=b-A*x
	p=r
	rsold=dot(r,r)
	TR=typeof(real(rsold))
	for i=1:n
		Ap=A*p
		alpha=rsold/dot(p,Ap)
		x=x+alpha*p
		r=r-alpha*Ap
		rsnew=dot(r,r)
		if real(sqrt(rsnew))<10eps(TR)
			return x
		end
		p=r+rsnew/rsold*p
		rsold=rsnew
	end
	return x
end

function cg{T1<:Number,T2<:Number,T3<:Number}(A::AbstractMatrix{T1},b::Vector{T2},P::AbstractMatrix{T3})
	n=length(b)
	n1,n2=size(A)
	n3,n4=size(P)
	n == n1 == n2 == n3 == n4 || throw(DimensionMismatch(""))
	T=promote_type(T1,T2,T3)
	x=P\b
	r=b-A*x
	z=P\r
	p=z
	rsold=dot(r,z)
	TR=typeof(real(rsold))
	for i=1:n
		Ap=A*p
		alpha=rsold/dot(p,Ap)
		x=x+alpha*p
		r=r-alpha*Ap
		z=P\r
		rsnew=dot(r,z)
		if real(sqrt(rsnew))<10eps(TR)
			return x
		end
		p=z+rsnew/rsold*p
		rsold=rsnew
	end
	return x
end

function bicgstab{T1<:Number,T2<:Number}(A::AbstractMatrix{T1},b::Vector{T2})
	n=length(b)
	n1,n2=size(A)
	n == n1 == n2 || throw(DimensionMismatch(""))
	T=promote_type(T1,T2)
	x=zeros(T,n)
	r=b-A*x
	rhat = r
	ρold = one(T)
	α =one(T)
	ωold = one(T)
	v=zeros(T,n)
	p=zeros(T,n)
	TR = typeof(real(α))
	for i=1:n
		ρnew = dot(rhat,r)
		β = (ρnew/ρold)*(α/ωold)
		p = r + β*(p-ωold*v)
		v = A*p
		α = ρnew/dot(rhat,v)
		s = r - α*v
		t = A*s
		ωnew = dot(t,s)/dot(t,t)
		upd = α*p + ωnew*s
		x = x + upd
		if norm(upd)<10eps(TR)
			return x
		end
		r = s - ωnew*t
		ρold = ρnew
		ωold = ωnew
	end
	return x
end

function bicgstab{T1<:Number,T2<:Number,T3<:Number}(A::AbstractMatrix{T1},b::Vector{T2},P::AbstractMatrix{T3})
	n=length(b)
	n1,n2=size(A)
	n3,n4=size(P)
	n == n1 == n2 == n3 == n4 || throw(DimensionMismatch(""))
	T=promote_type(T1,T2,T3)
	x=P\b
	r=b-A*x
	rhat = r
	ρold = one(T)
	α =one(T)
	ωold = one(T)
	v=zeros(T,n)
	p=zeros(T,n)
	TR = typeof(real(α))
	for i=1:n
		ρnew = dot(rhat,r)
		β = (ρnew/ρold)*(α/ωold)
		p = r + β*(p-ωold*v)
		y = P\p
		v = A*y
		α = ρnew/dot(rhat,v)
		s = r - α*v
		z = P\s
		t = A*z
		zt = P\t
		ωnew = dot(zt,z)/dot(zt,zt)
		upd = α*y + ωnew*z
		x = x + upd
		if norm(upd)<10eps(TR)
			return x
		end
		r = s - ωnew*t
		ρold = ρnew
		ωold = ωnew
	end
	return x
end

function Lanczos{T<:Number}(A::AbstractMatrix{T})
	n,n1 = size(A)
	n == n1 || throw(DimensionMismatch(""))
	z = Array(T,n)
	vold = zero(T)
	vnew = [one(T),zeros(T,n-1)]
	beta = zeros(T,n)
	alpha = zeros(T,n)
	for j = 1:n-1
		w = A*vnew
		alpha[j] = dot(w,vnew)
		w = w - alpha[j]*vnew - beta[j]*vold
		beta[j+1] = norm(w)
		vold = vnew
		vnew = w/beta[j+1]
	end
	w = A*vnew
	alpha[n] = dot(w,vnew)
	@time steig!(alpha,[beta[2:end],zero(T)],z,10000)
	return sort!(alpha)
 end


"""
 Take the L2 Tikhonov regularised inverse of a matrix `m`.

 The regularisation parameter is the larger of `delta` (the optional argument that defaults
 to zero) and square root of machine epsilon. The inverse is done using an SVD.
 """
 function reginv(a::AbstractMatrix, δ = defaulttol(a))
     U, S, V = svd(a)
     Sinv = inv.(hypot.(S, δ))
     return  V * Diagonal(Sinv) * U'
 end

 function posreginv(a::AbstractMatrix, δ = defaulttol(a))
     # assumes a is positive definite (or close to)
     D, V = eigen(Hermitian(a))
     Dinv = inv.(hypot.(D, δ))
     return  V * Diagonal(Dinv) * V'
 end