using SimpleSolvers
using Test

n = 2
x = rand(n)
g = 2x
T = eltype(x)

function F(x::Vector)
    1 + sum(x.^2)
end

function ∇F!(g::Vector, x::Vector)
    g .= 0
    for i in eachindex(x,g)
        g[i] = 2x[i]
    end
end

∇PAD = GradientParameters{T}(F, n; mode = :autodiff, diff_type = :forward)
∇PFD = GradientParameters{T}(F, n; mode = :autodiff, diff_type = :finite)
∇PUS = GradientParameters{T}(∇F!, n; mode = :user)

@test typeof(∇PAD) <: GradientParametersAD
@test typeof(∇PFD) <: GradientParametersFD
@test typeof(∇PUS) <: GradientParametersUser

function test_grad(g1, g2, atol)
    for i in eachindex(g1,g2)
        @test g1[i] ≈ g2[i] atol=atol
    end
end

gad = zero(g)
gfd = zero(g)
gus = zero(g)

compute_gradient!(gad, x, ∇PAD)
compute_gradient!(gfd, x, ∇PFD)
compute_gradient!(gus, x, ∇PUS)

test_grad(gad, g, eps())
test_grad(gfd, g, 1E-7)
test_grad(gus, g, 0)

gad1 = zero(g)
gfd1 = zero(g)
gus1 = zero(g)

compute_gradient!(gad1, x, F; mode = :autodiff, diff_type = :forward)
compute_gradient!(gfd1, x, F; mode = :autodiff, diff_type = :finite)
compute_gradient!(gus1, x, ∇F!; mode = :user)

test_grad(gad, gad1, 0)
test_grad(gfd, gfd1, 0)
test_grad(gus, gus1, 0)

gad2 = zero(g)
gfd2 = zero(g)

compute_gradient_ad!(gad2, x, F)
compute_gradient_fd!(gfd2, x, F)

test_grad(gad, gad2, 0)
test_grad(gfd, gfd2, 0)


# Load Julia packages (libraries) needed

using StatisticalRethinking, CmdStan
#gr(size=(600,600));

# CmdStan uses a tmp directory to store the output of cmdstan

ProjDir = rel_path("..", "scripts", "02")
cd(ProjDir)

# Define the Stan language model

binomialstanmodel = "
// Inferring a Rate
data {
  int N;
  int<lower=0> k[N];
  int<lower=1> n[N];
}
parameters {
  real<lower=0,upper=1> theta;
  real<lower=0,upper=1> thetaprior;
}
model {
  // Prior Distribution for Rate Theta
  theta ~ beta(1, 1);
  thetaprior ~ beta(1, 1);

  // Observed Counts
  k ~ binomial(n, theta);
}
";

# Define the Stanmodel and set the output format to :mcmcchains.

stanmodel = Stanmodel(name="binomial", monitors = ["theta"], model=binomialstanmodel,
  output_format=:mcmcchains);

# Use 16 observations

N2 = 15
d = Binomial(9, 0.66)
n2 = Int.(9 * ones(Int, N2));

# Show first 5 (generated) observations

k2 = rand(d, N2);
k2[1:min(5, N2)]

# Input data for cmdstan

binomialdata = Dict("N" => length(n2), "n" => n2, "k" => k2);

# Sample using cmdstan

rc, chn, cnames = stan(stanmodel, binomialdata, ProjDir, diagnostics=false,
  CmdStanDir=CMDSTAN_HOME);

# Describe the draws

MCMCChains.describe(chn)

# Allocate array of Normal fits

fits = Vector{Normal{Float64}}(undef, 4)
for i in 1:4
  fits[i] = fit_mle(Normal, convert.(Float64, chn.value[:, 1, i]))
  println(fits[i])
end

# Plot the 4 chains

mu_avg = sum([fits[i].μ for i in 1:4]) / 4.0;
sigma_avg = sum([fits[i].σ for i in 1:4]) / 4.0;

if rc == 0
  p = Vector{Plots.Plot{Plots.GRBackend}}(undef, 4)
  x = 0:0.001:1
  for i in 1:4
    vals = convert.(Float64, chn.value[:, 1, i])
    μ = round(fits[i].μ, digits=2)
    σ = round(fits[i].σ, digits=2)
    p[i] = density(vals, lab="Chain $i density",
       xlim=(0.45, 1.0), title="$(N2) data points")
    plot!(p[i], x, pdf.(Normal(fits[i].μ, fits[i].σ), x), lab="Fitted Normal($μ, $σ)")
  end
  plot(p..., layout=(4, 1))
  #savefig("m2.1s.pdf")
end

# Show the hpd region

hpd(chn, alpha=0.055)

# Compute the hpd bounds for plotting

d, p, c = size(chn);
theta = convert(Vector{Float64}, reshape(chn.value, (d*p*c)));
bnds = quantile(theta, [0.045, 0.945])

# Show hpd region

println("hpd bounds = $bnds\n")

# quadratic approximation

# Compute MAP, compare with CmndStan & MLE

tmp = convert(Array{Float64,3}, chn.value)
draws = reshape(tmp, (size(tmp, 1)*size(tmp, 3)),)

# Compute MAP

using Optim

x0 = [0.5]
lower = [0.2]
upper = [1.0]

inner_optimizer = GradientDescent()

function loglik(x)
  ll = 0.0
  ll += log.(pdf.(Beta(1, 1), x[1]))
  ll += sum(log.(pdf.(Binomial(9, x[1]), k2)))
  -ll
end

res = optimize(loglik, lower, upper, x0, Fminbox(inner_optimizer))

# Summarize mean and sd estimates

# CmdStan mean and sd:

[mean(chn.value), std(chn.value)]

# MAP estimate and associated sd:

[Optim.minimizer(res)[1], std(draws, mean=mean(chn.value))]

# MLE of mean and sd:

[mu_avg, sigma_avg]

# Turing Chain &  89% hpd region boundaries

plot( x, pdf.(Normal( mu_avg , sigma_avg  ) , x ),
xlim=(0.0, 1.2), lab="Normal approximation using MLE")
plot!( x, pdf.(Normal( Optim.minimizer(res)[1] , std(draws, mean=mean(chn.value))) , x),
lab="Normal approximation using MAP")
density!(draws, lab="CmdStan chain")
vline!([bnds[1]], line=:dash, lab="hpd lower bound")
vline!([bnds[2]], line=:dash, lab="hpd upper bound")

# End of `02/clip_08s.jl`


# Copyright (c) 2021 Idiap Research Institute, http://www.idiap.ch/
#  Niccolò Antonello <nantonel@idiap.ch>

# compose
# using ex from Mohri et al. Speech recognition with FiniteStateTransducers
L = String
W = TropicalWeight{Float32}
A = WFST(["a","b","c"]; W = W)
add_arc!(A, 1, 2, "a", "b", 0.1)
add_arc!(A, 2, 2, "c", "a", 0.3)
add_arc!(A, 2, 4, "a", "a", 0.4)
add_arc!(A, 1, 3, "b", "a", 0.2)
add_arc!(A, 3, 4, "b", "b", 0.2)
final!(A, 4, 0.6)
initial!(A, 1)
#println(A)
size_A = size(A)
B = WFST(get_isym(A); W = W)
add_arc!(B, 1, 2, "b", "c", 0.3)
add_arc!(B, 2, 3, "a", "b", 0.4)
add_arc!(B, 3, 3, "a", "b", 0.6)
final!(B, 3, 0.7)
initial!(B, 1)
size_B = size(B)
#println(B)

C = ∘(A,B; filter=Trivial)
#println(C)
@test size(A) == size_A
@test size(B) == size_B
@test isinitial(C,1)
@test isfinal(C,4)
@test get_weight(C,4) ≈ W(1.3)
@test get_weight(C[1][1]) ≈ W(0.4)
@test get_weight(C[2][1]) ≈ W(0.7)
@test get_weight(C[2][2]) ≈ W(0.8)
@test get_weight(C[3][1]) ≈ W(0.9)
@test get_weight(C[3][2]) ≈ W(1.0)
@test size(C) == (4,5)

# using ex from http://openfst.org/twiki/bin/view/FST/ComposeDoc
L = String
W = TropicalWeight{Float64}
A = WFST(["a","c"],["q","r","s"]; W=W)
add_arc!(A, 1, 2, "a", "q", 1.0)
add_arc!(A, 2, 2, "c", "s", 1.0)
add_arc!(A, 1, 3, "a", "r", 2.5)
initial!(A,1)
final!(A,2,0)
final!(A,3,2.5)
#print(A)

W = TropicalWeight{Float64}
B = WFST(get_osym(A),["f","h","g","j"]; W=W)
add_arc!(B, 1, 2, "q", "f", 1.0)
add_arc!(B, 1, 3, "r", "h", 3.0)
add_arc!(B, 2, 3, "s", "g", 2.5)
add_arc!(B, 3, 3, "s", "j", 1.5)
initial!(B,1)
final!(B,3,2)
#print(B)

C = ∘(A,B,filter=Trivial)
#println(C)

@test isinitial(C,1)
@test isfinal(C,3)
@test isfinal(C,4)
@test get_weight(C,3) ≈ W(4.5)
@test get_weight(C,4) ≈ W(2.0)
@test get_weight(C[1][1]) ≈ W(2.0)
@test get_ilabel(C,1,1) == "a"
@test get_olabel(C,1,1) == "f"
@test get_nextstate(C[1][1]) == 2
@test get_weight(C[1][2]) ≈ W(5.5)
@test get_ilabel(C,1,2) == "a"
@test get_olabel(C,1,2) == "h"
@test get_nextstate(C[1][2]) == 3
@test get_weight(C[2][1]) ≈ W(3.5)
@test get_ilabel(C,2,1) == "c"
@test get_olabel(C,2,1) == "g"
@test get_nextstate(C[2][1]) == 4
@test get_weight(C[4][1]) ≈ W(2.5)
@test get_ilabel(C,4,1) == "c"
@test get_olabel(C,4,1) == "j"
@test get_nextstate(C[4][1]) == 4
@test size(C) == (4,4)

# using ex fig.8 (with different weights) from Mohri et al. Speech recognition with FiniteStateTransducers
# with epsilon symbols
A = txt2fst("openfst/A.fst", "openfst/sym.txt")
B = txt2fst("openfst/B.fst", "openfst/sym.txt")
# TODO add tests
# for the moment we test equivalence of fst with sorted weights
C = ∘(A,B;filter=Trivial)
C_openfst = txt2fst("openfst/C_trivial.fst", "openfst/sym.txt")
@test size(C) == size(C_openfst)
w = sort([get_weight(a).x for a in get_arcs(C)])
w_openfst = sort([get(get_weight(a)) for a in get_arcs(C_openfst)])
@test all(w .== w_openfst)
@test all(sort([values(C.finals)...]) .== sort([values(C_openfst.finals)...] ))
#println(C)
#println(C_openfst)

C = ∘(A,B;filter=EpsMatch)
C_openfst = txt2fst("openfst/C_match.fst", "openfst/sym.txt")
@test size(C) == size(C_openfst)
w = sort([get_weight(a).x for a in get_arcs(C)])
w_openfst = sort([get(get_weight(a)) for a in get_arcs(C_openfst)])
@test all(w .== w_openfst)
@test all(sort([values(C.finals)...]) .== sort([values(C_openfst.finals)...] ))
#println(C)
#println(C_openfst)

C = ∘(A,B;filter=EpsSeq)
C_openfst = txt2fst("openfst/C_sequence.fst", "openfst/sym.txt")
@test size(C) == size(C_openfst)
w = sort([get_weight(a).x for a in get_arcs(C)])
w_openfst = sort([get(get_weight(a)) for a in get_arcs(C_openfst)])
@test all(w .== w_openfst)
@test all(sort([values(C.finals)...]) .== sort([values(C_openfst.finals)...] ))
#println(C)
#println(C_openfst)

##
L = txt2fst("openfst/L.fst", "openfst/chars.txt", "openfst/words.txt")
#println(L)
T = txt2fst("openfst/T.fst", "openfst/words.txt", "openfst/words.txt")
#println(T)
LT = ∘(L,T;filter=EpsSeq)
#println(LT)
@test size(LT) ==(11,10)
@test_throws ErrorException T∘L # since A.osym != A.isym


f = open("input07.txt", "r")
    l = readlines(f)
close(f)

positions = [parse(Int, n) for n in split(l[1], ",")]

options = collect(minimum(positions):maximum(positions))
consumption = zeros(length(options))
for (i, k) in enumerate(options)
    diff = positions - ones(length(positions)) * k
    fuel = sum([abs(j) for j in diff])
    consumption[i] = fuel
end

best_fuel = minimum(consumption)
println("Result 1: $best_fuel")

consumption = zeros(length(options))
for (i, k) in enumerate(options)
    diff = positions - ones(length(positions)) * k
    eachfuel = [sum(collect(1:abs(j))) for j in diff]
    fuel = sum([abs(j) for j in eachfuel])
    consumption[i] = fuel
end

new_best_fuel = minimum(consumption)
println("Result 2: $new_best_fuel")


cd(@__DIR__)
using Revise, DifferentialEvolutionMCMC, Random, Distributions
using LinearAlgebra
Random.seed!(50514)

# number of variables
n_μ = 30
# number of observations per variable
n_d = 100
# random μ parameters
μs = rand(Normal(0.0, 1.0), n_μ)
# data
data = rand(MvNormal(μs, 1.0 * I), n_d)

# function for initial values
function sample_prior()
    μ = rand(Normal(0, 1), n_μ)
    σ = rand(truncated(Cauchy(0, 1), 0, Inf))
    return as_union([μ,σ])
end

# returns prior log likelihood
function prior_loglike(μ, σ)
    LL = 0.0
    LL += sum(logpdf.(Normal(0, 1), μ))
    LL += logpdf(truncated(Cauchy(0, 1), 0, Inf), σ)
    return LL
end

# likelihood function
function loglike(data, μs, σ)
    return sum(logpdf(MvNormal(μs, σ^2 * I), data))
end

# upper and lower bounds of parameters
bounds = ((-Inf,Inf),(0.0,Inf))
# parameter names
names = (:μ,:σ)

# model object
model = DEModel(;
    sample_prior,
    prior_loglike,
    loglike,
    data,
    names
)

# DEMCMC sampler
de = DE(;
    sample_prior,
    bounds,
    sample = resample,
    burnin = 5000,
    n_initial = (n_μ + 1) * 4,
    Np = 3,
    n_groups = 1,
    θsnooker = 0.1,
)
# sample from the posterior distribution
n_iter = 50_000
chains = sample(model, de, MCMCThreads(), n_iter, progress=true)


using CSV, GLM, Plots; pyplot()

df = CSV.read("../data/IQalc.csv")
groupA = df[df.Group .== "A", :]
groupB = df[df.Group .== "B", :]
groupC = df[df.Group .== "C", :]

model  = fit(LinearModel, @formula(AlcConsumption ~ IQ), df)
modelA = fit(LinearModel, @formula(AlcConsumption ~ IQ), groupA)
modelB = fit(LinearModel, @formula(AlcConsumption ~ IQ), groupB)
modelC = fit(LinearModel, @formula(AlcConsumption ~ IQ), groupC)

pred(x)  = coef(model)'  * [1, x]
predA(x) = coef(modelA)' * [1, x]
predB(x) = coef(modelB)' * [1, x]
predC(x) = coef(modelC)' * [1, x]

xlims = collect(extrema(df.IQ))

p1 = scatter(df.IQ, df.AlcConsumption, c=:black, msw=0, ma=0.2, label="")
     plot!(xlims, pred.(xlims), c=:black, label="All data")

p2 = scatter(groupA.IQ, groupA.AlcConsumption, c=:blue, msw=0, ma=0.2, label="")
     scatter!(groupB.IQ, groupB.AlcConsumption, c=:red, msw=0, ma=0.2, label="")
     scatter!(groupC.IQ, groupC.AlcConsumption, c=:green,msw=0, ma=0.2, label="")
     plot!(xlims, predA.(xlims), c=:blue, label="Group A")
     plot!(xlims, predB.(xlims), c=:red, label="Group B")
     plot!(xlims, predC.(xlims), c=:green, label="Group C")

plot(p1, p2, xlims=(xlims), ylims=(0,1),
	xlabel="IQ", ylabel="Alcohol Metric", size=(800,400))


#    Copyright 2015 Raytheon BBN Technologies
#
#     Licensed under the Apache License, Version 2.0 (the "License");
#     you may not use this file except in compliance with the License.
#     You may obtain a copy of the License at
#
#       http://www.apache.org/licenses/LICENSE-2.0
#
#     Unless required by applicable law or agreed to in writing, software
#     distributed under the License is distributed on an "AS IS" BASIS,
#     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
#     See the License for the specific language governing permissions and
#     limitations under the License.

module SchattenNorms

export snorm, nucnorm, trnorm, specnorm, fnorm, dnorm

"""
Computes the nuclear norm of a matrix `m`.
"""
function nucnorm(m::AbstractMatrix)
    norm(svdvals(m),1)
end

"""
Computes the trace norm of a matrix `m`.
"""
trnorm(m::AbstractMatrix) = nucnorm(m)

"""
Computes the Frobenius norm of a matrix `m`.
"""
fnorm(m::AbstractMatrix) = vecnorm(m,2)

"""
Computes the spectral norm of a matrix `m` (i.e., the maximum singular value).
"""
function specnorm(m::AbstractMatrix)
    return norm(svdvals(m),Inf)
end

"""
Computes the `p`-Schatten norm of a matrix `m`.
"""
function snorm(m::AbstractMatrix,p=2.0)
    p == 2.0 && return vecnorm(m,2)
    return norm(svdvals(m),p)
end

include("dnorm.jl")

end # module


# norm of off diagonal terms of a square matrix
function norm_off(M)
    if size(M[1],1)>1
        return sqrt(sum(abs2(M[i,j]) + abs2(M[j,i]) for i in 1:size(M,1) for j in i+1:size(M,1)))
    else
        return 0.0
    end
end

function diagonalization_iter(D)
    n = size(D[1],1)
    s = length(D)

    X = fill(zero(D[1][1,1]),n,n)
    Y = fill(zero(D[1][1,1]),n,n)

    A = fill(zero(D[1][1,1]),s,2)
    b = fill(zero(D[1][1,1]),s)
    for i in 1:n
        for j in 1:n
            if i != j
                for k in 1:s
                    A[k,1] = D[k][i,i]
                    A[k,2] = D[k][j,j]
                    b[k]   = -D[k][i,j]
                end
                v = A\b
                X[i,j] =  v[1]
                Y[i,j] =  v[2]
            end
        end
    end
    for i in 1:n
        X[i,i]=1
        Y[i,i]=1
    end
    return X, Y
end

function diagonalization(M::Vector{Matrix{C}},
                         Info = Dict{String,Any}(
                             "maxIter" => 10,
                             "epsIter" => 1.e-3)) where C
    n  = length(M)
    r  = size(M[1],1)

    N   = (haskey(Info,"maxIter") ? Info["maxIter"] : 10)
    eps = (haskey(Info,"epsIter") ? Info["epsIter"] : 1.e-3)

    M1 = sum(M[i]*randn(Float64) for i in 1:n)
    E  = eigvecs(M1)

    F  = inv(E)

    D  = vcat([Matrix{C}(I,r,r)],[F*M[i]*E for i in 1:length(M)])
    err = sum(norm_off.(D))
    delta = sum(norm.(D))
    #println("diag off: ", err)

    Info["d0"] = err
    nit = 0

    if err/delta > 5.e-2
        delta = err
        while nit < N && delta > eps
            err0 = err
            X,Y = diagonalization_iter(D)
            D = [Y*D[i]*X for i in 1:length(D)]
            E = E*X
            F = Y*F
            nit+=1
            err = sum(norm_off.(D))
            delta = err0-err
            #println("Off", nit,": ", err, "   delta: ", delta)
        end
        Info["d*"]= err
    end
    Info["nIter"] = nit

    Xi = fill(zero(E[1,1]),n,r)
    for i in 1:r
    	for j in 1:n
	    Xi[j,i] = D[j+1][i,i]/D[1][i,i]
            #Xi[j,i] =(E[:,i]\(M[j]*E[:,i]))[1]
	end
    end
    return Xi, E, Info
end


ν(G, L, Es, p) = p[1]*(G*L-Es/p[2])/(L + p[3]*G)

function rate(C)
    G, L, Es = C
    p = (9.03f-2, 5.0f-2, 1.9f1)
    ν(G, L, Es, p)
end

function reaction(du, u, p, t)
    G, L, Es = u
    dG  = du[1] = - ν(G, L, Es, p)
    dL  = du[2] = - ν(G, L, Es, p)
    dEs = du[3] =   ν(G, L, Es, p)
    return du
end

function reaction!(du, u, p, t)
    du = reaction(du, u, p, t)
end

function generate_true_solution(u₀, tₑ; nPoints=20)
    p = (9.03f-2, 5.0f-2, 1.9f1)
    tspan = (0.0, tₑ)

    prob = ODEProblem(reaction!, u₀, tspan, p, saveat=0:tₑ/nPoints:tₑ)
    sol = solve(prob, Tsit5())
    G = [c[1] for c in sol.u]
    L = [c[2] for c in sol.u]
    Es = [c[3] for c in sol.u]
    time = sol.t

  return permutedims([G L Es]), time
end

function plot_reaction(data)
  C, time = data

  pl = plot(xlabel="Time (min)", ylabel="Concentration (mM)",
      ylims=(-2, 1.2*maximum(C)),
      xlims=(-2, time[end]*1.01))
  scatter!(pl, time, C[1,:], label="Sucrose")
  scatter!(pl, time, C[2,:], label="Stearic acid")
  scatter!(pl, time, C[3,:], label="Sucr-Stea-Esther")
  return pl
end

function prediction_vs_data(data, pred)
    C, time = data

    pl = plot_reaction(data)
    lcs = pl.series_list
    plot!(pl, time, pred[1,:], label = "", lc=lcs[1][:linecolor])
    plot!(pl, time, pred[2,:], label = "",  lc=lcs[2][:linecolor])
    plot!(pl, time, pred[3,:], label = "",  lc=lcs[3][:linecolor])
    return pl
end


using DelimitedFiles

function ReadData(fname::AbstractString)
    return readdlm(fname)
end

function Part1(instructions::AbstractArray)
    x, y = 0, 0
    for i = 1:length(instructions[:,1])
        if instructions[i,1] == "forward"
            x += instructions[i,2]
        elseif instructions[i,1] == "up"
            y -= instructions[i,2]
        elseif instructions[i,1] == "down"
            y += instructions[i,2]
        end
    end

    return x*y
end

function Part2(instructions::AbstractArray)
    x, y, aim = 0, 0, 0
    for i = 1:length(instructions[:,1])
        if instructions[i,1] == "forward"
            x += instructions[i,2]
            y += instructions[i,2]*aim
        elseif instructions[i,1] == "up"
            aim -= instructions[i,2]
        elseif instructions[i,1] == "down"
            aim += instructions[i,2]
        end
    end

    return x*y
end

instructions = ReadData("../data/day2.txt")
println(Part1(instructions))
println(Part2(instructions))


### jacobisymbol

# `jacobisymbol(a,n)` returns the Jacobi symbol. This is limited to bitstype integers.
# This is faster than Combinatorics.jacobisymbol for bitstype inputs, but slower for
#     BigInt inputs. Thus, these methods are complementary.

export jacobisymbol

macro flip!(n) :($(esc(n)) = -$(esc(n))) end
function jacobisymbol(a::Union{Signed,Unsigned},n::Union{Signed,Unsigned})
    if n <= 0 || iseven(n) throw(DomainError()) end
    j = 1
    if n < 0
        n % 4 == 3  ? @flip!(j)  : nothing
        @flip!(n)
    end
    while a != 0
        while iseven(a)
            n % 8 == 3 || n % 8 == 5 ? @flip!(j) : nothing
            a >>= 1
        end
        (a,n) = (n,a)
        a % 4 == 3 && n % 4 == 3 ? @flip!(j) : nothing
        a %= n
    end
    return n == 1 ? j : 0
end

#Base.@vectorize_2arg Union{Signed,Unsigned} jacobisymbol


# alex's funcion
c=0.5
c1mlogc=c*(1-log(c))

function alex(x::Array)
    ind1=x.>-c
    ind2=x.<=-c
    tmp=zeros(size(x))
    tmp[ind1]=x[ind1]
    tmp[ind2]=-log(-x[ind2]).*c-c1mlogc
    ind1=ind2=0
    return tmp
end

function gradalex(x::Array)
    ind1=x.>-c
    ind2=x.<=-c
    tmp=zeros(size(x))
    tmp[ind1]=1.0
    tmp[ind2]=-c./x[ind2]
    ind1=ind2=0
    return tmp
end

Falex(x::Array{Float64,2})=( alex(x) ,[]);

function Falex_inplace(value,auxvalue,x::Array{Float64,2})
    ind1=x.>-c
    ind2=x.<=-c
    tmp=zeros(size(x))
    tmp[ind1]=x[ind1]+c1mlogc
    tmp[ind2]=-log(-x[ind2]).*c
    ind1=ind2=0
    copy!(value,tmp)
end

Dalex(derivativeIDX,f_c,faux_c,grad_c,grad_n,x)=axpy!(1.0,grad_c.*gradalex(x),grad_n)

if PROC=="GPU" # TODO
    #Falex(x::CudaArray)=(alex(x),[])
    Falex_inplace(value,aux,x::CudaArray)=alex!(x,value)

    Dalex(derivativeIDX,f_c,faux_c,grad_c,grad_n,x::CudaArray)=gradalex!(grad_c,x,grad_n)
end

Derivative[Falex]=Dalex
Inplace[Falex]=Falex_inplace

alex(A::ADnode)=ADnode(Falex,A)
export alex


# Elliptical Frechet and Marshal Olkim copulas generators

## Elliptical copulas

"""
    Gaussian_cop

Gaussian copula

Fields:
   - Σ - the correlation matrix must be symmetric, positively defined and with ones on diagonal

Constructor

    Gaussian_cop(Σ::Matrix{T}) where T <: Real

```jldoctest

julia> Gaussian_cop([1. 0.5; 0.5 1.])
Gaussian_cop([1.0 0.5; 0.5 1.0])

```
"""
struct Gaussian_cop{T} <: Copula{T}
  Σ::Matrix{T}
  n::Int
  function(::Type{Gaussian_cop})(Σ::Matrix{T}) where T <: Real
    Σ ≈ transpose(Σ) || throw(DomainError("Σ matrix not symmetric"))
    isposdef(Σ) || throw(DomainError("Σ matrix not positivelly defined"))
    prod(diag(Σ)) ≈ 1.0 || throw(DomainError("Σ matrix do not have ones on diagonal"))
    new{T}(Σ,size(Σ,1))
  end
end

function simulate_copula!(U, copula::Gaussian_cop{T}; rng = Random.GLOBAL_RNG) where T
  Σ = copula.Σ
  z = transpose(rand(rng, MvNormal(Σ),size(U,1)))
  for i in 1:size(Σ, 1)
    d = Normal(0, sqrt.(Σ[i,i]))
    z[:,i] = cdf.(d, z[:,i])
  end
  U .= Array(z)
  return nothing
end

"""
    Student_cop

t-Student copula

fields
  - Σ::Matrix{Real} - the correlation matrix must be symmetric, positively defined and with ones on diagonal
  - ν::Int - the parameter n.o. degrees of freedom we require ν > 0

Constructor:

    Student_cop(Σ::Matrix{Real}, ν::Int)

```jldoctest

julia> Student_cop([1. 0.5; 0.5 1.], 4)
Student_cop([1.0 0.5; 0.5 1.0], 4)

```
"""
struct Student_cop{T} <: Copula{T}
  Σ::Matrix{T}
  ν::Int
  n::Int
  function(::Type{Student_cop})(Σ::Matrix{T}, ν::Int) where T <: Real
    Σ ≈ transpose(Σ) || throw(DomainError("Σ matrix not symmetric"))
    isposdef(Σ) || throw(DomainError("Σ matrix not positivelly defined"))
    prod(diag(Σ)) ≈ 1.0 || throw(DomainError("Σ matrix do not have ones on diagonal"))
    ν > 0 || throw(DomainError("ν lower or equal zero"))
    new{T}(Σ, ν,size(Σ,1))
  end
end

function simulate_copula!(U, copula::Student_cop{T}; rng = Random.GLOBAL_RNG) where T
  Σ = copula.Σ
  ν = copula.ν
  z = transpose(rand(rng, MvNormal(Σ),size(U,1)))
  V = rand(rng, Chisq(ν), size(z, 1))
  V = T.(V)
  for i in 1:size(Σ, 1)
    x = z[:,i].*sqrt.(ν./V)./sqrt(Σ[i,i])
    z[:,i] = cdf.(TDist(ν), x)
  end
  U .= Array(z)
  return nothing
end

"""
    Frechet_cop

The Frechet copula

Fileds:
  - n - number of marginals
  - α - the parameter of the maximal copula
  - β - the parameter of the minimal copula

Constructor

    Frechet_cop(n::Int, α::Real)

The one parameter Frechet copula is a combination of maximal copula with  weight α
and independent copula with  weight 1-α.

Constructor

    Frechet_cop(n::Int, α::Real, β::Real)

The two parameters Frechet copula C = α C{max} + β C{min} + (1- α - β) C{⟂}, supported
only for n = 2.

```jldoctest
julia> Frechet_cop(4, 0.5)
Frechet_cop(4, 0.5, 0.0)

julia> Frechet_cop(2, 0.5, 0.3)
Frechet_cop(2, 0.5, 0.3)
```
"""
struct Frechet_cop{T} <: Copula{T}
  n::Int
  α::T
  β::T
  function(::Type{Frechet_cop})(n::Int, α::T) where T <: Real
    0 <= α <= 1 || throw(DomainError("generaton not supported for α ∉ [0,1]"))
    n > 1 || throw(DomainError("n must be greater than 1"))
    new{T}(n, α, 0.)
  end
  function(::Type{Frechet_cop})(n::Int, α::T, β::T) where T <: Real
    0 <= α <= 1 || throw(DomainError("generaton not supported for α ∉ [0,1]"))
    0 <= β <= 1 || throw(DomainError("generaton not supported for β ∉ [0,1]"))
    n == 2 || throw(AssertionError("two parameters Frechet copula supported only for n = 2"))
    0 <= α+β <= 1 || throw(DomainError("α+β must be in range [0,1]"))
    new{T}(n, α, β)
  end
end

"""
    simulate_copula!(U::Matrix{Real}, copula::Frechet_cop; rng::AbstractRNG = Random.GLOBAL_RNG)

Given the preallocated output U, Returns size(U,1) realizations from the Frechet copula - Frechet_cop
N.o. marginals is size(U,2), requires size(U,2) == copula.n

```jldoctest
julia> f = Frechet_cop(3, 0.5)
Frechet_cop(3, 0.5, 0.0)

julia> u = zeros(1,3)
1×3 Array{Real,2}:
 0.0  0.0  0.0

julia> Random.seed!(43);

julia> simulate_copula!(u,f)

julia> u
1×3 Array{Real,2}:
 0.180975  0.775377  0.888934
```
"""
function simulate_copula!(U, copula::Frechet_cop{T}; rng = Random.GLOBAL_RNG) where T
  n = copula.n
  α = copula.α
  β = copula.β
  size(U, 2) == n || throw(AssertionError("n.o. margins in pre allocated output and copula not equal"))
  if (β > 0) & (n == 2)
    for j in 1:size(U,1)
      u_el = rand(rng, T, n)
      frechet_el2!(u_el, α, β, rand(rng, T))
      U[j,:] = u_el
    end
  else
    for j in 1:size(U,1)
      u_el = rand(rng, T, n)
      frechet_el!(u_el, α, rand(rng))
      U[j,:] = u_el
    end
  end
end

"""
  frechet(t::Int, n::Int, α::Real; rng::AbstractRNG)

Given n-variate random data u ∈ R^{t, n}
Returns t realization of n variate data generated from one parameter Frechet_cop(n, α).

```jldoctest
julia> Random.seed!(43);

julia> u = rand(10, 2);

julia> frechet(0.5, u)
10×2 Array{Real,2}:
 0.180975   0.661781
 0.0742681  0.0742681
 0.888934   0.125437
 0.0950087  0.0950087
 0.130474   0.130474
 0.912603   0.740184
 0.828727   0.00463791
 0.400537   0.0288987
 0.521601   0.521601
 0.955881   0.851275
```
"""
function frechet(α::T, u; rng) where T
  for j in 1:size(u, 1)
    v = rand(rng, T)
    el = u[j, :]
    frechet_el!(el, α, v)
    u[j,:] = el
  end
  u
end

"""
  frechet_el!(u::Vector{Real}, α::Real, v::Real)

Given n-variate random vector changes it to such modeled by the two parameters Frechet_cop(n, α, β).
v is the random number form [0,1].
"""
function frechet_el!(u, α, v)
  if (α >= v)
    for i in 1:length(u)-1
      u[i] = u[end]
    end
  end
end

"""
  function frechet_el2!(u::Vector{Real}, α::Real, β::Real, v::Real)

Given bivariate random vector changes it to such modeled by the two parameters Frechet_cop(n, α, β).
v is the random number form [0,1]

"""
function frechet_el2!(u, α, β, v)
  if (α >= v)
    u[1] = u[2]
  elseif (α < v <= α+β)
    u[1] = 1-u[2]
  end
end

### Marshall - Olkin familly

"""
    Marshall_Olkin_cop

Fields:
  - n::Int - number of marginals
  - λ::Vector{Real} - vector of non-negative parameters λₛ, i.e.:
      λ = [λ₁, λ₂, ..., λₙ, λ₁₂, λ₁₃, ..., λ₁ₙ, λ₂₃, ..., λₙ₋₁ₙ, λ₁₂₃, ..., λ₁₂...ₙ]
      and n = ceil(Int, log(2, length(λ)-1)).

Constructor

    Marshall_Olkin_cop(λ)

length(λ) ≧ 3 is required

```jldoctest
julia> Marshall_Olkin_cop([0.5, 0.5, 0.6])
Marshall_Olkin_cop(2, [0.5, 0.5, 0.6])

julia> Marshall_Olkin_cop([0.5, 0.5, 0.6, 0.7, 0.7, 0.7, 0.8])
Marshall_Olkin_cop(3, [0.5, 0.5, 0.6, 0.7, 0.7, 0.7, 0.8])
```
"""
struct Marshall_Olkin_cop{T} <: Copula{T}
  n::Int
  λ::Vector{T}
  function(::Type{Marshall_Olkin_cop})(λ::Vector{T}) where T <: Real
    minimum(λ) >= 0 || throw(AssertionError("all parameters must by >= 0 "))
    length(λ) >= 3 || throw(AssertionError("not supported for length(λ) < 3"))
    n = floor(Int, log(2, length(λ)+1))
    new{T}(n, λ)
  end
end

"""
    simulate_copula!(U::Matrix{Real}, copula::Marshall_Olkin_cop; rng::AbstractRNG = Random.GLOBAL_RNG)

Given the preallocated output U, Returns size(U,1) realizations from the Marshall  Olkin copula - Marshall_Olkin_cop
N.o. marginals is size(U,2), requires size(U,2) == copula.n

```jldoctest
julia> u = zeros(1,2)
1×2 Array{Float64,2}:
 0.0  0.0

julia> cop = Marshall_Olkin_cop([1.,2.,3.])
Marshall_Olkin_cop(2, [1.0, 2.0, 3.0])

julia> Random.seed!(43);

julia> simulate_copula!(u,cop)

julia> u
1×2 Array{Float64,2}:
 0.854724  0.821831
```
"""
function simulate_copula!(U, copula::Marshall_Olkin_cop{T}; rng = Random.GLOBAL_RNG) where T
  λ = copula.λ
  n = copula.n
  size(U, 2) == n || throw(AssertionError("n.o. margins in pre allocated output and copula not equal"))
  s = collect(combinations(1:n))
  for j in 1:size(U,1)
    u = rand(rng, T, 2^n-1)
    U[j,:] = mocopula_el(u, n, λ, s)
  end
end

"""
  mocopula(u::Matrix{Real}, n::Int, λ::Vector{Real})

  Returns: t x n Matrix{Float}, t realizations of n-variate data generated from Marshall-Olkin
  copula with parameter vector λ of non-negative elements λₛ, given [0,1]ᵗˣˡ ∋ u, where
  l = 2ⁿ-1

```jldoctest

  julia> mocopula([0.2 0.3 0.4; 0.3 0.4 0.6; 0.4 0.5 0.7], 2, [1., 1.5, 2.])
  3×2 Array{Float64,2}:
   0.252982  0.201189
   0.464758  0.409039
   0.585662  0.5357

```
"""
function mocopula(u, n, λ)
  T = eltype(u)
  t = size(u,1)
  U = zeros(T, t, n)
  s = collect(combinations(1:n))
  for j in 1:t
      U[j,:] = mocopula_el(u[j,:], n, λ, s)
  end
  U
end

"""
    mocopula_el(u::Vector{Real}, n::Int, λ::Vector{Real}, s::Vector{Vector{Int}})

```jldoctest

julia> mocopula_el([0.1, 0.2, 0.3], 2, [1., 2., 3.], s)
2-element Array{Float64,1}:
 0.20082988502465082
 0.1344421423967149
```
"""
function mocopula_el(u, n, λ, s)
  T = eltype(u)
  l = length(u)
  U = zeros(T, n)
  for i in 1:n
    inds = findall([i in s[k] for k in 1:l])
    x = minimum([-log(u[k])./(λ[k]) for k in inds])
    Λ = sum(λ[inds])
    U[i] = exp.(-Λ*x)
  end
    U
end


module V3P

# import Base.+, Base.*, Base./, Base.norm, Base.==, Base.<, Base.>
import Base: +, *, /, ==, <, >, norm, dot
export Vec3, norm, dist

immutable Vec3{T}
    x::T
    y::T
    z::T
end

(+)(a::Vec3, b::Vec3) = Vec3(a.x+b.x, a.y+b.y, a.z+b.z)
(*)(p::Vec3, s::Real) = Vec3(p.x*s, p.y*s, p.z*s)
(*)(s::Real, p::Vec3) = p*s
(/)(p::Vec3, s::Real) = (1.0/s)*p

(==)(a::Vec3, b::Vec3) = (a.x==b.x)&&(a.y==b.y)&&(a.z==b.z) ? true : false;

dot(a::Vec3, b::Vec3) = a.x*b.x + a.y*b.y + a.z*b.z;
norm(a::Vec3) = sqrt(dot(a,a));

(<)(a::Vec3, b::Vec3) = norm(a) < norm(b) ? true : false;
(>)(a::Vec3, b::Vec3) = norm(a) > norm(b) ? true : false;

dist(a::Vec3, b::Vec3) = sqrt((a.x-b.x)*(a.x-b.x) + (a.y-b.y)*(a.y-b.y) + (a.z-b.z)*(a.z-b.z))

end


"""
    legendre_point(p::T) where {T<:Integer}

Calculate Legendre points of polynomial degree p
"""
legendre_point(p::T) where {T<:Integer} = gausslegendre(p + 1)[1]

"""
    ∂legendre(p::T, x) where {T<:Integer}

Calculate derivatives of Legendre polynomials of degree p at location x
"""
∂legendre(p::T, x) where {T<:Integer} = last(sf_legendre_Pl_deriv_array(p, x)[2])

function ∂legendre(p::I, x::T) where {I<:Integer,T<:AbstractArray{<:Real,1}}
    Δ = similar(x)
    for i in eachindex(Δ)
        Δ[i] = ∂legendre(p, x[i])
    end

    return Δ
end

"""
    ∂radau(p::TI, x::TU) where {TI<:Integer,TU<:Union{Real,AbstractArray{<:Real,1}}}

Calculate derivatives of Radau polynomials of degree p at location x
"""
function ∂radau(p::TI, x::TU) where {TI<:Integer,TU<:Union{Real,AbstractArray{<:Real,1}}}
    Δ = ∂legendre(p, x)
    Δ_plus = ∂legendre(p + 1, x)

    dgl = @. (-1.0)^p * 0.5 * (Δ - Δ_plus)
    dgr = @. 0.5 * (Δ + Δ_plus)

    return dgl, dgr
end


"""Функции для численного дифференцирования с использованием центральных разностей"""
module NumDiff
"""
Нахождение частной производной первого порядка
от векторозначной функции многих переменных

``derivative(func, [t,] x, k; p, h, order) -> df``

Для вычисления производных используется метод центральных разностей.

# Аргументы

* `func::Function`: функция в виде func(t, x) или func(x).
* `t::Float64`: обциональный скалярный аргумент функции (время).
* `x::Vector{Float64}`: векторный аргумент функции `x`.
* `k::Int`: номер переменной, по которой необходимо взять производную.
* `h::Float64`: шаг приращения, который необходимо использовать.
* `order::Int`: порядок точности {2, 4, 6, 8}.

# Возвращаемые значения

`df::Vector{Float64}`: вектор частных производных.
"""
function derivative(func::Function, t::Float64, x::Vector{Float64}; k=1, h=0.1, order=4)
  # Используются центральные конечные разности коэффициенты этих разностей задаются массивом
  # Заполняем массив коэффициентов
  a = [[-0.5, 0.0, 0.5], # Порядка 2
       [1.0/12.0, -2.0/3.0, 0.0, 2.0/3.0, -1.0/12.0], # Порядка 4
       [-1.0/60.0, 3.0/20.0, -3.0/4.0, 0.0, 3.0/4.0, -3.0/20.0, 1.0/60.0], # Порядка 6
       [1.0/280.0, -4.0/105.0, 1.0/5.0, -4.0/5.0, 0.0, 4.0/5.0, -1.0/5.0, 4.0/105.0, -1.0/280.0]] # Порядка 8

  # Проверяем по возвращаемому значению,
  # является ли функция векторной или скалярной
  if isa(func(t, x), Vector{Float64})
    df = zeros(func(t, x))
  elseif isa(func(t, x), Float64)
    df = 0.0
  end

  Ih = h * eye(Float64, length(x))

  if order == 2
    for j = 0:order
      df = df + a[1][j+1] * func(t, x + (j - 1) * Ih[:, k])
    end
  elseif order == 4
    for j = 0:order
      df = df + a[2][j+1] * func(t, x + (j - 2) * Ih[:, k])
    end
  elseif order == 6
    for j = 0:order
      df = df + a[3][j+1] * func(t, x + (j - 3) * Ih[:, k])
    end
  elseif order == 8
    for j = 0:order
      df = df + a[4][j+1] * func(t, x + (j - 4) * Ih[:, k])
    end
  else
    println(STDERR, "Variable order = {2, 4, 6, 8}! Using order=4")
    for j = 0:order
      df = df + a[2][j+1] * func(t, x + (j - 2) * Ih[:, k])
    end
  end

  return df / h
end

# Частные производные от функции без параметра t
function derivative(func::Function, x::Vector{Float64}; k=1, h=0.1, order=4)
  # Заполняем массив коэффициентов
  a = [[-0.5, 0.0, 0.5], # Порядка 2
       [1.0/12.0, -2.0/3.0, 0.0, 2.0/3.0, -1.0/12.0], # Порядка 4
       [-1.0/60.0, 3.0/20.0, -3.0/4.0, 0.0, 3.0/4.0, -3.0/20.0, 1.0/60.0], # Порядка 6
       [1.0/280.0, -4.0/105.0, 1.0/5.0, -4.0/5.0, 0.0, 4.0/5.0, -1.0/5.0, 4.0/105.0, -1.0/280.0]] # Порядка 8

  # Проверяем по возвращаемому значению,
  # является ли функция векторной или скалярной
  if isa(func(x), Vector{Float64})
    df = zeros(func(x))
  elseif isa(func(x), Float64)
    df = 0.0
  end

  Ih = h * eye(Float64, length(x))

  if order == 2
    for j = 0:order
      df = df + a[1][j+1] * func(x + (j - 1) * Ih[:, k])
    end
  elseif order == 4
    for j = 0:order
      df = df + a[2][j+1] * func(x + (j - 2) * Ih[:, k])
    end
  elseif order == 6
    for j = 0:order
      df = df + a[3][j+1] * func(x + (j - 3) * Ih[:, k])
    end
  elseif order == 8
    for j = 0:order
      df = df + a[4][j+1] * func(x + (j - 4) * Ih[:, k])
    end
  else
    println(STDERR, "Variable order = {2, 4, 6, 8}! Using order=4")
    for j = 0:order
      df = df + a[2][j+1] * func(x + (j - 2) * Ih[:, k])
    end
  end

  return df / h
end

# Частные производные от функции одного аргумента без параметра t
function derivative(func::Function, x::Float64; h=0.1, order=4)
  # Заполняем массив коэффициентов
  a = [[-0.5, 0.0, 0.5], # Порядка 2
       [1.0/12.0, -2.0/3.0, 0.0, 2.0/3.0, -1.0/12.0], # Порядка 4
       [-1.0/60.0, 3.0/20.0, -3.0/4.0, 0.0, 3.0/4.0, -3.0/20.0, 1.0/60.0], # Порядка 6
       [1.0/280.0, -4.0/105.0, 1.0/5.0, -4.0/5.0, 0.0, 4.0/5.0, -1.0/5.0, 4.0/105.0, -1.0/280.0]] # Порядка 8

  # Проверяем по возвращаемому значению,
  # является ли функция векторной или скалярной
  if isa(func(x), Vector{Float64})
    df = zeros(func(x))
  elseif isa(func(x), Float64)
    df = 0.0
  end

  if order == 2
    for j = 0:order
      df = df + a[1][j+1] * func(x + (j - 1) * h)
    end
  elseif order == 4
    for j = 0:order
      df = df + a[2][j+1] * func(x + (j - 2) * h)
    end
  elseif order == 6
    for j = 0:order
      df = df + a[3][j+1] * func(x + (j - 3) * h)
    end
  elseif order == 8
    for j = 0:order
      df = df + a[4][j+1] * func(x + (j - 4) * h)
    end
  else
    println(STDERR, "Variable order = {2, 4, 6, 8}! Using order=4")
    for j = 0:order
      df = df + a[2][j+1] * func(x + (j - 2) * h)
    end
  end

  return df / h
end

"""
Нахождение матрицы Якоби от векторозначной функции многих переменных

``jacobian(func, t, x; h=0.1, order=4) -> J``

# Аргументы

* `func::Function`: функция в виде func(t, x, p).
* `t::Float64`: скалярный аргумент функции (время).
* `x::Vector{Float64}`: векторный аргумент функции `x`.
* `p::Tuple`: параметры функции (даже если их нет).
* `h::Float64`: шаг приращения, который необходимо использовать.
* `order::Int`: порядок точности {2, 4, 6, 8}.

# Возвращаемые значения

* `J::Matrix{Float64}(n, n)`: матрица Якоби.
"""
function jacobian(func::Function, t::Float64, x::Vector{Float64}; h=0.1, order=4)
  n = length(x)
  J = Matrix{Float64}(n, n)

  for i in 1:n
    J[:, i] = derivative(func, t, x; k=i, h=h, order=order)
  end
  return J
end
end # module


"""
    MarcenkoPastur()
The Marcenko Pastur Distrbitution (sometimes called the Marchenko-Pastur
Distribution or Law) has a probability distribution

'''math
f(x) = \frac{√{β(ϱ) - x)(x - α(ϱ))}}{2πx} 1_{[α(ϱ),β(ϱ)]}(x)
'''
where 1_{[a,b]}(x) is the indicator function that returns 1 if x exists in
[a,b] and returns 0 otherwise

External Links

* https://en.wikipedia.org/wiki/Marchenko%E2%80%93Pastur_distribution

"""

using Statistics
using Distributions
import Distributions: @check_args
import Distributions: @distr_support

struct MarcenkoPastur{T<:Real} <: ContinuousUnivariateDistribution
    ρ::T
    MarcenkoPastur{T}(ρ::T) where {T<:Real} = new{T}(ρ)
end

function MarcenkoPastur(ρ::T, check_args=true) where {T <: Real}
    check_args && @check_args(MarcenkoPastur, ρ > zero(ρ))
    return MarcenkoPastur{T}(ρ)
end

MarcenkoPastur(ρ::Integer) = MarcenkoPastur(float(ρ))

@distr_support MarcenkoPastur (1-√d.ρ)^2 (1+√d.ρ)^2

#### Conversions
function convert(::Type{MarcenkoPastur{T}}, ρ::Real) where T<:Real
    MarcenkoPastur(T(ρ))
end
function convert(::Type{MarcenkoPastur{T}},d::MarcenkoPastur{S}) where {T <: Real, S<: Real}
    MarcenkoPastur(T(d.ρ),check_args=false)
end

α(ρ) = (1-√ρ)^2
β(ρ) = (1+√ρ)^2

### Parameters
leftbound(d::MarcenkoPastur) = α(d.ρ)
rightbound(d::MarcenkoPastur) = β(d.ρ)
params(d::MarcenkoPastur) = (d.ρ)

### Statistics
mean(d::MarcenkoPastur) = d.ρ
mode(d::MarcenkoPastur) = 2*α(d.ρ)*β(d.ρ)/(α(d.ρ)+β(d.ρ))
# TODO
# median(d::MarcenkoPastur)
# TODO gather analytic form for each and use to determine which one should be
# in terms of the other
# var(d::MarcenkoPastur) =
# std(d::MarcenkoPastur) =
#
# skewness(d::MarcenkoPastur)
# kurtosis(d::MarcenkoPastur)
#
# entropy(d::MarcenkoPastur)

function pdf(d::MarcenkoPastur, x::Real)
    if ρ < 1
        return pdf(d, d.ρ*x)
    √((β(d.ρ)-x)*(x-α(d.ρ)))/(2π*x)
end

function logpdf(d::MarcenkoPastur, x::Real)
    0.5*(log(β(d.ρ)-x)+log(x-α(d.ρ))) - log(2π*x)
end

# TODO
# function cdf(d::MarcenkoPastur)
#
# end
#
# function quantile(d::MarcenkoPastur)
#
# end
#
# function cquantile(d::MarcenkoPastur)
#
# end


"""`tmean(x; tr=0.2)`

Trimmed mean of real-valued array `x`.

Find the mean of `x`, omitting the lowest and highest `tr` fraction of the data.
This requires `0 <= tr <= 0.5`. The amount of trimming defaults to `tr=0.2`.
"""
function tmean{S <: Real}(x::AbstractArray{S}; tr::Real=0.2)
    tmean!(copy(x), tr=tr)
end

"""`tmean!(x; tr=0.2)`

Trimmed mean of real-valued array `x`, which sorts the vector `x` in place.

Find the mean of `x`, omitting the lowest and highest `tr` fraction of the data.
This requires `0 <= tr <= 0.5`. The trimming fraction defaults to `tr=0.2`.
"""
function tmean!{S <: Real}(x::AbstractArray{S}; tr::Real=0.2)
    if tr < 0 || tr > 0.5
        error("tr cannot be smaller than 0 or larger than 0.5")
    elseif tr == 0
        return mean(x)
    elseif tr == .5
        return median!(x)
    else
        n   = length(x)
        lo  = floor(Int64, n*tr)+1
        hi  = n+1-lo
        return mean(sort!(x)[lo:hi])
    end
end

"""`winval(x; tr=0.2)`

Winsorize real-valued array `x`.

Return a copy of `x` in which extreme values (that is, the lowest and highest
fraction `tr` of the data) are replaced by the lowest or highest non-extreme
value, as appropriate. The trimming fraction defaults to `tr=0.2`.
"""
function winval{S <: Real}(x::AbstractArray{S}; tr::Real=0.2)
    const n = length(x)
    xcopy   = sort(x)
    ibot    = floor(Int64, tr*n)+1
    itop    = n-ibot+1
    xbot, xtop = xcopy[ibot], xcopy[itop]
    return  [x[i]<=xbot ? xbot : (x[i]>=xtop ? xtop : x[i]) for i=1:n]
end

"""`winmean(x; tr=0.2)`

Winsorized mean of real-valued array `x`.

See `winval` for what Winsorizing (clipping) signifies.
"""
winmean{S <: Real}(x::AbstractArray{S}; tr=0.2) = mean(winval(x, tr=tr))

"""`winvar(x; tr=0.2)`

Winsorized variance of real-valued array `x`.

See `winval` for what Winsorizing (clipping) signifies.
"""
winvar{S <: Real}(x::AbstractArray{S}; tr=0.2) = var(winval(x, tr=tr))

"""`winstd(x; tr=0.2)`

Winsorized standard deviation of real-valued array `x`.

See `winval` for what Winsorizing (clipping) signifies.
"""
winstd{S <: Real}(x::AbstractArray{S}; tr=0.2) = std(winval(x, tr=tr))

"""`wincov(x, y; tr=0.2)`

Compute the Winsorized covariance between `x` and `y`.

See `winval` for what Winsorizing (clipping) signifies.
"""
function wincov{S <: Real, T <: Real}(x::AbstractArray{S}, y::AbstractArray{T}; tr::Real=0.2)
    xvec = winval(x, tr=tr)
    yvec = winval(y, tr=tr)
    wcov = cov(xvec, yvec)
end

"""`trimse(x; tr=0.2)`

Estimated standard error of the mean for Winsorized real-valued array `x`.

See `winval` for what Winsorizing (clipping) signifies.
"""
trimse{S <: Real}(x::AbstractArray{S}; tr::Real=0.2) =
    sqrt(winvar(x,tr=tr))/((1-2tr)*sqrt(length(x)))

"""`trimci(x; tr=0.2, alpha=0.05, ...)`

Compute a (1-α) confidence interval for the trimmed mean.

Returns a `RobustStats.testOutput` object.
"""
function trimci{S <: Real}(x::AbstractArray{S}; tr::Real=0.2, alpha::Real=0.05, nullvalue::Real=0, method=true)
    se  = trimse(x, tr=tr)
    n   = length(x)
    df::Int64   = n-2*floor(tr*n)-1
    estimate    = tmean(x, tr=tr)
    confint     = [estimate-Rmath.qt(1-alpha/2, df)*se,
                   estimate+Rmath.qt(1-alpha/2, df)*se]
    statistic   = (estimate-nullvalue)/se
    pval        = 2*(1-Rmath.pt(abs(statistic),df))
    METHOD      = method ? "(1-α) confidence interval for the trimmed mean\n": nothing
    output           = testOutput()
    output.method    = METHOD
    output.df        = df
    output.estimate  = estimate
    output.ci        = confint
    output.statistic = statistic
    output.p         = pval
    return output
end

"""`idealf(x)`

Compute the ideal fourths (interpolated quartiles) of real-valued array `x`.

Returns a tuple of (1st_quartile, 3rd_quartile)
"""
function idealf{S <: Real}(x::AbstractArray{S})
    y       = sort(x)
    n       = length(x)
    j       = floor(Int64, n/4+5/12) # 25%ile is in [y[j], y[j+1]]
    k       = n-j+1        # 75%ile is in [y[k],y[k-1]]
    g       = n/4+5/12 - j   # weighting for the two data surrounding quartiles.
    (1-g).*y[j]+g.*y[j+1], (1-g).*y[k]+g.*y[k-1]
end

"""`pbvar(x; beta=0.2)`

Return the percentage bend midvariance of real-valued array `x`, a robust, efficient
measure of scale (dispersion). Lower values of beta increase efficiency but reduce
robustness.
This requires `0 <= beta <= 0.5`. The trimming fraction defaults to `beta=0.2`.
"""
function pbvar{S <: Real}(x::AbstractArray{S}; beta::Real=0.2)
    const n = length(x)
    med = median(x)
    absdev = abs.(x-med)
    sort!(absdev)

    m = floor(Int64, (1-beta)*n+0.5)
    ω = absdev[m]
    if ω <= 0   # At least a fraction (1-beta) of all values are identical
        return 0.0
    end

    z = 0.0
    counter = 0
    for i = 1:n
        ψ = absdev[i]/ω
        if abs(ψ) >= 1.0
            z += 1.0
        else
            z += ψ^2
            counter += 1
        end
    end
    n*(ω^2)*z/(counter^2)
end

"""`bivar(x; beta=0.2)`

Return the biweight midvariance of real-valued array `x`, a robust, efficient
measure of scale (dispersion). Lower values of beta increase efficiency but reduce
robustness.
This requires `0 <= beta <= 0.5`. The trimming fraction defaults to `beta=0.2`.
"""
function bivar{S <: Real}(x::AbstractArray{S})
    const n = length(x)
    med = median(x)
    MAD = mad(x)
    q = Rmath.qnorm(0.75)
    top = bot = 0.0
    for i = 1:n
        u = abs(x[i]-med)./(9.*q.*MAD)
        if u<1.0
            top += n*(x[i]-med)*(x[i]-med)*(1-u*u).^4
            bot += (1-u*u)*(1-5*u*u)
        end
    end
    top/(bot^2)
end

"""`tauloc(x; cval=4.5)`

Return the tau measure of location of real-valued array `x`, a robust, efficient
estimator.
"""
function tauloc{S <: Real}(x::AbstractArray{S}; cval::Real=4.5)
    const n = length(x)
    med = median(x)
    s = Rmath.qnorm(0.75)*mad(x)
    Wnom = Wden = 0.0
    for i in 1:n
        y = (x[i]-med)/s
        temp = (1.0-(y/cval)^2)^2
        if abs(temp) <= cval
            Wnom += temp*x[i]
            Wden += temp
        end
    end
    Wnom/Wden
end

"""`tauvar(x; cval=3.0)`

Return the tau measure of dispersion of real-valued array `x`, a robust, efficient
estimator.
"""
function tauvar{S <: Real}(x::AbstractArray{S}; cval::Real=3.0)
    const n = length(x)
    s     = Rmath.qnorm(0.75)*mad(x)
    tloc  = tauloc(x)
    W     = 0.0
    cval2 = cval*cval
    [W    += min(((x[i]-tloc)/s)*((x[i]-tloc)/s), cval2) for i=1:n]
    s*s*W/n
end

"""`outbox(x; mbox::Bool=false, ...)`

Use a modified boxplot rule based on the ideal fourths (`idealf`). When the named argument
`mbox` is set to true, a modification of the boxplot rule suggested by Carling (2000) is used.

Returns an object with vectors `keepid` and `outid` giving the kept/rejected element numbers,
`nout` (the number of rejected elements), and `outval`, an array of the outlier values.
"""
function outbox{S <: Real}(x::AbstractArray{S}; mbox::Bool=false, gval::Real=NaN, method::Bool=true)
    const n = length(x)
    lower_quartile, upper_quartile = idealf(x)
    IQR = upper_quartile-lower_quartile
    cl = cu = 0.0
    if mbox
        if isnan(gval)
            gval=(17.63*n-23.64)/(7.74*n-3.71)
        end
        cl = median(x) - gval*IQR
        cu = median(x) + gval*IQR
    elseif !mbox
        if isnan(gval)
            gval=1.5
        end
        cl = lower_quartile - gval*IQR
        cu = upper_quartile + gval*IQR
    end
    flag = (x.<cl) .| (x.>cu)
    vec = 1:n
    outid  = vec[flag]
    keepid = vec[!flag]
    outval = x[flag]
    nout = length(outid)
    if method && !mbox
        METHOD = "Outlier detection method using \nthe ideal-fourths based boxplot rule\n"
    elseif method && mbox
        METHOD = "Outlier detection method using \nthe ideal-fourths based boxplot rule\n(using the modification suggested by Carling (2000))\n"
    else
        METHOD = nothing
    end
    outOutput(outid, keepid, outval, nout, METHOD)
end

"""`msmedse(x)`

Return the standard error of the median, computed through the method recommended
by McKean and Sshrader (1984)."""
function msmedse{S <: Real}(x::AbstractArray{S})
    const n = length(x)
    y = sort(x)
    if duplicated(y)
        warn("Tied values detected. Estimate of standard error might be highly inaccurate, even with n large")
    end
    q995 = Rmath.qnorm(.995)
    av::Int = round((n+1)/2 - q995*sqrt(n/4))
    if av == 0
        av = 1
    end
    top::Int = n-av+1
    abs((y[top]-y[av])/(2q995))
end

"""`binomci(s, n; alpha=0.05)`

Compute the (1-α) confidence interval for p, the binomial probability of success, given
`s` successes in `n` trials. Returns an object with components `p_hat` (the observed
fraction of successes) and `confint=[lo,hi]` (the confidence interval). The computation
uses Pratt's method.

Can also use `binomci(x; alpha=0.05)`, where x is an array consisting only of 0s
and 1s. It's equivalent to `binomci(sum(x), length(x), alpha=alpha)`."""
function binomci(s::Int, n::Int; alpha::Real=0.05)
    if s > n
        error("binomci requires s≤n (no more successes than trials)")
    elseif s < 0
        error("binomci requires s≥0")
    elseif n <= 1
        error("binomci requires n≥2 (at least 2 trials)")
    end
    p_hat=s/n
    if s == 0
        upper = 1.0-alpha.^(1/n)
        return binomciOutput(p_hat, [0,upper], n)
    elseif s == 1
        lower = 1-(1-alpha/2).^(1/n)
        upper = 1-(alpha/2).^(1/n)
        return binomciOutput(p_hat, [lower, upper], n)
    elseif s == (n-1)
        lower = (alpha/2).^(1/n)
        upper = (1-alpha/2).^(1/n)
        return binomciOutput(p_hat, [lower, upper], n)
    elseif s == n
        lower = alpha.^(1/n)
        upper = 1
        return binomciOutput(p_hat, [lower, upper], n)
    end

    z     = Rmath.qnorm(1-alpha/2)
    A     = ((s+1)/(n-s))*((s+1)/(n-s))
    B     = 81.*(s+1)*(n-s)-9.*n-8
    C     = (0-3)*z*sqrt(9.*(s+1)*(n-s)*(9*n+5-z^2)+n+1)
    D     = 81.*(s+1)^2-9.*(s+1)*(2+z^2)+1
    E     = 1+A*((B+C)/D)^3
    upper = 1/E

    A     = (s/(n-s-1))*(s/(n-s-1))
    B     = 81.*s*(n-s-1)-9.*n-8
    C     = 3.*z*sqrt(9.*s*(n-s-1)*(9.*n+5-z^2)+n+1)
    D     = 81.*s^2-9.*s*(2+z^2)+1
    E     = 1+A*((B+C)/D)^3
    lower = 1/E
    binomciOutput(p_hat, [lower, upper], n)
end

function binomci(x::Vector{Int}; alpha::Real=0.05)
    for i = 1:length(x)
        if x[i]<0 || x[i] > 1
            error("x vector must contain only values 0 or 1.")
        end
    end
    binomci(sum(x), length(x), alpha=alpha)
end

"""`acbinomci(s, n; alpha=0.05)`

Compute the (1-α) confidence interval for p, the binomial probability of success, given
`s` successes in `n` trials. Returns an object with components `p_hat` (the observed
fraction of successes) and `confint=[lo,hi]` (the confidence interval). The computation
uses a generalization of the Agresti-Coull  method that was studied by Brown, Cai, & DasGupta.

Can also use `acbinomci(x; alpha=0.05)`, where `x` is an array consisting only of 0s
and 1s. It's equivalent to `acbinomci(sum(x), length(x), alpha=alpha)`."""
function acbinomci(s::Int, n::Int; alpha::Real=0.05)
    if s > n
        error("acbinomci requires s≤n (no more successes than trials)")
    elseif s < 0
        error("acbinomci requires s≥0")
    elseif n <= 1
        error("acbinomci requires n≥2 (at least 2 trials)")
    end
    p_hat=s/n

    if s == 0
        upper = 1.0-alpha.^(1/n)
        return binomciOutput(p_hat, [0, upper], n)
    elseif s == 1
        lower = 1-(1-alpha/2)^(1/n)
        upper = 1-(alpha/2)^(1/n)
        return binomciOutput(p_hat, [lower, upper], n)
    elseif s == (n-1)
        lower = (alpha/2)^(1/n)
        upper = (1-alpha/2)^(1/n)
        return binomciOutput(p_hat, [lower, upper], n)
    elseif s == n
        lower = alpha^(1/n)
        upper = 1
        return binomciOutput(p_hat, [lower, upper], n)
    end

    cr    = Rmath.qnorm(1-alpha/2)
    ntil  = n+cr^2
    ptil  = (s+cr^2/2)/ntil
    lower = ptil-cr*sqrt(ptil*(1-ptil)/ntil)
    upper = ptil+cr*sqrt(ptil*(1-ptil)/ntil)
    binomciOutput(p_hat, [lower, upper], n)
end

function acbinomci(x::Vector{Int}; alpha::Real=0.05)
    for i = 1:length(x)
        if x[i]<0 || x[i] > 1
            error("x vector must contain only values 0 or 1.")
        end
    end
    acbinomci(sum(x), length(x), alpha=alpha)
end

"""`_estimate_dispersion(x)`

Estimate dispersion by the following methods. Return the first value that gives
a non-zero dispersion. Each are normalized to 1.0 for Gaussian distributions:

1. Normalized median absolute deviation `mad`,
1. Normalized inter-quartile range `iqrn`,
1. Normalized winsorized variance `winvar`."""
function _estimate_dispersion{S <: Real}(x::AbstractArray{S})
    m =  mad(x)
    m > 0 && return m

    m = iqrn(x)
    m > 0 && return m

    m =  sqrt(winvar(x)./0.4129)
    m > 0 && return m

    error("All measures of dispersion are equal to 0")
end

"""`sint(x; alpha=.05)`
`sint(x, testmedian; alpha=.05)`

Compute the (1-α) confidence interval for the median. In the second form,
use the Hettmansperger and Sheather interpolation method to estimate a p-value
for the `testmedian`."""
function sint{S <: Real}(x::AbstractArray{S}; alpha::Real=0.05, method::Bool=true)
    const n = length(x)
    k = Int(Rmath.qbinom(alpha/2.0, n, 0.5))
    gk = Rmath.pbinom(n-k, n, .5) - Rmath.pbinom(k-1, n, .5)
    if gk < (1 - alpha)
        k = k - 1
        gk = Rmath.pbinom(n-k, n, .5) - Rmath.pbinom(k-1, n, .5)
    end
    gkp1 = Rmath.pbinom(n-k-1, n, .5) - Rmath.pbinom(k, n, .5)
    kp = k + 1

    xsort=sort(x)
    nmk = n-k
    nmkp = nmk+1
    ival = (gk-1+alpha)/(gk-gkp1)
    lam = ((n-k)*ival)/(k+(n-2k)*ival)
    low = lam*xsort[kp]+(1-lam)*xsort[k]
    hi = lam*xsort[nmk]+(1-lam)*xsort[nmkp]
    if method
        METHOD="Confidence interval for the median\n"
        if duplicated(x)
            METHOD *= "Duplicate values detected; hdpb() might have more power\n"
        end
    else
        METHOD=nothing
    end
    output=testOutput()
    output.method=METHOD
    output.ci=[low, hi]
    output
end

function sint{S <: Real}(x::AbstractArray{S}, testmedian;
    alpha::Real=0.05, method::Bool=true)
    ci = sint(x, alpha=alpha, method=false).ci
    med = median(x)
    cichoice = testmedian<med ? 1 : 2

    # Find the pvalue that excludes testmedian by binary search.
    minloga = -8.0
    maxloga = -0.001
    ciA = sint(x, alpha=exp(minloga)).ci[cichoice]-testmedian
    ciB = sint(x, alpha=exp(maxloga)).ci[cichoice]-testmedian
    if ciA*ciB > 0
        if ciB*(med-testmedian)<0
            pval = 1.0
        else
            pval = 0.0
        end
    else
        while (maxloga-minloga > .0001)
            newloga = (maxloga+minloga)/2
            newci = sint(x, alpha=exp(newloga)).ci[cichoice]-testmedian
            if newci*ciB >= 0
                ciB = newci
                maxloga = newloga
            else
                ciA = newci
                minloga = newloga
            end
        end
        pval = exp((maxloga+minloga)/2.0)
    end
    if method
        METHOD="Confidence interval for the median with p-val.\n"
        if duplicated(x)
            METHOD *= "Duplicate values detected; hdpb() might have more power\n"
        end
    else
        METHOD=nothing
    end
    output = testOutput()
    output.method = METHOD
    output.ci     = ci
    output.p      = pval
    output
end

"""`hpsi(x, bend=1.28)`

Evaluate Huber's ψ function for each value in the vector `x`.
ψ(x) = max( min(x,bend), -bend)."""
function hpsi{S <: Real}(x::AbstractArray{S}, bend::Real=1.28)
    ψ = Array(x)
    ψ[x .> bend] = bend
    ψ[x .< -bend] = -bend
    ψ
end

"""`onestep(x, bend=1.28)`

Compute one-step M-estimator of location using Huber's ψ."""
function onestep{S <: Real}(x::AbstractArray{S}, bend::Real=1.28)
    MED = median(x)
    MAD = mad(x)
    y = (x-MED)/MAD
    A = sum(hpsi(y, bend))
    B = sum(abs.(y) .<= bend)
    return MED + MAD*A/B
end

"""`bootstrapci(x; est=onestep, alpha=0.05, nboot=2000, nullvalue=NaN)`

Compute a (1-α) confidence interval for the location-estimator function `est`
using a bootstrap calculation. The default estimator is `onestep`. If `nullvalue` is
given, it is the target value used when computing a p-value.
"""
function bootstrapci{S <: Real}(x::AbstractArray{S}; est::Function=onestep,
    alpha::Real=0.05, nboot::Integer=2000, seed=2, nullvalue::Real=NaN)
    if isa(seed, Int)
        srand(seed)
    elseif seed
        srand(2)
    end
    const n = length(x)
    bvec = zeros(nboot)
    for i = 1:nboot
        randid=rand(1:n, n)
        bvec[i]=est(x[randid])
    end
    low::Int = round((alpha/2)*nboot) + 1
    up = nboot-low + 1
    sort!(bvec)

    pv = NaN
    if nullvalue != NaN
        pv = mean(bvec.>nullvalue)+0.5*mean(bvec.==nullvalue)
        pv = 2min(pv, 1-pv)
    end
    estimate = est(x)
    output = testOutput()
    output.estimate = estimate
    output.ci = [bvec[low], bvec[up]]
    output.p = pv
    output
end

"""`bootstrapse(x; est=median, alpha=0.05, nboot=2000)`

Compute the standard error of the location-estimator function `est`
using a bootstrap calculation. The default estimator is `median`.
"""
function bootstrapse{S <: Real}(x::AbstractArray{S};
        nboot::Integer=1000, est::Function=median, seed=2)
    if isa(seed, Int)
        srand(seed)
    elseif seed
        srand(2)
    end
    const n = length(x)
    bvec = zeros(nboot)
    for i = 1:nboot
        randid=rand(1:n, n)
        bvec[i]=est(x[randid])
    end
    std(bvec)
end

"""`mom(x; bend=2.24)`

Returns a modified one-step M-estimator of location (MOM), which is the unweighted
mean of all values not more than (bend times the `mad(x)`) away from the data
median.
"""
function mom{S <: Real}(x::AbstractArray{S}; bend::Real=2.24)
    mom!(copy(x), bend=bend)
end

"""`mom!(x)`

Like `mom`, but will sort the input vector."""
function mom!{S <: Real}(x::AbstractArray{S}; bend::Real=2.24)
    const n = length(x)
    med = median!(x)
    MAD = mad(x)
    not_extreme = abs.(x-med) .<= bend*MAD
    mean(x[not_extreme])
end

"""`momci(x; bend=2.24, alpha=0.05, nboot=2000)`

Compute a bootstrap, (1-α) confidence interval for the MOM-estimator of location based on Huber's ψ.
The default number of bootstrap resamplings is nboot=2000."""
function momci{S <: Real}(x::AbstractArray{S}; bend::Real=2.24, alpha::Real=0.05,
    nboot::Integer=2000, seed=2, nullvalue::Real=NaN)
    estimator(z) = mom!(z, bend=bend)
    bootstrapci(copy(x), est=estimator, alpha=alpha, nboot=nboot, seed=seed, nullvalue=nullvalue)
end

"""`contam_randn([T=Float64], n; epsilon=0.1, k=10.0)`

Contaminated normal distribution N(0,1). (That is, with μ=0, σ=1.) A fraction `epsilon` of
values will be N(0,`k`)."""
function contam_randn(T::Type, n::Integer; epsilon::Real=0.1, k::Real=10)
    k <= 0 && error("k > 0 is required")
    epsilon > 1 || epsilon < 0 && error("0 ≤ epsilon ≤ 1 is required")
    output = randn(T, n)
    contaminated = rand(n) .< epsilon
    output[contaminated] *= k
    return output
end

contam_randn(n::Integer; epsilon::Real=0.1, k::Real=10) =
    contam_randn(Float64, n, epsilon=epsilon, k=k)

"""`trimpb(x; tr=0.2, alpha=0.05, nboot=2000, win=false, nullvalue=0.0)`

Compute a (1-α) confidence interval for a trimmed mean with a trimming fraction of `tr`.

Use `nboot` bootstrap samples and `alpha` for α.

If `win` is a real number, it is the amount of Winsorizing before bootstrapping.
If `win` is true, then use 10% Winsorizing. If `win` is false, no Winsorizing is done.

The p-value is for the hypothesis that trimmed mean equals `nullvalue`.
"""
function trimpb{S <: Real}(x::AbstractArray{S}; tr::Real=0.2, alpha::Real=0.05, nboot::Integer=2000,
                win=false, nullvalue::Real=0.0, seed=2)
    if isa(win, Bool) && win
        win = 0.1
    end
    if win > tr
        error("trimpb() requires that the amount of Winsorizing ≤ the amount of trimming.")
    end
    wx = winval(x, tr=win)

    estimator(x) = tmean!(x, tr=tr)
    bootstrapci(wx, est=estimator, alpha=alpha, nboot=nboot, seed=seed, nullvalue=nullvalue)
end

"""`procb(x, y; seed=2)`

Compute a (1-α) confidence interval for Pearson's correlation coefficient.

This function uses an adjusted percentile bootstrap method that
gives good results when the error term is heteroscedastic.
"""
function pcorb{S <: Real, T <: Real}(x::AbstractArray{S}, y::AbstractArray{T}; seed=2)
   if isa(seed, Bool)
        seed && srand(2)
    else
        srand(seed)
    end
    const n = length(x)
    # Wow. Every number in this function is totally magic.
    bvec=zeros(Float64, 599)
    for i=1:599
        randid=rand(1:n, n)
        tempx = x[randid]
        tempy = y[randid]
        bvec[i]=cor(tempx, tempy)
    end
    if n >= 250
        ilow, ihi = 15, 584
    elseif n >= 180
        ilow, ihi = 14, 585
    elseif n >= 80
        ilow, ihi = 11, 588
    elseif n >= 40
        ilow, ihi = 8, 592
    else
        ilow, ihi = 7, 593
    end
    sort!(bvec)
    output = testOutput()
    output.estimate = cor(x, y)
    output.ci = [bvec[ilow], bvec[ihi]]
    output
end

"""`yuend(x,y; tr=0.2, alpha=0.05)`

Compare the trimmed means of two dependent random variables `x` and `y`.
The default amount of trimming `tr` is 20%.

A (1-α) confidence interval for the difference of trimmed mean of `x` minus
the trimmed mean of `y` is computed and returned in `yuend.ci`.
The significance level is returned in `yuend.siglevel`.
"""
function yuend{S <: Real, T <: Real}(x::AbstractArray{S}, y::AbstractArray{T};
        tr::Real=0.2, alpha::Real=0.05, method::Bool=true)
    const n = length(x)
    if n != length(y)
        error("`x` and `y` must agree in length")
    end
    h1::Integer = n - 2*floor(tr*n)
    q1 = (n - 1)*winvar(x, tr=tr)
    q2 = (n - 1)*winvar(y, tr=tr)
    q3 = (n - 1)*wincov(x, y, tr=tr)
    df = h1 - 1
    se = sqrt((q1 + q2 - 2q3)/(h1*(h1-1)))
    crit = Rmath.qt(1 - alpha/2, df)
    meandif = tmean(x, tr=tr) - tmean(y, tr=tr)
    confint = [meandif - crit*se, meandif + crit*se]
    test = meandif/se
    p = 2*(1 - Rmath.pt(abs(test), df))
    if method
        METHOD="Comparing the trimmed means of two dependent variables.\n"
    else
        METHOD=nothing
    end
    output = testOutput()
    output.method = METHOD
    output.ci = confint
    output.p = p
    output.estimate = meandif
    output.se = se
    output.statistic = test
    output.n = n
    output.df = df
    output
end

function pbos{S <: Real}(x::AbstractArray{S}; beta::Real=0.2)
    temp    = sort( abs.( x - median(x) ))
    nval    = length( x )
    omhatid::Integer = floor( (1 - beta)*nval )
    omhatx  = temp[ omhatid ]
    psi     = ( x - median(x) )./ omhatx
    i1      = length(psi[ psi .< -1 ])
    i2      = length(psi[ psi .> 1 ])
    sx      = 0.0
    [ sx += psi[i] < -1 ? 0 : [ psi[i] > 1 ? 0 : x[i] ]  for i=1:nval ]
    return ( sx  + omhatx * (i2 - i1))/(nval - i1 - i2)
end

#Compute the percentage bend correlation between x and y
#beta is the bending constant for omega sub N.
function pbcor{S <: Real, T <: Real}(x::AbstractArray{S}, y::AbstractArray{T}; beta::Real=0.2)
    nval = length(x)
    if length(y) != nval
        error("x and y do not agree in length.")
    end
    temp    = sort( abs.( x - median(x) ))
    omhatid::Integer = floor( (1 - beta)*nval )
    omhatx  = temp[ omhatid ]
    temp    = sort( abs.( y - median(y) ))
    omhaty  = temp[ omhatid ]
    a       = (x .- pbos(x, beta=beta) )./omhatx
    b       = (y .- pbos(y, beta=beta) )./omhaty
    for i = 1:nval
        if a[i] < -1
            a[i] = -1
        elseif a[i] > 1
            a[i] = 1
        end
        if b[i] < -1
            b[i] = -1
        elseif b[i] > 1
            b[i] = 1
        end
    end
    Pbcor   = sum( a.*b )/sqrt(sum( a.*a ) * sum( b.*b ))
    test    = Pbcor*sqrt( ( nval - 2 )/( 1 - Pbcor*Pbcor ) )
    sig     = 2*( 1 - Rmath.pt(abs(test), nval-2))

    METHOD=nothing

    output = testOutput()
    output.method = METHOD
    output.p = sig
    output.estimate = Pbcor
    output.statistic = test
    output.n = nval
    output.df = nval - 2
    output
end


using Base.Test
using Einsum

## Test that vars in Main aren't overwritten by einsum
i = -1
y = randn(10)
@einsum x[i] := y[i]
@test i == -1

## Test with preallocated array ##

A = zeros(5,6,7);
X = randn(5,2);
Y = randn(6,2);
Z = randn(7,2);

@einsum A[i,j,k] = X[i,r]*Y[j,r]*Z[k,r]

for i = 1:5
    for j = 1:6
        for k = 1:7
            s = 0
            for r = 1:2
                s += X[i,r]*Y[j,r]*Z[k,r]
            end
            @test A[i,j,k] == s
        end
    end
end

## Test without preallocated array ##

@einsum A2[i,j,k] := X[i,r]*Y[j,r]*Z[k,r]
@test isapprox(A,A2)

# Interesting test case, can throw an error that
# local vars are declared twice. Solution was to wrap
# everything in a let statement.
if true
    @einsum A[i,j,k] = X[i,r]*Y[j,r]*Z[k,r]
else
    @einsum A[i,j,k] = X[i,r]*Y[j,r]*Z[k,r]
end

# At one point this threw an error because the lhs
# had no indices/arguments
x = randn(10)
y = randn(10)
@einsum k := x[i]*y[i]
@test isapprox(k,dot(x,y))

# Elementwise multiplication (this should create nested loops with no
# no summation, due to lack of repeated variables.)
x = randn(10)
y = randn(10)
@einsum k[i] := x[i]*y[i]
@test isapprox(k,x.*y)

# Transpose a block matrix
z = Any[ rand(2,2) for i=1:2, j=1:2]
@einsum t[i,j] := transpose(z[j,i])
@test isapprox(z[1,1], t[1,1]')
@test isapprox(z[2,2], t[2,2]')
@test isapprox(z[1,2], t[2,1]')
@test isapprox(z[2,1], t[1,2]')

# Example from numpy
A = reshape(collect(1:25),5,5)
@einsum B[i] := A[i,i]
@test all(B .== [1,7,13,19,25])


using DrWatson
@quickactivate "StatReth"

# %%
"""2H1
Pr(twins|speciesA) = 0.1
Pr(twins|speciesB) = 0.2
Pr(speciesA) = Pr(speciesB) = 0.5
Pr(twins) = Pr(twins|speciesA) * Pr(speciesA) + Pr(twins|speciesB) * Pr(speciesB)
          = 0.1 * 0.5 + 0.2 * 0.5 = 0.15

Now births twins.

Bayes:
Pr(species|twins) = Pr(twins|species) * Pr(species) / Pr(twins)
Pr(panda = speciesA|twins) = 0.1 * 0.5 / 0.15 = 1/3
Pr(panda = speciesB|twins) = 0.2 * 0.5 / 0.15 = 2/3

Pr(twin|panda) = Pr(twins|speciesA) * Pr(panda = speciesA) +
                 Pr(twins|speciesB) * Pr(panda = speciesB)
               = 0.1 * 1/3 + 0.2 * 2/3 = 0.167

"""
# %%
"""2H2
Pr(twins|speciesA) = 0.1
Pr(twins|speciesB) = 0.2
Pr(speciesA) = Pr(speciesB) = 0.5
Pr(twins) = Pr(twins|speciesA) * Pr(speciesA) + Pr(twins|speciesB) * Pr(speciesB)
          = 0.1 * 0.5 + 0.2 * 0.5 = 0.15

Now births twins.

Bayes:
Pr(species|twins) = Pr(twins|species) * Pr(species) / Pr(twins)
Pr(panda = speciesA|twins) = 0.1 * 0.5 / 0.15 = 1/3
"""
# %%
"""2H3
Pr(twins|speciesA) = 0.1  |  Pr(singleinfant|speciesA) = 0.9
Pr(twins|speciesB) = 0.2  |  Pr(singleinfant|speciesB) = 0.8

Now:
Pr(speciesA) = 1/3
Pr(speciesB) = 2/3
Pr(twin|panda) = 0.167

Pr(singleinfant) = Pr(singleinfant|speciesA) * Pr(speciesA) +
                   Pr(singleinfant|speciesB) * Pr(speciesB)
                 = 0.9 * 1/3 + 0.8 * 2/3 = 0.833

Now births single infant.

Bayes:
Pr(species|singleinfant) = Pr(singleinfant|species) * Pr(species) / Pr(singleinfant)
Pr(panda = speciesA|singleinfant) = 0.9 * 1/3 / 0.833 = 0.36
"""
# %%
"""2H4
I understood the test like this:

                          panda is:
                    speciesA    speciesB
test    speciesA      0.8         0.35
says:   speciesB      0.2         0.65

Now the test comes back as speciesA.

We would like to know Pr(panda = speciesA | test = speciesA).

Bayes:
Pr(test = A | panda = A) = 0.8
Pr(panda = A) = Pr(panda = B) = 0.5
Pr(test = A) = 0.8 * 0.5 + 0.35 * 0.5 = 0.575
Pr(test = B) = 0.2 * 0.5 + 0.65 * 0.5 = 0.425

Pr(panda = A | test = A)
            = Pr(test = A | panda = A) * Pr(panda = A) / Pr(test = A)
            = 0.8 * 0.5 / 0.575 = 0.696
Pr(panda = B | test = A)
            = Pr(test = A | panda = B) * Pr(panda = B) / Pr(test = A)
            = 0.35 * 0.5 / 0.425 = 0.304

This will be our new prior:
=> Pr(speciesA) = 0.696
=> Pr(speciesB) = 0.304

Now births twins:
We want to know: Pr(speciesA|twins)

Pr(twins|speciesA) = 0.1  |  Pr(singleinfant|speciesA) = 0.9
Pr(twins|speciesB) = 0.2  |  Pr(singleinfant|speciesB) = 0.8

Bayes:
Pr(speciesA|twins) = Pr(twins|speciesA) * Pr(speciesA) / Pr(twins)
Pr(twins|speciesA) = 0.1
Pr(speciesA) = 0.696
Pr(twins) = 0.1 * 0.696 + 0.2 * 0.304 = 0.130
=> Pr(speciesA|twins) = 0.1 * 0.696 / 0.130 = 0.535
=> Pr(speciesB|twins) = 0.2 * 0.304 / 0.130 = 0.468
(including some rounding errors)

New priors:
=> Pr(speciesA) = 0.535
=> Pr(speciesB) = 0.468

Now births twins:
We want to know: Pr(speciesA|singleinfant)

Bayes:
Pr(speciesA|singleinfant) = Pr(singleinfant|speciesA) * Pr(speciesA) / Pr(singleinfant)
Pr(singleinfant|speciesA) = 0.9
Pr(speciesA) = 0.535
Pr(singleinfant) = 0.9 * 0.535 + 0.8 * 0.468 = 0.856
=> Pr(speciesA|twins) = 0.9 * 0.535 / 0.856 = 0.563
=> Pr(speciesB|twins) = 0.8 * 0.468 / 0.856 = 0.437

"""

# %%
using Statistics
using BenchmarkTools

function genetest(p)
    if p == true  # panda is A
        return rand() < 0.8
    else          # panda is B
        return rand() > 0.65
    end
end

function twinbirth(p)
    if p == true  # panda is A
        return rand() < 0.1
    else          # panda is B
        return rand() < 0.2
    end
end

function singleinfant(p)
    if p == true  # panda is A
        return rand() < 0.9
    else          # panda is B
        return rand() < 0.8
    end
end

f(p) = mean(p[@. genetest(p) & twinbirth(p) & singleinfant(p)])

function g(p)
    countA = count(p) do p
        genetest(p) && twinbirth(p) && singleinfant(p) && p
    end
    countB = count(p) do p
        genetest(p) && twinbirth(p) && singleinfant(p) && !p
    end
    countA / (countA + countB)
end

function h(pandasA)
    countA = 0
    countB = 0
    for p in pandasA
        if genetest(p) && twinbirth(p) && singleinfant(p)
            if p
                countA += 1
            else
                countB += 1
            end
        end
    end
    countA / (countA + countB)
end

n = 10000000
pandasA = rand(Bool, n)

f(pandasA)
g(pandasA)
h(pandasA)


#    Problem :
#    *********
#	 The separable extension of Rosenbrock's function.
#
#    Origonal SIF Source:problem 21 in
#    J.J. More', B.S. Garbow and K.E. Hillstrom,
#    "Testing Unconstrained Optimization Software",
#    ACM Transactions on Mathematical Software, vol. 7(1), pp. 17-41, 1981.
#
#    SROSENBR.SIF classification SUR2-AN-V-0
#
#    Number of variables is variable
#
# Daniel Henderson, 08/2021

f = (x) -> begin
	fx = 0.0
	for i in firstindex(x):2:lastindex(x)
		t1     = 1 - x[i]
		t2     = 10(x[i+1] - x[i]^2)
		fx    += t1^2 + t2^2
	end
    return fx
end

g! = (g, x) -> begin
	for i in firstindex(x):2:lastindex(x)
		t1     = 1 - x[i]
		t2     = 10(x[i+1] - x[i]^2)
		g[i+1] = 20t2
		g[i]   = -2(x[i] * g[i+1] + t1)
	end
    return g
end

fg! = (g, x) -> begin
	fx = 0.0
	for i in firstindex(x):2:lastindex(x)
		t1     = 1 - x[i]
		t2     = 10(x[i+1] - x[i]^2)
		g[i+1] = 20t2
		g[i]   = -2(x[i] * g[i+1] + t1)
		fx    += t1^2 + t2^2
	end
    return fx, g
end

init = (n::Int=5000) -> begin
	mod(n, 2) > 0 && @warn "SROSENBR: number of variables must be even"
	q = max(1, div(n, 2))
	n = 2q

    x0 = [j % 2 == 1 ? 1.2 : 1.0 for j in 1:n]
    return n, x0
end

TestSet["SROSENBR"] = UncProgram("SROSENBR",  f, g!, fg!, init)


using LinearAlgebra
using Distributions

function kinetic_matrix(num_sites, t)   # one-body matrix, periodic boundary condition
    K_matrix = zeros(num_sites, num_sites)
    for i =  1 : num_sites
        for j = 1 : num_sites
            if abs(i - j) == 1 || abs(i - j) == num_sites - 1
                K_matrix[i, j] = -t
            end
        end
    end
    if flag_background_subtraction == 1
        K_matrix += Diagonal(background_dist) * U
    end
    return K_matrix
end

function auxiliary_field_matrix()
    ϕ = rand(Normal(), num_sites)
    coefficent_vector = ϕ * AF_coefficient
    AF_matrix = complex(zeros(num_sites, num_sites))
    for i = 1 : num_sites
        AF_matrix[i, i] = coefficent_vector[i]
    end
    if flag_background_subtraction == 1
        background_exponent = sum(-coefficent_vector .* background_dist)
        background_weight = exp(background_exponent)
        return background_weight * exp(AF_matrix)
    elseif flag_background_subtraction == 0
        return exp(AF_matrix)
    end
end


using FractionalDiffEq, Plots, LinearAlgebra

h = 1e-2; tspan = (0, 30)
rightfun(x, y) = 172/125*cos(4/5*x)
multitermprob = MultiTermsFODEProblem([1, 1/16, 4/5, 3/2, 1/25, 6/5], [3, 2.5, 2, 1, 0.5, 0], rightfun, [1, 4/5, -16/25, 0, 0, 0], tspan)

realfun(x)=sqrt(2)*sin(4*x/5+π/4)
sol1 = solve(multitermprob, h, PIEX())
sol2 = solve(multitermprob, h, PIIMRect())
sol3 = solve(multitermprob, h, PIIMTrap())
sol4 = solve(multitermprob, h, PIPECE())
#=
plot(sol1)
plot!(sol2)
plot!(sol3)
plot!(sol4)
=#
#plot!(collect(0:0.01:30), realfun.(collect(0:0.01:30)))

realsol = realfun.(collect(0:h:30))
err1 = norm(sol1.u-realsol)
err2 = norm(sol2.u-realsol)
err3 = norm(sol3.u-realsol)
err4 = norm(sol4.u-realsol)


#=
# Euclidean Distance Matrix Completion
This example illustrates how to use Chordal.jl to complete a partially-specified
Euclidean Distance Matrix (or determine no such completion exists).
=#

#=
## Euclidean Distance Matrices
Given $N$ vectors $x_1,\dots,x_n$ in $\mathbb{R}^n$, the associated Euclidean
Distance Matrix (EDM) $D \in \mathbb{R}^{N \times N}$ records the squared distance
between each pair $x_i, x_j$. Specifically,
$$
D_{ij} = \|x_i - x_j\|_2^2.
$$

Given a partially-specified EDM $D$ which has a chordal sparsity pattern, we would
like to find the missing entries.
=#

using Chordal
using LinearAlgebra, SparseArrays

#=
## Small Example
This small example is taken from Jon Dattorro's [Convex Optimization & Euclidean Distance Geometry](https://ccrma.stanford.edu/~dattorro/EDM.pdf)
eq (1041). First, we construct the partially-specified EDM.
=#
D = sparse([
    0.0 1 5 0;
    1 0 4 1;
    5 4 0 1;
    0 1 1 0
])
@show D

## Next, we complete the matrix
D_complete = sparse(edm_completion(D))
println("\nCompletion:")
@show D_complete

#=
## Larger Random Example

First, we generate an EDM from 17 vectors in $\mathbb{R}^3}$.
=#
n = 17
r = 3
V = randn(n, r)
VV = V*V'
D_full = diag(VV)*ones(n)' + ones(n)*diag(VV)' - 2VV;

#=
Next, we remove entries. and complete the
The sparsity pattern is from Figure 4.2 in Lieven Vandenberghe and Martin S. Andersen's
[Chordal Graphs and Semidefinite Optimization](https://www.seas.ucla.edu/~vandenbe/publications/chordalsdp.pdf).
=#
ijs = [(3,1), (3,2), (4,1), (4,2), (4,3), (5,1), (5,3), (5,4), (8,7),
       (9,5), (9,6), (9,7), (9,8), (11,10), (13,10), (13,11), (13,12),
       (14,10), (14,11), (14,12), (14,13),
       (15, 1), (15,3), (15,4), (15,5), (15,7), (15,8), (15,9),
       (16,5), (16,6), (16,9), (16,12), (16,13), (16,14), (16,15),
       (17,10), (17,11), (17,12), (17,13), (17,14), (17,15), (17,16)]
append!(ijs, [(i,i) for i in 1:n])
II, JJ = Chordal.unzip(ijs)
sp = sparse(II, JJ, ones(length(II)))
sp = sp + tril(sp)'

## Remove entries s.t. the remaining entries have a chordal sparsity pattern
D = sp .* D_full

## Complete the matrix
@show Chordal.is_edm(edm_completion(D))


function varianceSD(x)

    n = length(x)

    (1/(n-1)) * sum((x .- meanSD(x)).^2)

end


function calc_phi_deriv_deriv(x,xi,n,nn,c,vars,var_seq)
    phi_der=zeros(nn,n)
    for i=1:nn
        for j=1:n
            dr=0
            for k=1:vars
                dr = dr + (xi[i,k] - x[j,k])^2
            end
            r = (dr)

            # 2 r^4
            # phi_der[i,j] = -12*((xi[i,var_seq]-x[j,var_seq])^2)*(r^2) - 2*(r^3)

            # 1 Gaussian
            phi_der[i,j]=-2*exp(-r/c)/c + ((-2*xi[i,var_seq] + 2*x[j,var_seq])^2)*exp(-r/c)/(c^2)

        end
    end
    return phi_der
end

# phi[i,j]=exp(-dr^2/c^2)*(-2*dr/c)
# phi(i,j) = exp(-r^2)/2 + (r*pi^(1/2)*erf(r))/2;
# phi[i,j] = 2*(1./(1+exp.(-r)))-1;
# phi[i,j] = (-2*r/c[i,j])*exp(-r^2/c[i,j])
# phi[i,j]=tanh.(r/c[i,j])
# phi_der[i,j] = -(r^2)*(2*(xi[i,var_seq]-x[j,var_seq]))
# phi_der[i,j] = -2*exp(-r/c)/c + ((-2*xi[i,var_seq] + 2*x[j,var_seq])^2)*exp(-r/c)/(c^2)
# phi_der[i,j] = exp(-r/c^2)


#=
Compute the fixed point of a given operator T, starting from
specified initial condition v.

@author : Spencer Lyon <spencer.lyon@nyu.edu>

@date: 2014-07-05

References
----------

https://lectures.quantecon.org/jl/optgrowth.html
=#

doc"""
Repeatedly apply a function to search for a fixed point

Approximates ``T^∞ v``, where ``T`` is an operator (function) and ``v`` is an initial
guess for the fixed point. Will terminate either when `T^{k+1}(v) - T^k v <
err_tol` or `max_iter` iterations has been exceeded.

Provided that ``T`` is a contraction mapping or similar,  the return value will
be an approximation to the fixed point of ``T``.

##### Arguments

* `T`: A function representing the operator ``T``
* `v::TV`: The initial condition. An object of type ``TV``
* `;err_tol(1e-3)`: Stopping tolerance for iterations
* `;max_iter(50)`: Maximum number of iterations
* `;verbose(2)`: Level of feedback (0 for no output, 1 for warnings only, 2
        for warning and convergence messages during iteration)
* `;print_skip(10)` : if `verbose == 2`, how many iterations to apply between
        print messages

##### Returns
---

* '::TV': The fixed point of the operator ``T``. Has type ``TV``

##### Example

```julia
using QuantEcon
T(x, μ) = 4.0 * μ * x * (1.0 - x)
x_star = compute_fixed_point(x->T(x, 0.3), 0.4)  # (4μ - 1)/(4μ)
```

"""
function compute_fixed_point(T::Function,
                            v::TV;
                            err_tol=1e-4,
                            max_iter=100,
                            verbose=2,
                            print_skip=10) where TV

    if !(verbose in (0, 1, 2))
        throw(ArgumentError("verbose should be 0, 1 or 2"))
    end

    iterate = 0
    err = err_tol + 1
    while iterate < max_iter && err > err_tol
        new_v = T(v)::TV
        iterate += 1
        err = Base.maximum(abs, new_v - v)
        if verbose == 2
            if iterate % print_skip == 0
                println("Compute iterate $iterate with error $err")
            end
        end
        v = new_v
    end

    if verbose >= 1
        if err > err_tol
            warn("max_iter attained in compute_fixed_point")
        elseif verbose == 2
            println("Converged in $iterate steps")
        end
    end

    return v
end


# # Five dim sys

# ## Model

module five_dim_sys #jl

using ReachabilityAnalysis

## system matrix
D = [-1.0 -4.0  0.0  0.0  0.0;
      4.0 -1.0  0.0  0.0  0.0;
      0.0  0.0 -3.0  1.0  0.0;
      0.0  0.0 -1.0 -3.0  0.0;
      0.0  0.0  0.0  0.0 -2.0]
P = [ 0.6 -0.1  0.1  0.7 -0.2;
     -0.5  0.7 -0.1 -0.8  0.0;
      0.9 -0.5  0.3 -0.6  0.1;
      0.5 -0.7  0.5  0.6  0.3;
      0.8  0.7  0.6 -0.3  0.2]
A = P * D * inv(P)

## state domain
X = Universe(5)

## input domain
U = Ball2(zeros(5), 0.01)

function model(X0)
    S = @system(x' = Ax + u, x ∈ X, u ∈ U)
    return IVP(S, X0)
end

end #jl

# ## References


@testset "Field Element Operations" begin
    @testset "Addition" begin
        a = ECC.FieldElement(2, 31)
        b = ECC.FieldElement(15, 31)
        @test a+b == ECC.FieldElement(17, 31)
        a = ECC.FieldElement(17, 31)
        b = ECC.FieldElement(21, 31)
        @test a+b == ECC.FieldElement(7, 31)
    end
    @testset "Substraction" begin
        a = ECC.FieldElement(29, 31)
        b = ECC.FieldElement(4, 31)
        @test a-b == ECC.FieldElement(25, 31)
        a = ECC.FieldElement(15, 31)
        b = ECC.FieldElement(30, 31)
        @test a-b == ECC.FieldElement(16, 31)
    end
    @testset "Multiplication" begin
        a = ECC.FieldElement(24, 31)
        b = ECC.FieldElement(19, 31)
        @test a*b == ECC.FieldElement(22, 31)
    end
    @testset "Power" begin
        a = ECC.FieldElement(17, 31)
        @test a^3 == ECC.FieldElement(15, 31)
        a = ECC.FieldElement(5, 31)
        b = ECC.FieldElement(18, 31)
        @test a^5 * b == ECC.FieldElement(16, 31)
    end
    @testset "Division" begin
        a = ECC.FieldElement(3, 31)
        b = ECC.FieldElement(24, 31)
        @test a/b == ECC.FieldElement(4, 31)
        a = ECC.FieldElement(17, 31)
        @test a^-3 == ECC.FieldElement(29, 31)
        a = ECC.FieldElement(4, 31)
        b = ECC.FieldElement(11, 31)
        @test a^-4*b == ECC.FieldElement(13, 31)
    end
end


@inline function l2_norm(z::AbstractVector)
    return sqrt(sum(abs2, z))
end

@inline function l2_distance(x::AbstractVector, y::AbstractVector)
    z = x - y
    return l2_norm(z)
end


export problem2

"""
  problem2(N=4e6)

# Problem 2
## Even Fibonacci numbers

By considering the terms in the Fibonacci sequence whose values do not exceed four million, find the sum of the even-valued terms.
"""
function problem2(N=4e6)
  n = 0
  f = 0
  s = 0

  while f < N
    n += 3
    s += f
    f = fib(n)
  end
  return s
end

function fib(n::Int)
  n < 0 && return 0
  n < 2 && return n
  a = 1
  b = 0
  c = 0

  while n > 2
    n -= 1
    c = a
    a += b
    b = c
  end

  return a+b
end


function calc_phi(x::Array{Float64,2},xi::Array{Float64,2},n::Int64,nn::Int64,c::Float64,vars::Int64)
  phi=zeros(nn,n)
  for i=1:nn
      for j=1:n
          # 2 r^4
          dr=0
          for k=1:vars
            dr = dr + (xi[i,k] - x[j,k])^2
          end
          # r = (dr)
          # phi[i,j] = -(dr^4)/4/c

          # 1 Gaussian
          # phi[i,j] = exp(-sum((xi[i,:] - x[j,:]).^2)/c)
          phi[i,j] = exp(-dr/c)
      end
      # if i in 1:100:nn println(Dates.format(now(), "HH:MM:SS "),i," of ",nn) end
  end

  # xi_all=Array{Float64,3}(undef, nn,1,vars)
  # xi_all[:,1,:]=xi
  # # println(size(xi_all))
  # xi_all=repeat(xi_all,1,n,1)
  # # println(size(xi_all))

  # x_all=Array{Float64,3}(undef, n,1,vars)
  # x_all[:,1,:]=x
  # # println(size(x_all))
  # x_all=repeat(x_all,1,nn,1)
  # # println(size(x_all))
  # x_all = permutedims(x_all, [2, 1, 3])
  # # println(size(x_all))

  # # @time m1=sum((xi_all - x_all).^2,dims=3)
  # # # println(size(m1))
  # phi2=exp.(-sum((xi_all - x_all).^2,dims=3)/c)[:,:,1]
  # # println(size(phi2))
  # # println(mean(abs.(phi-phi2)))

  return phi
end

# phi[i,j] = exp(-sqrt(dr)/c)
# phi[i,j] = exp(-r^2)/2 + (r*pi^(1/2)*erf(r))/2;
# phi[i,j] = 2*(1./(1+exp.(-r)))-1;
# phi[i,j] = exp(-r^2/c[i,j])
# phi[i,j] = exp(-dr/c)
# phi[i,j]=tanh.(r^2/c[i,j])
# phi[i,j] = -(r^3)/3
# phi[i,j] = exp(-r/c)
# phi[i,j] =(c^2*exp(-r/c^2))/2 + (c*sqrt(pi)*erf(sqrt(r)/c)*sqrt(r))/2;
# phi[i,j] = -sum((xi[i,:] - x[j,:]).^2)


function minimize_molzahn(cliques::AbstractVector, cliquetree::AbstractVector)
    deltas = [molzahn_heuristic(cliques, edge) for edge in cliquetree]
    index = argmin(deltas)
    return cliquetree[index]
end

function molzahn_heuristic(cliques::AbstractVector, edge)
    size_intersect = length(intersect(cliques[edge[1]], cliques[edge[2]]))
    size_clique1 = length(cliques[edge[1]])
    size_clique2 = length(cliques[edge[2]])
    size_fusion = size_clique1 + size_clique2 - size_intersect
    delta = size_fusion * (2 * size_fusion + 1) -
            size_clique1 * (2 * size_clique1 + 1) -
            size_clique2 * (2 * size_clique2 + 1) -
            size_intersect * (2 * size_intersect + 1)
    return delta
end

function select(cliques::AbstractVector, cliquetree::AbstractVector;
                selection=minimize_molzahn)
    return selection(cliques, cliquetree)
end

function merge_molzahn(cliques::AbstractVector, cliquetree::AbstractVector;
                       treshold::Union{Int,Function}=treshold_percent(cliques, 0.1),
                       selection::Function=minimize_molzahn, kwargs...)
    edges = Set()
    if typeof(treshold) <: Int
        treshold_value = treshold
        treshold = (x;kwargs...) -> length(x) >= treshold_value
    end
    while treshold(cliques; kwargs...)
        edge = selection(cliques, cliquetree)
        edges = vcat(edges..., get_added_edges(cliques[edge[1]], cliques[edge[2]])...)
        cliques, cliquetree = merge_cliques(cliques, cliquetree, edge)
    end
    return cliques, cliquetree, edges
end


using Distributions
using Plots

population = sort(convert(Array{Int64}, rand(Bernoulli(.7), 100)))
population_over_time = []
n_iter = 100
gr()

function single_reproduction(population::AbstractArray)
    n = length(population)
    pₐ = sum(population) / length(population)
    fₐ = Bernoulli(pₐ)
    sort(convert(Array{Int64}, rand(fₐ, n)))
end

function multiple_reproductions(seed_array::AbstractArray, population_over_time::AbstractArray, n_iterations::Int64)
    seed_array = single_reproduction(seed_array)
    push!(population_over_time, seed_array)
    if n_iterations == 1
        hcat(population_over_time...)
    else
        multiple_reproductions(seed_array, population_over_time, n_iterations-1)
    end
end

function plot_many_samples(n_samples::Int64, n_iterations::Int64, Nₑ::Int64, pₐ::Float64)
    p = false
    for i in 1:n_samples
        population = sort(convert(Array{Int64}, rand(Bernoulli(pₐ), Nₑ)))
        population_at_time = multiple_reproductions(population, [], n_iterations)
        if p
            display(plot!(dropdims(sum(population_at_time, dims=1), dims=1), legend=false))
        else
            display(plot(dropdims(sum(population_at_time, dims=1), dims=1), xlims=(0, n_iterations), ylims=(0, Nₑ)))
            p = true
        end
    end
end

plot_many_samples(10, 10000, 10000, .5)
# j = multiple_reproductions(population, population_over_time, n_iter)
# plot(dropdims(sum(j, dims=1), dims=1))
# j = multiple_reproductions(population, [], n_iter)
# plot!(dropdims(sum(j, dims=1), dims=1))
# j = multiple_reproductions(population, [], n_iter)
# plot!(dropdims(sum(j, dims=1), dims=1))


# SVD Algorithms
# --------------

# Truncated SVD using implicitly restarted Lonczos iterations.
function tsvd(A::Matrix{T}, k::Integer) where T
    m, n = size(A)
    F, = svds(A, nsv=k)
    U, Σ, V = F.U, F.S, F.V
    @assert size(U, 2) == length(Σ) == size(V, 2) == k
    return U, Σ, V
end

# H. Li, G. C. Linderman, et al. "Algorithm 971: An Implementation of a
# Randomized Algorithm for Principal Component Analysis", ACM Transactions on
# Mathematical Software (TOMS), 2017
# DOI: https://doi.org/10.1145/3004053
function rsvd(A::Matrix{T}, k::Integer; its=3, l=k+5) where T
    m, n = size(A)
    l = min(l, m, n)
    @assert 0 < k ≤ l ≤ min(m, n)
    Q::Matrix{T} = rand(eltype(T), n, l) .- T(0.5)
    Y = A*Q
    F = lu!(Y)
    for i in 1:its
        Y = A'F.L
        F = lu!(Y)
        Y = A*F.L
        if i < its
            F = lu!(Y)
        else
            F = qr!(Y)
        end
    end
    Q = Matrix(F.Q)
    B = Q'A
    W, Σ, V = svd(B)
    U = Q*W
    return U[:,1:k], Σ[1:k], V[:,1:k]
end


export joLagrangeInterp1D

# 1D Cubic Lagrange Interpolation
#
# Parameters:
#   xin  - input grid
#   xout - output grid
#   T    - vector data type
#
# Note: the interval [minimum(xout),maximum(xout)] must be contained
# in the interval [minimum(xin),maximum(xin)]

function joLagrangeInterp1D(xin,xout,T)
    ik = find((xout .>= xin[1]) .& (xout .<= xin[end]))
    nout = length(xout)
    nin = length(xin)
    nk = length(ik)
    nk > 0 || error("The set generated by xout must be contained within the interval [xin[1],xin[end]]")
    I = zeros(Int64,4*nk)
    J = zeros(Int64,4*nk)
    S = zeros(T,4*nk)
    a,b,c,d = 1,2,3,4
    l=1
    for i = 1:nk
        k = ik[i];
        if xout[k]<xin[b]
            while (xout[k]<xin[b])&&(b-1>1)
                b=b-1;
            end
            a=b-1;c=b+1;d=c+1;
        elseif xout[k]>xin[c]
            while (xout[k]>xin[c])&&(c+1<nin)
                c=c+1;
            end
            a=c-2;b=c-1;d=c+1;
        end
        I[l:l+3] = k;
        J[l]   = a;
        S[l]   = ((xout[k]-xin[b])*(xout[k]-xin[c])*(xout[k]-xin[d]))/((xin[a]-xin[b])*(xin[a]-xin[c])*(xin[a]-xin[d]));

        J[l+1] = b;
        S[l+1] = ((xout[k]-xin[a])*(xout[k]-xin[c])*(xout[k]-xin[d]))/((xin[b]-xin[a])*(xin[b]-xin[c])*(xin[b]-xin[d]));

        J[l+2] = c;
        S[l+2] = ((xout[k]-xin[b])*(xout[k]-xin[a])*(xout[k]-xin[d]))/((xin[c]-xin[b])*(xin[c]-xin[a])*(xin[c]-xin[d]));

        J[l+3] = d;
        S[l+3] = ((xout[k]-xin[b])*(xout[k]-xin[c])*(xout[k]-xin[a]))/((xin[d]-xin[b])*(xin[d]-xin[c])*(xin[d]-xin[a]));

        l += 4;
    end

    return joMatrix(sparse(I[1:l-1],J[1:l-1],S[1:l-1],nout,nin),DDT=T,name="joLagrangeInterp1D")
end


using GalerkinSparseGrids
using Base.Test
using Cubature
using ODE

#--------------------------------------
# Testing PDE solvers
#--------------------------------------

# The wave equation, testing conservation of energy

# In the position basis:

import GalerkinSparseGrids.energy_func_1D
import GalerkinSparseGrids.energy_func

print("Testing wave equation solver 1-D position DG basis... ")

k = 4
level = 4
f0 = x->sin(2*pi*x[1])
v0 = x->2*pi*cos(2*pi*x[1])
pos_soln = wave_evolve_1D(k, level, f0, v0, 0, 1; basis="pos", order="45")
energy_soln = energy_func_1D(k, level, pos_soln; basis="pos")
for i in 1:length(energy_soln[2])
    @test sqrt(energy_soln[2][i]) ≈ 2*pi atol=1.0e-7
end

println("Test Passed.")

# In the hierarchical basis:

print("Testing wave equation solver 1-D hierarchical DG basis... ")

hier_soln  = wave_evolve_1D(k, level, f0, v0, 0, 1; basis="hier", order="45")
energy_soln = energy_func_1D(k, level, hier_soln; basis="hier")
for i in 1:length(energy_soln[2])
    @test sqrt(energy_soln[2][i]) ≈ 2*pi atol=1.0e-7
end

hier_soln  = wave_evolve(1, k, level, f0, v0, 0, 1; order="78")
energy_soln = energy_func(1, k, level, hier_soln)
for i in 1:length(energy_soln[2])
    @test sqrt(energy_soln[2][i]) ≈ 2*pi atol=1.0e-7
end

println("Test Passed.")

# 2D Sparse case:

print("Testing wave equation solver on 2-D sparse DG basis... ")

D = 2
k_used = 3
n_used = 5
f0 = x->sin(2*pi*x[1])*sin(2*pi*x[2])
v0 = x->0
sparse_soln = wave_evolve(D, k_used, n_used, f0, v0, 0, 1; order="78", scheme="sparse");
senergy = energy_func(D, k_used, n_used, sparse_soln)
@test senergy[2][1]-senergy[2][end] > 0
@test abs(senergy[2][1]-senergy[2][end]) < 1.0e-8
for i in 1:length(senergy[1])
    @test sqrt(senergy[2][i]) ≈ sqrt(2)*pi atol=1.0e-4
end

sparse_soln = wave_evolve(D, k_used, n_used, f0, v0, 0,1; order="45", scheme="sparse");
senergy = energy_func(D, k_used, n_used, sparse_soln)
@test senergy[2][1] > senergy[2][end]
@test senergy[2][1] ≈ senergy[2][end] atol=1.0e-8
for i in 1:length(senergy[1])
    @test sqrt(senergy[2][i]) ≈ sqrt(2)*pi atol=1.0e-4
end

println("Test Passed.")

print("Testing traveling wave example in 2-D... ")

m = [1, 2]
truesoln = x -> cos(2*pi*(vecdot(m,x) - sqrt(vecdot(m,m))*0.54))

D = length(m)
k_used = 3
n_used = 6

soln = traveling_wave_solver(k_used, n_used, m, 0, 0.54; order="45")
dict = V2D(D, k_used, n_used, soln[2][end]; scheme="sparse")
@test mcerr(x->reconstruct_DG(dict, [x...]), truesoln, D) < 0.05

soln = traveling_wave_solver(k_used, n_used, m, 0, 0.54; order="78")
dict = V2D(D, k_used, n_used, soln[2][end]; scheme="sparse")
@test mcerr(x->reconstruct_DG(dict, [x...]), truesoln, D) < 0.05

println("Test passed.")


# Pentagonal numbers are generated by the formula, Pn=n(3n−1)/2. The first ten
# pentagonal numbers are:
#
# 1, 5, 12, 22, 35, 51, 70, 92, 117, 145, ...
#
# It can be seen that P4 + P7 = 22 + 70 = 92 = P8. However, their difference,
# 70 − 22 = 48, is not pentagonal.
#
# Find the pair of pentagonal numbers, Pj and Pk, for which their sum and
# difference are pentagonal and D = |Pk − Pj| is minimised; what is the value
# of D?

using ProjectEulerSolutions

# Calculate pentagonal numbers, store in set, then check for whether
# differences and additions are also in the set.  Could probably be faster if
# searchsorted was used and the minimum bound for j was a function of i.
function p044solution(n::Integer=10)::Integer

    vals = collect(1:n)
    pentagonals = fld.(vals .* (3 .* vals .- 1), 2)
    pentagonalset = Set(pentagonals)

    for i in 4:n
        for j in 3:(i-1)
            if pentagonals[i] - pentagonals[j] in pentagonalset && pentagonals[i] + pentagonals[j] in pentagonalset
                return pentagonals[i] -  pentagonals[j]
            end
        end
    end

    return -1
end

p044 = Problems.Problem(p044solution)

Problems.benchmark(p044, 3000)


function Base.binomial{T<:FloatingPoint}(n::T, k::T)
    exp(lfact(n) - lfact(n - k) - lfact(k))
end

function Base.factorial{T<:FloatingPoint}(n::T, k::T)
    exp(lfact(n) - lfact(k))
end

⊞{T<:Real}(n::T, k::T) = binomial(n, k)
⊠{T<:Real}(n::T, k::T) = factorial(n, n-k)


function rsaencode(clearmsg::AbstractString, nmod::Integer, expub::Integer)
    bytes = parse(BigInt, "0x" * bytes2hex(collect(UInt8, clearmsg)))
    return powermod(bytes, expub, nmod)
end

function rsadecode(cryptmsg::Integer, nmod::Integer, dsecr::Integer)
    decoded = powermod(encoded, dsecr, nmod)
    return join(Char.(hex2bytes(hex(decoded))))
end

msg = "Rosetta Code."
nmod = big"9516311845790656153499716760847001433441357"
expub = 65537
dsecr = big"5617843187844953170308463622230283376298685"

encoded = rsaencode(msg, nmod, expub)
decoded = rsadecode(encoded, nmod, dsecr)
println("\n# $msg\n -> ENCODED: $encoded\n -> DECODED: $decoded")


export Toeplitz, embed
"""
[`Toeplitz` matrix](http://en.wikipedia.org/wiki/Toeplitz_matrix)

Input is a vector of odd length.

```julia
julia> Toeplitz(collect(-4:4))
5x5 Toeplitz{Int64}:
 0  -1  -2  -3  -4
 1   0  -1  -2  -3
 2   1   0  -1  -2
 3   2   1   0  -1
 4   3   2   1   0
```
"""
struct Toeplitz{T} <: AbstractArray{T, 2}
	c :: Vector{T}
end

getindex(T::Toeplitz, i::Int, j::Int) = T.c[i-j+div(length(T.c)+1,2)]
isassigned(T::Toeplitz, i::Int, j::Int) = isassigned(T.c,i-j+div(length(T.c)+1,2))

size(T::Toeplitz, r::Int) = (r==1 || r==2) ? 1 + div(length(T.c),2) :
    throw(ArgumentError("Invalid dimension $r"))
size(T::Toeplitz) = size(T,1), size(T,2)

# Fast matrix x vector multiplication via embedding Toeplitz() into Circulant()
function *(A::Toeplitz{T},x::Vector{T}) where T
    n=length(A.c)
    k=div(n+1,2)
    C=Circulant([A.c[k:n];A.c[1:k-1]])
    (C*[x;zeros(T,k-1)])[1:k]
end

function mul!(y::StridedVector{T},A::Toeplitz{T},x::StridedVector{T}) where T
    n=length(A.c)
    k=div(n+1,2)
    C=Circulant([A.c[k:n];A.c[1:k-1]])
    xx=[x;zeros(T,k-1)]
    yy=mul!(similar(xx),C,xx)
    copyto!(y, 1, yy, 1, length(y))
    return y
end

function Matrix(To::Toeplitz{T}) where T
	n=size(To, 1)
	M=Array{T}(undef,n,n)
	for i=1:n
		M[i:n,i] = To.c[n:2n-i]
		M[1:i-1,i] = To.c[n-i+1:n-1]
	end
	M
end

function embed(To::Toeplitz{T}) where T
    return Circulant([To.c[div(end+1,2):end];To.c[1:div(end-1,2)]])
end


# In this example, we create piece-wise linear functions that has a single line segment that has a small slope. The interpretation of using such a function as a transport map is to move mass from a large coverage to a concentrated coverage. The intervals in the domain and range that correspond to this segment is the *focus interval*, and is controlled by `range_percentage` and `domain_percentage`. We then fit a logistic-logit function to each of the piece-wise linear functions. The fitted parameters can then be interpolated so that we get a logistic-logit function for any given focus interval.

include("../src/MonotoneMaps.jl")
import .MonotoneMaps
#import MonotoneMaps

import PyPlot # works better in REPL.

#using Plots; pyplot() # easier to work with in .jmd / Weave.jl.

import Random
using LinearAlgebra

PyPlot.close("all")
fig_num = 1

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

Random.seed!(25)

# Get the set of piece-wise linear functions.
p_lb = 0.0
p_ub = 1.0
range_percentage = 0.1
N_itp_samples = 10
domain_percentage = 0.7
infos, zs, p_range = MonotoneMaps.getendomorphismpiecewiselinear(p_lb, p_ub, range_percentage; N_itp_samples = N_itp_samples, domain_percentage = domain_percentage)

# construct the set of piece-wise functions.
fs = collect( xx->MonotoneMaps.evalpiecewise2Dlinearfunc(xx, infos[i]) for i = 1:length(infos) )

# visualize.
display_t = LinRange(0.0, 1.0, 5000)

PyPlot.figure(fig_num)
fig_num += 1

for i = length(fs):length(fs)
    PyPlot.plot(display_t, fs[i].(display_t), label = "f[$(i)]")
end

PyPlot.legend()
PyPlot.xlabel("x")
PyPlot.ylabel("y")
PyPlot.title("Piece-wise linear functions, each with a single focus subinterval")

# display_mat = [ fs[c](display_t[r]) for r = 1:length(display_t), c = 1:length(fs) ]
# display_labels = ["$(i)-th function" for j = 1:1, i = 1:length(fs)]
#
# default(titlefont = (20, "times"), legendfontsize = 15, guidefont = (18, :black),
# tickfont = (12, :black))
#
# plot_handle = plot(display_t, display_mat,
# label = display_labels,
# title = "Piece-wise linear functions, each with a single focus subinterval",
# xlabel = "x",
# ylabel = "y",
# linewidth = 2, legend = :outerright, aspect_ratio=:equal, size = (800,800))
# display(plot_handle)

# fit the compact sigmoids (composite function of applying probit then logistic functions).
p0 = [0.5; 0.0]
p_lb = [0.1; -5.0]
p_ub = [0.6; 5.0]
optim_algorithm = :LN_BOBYQA # a local derivative-free algorithm. For other algorithms in NLopt, see https://nlopt.readthedocs.io/en/latest/NLopt_Algorithms/
costfuncs, minxs, rets = MonotoneMaps.getcompactsigmoidparameters(infos; p0 = p0, p_lb = p_lb, p_ub = p_ub, optim_algorithm = optim_algorithm)
qs = collect( tt->MonotoneMaps.evalcompositelogisticprobit(tt, minxs[i][1], minxs[i][2]) for i = 1:length(minxs) )

## visualize oracle vs. fitted sigmoids.
PyPlot.figure(fig_num)
fig_num += 1

for l = 1:length(qs)
    PyPlot.plot(display_t, fs[l].(display_t), label = "f[$(l)]")
    PyPlot.plot(display_t, qs[l].(display_t), "--", label = "q[$(l)]")
end

PyPlot.legend()
PyPlot.xlabel("x")
PyPlot.ylabel("")
PyPlot.title("target vs fit")

# display_mat = [ fs[c](display_t[r]) for r = 1:length(display_t), c = 1:length(fs) ]
# display_labels = ["$(i)-th piece-wise linear" for j = 1:1, i = 1:length(fs)]
#
# display_mat2 = [ qs[c](display_t[r]) for r = 1:length(display_t), c = 1:length(fs) ]
# display_labels2 = ["$(i)-th logistic-logit" for j = 1:1, i = 1:length(qs)]
#
# plot_handle2 = plot(display_t, display_mat,
# label = display_labels,
# title = "Piece-wise linear and logistic-logit functions",
# xlabel = "x",
# ylabel = "y",
# linewidth = 2, legend = :outerright, aspect_ratio=:equal, size = (800,800))
#
# plot!(display_t, display_mat2,
# label = display_labels2,
# style = :dash)
#
# display(plot_handle2)

# print fit cost.
for l = 1:length(costfuncs)
    println("cost of p_star $(l) is ", costfuncs[l](minxs[l]))
end


# Parts of this code were taken / derived from Graphs.jl. See LICENSE for
# licensing details.

"""
    struct FloydWarshallState{T, U}

An [`AbstractPathState`](@ref) designed for Floyd-Warshall shortest-paths calculations.
"""
struct FloydWarshallState{T,U<:Integer} <: AbstractPathState
    dists::Matrix{T}
    parents::Matrix{U}
end

@doc_str """
floyd_warshall_shortest_paths(g, distmx=weights(g))
Use the [Floyd-Warshall algorithm](http://en.wikipedia.org/wiki/Floyd–Warshall_algorithm)
to compute the shortest paths between all pairs of vertices in graph `g` using an
optional distance matrix `distmx`. Return a [`FloydWarshallState`](@ref) with relevant
traversal information.

### Performance
Space complexity is on the order of ``\\mathcal{O}(|V|^2)``.
"""
function floyd_warshall_shortest_paths(
    g::AbstractGraph,
    distmx::AbstractMatrix{T} = weights(g)
) where T
    U = eltype(g)
    n_v = nv(g)
    dists = fill(typemax(T), (Int(n_v), Int(n_v)))
    parents = zeros(U, (Int(n_v), Int(n_v)))

    # fws = FloydWarshallState(Matrix{T}(), Matrix{Int}())
    for v in 1:n_v
        dists[v, v] = zero(T)
    end
    undirected = !is_directed(g)
    for e in edges(g)
        u = src(e)
        v = dst(e)

        d = distmx[u, v]

        dists[u, v] = min(d, dists[u, v])
        parents[u, v] = u
        if undirected
            dists[v, u] = min(d, dists[v, u])
            parents[v, u] = v
        end
    end
    for w in vertices(g), u in vertices(g), v in vertices(g)
        if dists[u, w] == typemax(T) || dists[w, v] == typemax(T)
            ans = typemax(T)
        else
            ans = dists[u, w] + dists[w, v]
        end
        if dists[u, v] > ans
            dists[u, v] = dists[u, w] + dists[w, v]
            parents[u, v] = parents[w, v]
        end
    end
    fws = FloydWarshallState(dists, parents)
    return fws
end

function enumerate_paths(s::FloydWarshallState{T,U}, v::Integer) where T where U<:Integer
    pathinfo = s.parents[v, :]
    paths = Vector{Vector{U}}()
    for i in 1:length(pathinfo)
        if (i == v) || (s.dists[v, i] == typemax(T))
            push!(paths, Vector{U}())
        else
            path = Vector{U}()
            currpathindex = i
            while currpathindex != 0
                push!(path, currpathindex)
                currpathindex = pathinfo[currpathindex]
            end
            push!(paths, reverse(path))
        end
    end
    return paths
end

enumerate_paths(s::FloydWarshallState) = [enumerate_paths(s, v) for v in 1:size(s.parents, 1)]
enumerate_paths(st::FloydWarshallState, s::Integer, d::Integer) = enumerate_paths(st, s)[d]