"Z-normalize the input array"
function znorm(x::AbstractVector)
    x = x .- mean(x)
    x ./= std(x, mean=0, corrected=false)
end

function znorm!(x::AbstractVector)
    x .= x .- mean(x)
    x ./= std(x, mean=0, corrected=false)
end

"""
    meanstd(x::AbstractArray)

Return mean and std of `x`. Is faster but less accurate than built-in functions.
"""
function meanstd(x::AbstractArray{T}) where T
    s = ss = zero(float(T))
    n = length(x)
    @avx for i in eachindex(x)
        s  += x[i]
        ss += x[i]^2
    end
    m   = s/n
    sig = √(ss/n - m^2)
    m, sig
end

import Base: @propagate_inbounds, @boundscheck, getindex
import LinearAlgebra: normalize

abstract type AbstractNormalizer{T,N} <: AbstractArray{T,N} end

advance!(x) = 0 # Catches everything that's not a normalizer

setup_normalizer(n::Val{Nothing}, q, y) = n, q, y

normalize(::Val{Nothing}, q) = q

normalize(T::Type, q) = normalize(Val(T), q)

abstract type AbstractZNormalizer{T,N} <: AbstractNormalizer{T,N} end

"""
    ZNormalizer{T} <: AbstractNormalizer{T, 1}

Utility object that normalizes an input array on the fly.
Works like a vector, index into window with `z[i]`, index into normalized window with `z[!,i]`.

# Arguments:
- `x::Vector{T}`: The vecetor to operate on
- `n::Int`: The lenght of a window
- `μ::T`: mean over window
- `σ::T`: std over window
- `s::T`: sum over window
- `ss::T`: sum^2 over window
- `i::Int`: the current index
- `buffer::Vector{T}`: stores calculated normalized values, do not access this, it might not be complete
- `bufi::Int`: index of last normalized value
"""
mutable struct ZNormalizer{T} <: AbstractZNormalizer{T,1}
    x::Vector{T}
    n::Int
    μ::T
    σ::T
    s::T
    ss::T
    i::Int
    buffer::Vector{T}
    bufi::Int
end

function ZNormalizer(x::AbstractVector{T}, n) where T
    @assert length(x) >= n
    s = ss = zero(T)
    @avx for i in 1:n
        s += x[i]
        ss += x[i]^2
    end
    μ = s/n
    σ = sqrt(ss/n - μ^2)
    buffer = similar(x, n)
    ZNormalizer(x, n, μ, σ, s, ss, 0, buffer, 0)
end

function normalize(::Val{ZNormalizer}, q::AbstractVector)
    q = q .- mean(q)
    q ./= std(q, corrected=false, mean=0)
end

setup_normalizer(z::Val{ZNormalizer}, q::AbstractVector, y::AbstractVector) = z, normalize(z, q), ZNormalizer(y, length(q))

@propagate_inbounds function advance!(z::ZNormalizer{T}) where T

    if z.i == 0
        return z.i = 1
    end
    @boundscheck if z.i + z.n > length(z.x)
        return z.i += 1
    end
    z.bufi = 0

    # Remove old point
    x = z.x[z.i]
    z.s -= x
    z.ss -= x^2

    # Add new point
    x = z.x[z.i+z.n]
    z.s += x
    z.ss += x^2
    z.μ = z.s/z.n
    z.σ = sqrt(z.ss/z.n - z.μ^2)
    z.i += 1
end

@inline @propagate_inbounds function getindex(z::ZNormalizer, i)
    @boundscheck 1 <= i <= z.n || throw(BoundsError(z,i))
    xi = i+z.i-1
    @boundscheck xi <= length(z.x) || throw(BoundsError(z,i))
    z.x[xi]
end

@inline @propagate_inbounds function getindex(z::ZNormalizer, ::typeof(!), i::Int, inorder = i == z.bufi + 1)
    y = (z[i]-z.μ) / z.σ
    if inorder
        z.bufi = i
        z.buffer[i] = y
    end
    y
end

@inline @propagate_inbounds function getindex(z::ZNormalizer, ::typeof(!), i::AbstractRange)
    @boundscheck (i[1] == z.i && length(i) == z.n) || throw(ArgumentError("ZNormalizers can only be indexed by ranges corresponding to their current state. Got range $i but state was $(z.i) corresponding to range $(z.i):$(z.i+z.n-1)"))
    z
end

Statistics.mean(z::AbstractZNormalizer) = z.μ
Statistics.std(z::AbstractZNormalizer) = z.σ

SlidingDistancesBase.lastlength(z::AbstractZNormalizer) = z.n
Base.length(z::AbstractZNormalizer) = z.n
Base.size(z::ZNormalizer) = (z.n,)
actuallastlength(x::AbstractZNormalizer) = lastlength(x.x)

# Multi dim ==================================================================================
mutable struct IsoZNormalizer{T} <: AbstractZNormalizer{T,2}
    x::Array{T,2}
    n::Int
    μ::Array{T,1}
    σ::Array{T,1}
    s::Array{T,1}
    ss::Array{T,1}
    i::Int
    buffer::Array{T,2}
    bufi::Int
end

function IsoZNormalizer(x::AbstractArray{T,2}, n) where T
    @assert length(x) >= n
    s  = zeros(T, size(x,1))
    ss = zeros(T, size(x,1))
    @inbounds @simd for i in 1:n
        s  .+= x[!, i]
        ss .+= x[!, i].^2
    end
    μ = s./n
    σ = sqrt.(ss./n .- μ.^2)
    buffer = similar(x, size(x,1), n)
    IsoZNormalizer(x, n, μ, σ, s, ss, 0, buffer, 0)
end

function normalize(::Val{IsoZNormalizer}, q::AbstractMatrix)
    q = q .- mean(q, dims=2) # TODO: this will cause a ton of allocations
    q ./= std(q, dims=2, corrected=false)
end

setup_normalizer(z::Val{IsoZNormalizer}, q, y) = z, normalize(z, q), IsoZNormalizer(y, lastlength(q))
setup_normalizer(z::Val{ZNormalizer}, q::AbstractMatrix, y::AbstractMatrix) = setup_normalizer(Val(IsoZNormalizer), q, y) # Only need to expose ZNormalizer to the user

@propagate_inbounds function advance!(z::IsoZNormalizer{T}) where T

    if z.i == 0
        return z.i = 1
    end
    @boundscheck if z.i + z.n > length(z.x)
        return z.i += 1
    end
    z.bufi = 0

    # Remove old point
    x = z.x[!, z.i]
    @avx z.s .-= x
    @avx z.ss .-= x.^2

    # Add new point
    x = z.x[!, z.i+z.n]
    @avx z.s .+= x
    @avx z.ss .+= x.^2
    @avx z.μ .= z.s./z.n
    @avx z.σ .= sqrt.(z.ss./z.n .- z.μ.^2)
    z.i += 1
end

@inline @propagate_inbounds function getindex(z::IsoZNormalizer, i::Union{Number, AbstractRange}, j)
    @boundscheck 1 <= j <= z.n || throw(BoundsError(z,j))
    @boundscheck 1 <= 1 <= size(z.x, 1) || throw(BoundsError(z,i))
    xj = j+z.i-1
    @boundscheck xj <= lastlength(z.x) || throw(BoundsError(z,j))
    z.x[i, xj]
end

@inline @propagate_inbounds function getindex(z::IsoZNormalizer, ::typeof(!), i, inorder = i == z.bufi + 1)
    j = inorder ? i : z.n
    xj = z.i + i - 1
    @avx for k = 1:size(z.x, 1)
        z.buffer[k, j] = (z.x[k, xj] - z.μ[k]) / z.σ[k]
    end
    if inorder
        z.bufi = i
    end
    z.buffer[!, j]
end

@inline @propagate_inbounds function getindex(z::IsoZNormalizer, ::typeof(!), i::AbstractRange)
    @boundscheck (i[1] == z.i && length(i) == z.n) || throw(ArgumentError("ZNormalizers can only be indexed by ranges corresponding to their current state. Got range $i but state was $(z.i) corresponding to range $(z.i):$(z.i+z.n-1)"))
    z
end

Base.Matrix(z::IsoZNormalizer) = z.x[:,z.i:z.i+z.n-1]

Base.length(z::IsoZNormalizer) = size(z.x,1) * z.n
Base.size(z::IsoZNormalizer) = (size(z.x,1), z.n)

for T in [ZNormalizer, IsoZNormalizer]
    @eval @inline @propagate_inbounds function normalize(::Val{$T}, z::$T)
        if z.bufi == z.n
            return z.buffer
        end
        for i = z.bufi+1:z.n
            z[!, i, true] # This populates the buffer
        end
        return z.buffer
    end
end


export Bloating

"""
    Bloating{N<:AbstractFloat, S<:LazySet{N}} <:LazySet{N}

Type that represents a uniform expansion of a convex set in a given norm (also
known as *bloating*).

### Fields

- `X` -- convex set
- `ε` -- (positive) bloating factor
- `p` -- ``p``-norm (``≥ 1``; default: ``2``)

### Notes

`Bloating(X, ε, p)` is equivalent to the Minkowski sum of `X` and a ball in the
`p`-norm of radius `ε` centered in the origin `O` (i.e., `X ⊕ Ballp(p, O, ε)`).
"""
struct Bloating{N<:AbstractFloat, S<:LazySet{N}} <:LazySet{N}
    X::S
    ε::N
    p::N

    function Bloating(X::S, ε::N, p::N=N(2)) where {N<:AbstractFloat,
                                                    S<:LazySet{N}}
        @assert ε > zero(N) "bloating requires a distance > 0, but $ε was given"
        @assert p >= one(N) "bloating requires a norm >= 1, but $p was given"

        return new{N, S}(X, ε, p)
    end
end

isoperationtype(::Type{<:Bloating}) = true

"""
    dim(B::Bloating)

Return the dimension of a bloated convex set.

### Input

- `B` -- bloated convex set

### Output

The ambient dimension of the bloated set.
"""
function dim(B::Bloating)
    return dim(B.X)
end

# helper function to compute the bloating ball
function _bloating_ball(B::Bloating{N}) where {N<:AbstractFloat}
    return Ballp(B.p, zeros(N, dim(B)), B.ε)
end

"""
    σ(d::AbstractVector{N}, B::Bloating{N}) where {N<:AbstractFloat}

Return the support vector of a bloated convex set in a given direction.

### Input

- `d` -- direction
- `B` -- bloated convex set

### Output

The support vector of the bloated set in the given direction.
"""
function σ(d::AbstractVector{N}, B::Bloating{N}) where {N<:AbstractFloat}
    @assert !iszero(d) "the support vector of the zero direction is undefined"

    return σ(d, B.X) + σ(d, _bloating_ball(B))
end

"""
    ρ(d::AbstractVector{N}, B::Bloating{N}) where {N<:AbstractFloat}

Return the support function of a bloated convex set in a given direction.

### Input

- `d` -- direction
- `B` -- bloated convex set

### Output

The support function of the bloated set in the given direction.
"""
function ρ(d::AbstractVector{N}, B::Bloating{N}) where {N<:AbstractFloat}
    @assert !iszero(d) "the support function of the zero direction is undefined"

    return ρ(d, B.X) + ρ(d, _bloating_ball(B))
end

"""
    isbounded(B::Bloating)

Determine whether a bloated convex set is bounded.

### Input

- `B` -- bloated convex set

### Output

`true` iff the wrapped set is bounded.
"""
function isbounded(B::Bloating)
    return isbounded(B.X)
end

"""
    isempty(B::Bloating)

Determine whether a bloated convex set is empty.

### Input

- `B` -- bloated convex set

### Output

`true` iff the wrapped set is empty.
"""
function isempty(B::Bloating)
    return isempty(B.X)
end

"""
    an_element(B::Bloating)

Return some element of a bloated convex set.

### Input

- `B` -- bloated convex set

### Output

An element in the bloated convex set.

### Algorithm

The implementation returns the result of `an_element` for the wrapped set.
"""
function an_element(B::Bloating)
    return an_element(B.X)
end


using Distributions, OnlineStats

function queue1(until::Real, burnin::Real,
                ad::Distribution, sd::Distribution)
    now, nextArrival, nextDeparture = 0.0, rand(ad), Inf
    queue, waits = Float64[], Mean()
    while now < until
        if nextArrival < nextDeparture
            now = nextArrival
            if isempty(queue)
                nextDeparture = nextArrival + rand(sd)
            end
            push!(queue, nextArrival)
            nextArrival += rand(ad)
        else
            now = nextDeparture
            insystem = nextDeparture - popfirst!(queue)
            burnin < now < until && fit!(waits, insystem)
            nextDeparture += isempty(queue) ? Inf : rand(sd)
        end
    end
    value(waits)
end

mm1_exact(ad::Exponential, sd::Exponential) = 1/(1/mean(sd)-1/mean(ad))

function bootCI(data, stat::Function, CI::Float64, reps::Integer)
    boot = [stat(rand(data, length(data))) for i in 1:reps]
    low, high = quantile(boot, [(1-CI)/2, (1+CI)/2])
    (value=stat(data), low=low, high=high)
end

Chapter04/06. Analyzing a queuing system/mm1.jl


#=
fibonacci:
- Julia version: 1.4.1
- Author: Baur Urazalinov
- Date: 05-05-2020
=#

function fib(n)
    if n == 0
        return 0
    end
    if n == 1
        return 1
    end
    return fib(n-1)+fib(n-2)
end


data = parse.(Int,permutedims(reduce(hcat, collect.(readlines(joinpath(@__DIR__, "./3.txt"))))))

function bitarr_to_int(arr)
  return sum(arr .* (2 .^ collect(length(arr)-1:-1:0)))
end

function gamma(arr)
  return BitArray(map((s) -> s >= size(arr)[1]/2,  sum(arr,dims=1)))
end

function epsilon(arr)
  return .!gamma(arr)
end

g = gamma(data)
e = epsilon(data)

# answer to part 1
bitarr_to_int(g') * bitarr_to_int(e')

function rating(data,rate_func,index=1)
  if size(data,1) == 1
    return bitarr_to_int(BitArray(data)')
  end
  rate = rate_func(data)
  filtered_data  = data[data[:,index].==rate[index],:]
  return rating(filtered_data, rate_func, index += 1)
end

o2 = rating(data, gamma)
co2 = rating(data, epsilon)

#answer to part 2
o2*co2


#####
##### other functions for computing Jacobians
#####

"""
    banded_function_evals(mat_fun::Fxn, U, Fargs...)

Computes block-banded matrix whose bands are entries of matrix-valued
function evals (e.g., a Jacobian). Returns SMatrix whose blocks correspond to function
components evaluated at values of U.

## Example:
```julia
julia> mat_fun(U) = [U[1] U[2]; U[2] U[1]]
julia> U = (randn(10),randn(10))
julia> banded_function_evals(mat_fun,U)
```
"""
function banded_function_evals(mat_fun::Fxn,U,Fargs...) where Fxn
    n = length(first(U))
    Nfields = length(U)
    A = SMatrix{Nfields,Nfields}([spzeros(n,n) for i = 1:Nfields, j = 1:Nfields])
    banded_function_evals!(A, mat_fun, U, Fargs...)
    return A
end

"""
    banded_function_evals!(A,mat_fun::Fxn, U, Fargs ...) where Fxn

Mutating version of [`banded_function_evals`](@ref).
"""
function banded_function_evals!(A,mat_fun::Fxn,U,Fargs...) where {Fxn}
    Nfields = size(A,2)
    num_pts = length(first(U))

    for i = 1:num_pts
        mat_i = mat_fun(getindex.(U,i),getindex.(Fargs,i)...)
        for n = 1:Nfields, m = 1:Nfields
            A[m,n][i,i] = mat_i[m,n] # TODO: replace with fast sparse constructor
        end
    end
end


abstract type Optim end

mutable struct SGD <: Optim
    """
    確率的勾配降下法
    Stocastic Gradient Descent
    """
    lr
    function SGD(; lr = 0.01)
        new(lr)
    end
end

function update!(self::SGD, params, grads)
    for i = 1:lastindex(params)
        for j = 1:lastindex(params[i])
            params[i][j] = params[i][j] .- self.lr .* grads[i][j] # .-=は破壊的変更
        end
    end
end

mutable struct Adam <: Optim
    """
    Adam
    (http://arxiv.org/abs/1412.6980v8)
    """
    lr
    β_1
    β_2
    iter
    m
    v
    function Adam(; lr = 0.001, β_1 = 0.9, β_2 = 0.999)
        self = new()
        self.lr = lr
        self.β_1 = β_1
        self.β_2 = β_2
        self.iter = 0
        self.m = nothing
        self.v = nothing
        return self
    end
end

function update!(self::Adam, params, grads)
    if self.m === nothing
        self.m, self.v = [], []
        for i = 1:lastindex(params)
            for j = 1:lastindex(params[i])
                push!(self.m, zero(params[i][j]))
                push!(self.v, zero(params[i][j]))
            end
        end
    end
    self.iter += 1
    lr_t = self.lr * sqrt(1.0 - self.β_2^self.iter) / (1.0 - self.β_1^self.iter)

    for i = 1:lastindex(params)
        for j = 1:lastindex(params[i])
            self.m[i] += (1 - self.β_1) .* (grads[i][j] .- self.m[i])
            self.v[i] += (1 - self.β_2) .* (grads[i][j] .^ 2 .- self.v[i])
            params[i][j] -= lr_t .* self.m[i] ./ (sqrt.(self.v[i]) .+ 1e-7)
        end
    end
end


# This file includes an example file for 1D interpolation.

using FractalTools
using Makie

# Generate data
xi, dx, xf = 0., 0.1, 10.
x = collect(xi : dx : xf)
y = sin.(x)
pts = collect.(zip(x, y))
interp = interpolate(pts, Interp1D(0.1))

# Calculate interpolant
xt = collect(xi : 0.1dx : xf)
yt = interp.(xt)

# Plot interpolation
fig, ax, plt = lines(xt, yt, color=yt, linewidth=5)
scatter!(ax, x, y, markersize=5, color=:black)


using SymbolicRegression
using Bijectors
I = collect(0:0.1:10)
f(x) = 0.2exp(-0.01*x)*x
Y = f.(I)

options = SymbolicRegression.Options(
    binary_operators=(+, *, /, -),
    unary_operators=(exp,),
    npopulations=20
)

hallOfFame = EquationSearch(I', Y, niterations=5, options=options, numprocs=4)
dominating = calculateParetoFrontier(I, Y, hallOfFame, options)
eqn = node_to_symbolic(dominating[end].tree, options)


using MappedArrays
using Base.Test

a = [1,4,9,16]

b = mappedarray(sqrt, a)
@test parent(b) === a
@test eltype(b) == Float64
@test @inferred(getindex(b, 1)) == 1
@test b[2] == 2
@test b[3] == 3
@test b[4] == 4
@test_throws ErrorException b[3] = 0
@test isa(eachindex(b), AbstractUnitRange)

c = mappedarray((sqrt, x->x*x), a)
@test parent(c) === a
@test @inferred(getindex(c, 1)) == 1
@test c[2] == 2
@test c[3] == 3
@test c[4] == 4
c[3] = 2
@test a[3] == 4
@test_throws InexactError c[3] = 2.2  # because the backing array is Array{Int}
@test isa(eachindex(c), AbstractUnitRange)

sb = similar(b)
@test isa(sb, Array{Float64})
@test size(sb) == size(b)


using Typeclass
using Base.Test

@class Eq T begin
    eq(x::T,y::T)=!noteq(x,y)
    noteq(x::T,y::T)=!eq(x,y)
    ==(x::T,y::T)=eq(x,y)        # ignored unless use @instance! form
end

@instance Eq Int begin
    eq(x::Int,y::Int)=x==y
end

@test noteq(3,4)

@instance Eq Float64 begin
    noteq(x::Float64,y::Float64)=x!=y
end

@test eq(3.0,3.0)

# Create a simple type to check what happens when we don't
# resolve circular definitions
type Bad
    b
end

@instance Eq Bad

# stack overflow because we didn't adequately resolve circulars
@test_throws StackOverflowError eq(Bad(0),Bad(0))

@class Semiring A begin
       plus(x::A,y::A)=x+y
       times(x::A,y::A)=x*y
end

type Foo
    f
end

# default Julia definition of == doesn't hold for Foo
@test (Foo(3)==Foo(3) )==false

@instance Eq Foo begin
   eq(a::Foo, b::Foo) = a.f==b.f
end
# since we used @instance, does not overwrite default Julia ==

# now our eq is defined
@test eq(Foo(3),Foo(3) )==true
# but == behavior is unchanged
@test (Foo(3)==Foo(3) )==false

# Now use the overwriting version which adds a new method for ==,
# overwriting the deafult Julia definition
@instance! Eq Foo begin
   eq(a::Foo, b::Foo) = a.f==b.f
end
# now == agrees with eq
@test (Foo(3)==Foo(3) )==true

# A typeclass of things that are equal if all their fields are equal
@class NameEq T begin
    function eq(x::T,y::T)
        for name in names(x)
            if getfield(x,name)!=getfield(y,name)
                return false
            end
        end
        return true
    end
    ==(x::T,y::T)=eq(x::T,y::T)
    !=(x::T,y::T)=!eq(x::T,y::T)
end

# a type that we'll declare as an instance
type TestNameEq
    field1
    field2
end

# default behavior of ==
@test (TestNameEq(3,4)==TestNameEq(3,4))==false
@instance NameEq TestNameEq
# same issue with needing to override default deliberately
@test (TestNameEq(3,4)==TestNameEq(3,4))==false
@test eq(TestNameEq(3,4),TestNameEq(3,4))==true
@instance! NameEq TestNameEq
@test (TestNameEq(3,4)==TestNameEq(3,4))==true

# Some math
@class Monoid T begin
       munit(::T)::T
       mappend(x::T,y::T)::T
end

@instance Monoid Array{Int} begin
       munit(::Array{Int})=Int[]
       mappend(x::Array{Int},y::Array{Int})=[x;y]
end

@test munit(Int[3])==Int[]
@test mappend([3,4],[4,5])==[3,4,4,5]

# test multiple type parameters
@class Deq S T begin
    deq(x::S,y::T)=x==y
    dneq(x::S,y::T)= !deq(x,y)
end

@instance Deq Int Float64 begin
    deq(a::Int,b::Float64)=a==b
end

@test dneq(3,4.0)
@test deq(3,3.0)

# test function blocks in @instance
# test things in Base
import Base.show #o/w clobbers
@class Showable T begin
    function show(x::T,y::T)
        3==4
        print(x,y)
    end
end

# another circular definition with heavily overloaded operator
@class Addable T begin
    add(x::T,y::T)=x+y
    +(x::T,y::T)=add(x,y)
end

# using @instance not @instance! avoids clobbering +, which would break Julia
@instance Addable Int

# test function blocks in @instance
@instance Addable Foo begin
    function add(x::Foo,y::Foo)
        5==3
        try
            garble
        catch x
            print(x)
        end
        return Foo(x.f + y.f)
    end
end

@test eq(Foo(4)+Foo(3),Foo(7))

type Addable1
    data::Int
end

type Addable2
    data::Int
end

@instance Addable Addable1 begin
    add(x::Addable1,y::Addable1)=x.data+y.data
end

@instance Addable Addable2 begin
    +(x::Addable2,y::Addable2)=x.data+y.data
end

# finally, the kind of stuff this was written to do

# typeclasses in a multiple dispatch language are properties of tuples of types
@class MonoidalCategory Ob Mor begin
    dom(f::Mor)::Ob
    cod(f::Mor)::Ob
    id(A::Ob)::Mor
    compose(f::Mor,g::Mor)::Mor #f*g
    otimes(f::Mor,g::Mor)::Mor
    otimes(A::Ob,B::Ob)::Ob
    munit(::Ob)::Ob
    munit(f::Mor)=munit(dom(f))
    # syntax, using unicode
    ∘(f::Mor,g::Mor)=compose(f,g)
    |(f::Mor,g::Mor)=compose(g,f)
    ⊗(f::Mor,g::Mor)=otimes(f,g)
    ⊗(A::Ob,B::Ob)=otimes(A,B)
end

typealias Mat Matrix{Float64}
@instance MonoidalCategory Int Mat begin
    dom(f::Mat)=size(f)[2]
    cod(f::Mat)=size(f)[1]
    id(A::Int)=eye(A)
    compose(f::Mat,g::Mat)=f*g
    otimes(f::Mat,g::Mat)=kron(f,g)
    otimes(A::Int,B::Int)=A*B
    munit(::Int)=1
end

@test id(2)⊗id(2) == id(4)
@test dom(rand(2,3))==3


include("smear_FD.jl")
include("calc_Focc.jl")
include("calc_entropy.jl")
include("sum_upto_E_fermi.jl")

function test_main(kT::Float64)
    Nstates = 8
    Nelectrons = 6
    evals = Array{Float64}(Nstates)
    evals = [-2.4, -1.0, -0.5, -0.2, -0.19, -0.10, -0.05, -0.05]
    println("\nkT = ", kT)
    spinpol = false
    println("\nspinpol = ", spinpol)
    Focc, E_fermi = calc_Focc(evals, Nelectrons, kT)
    for ist = 1:Nstates
        @printf("%18.10f %18.10f\n", evals[ist], Focc[ist])
    end
    @printf("E_fermi = %18.10f\n", E_fermi)
    integFocc = sum_upto_E_fermi( Focc, evals, E_fermi )
    @printf("integFocc = %18.10f\n", integFocc)
    @printf("sum(Focc) = %18.10f\n", sum(Focc))
    @printf("Entropy (-TS) = %18.10f\n", calc_entropy(Focc, kT, is_spinpol=spinpol))
end

test_main(0.01)


using SimpleConstants
using Test

@testset "SimpleConstants.jl" begin
    λ = rand()  # wavelength
    @test 2πc₀/λ == 2π * c₀ / λ
    @test ℎc₀/λ == ℎ * c₀ / λ

    @test exp(i2π/3) == exp(im * 2π / 3)
    @test exp(iπ/3) == exp(im * π / 3)

    @test e⁻ < 0
    @test e⁺ > 0
    @test e⁻ + e⁺ == 0

    @test 180° ≈ π

    λ_inµm = rand()
    E_ineV = meter2eV(λ_inµm * micro)
    @test eV2meter(E_ineV) ≈ λ_inµm * micro
    @test meter2eV(λ_inµm * micro) ≈ E_ineV
    @test round(λ_inµm * E_ineV, digits=2) == 1.24  # (λ in µm) times (E in eV) is roughly 1.24
end


# ---
# title: 162. Find Peak Element
# id: problem162
# author: Tian Jun
# date: 2020-10-31
# difficulty: Medium
# categories: Array, Binary Search
# link: <https://leetcode.com/problems/find-peak-element/description/>
# hidden: true
# ---
#
# A peak element is an element that is strictly greater than its neighbors.
#
# Given an integer array `nums`, find a peak element, and return its index. If
# the array contains multiple peaks, return the index to **any of the peaks**.
#
# You may imagine that `nums[-1] = nums[n] = -∞`.
#
#
#
# **Example 1:**
#
#
#
#     Input: nums = [1,2,3,1]
#     Output: 2
#     Explanation: 3 is a peak element and your function should return the index number 2.
#
# **Example 2:**
#
#
#
#     Input: nums = [1,2,1,3,5,6,4]
#     Output: 5
#     Explanation: Your function can return either index number 1 where the peak element is 2, or index number 5 where the peak element is 6.
#
#
#
# **Constraints:**
#
#   * `1 <= nums.length <= 1000`
#   * `-231 <= nums[i] <= 231 - 1`
#   * `nums[i] != nums[i + 1]` for all valid `i`.
#
#
#
# **Follow up:**  Could you implement a solution with logarithmic complexity?
#
#
## @lc code=start
using LeetCode

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


# ---
# title: 452. Minimum Number of Arrows to Burst Balloons
# id: problem452
# author: AquaIndigo
# date: 2020-11-23
# difficulty: Medium
# categories: Greedy, Sort
# link: <https://leetcode.com/problems/minimum-number-of-arrows-to-burst-balloons/description/>
# hidden: true
# ---
#
# There are some spherical balloons spread in two-dimensional space. For each
# balloon, provided input is the start and end coordinates of the horizontal
# diameter. Since it's horizontal, y-coordinates don't matter, and hence the
# x-coordinates of start and end of the diameter suffice. The start is always
# smaller than the end.
#
# An arrow can be shot up exactly vertically from different points along the
# x-axis. A balloon with `xstart` and `xend` bursts by an arrow shot at `x` if
# `xstart ≤ x ≤ xend`. There is no limit to the number of arrows that can be
# shot. An arrow once shot keeps traveling up infinitely.
#
# Given an array `points` where `points[i] = [xstart, xend]`, return  _the
# minimum number of arrows that must be shot to burst all balloons_.
#
#
#
# **Example 1:**
#
#
#
#     Input: points = [[10,16],[2,8],[1,6],[7,12]]
#     Output: 2
#     Explanation: One way is to shoot one arrow for example at x = 6 (bursting the balloons [2,8] and [1,6]) and another arrow at x = 11 (bursting the other two balloons).
#
#
# **Example 2:**
#
#
#
#     Input: points = [[1,2],[3,4],[5,6],[7,8]]
#     Output: 4
#
#
# **Example 3:**
#
#
#
#     Input: points = [[1,2],[2,3],[3,4],[4,5]]
#     Output: 2
#
#
# **Example 4:**
#
#
#
#     Input: points = [[1,2]]
#     Output: 1
#
#
# **Example 5:**
#
#
#
#     Input: points = [[2,3],[2,3]]
#     Output: 1
#
#
#
#
# **Constraints:**
#
#   * `0 <= points.length <= 104`
#   * `points[i].length == 2`
#   * `-231 <= xstart < xend <= 231 - 1`
#
#
## @lc code=start
using LeetCode

function find_min_arrow_shots(points::Vector{Vector{Int}})::Int
    if isempty(points)
        return 0
    end
    sort!(points; by=x -> x[2])
    ed, res = points[1][2], 1
    for point in points
        if point[1] > ed
            res += 1
            ed = point[2]
        end
    end
    return res
end
## @lc code=end


# # Modal analysis of a free-floating steel circle

# Source code: [`circle_modal_tut.jl`](circle_modal_tut.jl)

# ## Description

# Vibration analysis of a beam simply supported in one plane, and clamped in
# another. The results are compared with analytical expressions. This is a
# benchmark from the NAFEMS Selected Benchmarks for Natural Frequency Analysis,
# publication: Test VM09: Circular Ring --  In-plane and Out-of-plane
# Vibration.

# ## Reference frequencies

# There will be 6 rigid body modes (zero natural frequencies).

# The numerical results are due to the publication:
# NAFEMS Finite Element Methods & Standards, Abbassian, F., Dawswell, D. J., and
# Knowles, N. C. Selected Benchmarks for Natural Frequency Analysis, Test No.
# 6. Glasgow: NAFEMS, Nov., 1987.

# The reference values were analytically determined (Blevins, FORMULAS FOR
# DYNAMICS, ACOUSTICS AND VIBRATION, Table 4.16). Note that shear flexibility
# was neglected when computing the reference values.

# | Mode       |         Reference Value (Hz)  |  NAFEMS Target Value (Hz) |
# | -------   |     -------  |  ------- |
# | 7, 8 | (out of plane)   |        51.85          |         52.29  |
# | 9, 10 |  (in plane)       |       53.38         |          53.97  |
# | 11, 12 |  (out of plane)   |     148.8          |         149.7  |
# | 13, 14 |  (in plane)       |     151.0          |         152.4  |
# | 15, 16 |  (out of plane)   |     287.0          |         288.3  |
# |  17, 18 |  (in plane)      |      289.5         |          288.3  |

# ## Goals

# - Show convergence relative to reference values.
# - Demonstrate the optimization of eigenvalue accuracy by choosing mass type.

##
# ## Definition of the basic inputs

# The finite element code realize on the basic functionality implemented in this
# package.
using FinEtools

# The material parameters may be defined with the specification of the units.
# The elastic properties are:
E = 200.0 * phun("GPa")
nu = 0.3;

# The mass density is
rho = 8000 * phun("kg/m^3")
# Here are the cross-sectional dimensions and the length of the beam between
# supports.
radius = 1.0 * phun("m"); diameter = 0.1 * phun("m");

##
# ## Cross-section

# Cross-sectional properties are incorporated in the cross-section property. The
# three arguments supplied are functions. All are returning "constants". In
# particular the first two functions each return the dimension of the
# cross-section as a constant(the beam has a uniform cross-section); the third
# function defines the orientation of the cross-section in the global Cartesian
# coordinates. `[1.0, 0.0, 0.0]` is the vector that together with the tangent
# to the midline curve of the beam spans the $x_1x_2$ plane of the local
# coordinates for the beam.
using FinEtoolsFlexBeams.CrossSectionModule: CrossSectionCircle
cs = CrossSectionCircle(s -> diameter/2, s -> [1.0, 0.0, 0.0])
@show cs.parameters(0.0)

# For instance the the first out of plane mode is listed in the reference cited
# above as
R = radius
I = cs.parameters(0.0)[4]
m = rho * cs.parameters(0.0)[1]
J = cs.parameters(0.0)[2]
G = E/2/(1+nu)
i = 2 # the first non-rigid body mode
@show i*(i^2-1)/(2*pi*R^2)*sqrt(E*I/m/(i^2+E*I/G/J))

# The first "ovaling" (in-plane) mode is:
i = 2 # the first ovaling mode
@show i*(i^2-1)/(2*pi*R^2*(i^2+1)^(1/2))*sqrt(E*I/m)

# The purpose of the numerical model is to calculate approximation to the reference frequencies.

neigvs = 18;

# We will generate
n = 20
# beam elements along the member.
using FinEtoolsFlexBeams.MeshFrameMemberModule: frame_member
tolerance = radius/n/1000;
fens, fes = frame_member([0 0 0; 2*pi 0 0], n, cs)
for i in 1:count(fens)
    a = fens.xyz[i, 1]
    fens.xyz[i, :] .= (radius+radius*cos(a), radius*sin(a), 0)
end
fens, fes = mergenodes(fens, fes, tolerance, [1, n+1])

##
# ## Material

# Material properties can be now used to create a material: isotropic elasticity model of the `FinEtoolsDeforLinear` package is instantiated.
using FinEtoolsDeforLinear
material = MatDeforElastIso(DeforModelRed3D, rho, E, nu, 0.0)

##
# ## Fields

# Now we start constructing the discrete finite element model.
# We begin by constructing the requisite fields, geometry and displacement.
# These are the so-called "configuration variables", all initialized to 0.
# This is that geometry field.
geom0 = NodalField(fens.xyz)
# This is the displacement field, three unknown displacements per node.
u0 = NodalField(zeros(size(fens.xyz, 1), 3))
# This is the rotation field, three unknown rotations per node are represented
# with a rotation matrix, in total nine numbers. The utility function
# `initial_Rfield`
using FinEtoolsFlexBeams.RotUtilModule: initial_Rfield
Rfield0 = initial_Rfield(fens)
# Here we verify the number of nodes and the number of degrees of freedom in the
# rotation field per node.
@show nents(Rfield0)
@show ndofs(Rfield0)

# Finally, this is the displacement and rotation field for incremental changes,
# incremental displacements and incremental rotations. In total, 6 unknowns per
# node.
dchi = NodalField(zeros(size(fens.xyz, 1), 6))

# There are no support conditions.
applyebc!(dchi)
# The  the number of free
# (unknown) degrees of freedom is equal to the total number of degrees of freedom in the system.
numberdofs!(dchi);

##
# ## Assemble the global discrete system

using FinEtoolsFlexBeams.FEMMCorotBeamModule: FEMMCorotBeam
femm = FEMMCorotBeam(IntegDomain(fes, GaussRule(1, 2)), material);

# For disambiguation we will refer to the stiffness and mass functions by qualifying them with the corotational-beam module, `FEMMCorotBeamModule`.
using FinEtoolsFlexBeams.FEMMCorotBeamModule
CB = FEMMCorotBeamModule
# Thus we can construct the stiffness and mass matrix as follows:
# Note that the finite element machine is the first argument. This provides
# access to the integration domain. The next argument is the geometry field,
# followed by the displacement, rotations, and incremental
# displacement/rotation fields.
K = CB.stiffness(femm, geom0, u0, Rfield0, dchi);
M = CB.mass(femm, geom0, u0, Rfield0, dchi);
# We can compare the size of the stiffness matrix with the number of degrees of
# freedom that are unknown (20).
@show size(K)

##
# ## Solve the free-vibration problem

oshift = (2*pi*15)^2

# The Arnoldi algorithm implemented in the well-known `Arpack` package is used
# to solve the generalized eigenvalue problem with the sparse matrices. As is
# common in structural dynamics, we request the smallest eigenvalues in
# absolute value (`:SM`).
using Arpack
evals, evecs, nconv = eigs(K + oshift * M, M; nev=neigvs, which=:SM);
# First  we should check that the requested eigenvalues actually converged:
@show nconv == neigvs

# The eigenvalues (i. e. the squares of the angular frequencies) are returned in
# the vector `evals`. The mode shapes constitute the columns of the matrix `evecs`.
@show size(evecs)
# The natural frequencies are obtained from the squares of the angular
# frequencies. We note the use of `sqrt.` which broadcast the square root over
# the array `evals`.
fs = sqrt.([max(0, e - oshift) for e in evals]) / (2 * pi);

##
# ## Comparison of computed and analytical results

# The approximate and analytical frequencies are now reported.
sigdig(n) = round(n * 10000) / 10000
println("Approximate frequencies: $(sigdig.(fs)) [Hz]")

##
# ## Set up the visualization of the vibration modes

# The animation will show one of the vibration modes overlaid on the undeformed geometry. The configuration during the animation needs to reflect rotations. The function `update_rotation_field!` will update the rotation field given a vibration mode.
using FinEtoolsFlexBeams.RotUtilModule: update_rotation_field!

# The visualization utilities take advantage of the PlotlyJS library.
using PlotlyJS
using FinEtoolsFlexBeams.VisUtilModule: plot_space_box, plot_solid, render, react!, default_layout_3d, save_to_json

# The magnitude of the vibration modes (displacements  and rotations) will be amplified with this scale factor:
scale = 1.5

# In order to handle variables inside loops correctly, we create a local scope with the `let end` block.
vis(mode) = let
    # The extents of the box will be preserved during animation in order to eliminate changes in the viewing parameters.
    tbox = plot_space_box(reshape(inflatebox!(boundingbox(fens.xyz), 0.3 * radius), 2, 3))
    # This is the geometry of the structure without deformation (undeformed). It is displayed as gray, partially transparent.
    tenv0 = plot_solid(fens, fes; x=geom0.values, u=0.0 .* dchi.values[:, 1:3], R=Rfield0.values, facecolor="rgb(125, 155, 125)", opacity=0.3);
    # Initially the plot consists of the box and the undeformed geometry.
    plots = cat(tbox, tenv0; dims=1)
    # Create the layout for the plot. Set the size of the window.
    layout = default_layout_3d(;width=600, height=600)
    # Set the aspect mode to get the correct proportions.
    layout[:scene][:aspectmode] = "data"
    # Render the undeformed structure
    pl = render(plots; layout=layout, title="Mode $(mode)")
    sleep(2.115)
    # This is the animation loop.
    # 1. Distribute a fraction of the selected eigenvector into the incremental displacement/rotation field.
    # 2. Create the deformed configuration by defining displacement field `u1` and rotation field `Rfield1`.
    # 3. Create the plot for the deformed configuration, and add it to the list of plots.
    # 4. Call the `react!` function to update the display. Sleep for a brief period of time to give the display a chance to become current.
    for xscale in scale .* sin.(collect(0:1:89) .* (2 * pi / 21))
        scattersysvec!(dchi, xscale .* evecs[:, mode])
        u1 = deepcopy(u0)
        u1.values .= dchi.values[:, 1:3]
        Rfield1 = deepcopy(Rfield0)
        update_rotation_field!(Rfield1, dchi)
        tenv1 = plot_solid(fens, fes; x=geom0.values, u=dchi.values[:, 1:3], R=Rfield1.values, facecolor="rgb(50, 55, 125)");
        plots = cat(tbox, tenv0, tenv1; dims=1)
        react!(pl, plots, pl.plot.layout)
        sleep(0.115)
    end
end

##
# ## Visualize vibration mode

# Animate the harmonic motion of the mode given as argument:
# vis(7)

using FinEtoolsFlexBeams.FESetCorotBeamModule: MASS_TYPE_CONSISTENT_NO_ROTATION_INERTIA,
MASS_TYPE_CONSISTENT_WITH_ROTATION_INERTIA,
MASS_TYPE_LUMPED_DIAGONAL_NO_ROTATION_INERTIA,
MASS_TYPE_LUMPED_DIAGONAL_WITH_ROTATION_INERTIA

results = let
    results = Dict()
    for mtype in [
        MASS_TYPE_CONSISTENT_NO_ROTATION_INERTIA,
        MASS_TYPE_CONSISTENT_WITH_ROTATION_INERTIA,
        MASS_TYPE_LUMPED_DIAGONAL_NO_ROTATION_INERTIA,
        MASS_TYPE_LUMPED_DIAGONAL_WITH_ROTATION_INERTIA]
        M = CB.mass(femm, geom0, u0, Rfield0, dchi; mass_type = mtype);
        evals, evecs, nconv = eigs(K + oshift * M, M; nev=neigvs, which=:SM);
        results[mtype] = evals, evecs
    end
    results
end

colors = [
"rgb(125, 15, 15)",
"rgb(15, 155, 15)",
"rgb(15, 15, 155)",
"rgb(125, 115, 115)"
]

evals = results[MASS_TYPE_CONSISTENT_NO_ROTATION_INERTIA][1]
x = 1:length(evals); y = sqrt.([max(0, e - oshift) for e in evals]) / (2 * pi);
tc0 = scatter(;x=x, y=y, mode="markers", name = "cons, wo", line_color = "rgb(215, 15, 15)", marker = attr(size = 9, symbol = "diamond-open"))
evals = results[MASS_TYPE_CONSISTENT_WITH_ROTATION_INERTIA][1]
x = 1:length(evals); y = sqrt.([max(0, e - oshift) for e in evals]) / (2 * pi);
tc1 = scatter(;x=x, y=y, mode="markers", name = "cons, w", line_color = "rgb(15, 215, 15)", marker = attr(size = 9, symbol = "triangle-down"))
evals = results[MASS_TYPE_LUMPED_DIAGONAL_NO_ROTATION_INERTIA][1]
x = 1:length(evals); y = sqrt.([max(0, e - oshift) for e in evals]) / (2 * pi);
tc2 = scatter(;x=x, y=y, mode="markers", name = "lumped, wo", line_color = "rgb(15, 15, 215)", marker = attr(size = 9, symbol = "x-open"))
evals = results[MASS_TYPE_LUMPED_DIAGONAL_WITH_ROTATION_INERTIA][1]
x = 1:length(evals); y = sqrt.([max(0, e - oshift) for e in evals]) / (2 * pi);
tc3 = scatter(;x=x, y=y, mode="markers", name = "lumped, w", line_color = "rgb(165, 165, 15)", marker = attr(size = 9, symbol = "square"))

# 7, 8 (out of plane)         51.85                 52.29
# 9, 10 (in plane)            53.38                 53.97
# 11, 12 (out of plane)      148.8                 149.7
# 13, 14 (in plane)          151.0                 152.4
# 15, 16 (out of plane)      287.0                 288.3
# 17, 18 (in plane)          289.5                 288.3
rfs = vec(Float64[0 0 0 0 0 0 51.85 51.85 53.38 53.38 148.8 148.8 151.0 151.0 287.0 287.0 289.5 289.5])

rtc = scatter(;x=collect(1:length(rfs)), y=rfs, mode="lines", name = "ref", line_color = "rgb(15, 15, 15)")

# Set up the layout:
layout = Layout(;width=650, height=400, xaxis=attr(title="Mode", type = "linear"), yaxis=attr(title="Frequency [hertz]", type = "linear"), title = "Comparison of mass types")
# Plot the graphs:
pl = plot([rtc, tc0, tc1, tc2, tc3], layout; options = Dict(
        :showSendToCloud=>true,
        :plotlyServerURL=>"https://chart-studio.plotly.com"
        ))
display(pl)

##
# ## "Mixed" mass matrix

# As the graph suggests we can try mixing together mass matrices computed from
# different assumptions (lumped versus consistent, with or without rotation
# inertia). No particular justification can be provided for these numbers other
# than best low-frequency matching for linear bars suggests that 50% can be
# taken lumped and 50% consistent. Here it is obviously more complicated due to
# the presence of rotation inertia.
M = 0.5 .* CB.mass(femm, geom0, u0, Rfield0, dchi; mass_type = MASS_TYPE_LUMPED_DIAGONAL_NO_ROTATION_INERTIA) +
    0.5 .* CB.mass(femm, geom0, u0, Rfield0, dchi; mass_type = MASS_TYPE_CONSISTENT_WITH_ROTATION_INERTIA);
# With this mixed mass matrix we solve the free vibration problem again.
evals, evecs, nconv = eigs(K + oshift * M, M; nev=neigvs, which=:SM);

# Plotting the newly obtained data on top of the previously presented data, we
# can observe sometimes substantial improvement of accuracy of the mixed-matrix
# formulation relative to the individual mass matrix types.
x = 1:length(evals); y = sqrt.([max(0, e - oshift) for e in evals]) / (2 * pi);
mtc = scatter(;x=x, y=y, mode="markers", name = "mixed", line_color = "rgb(215, 15, 215)", marker = attr(size = 9, symbol = "circle"))
pl = plot([rtc, tc0, tc1, tc2, tc3, mtc], layout; options = Dict(
        :showSendToCloud=>true,
        :plotlyServerURL=>"https://chart-studio.plotly.com"
        ))
display(pl)


srand(0)
# Column-stochastic x
x_cs = rand(10, 10, 3)
x_cs = x_cs./sum(x_cs, 1)
# Row stochastic x
x_rs = similar(x_cs);
for k = 1:3
  x_rs[:, :, k] = x_cs[:, :, k]';
end

r = rand(10)
q = Array(Float64, 10, 3)

v = rand(10)
my_v = copy(v)

function normal_max(q, v, x, r)
  for k = 1:3
    q[:, k] = r + x[:, :, k] * v
  end
  [maximum(q[r, :]) for r = 1:10]
end

v = normal_max(q, v, x_rs, r)

function running_max(newv, oldv, x, r, q)
  newv[:] = copy(oldv)
  for k = 1:3
    for j = 1:10
      myq = r[j] + dot(x[:, j, k], oldv)
      #println( abs(myq - q[j, k]))
      if newv[j] < myq
        newv[j] = myq
      end
    end
  end
end

new_v = similar(my_v)
running_max(new_v, my_v, x_cs, r, q)

# myv and v should be equal
abs(v .- new_v) .< eps(max(maximum(abs(v)), maximum(abs(myv))))

# Indmax

x = rand(10, 3)

v1 = [maximum(x[r, :]) for r in 1:10]
p1 = [indmax(x[r, :]) for r in 1:10]

p2 = ones(Int, 10)
v2 = zeros(10)
for j = 1:3
  for i = 1:10
    if v2[i] < x[i, j]
      v2[i] = x[i, j]
      p2[i] = j
    end
  end
end


struct KruskalHeapEntry{T<:Real}
    edge::Edge
    dist::T
end

isless(e1::KruskalHeapEntry, e2::KruskalHeapEntry) = e1.dist < e2.dist

"""
    quick_find!(vs, p, q)

Perform [Quick-Find algorithm](https://en.wikipedia.org/wiki/Disjoint-set_data_structure)
on a given pair of vertices `p`and `q`, and make a connection between them in the vector `vs`.
"""
function quick_find!(vs, p, q)
    pid = vs[p]
    qid = vs[q]
    for i in 1:length(vs)
        if vs[i] == pid
            vs[i] = qid
        end
    end
end

"""
    kruskal_mst(g, distmx=DefaultDistance())

Return a vector of edges representing the minimum spanning tree of a connected, undirected graph `g` with optional
distance matrix `distmx` using [Kruskal's algorithm](https://en.wikipedia.org/wiki/Kruskal%27s_algorithm).
"""
function kruskal_mst end
@traitfn function kruskal_mst{T}(
    g::::(!IsDirected),
    distmx::AbstractMatrix{T} = DefaultDistance()
)

    U = eltype(g)
    edge_list = Vector{KruskalHeapEntry{T}}()
    mst = Vector{Edge}()
    connected_vs = collect(one(U):nv(g))

    sizehint!(edge_list, ne(g))
    sizehint!(mst, ne(g))

    for e in edges(g)
        heappush!(edge_list, KruskalHeapEntry{T}(e, distmx[src(e), dst(e)]))
    end

    while !isempty(edge_list) && length(mst) < nv(g) - 1
        heap_entry = heappop!(edge_list)
        v = src(heap_entry.edge)
        w = dst(heap_entry.edge)

        if connected_vs[v] != connected_vs[w]
            quick_find!(connected_vs, v, w)
            push!(mst, heap_entry.edge)
        end
    end

    return mst
end


# Uniform distribution

export Uniform

@parameterized Uniform()
@kwstruct Uniform()

###############################################################################
# Standard Uniform

function basemeasure(::Uniform{()})
    inbounds(x) = 0 < x < 1
    constℓ = 0.0
    varℓ() = 0.0
    base = Lebesgue(ℝ)
    FactoredBase(inbounds, constℓ, varℓ, base)
end

distproxy(::Uniform{()}) = Dists.Uniform()

logdensity(d::Uniform{()}, x) = 0.0

TV.as(::Uniform{()}) = as𝕀

Base.rand(rng::AbstractRNG, T::Type, μ::Uniform{()}) = rand(rng, T)

###############################################################################
# Uniform


using RDatasets
using GLM
using ANOVA

swiss = dataset("datasets","swiss")
mdl = lm(@formula(Fertility ~ Agriculture + Education + Examination), swiss)
anova(mdl)


"""
    φ , λ = yx2latlon(φ₀, λ₀, N, E)

Coordinate transformation between geographic and plane rectangular coordinates on the Gauss-Krüger Projection
This function is based on the following documents:
        https://www.gsi.go.jp/common/000061216.pdf
        https://vldb.gsi.go.jp/sokuchi/surveycalc/surveycalc/algorithm/xy2bl/xy2bl.htm
        https://vldb.gsi.go.jp/sokuchi/surveycalc/surveycalc/algorithm/bl2xy/bl2xy.htm

### input arguments
- φ₀, λ₀ : latitude of origin and central meridian
- N, E   : plane rectangular coordinates northing and easting
### return values
- φ , λ  : geographic latitude and longitude

### Examples
```julia-repl
julia> # ANSWER: lat = 36.10404755, lon = 140.08539843

julia> yx2latlon(36.0, 139.83333333, 11573.375, 22694.980)
(36.104047552508895, 140.08539842726532)
```

"""
function yx2latlon(φ₀, λ₀, N, E)

    S̅φ₀ = arclength(deg2rad(φ₀))

    # ξ and η
    ξ = (N + S̅φ₀)/A̅
    η = E/A̅

    # β terms
    βcoef = [1/2  -2/3   37/96       -1/360      -81/512; # β₁
             0.0  1/48    1/15    -437/1440       46/105; # β₂
             0.0   0.0  17/480      -37/840    -209/4480; # β₃
             0.0   0.0     0.0  4397/161280      -11/504; # β₄
             0.0   0.0     0.0          0.0  4583/161280; # β₅
            ]
    βj = βcoef * [n^k for k=1:5]

    ξ′ = ξ - sum([βj[j]sin(2j*ξ)cosh(2j*η) for j=1:5])
    η′ = η - sum([βj[j]cos(2j*ξ)sinh(2j*η) for j=1:5])

    # χ
    χ = asin(sin(ξ′)/cosh(η′))

    # δ terms
    δcoef = [2.0 -2/3  -2.0    116/45     26/45     -2854/675; # δ₁
             0.0  7/3  -8/5   -227/45  2704/315      2323/945; # δ₂
             0.0  0.0 56/15   -136/35 -1262/105    73814/2835; # δ₃
             0.0  0.0   0.0  4279/630   -332/35 -339572/14175; # δ₄
             0.0  0.0   0.0       0.0  4174/315  -144838/6237; # δ₅
             0.0  0.0   0.0       0.0       0.0  601676/22275; # δ₆
            ]
    δj = δcoef * [n^k for k=1:6]

    φ = rad2deg( χ + sum([δj[j]sin(2j*χ) for j=1:6]) )
    λ = λ₀ + rad2deg( atan(sinh(η′),cos(ξ′)) )

    #=
    σ′ = 1 - sum([2j*βj[j]*cos(2j*ξ)*cosh(2j*η) for j=1:5])
    τ′ =     sum([2j*βj[j]*sin(2j*ξ)*sinh(2j*η) for j=1:5])
    γ = atan(τ′+σ′*tan(ξ′)tanh(η′), σ′-τ′*tan(ξ′)tanh(η′))
    m = m₀*A₀*(1/(1+n))*sqrt( (cos(ξ′)^2+sinh(η′)^2)/(σ′^2+τ′^2) * (1+((1-n)tan(φ)/(1+n))^2) )
    =#

    return [φ λ]
end
# -------------------------------------
"""
    λ, φ = xy2lonlat(λ₀, φ₀, E, N)

See [`yx2latlon`](@ref)
"""
xy2lonlat(λ₀, φ₀, E, N) = reverse(yx2latlon(φ₀, λ₀, N, E), dims=2)
# -------------------------------------
function yx2latlon_ja(zone::Integer, N, E)
    !(0 < zone < 20) && error("zone ID must be a integer in the range of 1 to 19.")
    return yx2latlon(Origin_LatLon_Japan[zone,:]..., N, E)
end
# -------------------------------------
xy2lonlat_ja(zone::Integer, E, N) = reverse(yx2latlon_ja(zone, N, E), dims=2)
# -------------------------------------


module Backward

export Var, grad!

mutable struct Var{T<:AbstractFloat} <: AbstractFloat
    value::T
    grad::Union{Nothing, T}
    children::Vector{Tuple{Var{T}, T}}
end

Var(x::T) where T<:AbstractFloat = Var(x, nothing, Tuple{Var{T}, T}[])

function grad!(v::Var)
    if isnothing(v.grad)
        v.grad = sum(partial * grad!(u) for (u, partial) in v.children)
    end
    v.grad
end

import Base: +, -, *, /, ^
function +(u::Var{T}, v::Var{T}) where T<:AbstractFloat
    z = Var(u.value + v.value)
    push!(u.children, (z, one(T)))
    push!(v.children, (z, one(T)))
    z
end
function -(u::Var{T}, v::Var{T}) where T<:AbstractFloat
    z = Var(u.value - v.value)
    push!(u.children, (z, one(T)))
    push!(v.children, (z, -one(T)))
    z
end
function *(u::Var{T}, v::Var{T}) where T<:AbstractFloat
    z = Var(u.value * v.value)
    push!(u.children, (z, v.value))
    push!(v.children, (z, u.value))
    z
end
function /(u::Var{T}, v::Var{T}) where T<:AbstractFloat
    z = Var(u.value / v.value)
    # dz = d(u / v) = (du * v - u * dv) / v^2
    push!(u.children, (z, one(T) / v.value))
    push!(v.children, (z, -u.value / (v.value ^ 2)))
    z
end

function ^(u::Var, k::Int)
    z = Var(u.value ^ k)
    push!(u.children, (z, k * (u.value ^ (k - 1))))
    z
end

function Base.sin(u::Var)
    z = Var(sin(u.value))
    push!(u.children, (z, cos(u.value)))
    z
end
function Base.cos(u::Var)
    z = Var(cos(u.value))
    push!(u.children, (z, -sin(u.value)))
    z
end

function Base.exp(u::Var)
    exp_u = exp(u.value)
    z = Var(exp_u)
    push!(u.children, (z, exp_u))
    z
end
function Base.log(u::Var)
    z = Var(log(u.value))
    push!(u.children, (z, inv(u.value)))
    z
end

end


function calc_cost(reactor, R_0, B_0)
  this_term = 1 + reactor.b / ( reactor.epsilon * R_0 * reactor.kappa )
  that_term = 1 + reactor.b / ( reactor.epsilon * R_0 )

  K_TF = 10 * this_term * that_term * reactor.epsilon^2 * reactor.kappa
  K_C = K_TF / reactor.P_F

  K_C * (R_0^3) * (B_0^2)
end


using MathProgBase, Cbc

struct KPDSupply{T<:Integer}
    item::String
    weight::T
    value::T
    quant::T
end
Base.show(io::IO, kdps::KPDSupply) = print(io, kdps.quant, " ", kdps.item, " ($(kdps.weight) kg, $(kdps.value) €)")

function solve(gear::Vector{KPDSupply{T}}, capacity::Integer) where T<:Integer
    w = getfield.(gear, :weight)
    v = getfield.(gear, :value)
    q = getfield.(gear, :quant)
    sol = mixintprog(-v, w', '<', capacity, :Int, 0, q, CbcSolver())
    sol.status == :Optimal || error("this problem could not be solved")

    if all(q .== 1) # simpler case
        return gear[sol.sol == 1.0]
    else
        pack = similar(gear, 0)
        s = round.(Int, sol.sol)
        for (i, g) in enumerate(gear)
            iszero(s[i]) && continue
            push!(pack, KPDSupply(g.item, g.weight, g.value, s[i]))
        end
        return pack
    end
end


using BenchmarkTools
using Printf
using Transducers

struct BagCount
    n::Int64
    color::String
end

struct BagRule
    color::String
    contains::Array{BagCount, 1}
end

function parseBagDescriptor(s)
    p = findfirst(r"\s+bags?$", s)
    isnothing(p) && throw(DomainError(s, "invalid bag descriptor"))
    s[1:p.start-1]
end

function toBagCount(s)
    parts = split(s, " "; limit = 2)
    BagCount(parse(Int64, parts[1]), parseBagDescriptor(parts[2]))
end

function toBagRule(s)
    s = rstrip(s, '.')
    p = findfirst(" contain ", s)
    bagDescriptor = parseBagDescriptor(s[1:p.start-1])
    contents = s[p.stop+1:length(s)]
    BagRule(bagDescriptor, contents == "no other bags" ? [] : [toBagCount(content) for content = split(contents, ", ")])
end

####

exampleBagRules = readlines("src/advent/day7/example.txt") |> Map(line -> toBagRule(line)) |> collect
inputBagRules = readlines("src/advent/day7/input.txt") |> Map(line -> toBagRule(line)) |> collect

#### part 1

function canBeContainedBy(bagRules, color)
    entries = bagRules |> Map(rule -> [(entry.color, rule.color) for entry ∈ rule.contains]) |> Iterators.flatten |> collect
    dict = Dict(entry[1] => Set{String}() for entry ∈ entries)
    foreach(entry -> union!(dict[entry[1]], Set([entry[2]])), entries)

    function _canBeContainedBy(color)
        answer = Set{String}()
        !haskey(dict, color) && return answer
        foreach(child -> union!(answer, Set([child]), _canBeContainedBy(child)), dict[color])
        answer
    end

    _canBeContainedBy(color)
end

@assert length(canBeContainedBy(exampleBagRules, "shiny gold")) == 4
@printf("part1 = %d\n", length(canBeContainedBy(inputBagRules, "shiny gold")))
#@btime length(canBeContainedBy(inputBagRules, "shiny gold"));

#### part 2

function countBags(bagRules, color)
    bagRuleDict = Dict(bagRule.color => bagRule for bagRule ∈ bagRules)
    _countBags(color) = haskey(bagRuleDict, color) ? reduce(+, bagCount.n * (1 + _countBags(bagCount.color)) for bagCount ∈ bagRuleDict[color].contains; init = 0) : 0
    _countBags(color)
end

@assert countBags(exampleBagRules, "faded blue") == 0
@assert countBags(exampleBagRules, "dotted black") == 0
@assert countBags(exampleBagRules, "vibrant plum") == 11
@assert countBags(exampleBagRules, "dark olive") == 7
@assert countBags(exampleBagRules, "shiny gold") == 32

@printf("part2 = %d\n", countBags(inputBagRules, "shiny gold"))
#@btime countBags(inputBagRules, "shiny gold");


using Test
using HMatrices
using HMatrices: hilbert_linear_to_cartesian, hilbert_cartesian_to_linear

@testset "Hilbert points" begin
    # check that hilbert curves spans the lattice, and the inverse is correct
    for n in [2^p for p in 0:3]
        lattice = Set((i,j) for i in 0:n-1,j in 0:n-1)
        for d in 0:(n^2-1)
            x,y = hilbert_linear_to_cartesian(n,d)
            pop!(lattice,(x,y))
            @test hilbert_cartesian_to_linear(n,x,y) == d
        end
        @test isempty(lattice)
    end
end


@testset "$TEST $G" begin

g = CompleteDiGraph(5, DG)
@test nv(g) == 5 && ne(g) == 20
@test typeof(g) == DG

g = CompleteGraph(5, G)
@test nv(g) == 5 && ne(g) == 10
@test typeof(g) == G

g = CompleteBipartiteGraph(5, 8, G)
@test nv(g) == 13 && ne(g) == 40
@test typeof(g) == G
g = StarDiGraph(5, DG)
@test nv(g) == 5 && ne(g) == 4
g = StarGraph(5, G)
@test typeof(g) == G
@test nv(g) == 5 && ne(g) == 4
g = StarGraph(1, G)
@test typeof(g) == G
@test nv(g) == 1 && ne(g) == 0

g = PathDiGraph(5, DG)
@test typeof(g) == DG
@test nv(g) == 5 && ne(g) == 4
g = PathGraph(5, G)
@test typeof(g) == G
@test nv(g) == 5 && ne(g) == 4

g = CycleDiGraph(5, DG)
@test typeof(g) == DG
@test nv(g) == 5 && ne(g) == 5
g = CycleGraph(5, G)
@test typeof(g) == G
@test nv(g) == 5 && ne(g) == 5

g = WheelDiGraph(5, DG)
@test typeof(g) == DG
@test nv(g) == 5 && ne(g) == 8
g = WheelGraph(5, G)
@test typeof(g) == G
@test nv(g) == 5 && ne(g) == 8

g = Grid([3,3,4], G)
@test typeof(g) == G
@test nv(g) == 3*3*4
@test ne(g) == 75
@test maximum(degree(g)) == 6
@test minimum(degree(g)) == 3

g = CliqueGraph(3,5, G)
@test typeof(g) == G
@test nv(g) == 15 && ne(g) == 20
@test g[1:3] == CompleteGraph(3, G)

g = crosspath(BinaryTree(2, G), 3)
@test typeof(g) == G
@test nv(g) == 9
@test ne(g) == 12

g = DoubleBinaryTree(3, G)
@test typeof(g) == G
@test nv(g) == 14
@test ne(g) == 13

rg3 = RoachGraph(3, G)
@test typeof(g) == G
@test nv(g) == 14
@test ne(g) == 13

end # testset


# Test of minimum spanning tree algorithms

using Graphs
using Base.Test

g = simple_inclist(7, is_directed=false)

wedges = [
    (1, 2, 7.),
    (2, 3, 8.),
    (1, 4, 5.),
    (2, 4, 9.),
    (2, 5, 7.),
    (3, 5, 3.),
    (4, 5, 15.),
    (4, 6, 6.),
    (5, 6, 8.),
    (5, 7, 9.),
    (6, 7, 11.) ]

m = length(wedges)
eweights = zeros(m)

for i = 1 : m
    we = wedges[i]
    add_edge!(g, we[1], we[2])
    eweights[i] = we[3]
end

@assert num_vertices(g) == 7
@assert num_edges(g) == m

re, rw = prim_minimum_spantree(g, eweights, 1)
@test length(re) == 6
@test length(rw) == 6

function verify_redge(re, rw, p, w)
    return re.source == p[1] && re.target == p[2] && rw == w
end

@test verify_redge(re[1], rw[1], (1, 4), 5.)
@test verify_redge(re[2], rw[2], (4, 6), 6.)
@test verify_redge(re[3], rw[3], (1, 2), 7.)
@test verify_redge(re[4], rw[4], (2, 5), 7.)
@test verify_redge(re[5], rw[5], (5, 3), 3.)
@test verify_redge(re[6], rw[6], (5, 7), 9.)

re, rw = kruskal_minimum_spantree(g, eweights)

@test verify_redge(re[1], rw[1], (3, 5), 3.)
@test verify_redge(re[2], rw[2], (1, 4), 5.)
@test verify_redge(re[3], rw[3], (4, 6), 6.)
@test verify_redge(re[4], rw[4], (1, 2), 7.)
@test verify_redge(re[5], rw[5], (2, 5), 7.)
@test verify_redge(re[6], rw[6], (5, 7), 9.)


"""
    eom_ar1(x, p, n) -> Function

Equations of motion for a bivariate, order one autoregressive model, where
x -> y [1].

## Equations of motion

```math
\\begin{aligned}
dx &= a_1 x + \\xi_{1} \\\\
dy &= b_1 y - c_{xy} x + \\xi_{2},
\\end{aligned}
```

where ``\\xi_{1}`` and ``\\xi_{2}`` are drawn from normal distributions
with zero mean and standard deviation `σ` at each iteration.

## References

1. Paluš, M., Krakovská, A., Jakubík, J., & Chvosteková, M. (2018). Causality,
    dynamical systems and the arrow of time. Chaos: An Interdisciplinary Journal of
    Nonlinear Science, 28(7), 075307. http://doi.org/10.1063/1.5019944
"""
function eom_ar1_unidir(x, p, n)
    a₁, b₁, c_xy, σ = (p...,)
    x, y = (x...,)
    ξ₁ = rand(Normal(0, σ))
    ξ₂ = rand(Normal(0, σ))

    dx = a₁*x + ξ₁
    dy = b₁*y + c_xy*x + ξ₂
    return SVector{2}(dx, dy)
end

function ar1_unidir(uᵢ, a₁, b₁, c_xy, σ)
    p = [a₁, b₁, c_xy, σ]
    DiscreteDynamicalSystem(eom_ar1_unidir, uᵢ, p)
end

"""
    ar1(uᵢ, a₁ = 0.90693, b₁ = 0.40693, c_xy = 0.5,
        σ = 0.40662) -> DiscreteDynamicalSystem

A bivariate, order one autoregressive model, where ``x \\to y`` [1].

## Equations of motion

```math
\\begin{aligned}
dx &= a_1 x + \\xi_{1} \\\\
dy &= b_1 y - c_{xy} x + \\xi_{2},
\\end{aligned}
```

where ``\\xi_{1}`` and ``\\xi_{2}`` are drawn from normal distributions
with zero mean and standard deviation `σ` at each iteration.

## References

1. Paluš, M., Krakovská, A., Jakubík, J., & Chvosteková, M. (2018). Causality,
    dynamical systems and the arrow of time. Chaos: An Interdisciplinary Journal of
    Nonlinear Science, 28(7), 075307. http://doi.org/10.1063/1.5019944
"""
ar1_unidir(;uᵢ = rand(2), a₁ = 0.90693, b₁ = 0.40693, c_xy = 0.5, σ = 0.40662) =
    ar1_unidir(uᵢ, a₁, b₁, c_xy, σ)


# ---
# title: 462. Minimum Moves to Equal Array Elements II
# id: problem462
# author: Tian Jun
# date: 2020-10-31
# difficulty: Medium
# categories: Math
# link: <https://leetcode.com/problems/minimum-moves-to-equal-array-elements-ii/description/>
# hidden: true
# ---
#
# Given a **non-empty** integer array, find the minimum number of moves required
# to make all array elements equal, where a move is incrementing a selected
# element by 1 or decrementing a selected element by 1.
#
# You may assume the array's length is at most 10,000.
#
# **Example:**
#
#
#
#     Input:
#     [1,2,3]
#
#     Output:
#     2
#
#     Explanation:
#     Only two moves are needed (remember each move increments or decrements one element):
#
#     [1,2,3]  =>  [2,2,3]  =>  [2,2,2]
#
#
#
## @lc code=start
using LeetCode

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


using Test
using EBayes
using StructArrays
using Random

## Check single sample
ns = NormalSample(1.0)
@test isa(ns, EBayesSample)
@test isa(ns, EBayesSample{Float64})
@test response(ns) == 1.0
@test eltype(ns) == Float64
@test zero(ns) == 0
@test var(ns) == 1.0

standard_n = StandardNormalSample(1.0)
@test var(standard_n) == var(ns)
@test eltype(standard_n) == eltype(1.0)
@test zero(standard_n) == 0

ns2 = NormalSample(1.0, 1)
@test ns == ns2

# Check vectorized ebayes samples
nss = StructArray([ns; ns2])
@test isa(nss, NormalSamples)
@test isa(nss, NormalSamples{Float64})

@test isa(nss, EBayesSamples)
@test isa(nss, AbstractNormalSamples)
@test AbstractNormalSamples <: EBayesSamples
@test NormalSamples{Float64} <: EBayesSamples

nss2 = NormalSamples(nss.Z,nss.σ)
@test isa(nss2, NormalSamples)

@test response(nss) == response.(nss)
@test zeros(nss) == zeros(Float64, 2)
@test length(nss) == 2

tmp_zs = rand(10)
standard_ns = StandardNormalSample.(tmp_zs)
@test isa(standard_ns, EBayesSamples)
@test isa(standard_ns, AbstractNormalSamples)

@test response(standard_ns) == tmp_zs

## test predictions

@test predict(Normal(), 1, 0.0, ns) == 1/2
@test predict(Normal(), 1, 1.0, ns) == 1.0
@test predict(Normal(), 0, 0.0, ns) == 0.0

@test predict.(Normal(), 1, 0.0, nss) == 1/2 .* ones(2)
@test predict.(Normal(), [1;1], [0;0], nss) == 1/2 .* ones(2)

## test simulation code
Random.seed!(0)
sim1 = rand(EBayes.XieKouBrownExample1(1000))

nss_sim1 = NormalSamples(sim1)
@test isa(nss_sim1, NormalSamples)


"""
function nJacFD{T}(fcn, n::Int64, lda::Int64, x::Vector{T}, fx::Vector{T},
    yscal::Vector{T}, ajdel::T, ajmin::T, nFcn::Int64,
    a::Array{T,2})

Evaluation of a dense Jacobian matrix using finite difference approximation
adapted for use in nonlinear systems solver.

T = Float64 or BigFloat

## Input parameters
-------------------
| Variable | Description                                                     |
|:---------|:----------------------------------------------------------------|
| fcn      | Function of the form fcn(f, x) to provide right-hand side       |
| n        | Number of rows and columns of the Jacobian                      |
| lda      | Leading dimension of the array "a"                              |
| x[n]     | Current scaled vector                                           |
| fx[n]    | Vector containing fcn(x)                                        |
| yscal[n] | Vector containing scaling factors                               |
| ajdel    | Perturbation of component k: abs(y(k))*ajdel                    |
| ajmin    | Minimum perturbation is ajmin*ajdel                             |
| nFcn*    | fcn evaluation count                                            |

(* marks inout parameters)

## Output parameters
-------------------
| Variable | Description                                                     |
|:---------|:----------------------------------------------------------------|
| a[lda,n] | Array containing the approximated Jacobian                      |
| nFcn*    | fcn evaluation count adjusted                                   |
| iFail    | Return code non-zero if Jacobian could not be computed          |
"""
function nJacFD{T}(fcn, n::Int64, lda::Int64, x::Vector{T}, fx::Vector{T},
    yscal::Vector{T}, ajdel::T, ajmin::T, nFcn::Int64,
    a::Array{T,2})
    # Begin
    # Copy for internal purposes
    xa = x[:]
    iFail = 0
    fu = zero(x)
    for k = 1:n
        w = xa[k]
        su = sign(xa[k])
        if su == 0
            su = 1
        end
        u = max(max(abs(xa[k]),ajmin),yscal[k])*ajdel*su;
        xa[k] = w + u
        try
            fcn(fu,xa)
        catch
            iFail = -1
        end
        nFcn += 1
        if iFail != 0
            break
        end
        xa[k] = w
        a[1:n,k:k] = (fu-fx)./u
    end
    return (nFcn, iFail)
end

"""
function nJacFDb{T}(fcn, n::Int64, lda::Int64, ml::Int64, x::Vector{T},
    fx::Vector{T}, yscal::Vector{T}, ajdel::T, ajmin::T,
    nFcn::Int64, a::Array{T,2})

Evaluation of a banded Jacobian matrix using finite difference approximation
adapted for use in nonlinear systems solver

T = Float64 or BigFloat

## Input parameters
-------------------
| Variable | Description                                                     |
|:---------|:----------------------------------------------------------------|
| fcn      | Function of the form fcn(f, x) to provide right-hand side       |
| n        | Number of rows and columns of the Jacobian                      |
| lda      | Leading dimension of the array "a"                              |
| ml       | Lower bandwidth of the Jacobian matrix                          |
| x[n]     | Current scaled vector                                           |
| fx[n]    | Vector containing fcn(x)                                        |
| yscal[n] | Vector containing scaling factors                               |
| ajdel    | Perturbation of component k: abs(y(k))*ajdel                    |
| ajmin    | Minimum perturbation is ajmin*ajdel                             |
| nFcn*    | fcn evaluation count                                            |

(* marks inout parameters)

## Output parameters
-------------------
| Variable | Description                                                     |
|:---------|:----------------------------------------------------------------|
| a[lda,n] | Array containing the approximated Jacobian                      |
| nFcn*    | fcn evaluation count adjusted                                   |
| iFail    | Return code non-zero if Jacobian could not be computed          |
"""
function nJacFDb{T}(fcn, n::Int64, lda::Int64, ml::Int64, x::Vector{T},
    fx::Vector{T}, yscal::Vector{T}, ajdel::T, ajmin::T,
    nFcn::Int64, a::Array{T,2})
    # Begin
    # Copy for internal purposes
    xa = x[:]
    iFail = 0
    mu = lda - 2*ml -1
    ldab = ml + mu + 1
    fu = zero(x)
    w  = zero(x)
    u  = zero(x)
    for jj = 1:ldab
        for k = jj:ldab:n
            w[k] = xa[k]
            su = sign(xa[k])
            if su == 0
                su = 1
            end
            u[k]  = max(max(abs(xa[k]),ajmin),yscal[k])*ajdel*su
            xa[k] = w[k] + u[k]
        end
        try
            fcn(fu,xa)
        catch
            iFail = -1
        end
        nFcn += 1
        if iFail != 0
            break;
        end
        for k = jj:ldab:n
            xa[k] = w[k]
            i1 = max(1,k-mu)
            i2 = min(n,k+ml)
            mh = mu + 1 - k
            a[mh+i1:mh+i2,k:k] = (fu[i1:i2]-fx[i1:i2])/u[k]
        end
    end
    return (nFcn,iFail)
end

"""
function nJcf{T}(fcn, n::Int64, lda::Int64, x::Vector{T}, fx::Vector{T},
    yscal::Vector{T}, eta::Vector{T}, etamin::T, etamax::T,
    etadif::T, conv::T, nFcn::Int64, a::Array{T,2})

Approximation of dense Jacobian matrix for nonlinear systems solver with
feed-back control of discretization and rounding errors

T = Float64 or BigFloat

## Input parameters
-------------------
| Variable | Description                                                     |
|:---------|:----------------------------------------------------------------|
| fcn      | Function of the form fcn(f, x) to provide right-hand side       |
| n        | Number of rows and columns of the Jacobian                      |
| lda      | Leading dimension of the array "a"                              |
| x[n]     | Current scaled vector                                           |
| fx[n]    | Vector containing fcn(x)                                        |
| yscal[n] | Vector containing scaling factors                               |
| eta[n]*  | Vector of scaled denominator differences                        |
| etamin   | Minimum allowed scaled denominator                              |
| etamax   | Maximum allowed scaled denominator                              |
| etadif   | = sqrt(1.1*epMach)                                              |
| conv     | Maximum norm of last (unrelaxed) Newton correction              |
| nFcn*    | fcn evaluation count                                            |

(* marks inout parameters)

## Output parameters
-------------------
| Variable | Description                                                     |
|:---------|:----------------------------------------------------------------|
| a[lda,n] | Array containing the approximated Jacobian                      |
| eta[n]*  | Vector of scaled denominator differences adjusted               |
| nFcn*    | fcn evaluation count adjusted                                   |
| iFail    | Return code non-zero if Jacobian could not be computed          |
"""
function nJcf{T}(fcn, n::Int64, lda::Int64, x::Vector{T}, fx::Vector{T},
    yscal::Vector{T}, eta::Vector{T}, etamin::T, etamax::T,
    etadif::T, conv::T, nFcn::Int64, a::Array{T,2})
    # Constant
    small2 = 0.1
    # Copy for internal purposes
    xa = x[:]
    # Begin
    fu = zero(x)
    iFail = 0
    for k = 1:n
        is = 0
        qFine = false
        qExit = false
        while !qFine
            w = xa[k]
            su = sign(xa[k])
            if su == 0
                su = 1
            end
            u = eta[k]*yscal[k]*su
            xa[k] = w + u
            try
                fcn(fu,xa)
            catch
                iFail = -1
            end
            nFcn += 1
            if iFail != 0
                qExit = true
                break;
            end
            xa[k] = w
            sumd = 0.0
            for i = 1:n
                hg = max(abs(fx[i]),abs(fu[i]))
                fhi = fu[i] - fx[i]
                if hg != 0.0
                    sumd = sumd + (fhi/hg)^2
                end
                a[i,k] = fhi/u
            end
            sumd = sqrt(sumd/n)
            qFine = true
            if sumd != 0.0 && is == 0
                eta[k] = min(etamax,max(etamin,sqrt(etadif/sumd)*eta[k]))
                is = 1
                qFine = conv < small2 || sumd >= etamin
            end
        end
        if qExit
            break;
        end
    end
    return (nFcn,iFail)
end

"""
function nJcfb{T}(fcn, n::Int64, lda::Int64, ml::Int64, x::Vector{T},
    fx::Vector{T}, yscal::Vector{T}, eta::Vector{T},
    etamin::T, etamax::T, etadif::T, conv::T,
    nFcn::Int64, a::Array{T,2})

Approximation of banded Jacobian matrix for nonlinear systems solver with
feed-back control of discretization and rounding errors

T = Float64 or BigFloat

## Input parameters
-------------------
| Variable | Description                                                     |
|:---------|:----------------------------------------------------------------|
| fcn      | Function of the form fcn(f, x) to provide right-hand side       |
| n        | Number of rows and columns of the Jacobian                      |
| lda      | Leading dimension of the array "a"                              |
| ml       | Lower bandwidth of the Jacobian matrix                          |
| x[n]     | Current scaled vector                                           |
| fx[n]    | Vector containing fcn(x)                                        |
| yscal[n] | Vector containing scaling factors                               |
| eta[n]*  | Vector of scaled denominator differences                        |
| etamin   | Minimum allowed scaled denominator                              |
| etamax   | Maximum allowed scaled denominator                              |
| etadif   | = sqrt(1.1*epMach)                                              |
| conv     | Maximum norm of last (unrelaxed) Newton correction              |
| nFcn*    | fcn evaluation count                                            |

(* marks inout parameters)

## Output parameters
-------------------
| Variable | Description                                                     |
|:---------|:----------------------------------------------------------------|
| a[lda,n] | Array containing the approximated Jacobian                      |
| eta[n]*  | Vector of scaled denominator differences adjusted               |
| nFcn*    | fcn evaluation count adjusted                                   |
| iFail    | Return code non-zero if Jacobian could not be computed          |
"""
function nJcfb{T}(fcn, n::Int64, lda::Int64, ml::Int64, x::Vector{T},
    fx::Vector{T}, yscal::Vector{T}, eta::Vector{T},
    etamin::T, etamax::T, etadif::T, conv::T,
    nFcn::Int64, a::Array{T,2})
    # Constants
    small2 = 0.1
    # Copy for internal purposes
    xa = x[:]
    # Begin
    mu = lda - 2*ml - 1
    ldab = ml + mu + 1
    fu = zero(x)
    w  = zero(x)
    u  = zero(x)
    iFail = 0
    for jj = 1:ldab
        is = 0
        qFine = false
        qExit = false
        while !qFine
            for k = jj:ldab:n
                w[k] = xa[k]
                su = sign(xa[k])
                if su == 0
                    su = 1
                end
                u[k] = eta[k]*yscal[k]*su
                xa[k] = w[k] + u[k]
            end

            try
                fcn(fu,xa)
            catch
                iFail = -1
            end
            nFcn += 1
            if iFail != 0
                qExit = true
                break;
            end

            for k = jj:ldab:n
                xa[k] = w[k]
                sumd = 0.0
                i1 = max(1,k-mu)
                i2 = min(n,k+ml)
                mh = mu + 1 - k
                for i = i1:i2
                    hg = max(abs(fx[i]),abs(fu[i]))
                    fhi = fu[i] - fx[i]
                    if hg != 0.0
                        sumd += (fhi/hg)^2
                    end
                    a[mh+i,k:k] = fhi/u[k]
                end
                sumd = sqrt(sumd/n)
                qFine = true
                if sumd != 0.0 && is == 0
                    eta[k] = min(etamax,max(etamin,sqrt(etadif/sumd)*eta[k]))
                    is = 1
                    qFine = conv < small2 || sumd >= etamin
                end
            end
        end
        if qExit
            break;
        end
    end
    return (nFcn,iFail)
end


using BaseTestMulti
using Base.Test
using Distributions

@testset "BaseTestMulti examples" begin

	@mtestset "@mtestset block PASS" begin
		@mtest_values_vary rand(1:6)
	end

	@mtestset "@mtestset for a=$(a) b=$(b) PASS exc FAIL when a=b" for a in 3:-1:1, b in a:3
		x = rand(a:b)
		@test a <= x <= b
		@mtest_values_vary x
	end

	@mtestset "@mtestset block reps=1 FAIL" reps=1 begin
		@mtest_values_vary rand(1:6)
	end

	@testset "@mtestset with no description PASS" begin
		@mtestset begin
			@mtest_values_vary rand(1:6)
		end
	end

	@testset MultiTestSet "@testset MultiSet disables automatic repetitions FAIL" begin
		@mtest_values_vary rand(1:100)
	end

	@testset MultiTestSet "values_vary PASS" begin
		for x in [1,2,1]
			@mtest_values_vary x
		end
	end

	@testset MultiTestSet "values_vary FAIL" begin
		for x in [1,1,1]
			@mtest_values_vary x
		end
	end

	@testset MultiTestSet "values_are FAIL" begin
		for x in [:b,:a,:d]
			@mtest_values_are [:a,:d,:b] x
		end
	end

	@testset MultiTestSet "values_are FAIL" begin
		for x in [:b,:a,:d]
			@mtest_values_are [:a,:c,:b] x
		end
	end

	@testset MultiTestSet "values_include PASS" begin
		for x in ["i","j","k"]
			@mtest_values_include ["k","i"] x
		end
	end

	@testset MultiTestSet "values_include FAIL" begin
		for x in ["i","j","k"]
			@mtest_values_include ["k","q"] x
		end
	end

	@testset MultiTestSet "that_sometimes PASS" begin
		for x in 1:5
			@mtest_that_sometimes x % 2 == 0
		end
	end

	@testset MultiTestSet "that_sometimes FAIL" begin
		for x in [1,3,5]
			@mtest_that_sometimes x % 2 == 0
		end
	end

	@testset MultiTestSet "multiple (m)tests PASS" begin
		for x in 1:5
			@mtest_values_include [3,2,1] x
			@mtest_that_sometimes x % 2 == 0
			@test x > 0
			@mtest_values_vary x
			@mtest_values_are [4,5,3,2,1] x
		end
	end

	@testset MultiTestSet "evaluand (sometimes) raises an exception PASS & ERRORS" begin
		for x in [-4.0,-1.0,1.0,4.0,9.0]
			@mtest_values_include [2.0,3.0] sqrt(x)
		end
	end

	@testset MultiTestSet "distributed_as PASS" begin
		for x in 0:30
			@mtest_distributed_as DiscreteUniform(0,30) x
		end
	end

	@testset MultiTestSet "distributed_as FAIL" begin
		for x in 0:30
			@mtest_distributed_as Binomial(30,0.9) x
		end
	end

	@testset MultiTestSet "distributed_as against range PASS" alpha=0.05 begin
		for x in 0:30
			@mtest_distributed_as 0:30 x
		end
	end

	@mtestset "distributed_as uses correct scope (p=$p) FAIL (sometimes PASS for p=0.5)" reps=20 alpha=0.01 for p in [0.1, 0.5, 0.9]
		x = rand(DiscreteUniform(0,30))
		@mtest_distributed_as Binomial(30,p) x
	end

	@mtestset "@mtest macros inside if statements case: $case PASS (case1) & FAIL (case2)" for case in [:case1, :case2]
		x = rand(1:6)
		if case == :case1
			@mtest_values_include [1,3,4] x
		else
			@mtest_values_include [0,2,6] x
		end
	end

	@mtestset "distributed_as distribution" begin
		x = rand(Geometric(0.2))
		@mtest_distributed_as Geometric(0.2) x
	end

	@mtestset "distributed_as vector" begin
		x = rand(Geometric(0.3))
		@mtest_distributed_as [3  6  3  0  1  4  1  1  4  2] x
	end
end


_getlocation(d::Distributions.LogNormal) = d.μ
_getlocation(d::Distributions.MvLogNormal) = Distributions.location(d)
_getscale(d::Distributions.Normal) = var(d)
_getscale(d::Distributions.MvNormal) = cov(d)
_getscale(d::Distributions.LogNormal) = d.σ^2
_getscale(d::Distributions.MvLogNormal) = Distributions.scale(d)

#testing of the normal distribution
for p in [(1.0,2.0,4.0,2.0),
          (ones(2),4.0*eye(2),4.0*eye(2),2.0*ones(2)),
          (ones(2),2.0*ones(2),4.0*eye(2),2.0*ones(2))]
    cp2 = copy(p[2])
    n = distribution(:normal,p[1],p[2])
    @test_approx_eq mean(n) p[1]
    n = recenter(n,p[4])
    @test_approx_eq mean(n) p[4]
    @test_approx_eq _getscale(n) p[3]
    n = rescale(n,2.0)
    @test_approx_eq _getscale(n) 2.0*2.0*p[3]
    n = update(n,p[1],cp2)
    @test_approx_eq mean(n) p[1]
    @test_approx_eq _getscale(n) p[3]
end

for p in [(eye(2),4.0*eye(2)),
          (ones(2),4.0*eye(2))]
    n = distribution(:normal,eye(2))
    @test mean(n) == zeros(2) && cov(n) == eye(2)
    @test_throws MethodError recenter(n,ones(2))
    n = rescale(n,2.0)
    @test_approx_eq cov(n) 4.0*eye(2)
    n = update(n,p[1])
    @test_approx_eq cov(n) eye(2)
end

#testing of the lognormal distribution
for p in [(1.0,2.0,4.0,2.0),
          (ones(2),4.0*eye(2),4.0*eye(2),2.0*ones(2)),
          (ones(2),2.0*ones(2),4.0*eye(2),2.0*ones(2))]
    cp2 = copy(p[2])
    l1 = distribution(:lognormal,p[1],p[2])
    @test_approx_eq _getlocation(l1) log(p[1])
    l1 = recenter(l1,p[4])
    @test_approx_eq _getlocation(l1) log(p[4])
    @test_approx_eq _getscale(l1) p[3]
    l1 = rescale(l1,2.0)
    @test_approx_eq _getscale(l1) 4.0*p[3]
    l1 = update(l1,p[1],cp2)
    @test_approx_eq _getlocation(l1) log(p[1])
    @test_approx_eq _getscale(l1) p[3]
end

#testing various uniform distributions
for s in [:uniform,:laplace,:triangular]
    l1 = distribution(s,1.0,0.5)
    @test mean(l1) == 1.0 && scale(l1) == 0.5
    l1 = recenter(l1,2.0)
    @test mean(l1) == 2.0 && scale(l1) == 0.5
    l1 = rescale(l1,3.0)
    @test mean(l1) == 2.0 && scale(l1) == 1.5
    l1 = update(l1,1.0,1.0)
    @test mean(l1) == 1.0 && scale(l1) == 1.0
end

#testing the bactrian distribution
for p in [(:normal,1.0),
          (:laplace,sqrt(2.0)),
          (:triangular,6.0)]
    bs1 = GeneralizedMetropolisHastings._bactrianscale(Val{p[1]},0.5,0.95)
    bs2 = GeneralizedMetropolisHastings._bactrianscale(Val{p[1]},1.0,0.95)
    @test_approx_eq bs1 0.5*sqrt(p[2]*(1-0.95*0.95))
    @test_approx_eq bs2 1.0*sqrt(p[2]*(1-0.95*0.95))
    m1 = GeneralizedMetropolisHastings._mixturemodel(p[1],2.0,0.5,0.95)
    @test m1.components[1] == distribution(p[1],2.0-0.5*0.95,bs1)
    @test m1.components[2] == distribution(p[1],2.0+0.5*0.95,bs1)
    b1 = distribution(:bactrian,2.0,0.5,p[1],0.95)
    @test mean(b1.mixture) == 2.0 && b1.μ == 2.0 && b1.σ == 0.5 && b1.m == 0.95
    @test_approx_eq Distributions.params(b1.mixture.components[1])[1] 2.0-0.5*0.95
    @test_approx_eq Distributions.params(b1.mixture.components[2])[1] 2.0+0.5*0.95
    @test_approx_eq Distributions.params(b1.mixture.components[1])[2] bs1
    @test_approx_eq Distributions.params(b1.mixture.components[2])[2] bs1
    b1 = recenter(b1,1.0)
    @test mean(b1.mixture) == 1.0 && b1.μ == 1.0 && b1.σ == 0.5 && b1.m == 0.95
    @test_approx_eq Distributions.params(b1.mixture.components[1])[1] 1.0-0.5*0.95
    @test_approx_eq Distributions.params(b1.mixture.components[2])[1] 1.0+0.5*0.95
    @test_approx_eq Distributions.params(b1.mixture.components[1])[2] bs1
    @test_approx_eq Distributions.params(b1.mixture.components[2])[2] bs1
    b1 = rescale(b1,2.0)
    @test mean(b1.mixture) == 1.0 && b1.μ == 1.0 && b1.σ == 1.0 && b1.m == 0.95
    @test_approx_eq Distributions.params(b1.mixture.components[1])[1] 1.0-1.0*0.95
    @test_approx_eq Distributions.params(b1.mixture.components[2])[1] 1.0+1.0*0.95
    @test_approx_eq Distributions.params(b1.mixture.components[1])[2] bs2
    @test_approx_eq Distributions.params(b1.mixture.components[2])[2] bs2
    b1 = update(b1,2.0,0.5)
    @test mean(b1.mixture) == 2.0 && b1.μ == 2.0 && b1.σ == 0.5 && b1.m == 0.95
    @test_approx_eq Distributions.params(b1.mixture.components[1])[1] 2.0-0.5*0.95
    @test_approx_eq Distributions.params(b1.mixture.components[2])[1] 2.0+0.5*0.95
    @test_approx_eq Distributions.params(b1.mixture.components[1])[2] bs1
    @test_approx_eq Distributions.params(b1.mixture.components[2])[2] bs1
    @test_approx_eq Distributions.logpdf(b1,1.0) log(Distributions.pdf(b1.mixture,1.0))
    @test mean(b1) == mean(b1.mixture)
    @test var(b1) == var(b1.mixture)
    @test (srand(324) ; Distributions.rand(b1)) == (srand(324) ; Distributions.rand(b1.mixture))
    @test (srand(324) ; Distributions.rand!(b1,zeros(2))) == (srand(324) ; Distributions.rand!(b1.mixture,zeros(2)))
end

for p in [(:normal,Distributions.Normal),
          (:lognormal,Distributions.LogNormal),
          (:uniform,Distributions.Uniform),
          (:laplace,Distributions.Laplace),
          (:triangular,Distributions.SymTriangularDist)]
    d = distributions(p[1],[0.0,1.0],[1.0,2.0])
    @test eltype(d) == p[2]
end

#a test to ensure that all multivariate distributions copy their matrix arguments upon creation
#so that creating two from the same matrix does not introduce side-effects when recentering or rescaling
for p in [(:normal,ones(2),eye(2),2ones(2),mean,2ones(2),ones(2),cov,0.01*eye(2),eye(2)),
          (:lognormal,ones(2),eye(2),2ones(2),Distributions.location,log(2ones(2)),zeros(2),Distributions.scale,0.01*eye(2),eye(2))]
    println("Testing multivariate ",p[1]," copying behaviour")
    d1 = distribution(p[1],p[2],p[3])
    d2 = distribution(p[1],p[2],p[3])
    d1 = recenter(d1,p[4])
    @test_approx_eq p[5](d1) p[6]
    @test_approx_eq p[5](d2) p[7]
    d1 = rescale(d1,0.1)
    @test_approx_eq p[8](d1) p[9]
    @test_approx_eq p[8](d2) p[10]
end

for p in [(:normal,eye(2),cov,0.01eye(2),eye(2))]
    println("Testing multivariate ",p[1]," (zero mean) copying behaviour")
    d1 = distribution(p[1],p[2])
    d2 = distribution(p[1],p[2])
    d1 = rescale(d1,0.1)
    @test_approx_eq p[3](d1) p[4]
    @test_approx_eq p[3](d2) p[5]
end

#test of vectorized distribution factory function
d = distributions(:bactrian,[0.0,1.0],[1.0,2.0],:normal,0.95)
@test eltype(d) == GeneralizedMetropolisHastings.Bactrian{Float64}


# Collection of utility functions and commonly used constants.

using Statistics: mean

"Convert from spherical to Cartesian coordinates vector."
function sph2cart(r, ϕ, θ)
   r*[sin(θ)*cos(ϕ), sin(θ)*sin(ϕ), cos(θ)]
end

include("constants.jl")
include("current_sheet.jl")
include("dipole.jl")
include("confinement.jl")

"""
    set_axes_equal(ax)

Set 3D plot axes to equal scale for Matplotlib.
Make axes of 3D plot have equal scale so that spheres appear as spheres and cubes as cubes.
Required since `ax.axis('equal')` and `ax.set_aspect('equal')` don't work on 3D.
"""
function set_axes_equal(ax)
   limits = zeros(2,3)
   limits[:,1] .= ax.get_xlim3d()
   limits[:,2] .= ax.get_ylim3d()
   limits[:,3] .= ax.get_zlim3d()
   origin = mean(limits, dims=1)
   radius = @. 0.5 * max(abs(limits[2,:] - limits[1,:]))
   x, y, z = origin
   ax.set_xlim3d([x - radius[1], x + radius[1]])
   ax.set_ylim3d([y - radius[2], y + radius[2]])
   ax.set_zlim3d([z - radius[3], z + radius[3]])
end


function trs_boundary(P, q::AbstractVector{T}, r::T, A::AbstractMatrix{T}, b::AbstractVector{T}; kwargs...) where {T}
	project!, F = generate_nullspace_projector(A)
	x = find_feasible_point(b, r, project!, F)
	λ_max = max(maximum(eigs(P, nev=1, which=:LR)[1]), 0)
	return trs_boundary(P, q, r, project!, x, λ_max; kwargs...)
end

function generate_nullspace_projector(A::AbstractMatrix{T}) where T
	"""
	Generates a function project!(x) That projects x into the nullspace of A
	Also returns the factorization of the KKT matrix[I A'; A 0]
	"""
	F = factorize([I A'; A 0*I]) # KKT matrix
	n = size(A, 2)
	x_ = zeros(size(F, 1))
	y_ = zeros(size(F, 1))
	function project!(x::AbstractVector{T})
		copyto!(view(x_, 1:n), x)
		ldiv!(y_, F, x_)
		copyto!(x, view(y_, 1:n))
	end
	return project!, F
end

function find_feasible_point(b::AbstractVector{T}, r::T, project!, F::Factorization{T}) where T
	n = size(F, 1) - length(b)
	x = (F\[zeros(n); b])[1:n] # x is the minimizer of ‖x‖ with Ax = b
	@assert(norm(x) <= r, "The problem is infeasible.")
	d = project!(randn(n)) # Find a direction in the nullspace of A
	# Calculate alpha such that ‖x + alpha*d‖ = r
	alpha = roots(Poly([norm(x)^2 - r^2, 2*d'*x, norm(d)^2]))
	x += alpha[1]*d # Now ‖x‖ = r

	return x
end

function trs_boundary(P, q::AbstractVector{T}, r::T, project!, x::AbstractVector{T}, λ_max::T; kwargs...) where {T}
	"""
	Solves the TRS problem
	minimize    ½x'Px + q'x
	subject to  ‖x‖ = r
                Ax = b.
	Instead of passing A and b it is required to pass project! and x, where:
	- project!(x) projects (inplace) x to the nullspace of A; and
	- x is a point with ‖x‖ = r and Ax = b
	"""
	n = length(q)
	function p(y::AbstractVector{T}, x::AbstractVector{T}) where {T}
		mul!(y, P, x)
		# Substracting λ_max makes P negative definite which helps eigs for the constrained case
		axpy!(-λ_max, x, y)
		project!(y) # Project to the nullspace of A
	end
	x0 = x - project!(copy(x)) # x0 is perpendicular to the nullspace of A
	P_ = LinearMap{T}(p, n; ismutating=true, issymmetric=true)
	q_ = project!(q + P*x0 - λ_max*x0)
	r_ = norm(x - x0)
	output = trs_boundary((nev; kw...) -> eigenproblem(P_, q_, r_, nev;
			v0=[project!(randn(n)); project!(randn(n))], kw...,),
			(λ, V; kw...) -> pop_solution!(λ, V, P_, q_, r_, I; kw...); kwargs...)
	return shift_output(output..., x0, λ_max)
end

function shift_output(X, info, x0, λ_max)
	X .+= x0
	info.λ .-= λ_max
	return X, info
end

function trs(P, q::AbstractVector{T}, r::T, A::AbstractMatrix{T}, b::AbstractVector{T}; kwargs...) where {T}
	project!, F = generate_nullspace_projector(A)
	x = find_feasible_point(b, r, project!, F)
	λ_max = max(maximum(eigs(P, nev=1, which=:LR)[1]), 0)
	X, info = trs_boundary(P, q, r, project!, x, λ_max; kwargs...)

	# x0 is perpendicular to the nullspace of A
	x0 = X[:, 1] - project!(copy(X[:, 1]))
	# We remove x0 from the output(s) so that they belong to the nullspace of A
	shift_output(X, info, -x0, 0)
	# Define projected matrices (on the nullspace of A) for the cg
	P_projected = LinearMap{T}((y, x) -> project!(mul!(y, P, x)), length(x0);
		ismutating=true, issymmetric=true)
	q_projected = project!(q + P*x0)
	# Check interior solutions (if necessary) and add back x0
	X, info = check_interior!(X, info, P_projected, q_projected)
	return shift_output(X, info, x0, 0)
end


function hyperbolic_cross_weights(y::AbstractArray{T,1},nodes::Union{Array{T,1},Array{T,2}},multi_index::Array{S,2}) where {T <: AbstractFloat, S <: Integer}

  interpolation_matrix = zeros(size(nodes,1),size(nodes,1))

  n = 2*maximum(multi_index)+1

  unique_multi_index = sort(unique(multi_index))

  base_polynomials        = Array{Array{T,2},1}(undef,length(unique_multi_index))
  unique_base_polynomials = Array{Array{T,2},1}(undef,length(unique_multi_index))

  @inbounds for k in axes(nodes,1)

    # Construct the base polynomials

    for i in eachindex(unique_multi_index)
      base_polynomials[i] = chebyshev_polynomial(n-1,nodes[k,:])
    end

    # Compute the unique polynomial terms from the base polynomials

    for i in eachindex(unique_multi_index)
      if unique_multi_index[i] == 0
        unique_base_polynomials[i] = base_polynomials[i][:,1:1]
      else
        unique_base_polynomials[i] = base_polynomials[i][:,2*unique_multi_index[i]:2*unique_multi_index[i]+1]
      end
    end

    # Construct a row of the interplation matrix

    l = 1
    @inbounds @views for j in axes(multi_index,1)
      new_polynomials = unique_base_polynomials[multi_index[j,1]+1][1,:]
      for i = 2:size(multi_index,2)
        new_polynomials = kron(new_polynomials,unique_base_polynomials[multi_index[j,i]+1][i,:])
      end
      m = length(new_polynomials)
      interpolation_matrix[k,l:l+m-1] = new_polynomials
      l += m
    end

  end

  weights = interpolation_matrix\y

  return weights

end

function hyperbolic_cross_weights(y::AbstractArray{T,1},nodes::Union{Array{T,1},Array{T,2}},multi_index::Array{S,2},domain::Union{Array{T,1},Array{T,2}}) where {T<:AbstractFloat,S<:Integer}

  # Normalize nodes to the [-1.0 1.0] interval

  d = size(multi_index,2)
  grid = similar(nodes)
  for i = 1:d
    grid[:,i] = normalize_node(nodes[:,i],domain[:,i])
  end

  weights = hyperbolic_cross_weights(y,grid,multi_index)

  return weights

end

function hyperbolic_cross_weights_threaded(y::AbstractArray{T,1},nodes::Union{Array{T,1},Array{T,2}},multi_index::Array{S,2}) where {T<:AbstractFloat,S<:Integer}

  interpolation_matrix = zeros(size(nodes,1),size(nodes,1))

  n = 2*maximum(multi_index)+1

  unique_multi_index = sort(unique(multi_index))

  @inbounds @sync @qthreads for k in axes(nodes,1)

    base_polynomials        = Array{Array{T,2},1}(undef,length(unique_multi_index))
    unique_base_polynomials = Array{Array{T,2},1}(undef,length(unique_multi_index))

    # Construct the base polynomials

    for i in eachindex(unique_multi_index)
      base_polynomials[i] = chebyshev_polynomial(n-1,nodes[k,:])
    end

    # Compute the unique polynomial terms from the base polynomials

    for i in eachindex(unique_multi_index)
      if unique_multi_index[i] == 0
        unique_base_polynomials[i] = base_polynomials[i][:,1:1]
      else
        unique_base_polynomials[i] = base_polynomials[i][:,2*unique_multi_index[i]:2*unique_multi_index[i]+1]
      end
    end

    # Construct a row of the interplation matrix

    l = 1
    @inbounds @views for j in axes(multi_index,1)
      new_polynomials = unique_base_polynomials[multi_index[j,1]+1][1,:]
      for i = 2:size(multi_index,2)
        new_polynomials = kron(new_polynomials,unique_base_polynomials[multi_index[j,i]+1][i,:])
      end
      m = length(new_polynomials)
      interpolation_matrix[k,l:l+m-1] = new_polynomials
      l += m
    end

  end

  weights = interpolation_matrix\y

  return weights

end

function hyperbolic_cross_weights_threaded(y::AbstractArray{T,1},nodes::Union{Array{T,1},Array{T,2}},multi_index::Array{S,2},domain::Union{Array{T,1},Array{T,2}}) where {T<:AbstractFloat,S<:Integer}

  # Normalize nodes to the [-1.0 1.0] interval

  d = size(multi_index,2)
  grid = similar(nodes)
  for i = 1:d
    grid[:,i] = normalize_node(nodes[:,i],domain[:,i])
  end

  weights = hyperbolic_cross_weights_threaded(y,grid,multi_index)

  return weights

end

function hyperbolic_cross_weights(y::AbstractArray{T,1},inverse_interpolation_matrix::Array{T,2}) where {T<:AbstractFloat}

  weights = inverse_interpolation_matrix*y

  return weights

end


using LinearAlgebra, Plots, Statistics, TaylorSeries, Printf, JLD2, FileIO
# if !(pwd() in LOAD_PATH) push!(LOAD_PATH, pwd()) end
# using TaylorBigF

path1=realpath(dirname(@__FILE__)*"/..")
include(string(path1,"/src/TaylorBigF.jl"))

p=2000;setprecision(p); setrounding(BigFloat, RoundUp);
n=BigInt(20);# zigos lliws i prwti grammi tou Vall einai =0 sto x=0, (to idio k sta ODES)
nb=BigInt(2)*n+BigInt(2)*(n-BigInt(2)) # boundary points
dx=BigInt(1)/BigInt(99)
L=(n-BigInt(1))*dx
x0=zeros(BigFloat,n).-L/BigFloat("2")
x=Array{BigFloat}(undef,0)
for i=BigInt(1):n
    global x0,x,dx
    x=[x;x0.+(i-BigInt(1))*dx]
end
y=convert(Array{BigFloat},range(-L/BigFloat("2"),L/BigFloat("2"),length=n))
y=repeat(y,n)
# scatter(x,y)

V=TaylorBigF.calc_Vandermonde_2D(x,y,n*n,n*n+nb)
Vxxxx=TaylorBigF.calc_Vandermonde_xxxx_2D(x,y,n*n,n*n+nb)
Vyyyy=TaylorBigF.calc_Vandermonde_yyyy_2D(x,y,n*n,n*n+nb)
Vxxyy=TaylorBigF.calc_Vandermonde_xxyy_2D(x,y,n*n,n*n+nb)

Vall0=Vxxxx+2Vxxyy+Vyyyy
# boundaries
i_keep1=x.==-L/BigFloat("2")
i_keep2=x.==L/BigFloat("2")
i_keep3=y.==-L/BigFloat("2")
i_keep4=y.==L/BigFloat("2")
i_keep=i_keep1+i_keep2+i_keep3+i_keep4
i_b=i_keep.>0
# scatter!(x[i_b],y[i_b])
BigInt(sum(i_b))==nb
Vall1=TaylorBigF.calc_Vandermonde_2D(x[i_b],y[i_b],nb,n*n+nb)

Vall=[Vall0;Vall1]
q=[-ones(n*n);zeros(BigFloat,nb)]
@time a=Vall\q
maximum(abs.(Vall*a-q))
# scatter(x,y,zcolor=V*a)
# minimum(V*a)
# scatter3d(x,y,V*a,label=string("deflection (w), p=",p),legend=:topleft)

Vxxx=TaylorBigF.calc_Vandermonde_xxx_2D(x[i_b],y[i_b],nb,n*n+nb)
Vyyy=TaylorBigF.calc_Vandermonde_yyy_2D(x[i_b],y[i_b],nb,n*n+nb)
Vxyy=TaylorBigF.calc_Vandermonde_xyy_2D(x[i_b],y[i_b],nb,n*n+nb)
Vyxx=TaylorBigF.calc_Vandermonde_yxx_2D(x[i_b],y[i_b],nb,n*n+nb)
Qx=-(Vxxx+Vxyy)*a
Qy=-(Vyxx+Vyyy)*a
# scatter(x[i_b],y[i_b],zcolor=Qx)

i1=x.==-L/BigFloat("2")
i2=x.==L/BigFloat("2")
i12=i1+i2
i12=i12.>0
Vxxx=TaylorBigF.calc_Vandermonde_xxx_2D(x[i12],y[i12],2n,n*n+nb)
Vxyy=TaylorBigF.calc_Vandermonde_xyy_2D(x[i12],y[i12],2n,n*n+nb)
Qx=-(Vxxx+Vxyy)*a
# scatter(x[i12],y[i12],zcolor=Qx)
i3=y.==-L/BigFloat("2")
i4=y.==L/BigFloat("2")
i34=i3+i4
i34=i34.>0
Vyyy=TaylorBigF.calc_Vandermonde_yyy_2D(x[i34],y[i34],2n,n*n+nb)
Vyxx=TaylorBigF.calc_Vandermonde_yxx_2D(x[i34],y[i34],2n,n*n+nb)
Qy=-(Vyxx+Vyyy)*a
# scatter!(x[i34],y[i34],zcolor=Qy)

mean(abs.(Qx))*2*L+mean(abs.(Qy))*2*L-1*L*L

# point load
q=zeros(BigFloat,n*n+nb)
dd=zeros(BigFloat,n*n)
for i=1:n*n
    dd[i]=sqrt(x[i].^2+y[i].^2)
end
iso=sortperm(dd)[1:4]
# scatter(x[iso],y[iso])
q[iso].=-BigFloat("1")

@time a=Vall\q
maximum(abs.(Vall*a-q))
# scatter(x,y,zcolor=V*a)
minimum(V*a)
# scatter3d(x,y,V*a,label=string("deflection (w), p=",p),legend=:topleft)
# savefig(string("pdes_p_",p,".pdf"))

Qx=-(Vxxx+Vxyy)*a
Qy=-(Vyxx+Vyyy)*a
# scatter(x[i34],y[i34],zcolor=Qy)

mean(abs.(Qx))*2*L+mean(abs.(Qy))*2*L-1*dx*dx


function spharm( l, m, Gvec )

  Ng = size(Gvec)[2]

  r = zeros( Float64, Ng )
  for ig = 1:Ng
    r[ig] = sqrt( Gvec[1,ig]^2 + Gvec[2,ig]^2 + Gvec[3,ig]^2 )
  end
  # This will fail for very small r[ig]

  ylm = zeros( Float64, Ng )

  if l == 0
    for ig = 1:Ng
      ylm[ig] = 0.5*sqrt(1.0/pi)
    end
    return ylm

  elseif l == 1
    #
    if m == -1
      for ig = 1:Ng
        ylm[ig] = 0.5*sqrt(3/pi)*Gvec[2,ig]/r[ig]
      end
      return ylm
    #
    elseif m == 0
      for ig = 1:Ng
        ylm[ig] = 0.5*sqrt(3/pi)*Gvec[3,ig]/r[ig]
      end
      return ylm
    #
    elseif m == 1
      for ig = 1:Ng
        ylm[ig] = 0.5*sqrt(3/pi)*Gvec[1,ig]/r[ig]
      end
      return ylm
    end

  elseif l == 2
    #
    if m == -2
      for ig = 1:Ng
        ylm[ig] = 0.5*sqrt(15/pi)*Gvec[1,ig]*Gvec[2,ig]/r[ig]^2
      end
      return ylm
    #
    elseif m == -1
      for ig = 1:Ng
        ylm[ig] = 0.5*sqrt(15/pi)*Gvec[2,ig]*Gvec[3,ig]/r[ig]^2
      end
      return ylm
    #
    elseif m == 0
      for ig = 1:Ng
        ylm[ig] = 0.25*sqrt(5/pi)*( -Gvec[1,ig]^2 - Gvec[2,ig]^2 + 2*Gvec[3,ig]^2 )/r[ig]^2
      end
      return ylm
    #
    elseif m == 1
      for ig = 1:Ng
        ylm[ig] = 0.5*sqrt(15/pi)*Gvec[3,ig]*Gvec[1,ig]/r[ig]^2
      end
      return ylm
    #
    elseif m == 2
      for ig = 1:Ng
        ylm[ig] = 0.25*sqrt(15/pi)*(Gvec[1,ig]^2 - Gvec[2,ig]^2)/r[ig]^2
      end
      return ylm

    end

  end

end


julia> using IntervalArithmetic

julia> n = 2.0
2.0

julia> @interval 2n/3 + 1
[2.33333, 2.33334]

julia> showall(ans)
Interval(2.333333333333333, 2.3333333333333335)

julia> a = @interval(0.1, 0.3)
[0.0999999, 0.300001]

julia> b = @interval(0.3, 0.6)
[0.299999, 0.600001]

julia> a + b
[0.399999, 0.900001]


######################### Vector Functions ########################
# Assumes sizes are equal
function dot(vec1, vec2)
    sum = 0.0
    @fastmath for i in eachindex(vec1)
        sum += vec1[i]*vec2[i]
    end
    return sum
end

# Assumes sizes are equal
function dot(vec1::Vector{Float64}, vec2::Vector{Float64})
    sum = 0.0
    @fastmath for i in eachindex(vec1)
        sum += vec1[i]*vec2[i]
    end
    return sum
end

function dot(vec::Vector{Float64}, matrix::Vector{Vector{Float64}})
    nRows = size(matrix, 1)
    result = Array{Float64, 1}(undef, nRows)
    @fastmath for i in 1:nRows
        result[i] = dot(vec, matrix[i])
    end
    return result
end

function dot(vec::Vector{Float64}, matrix::Matrix{Float64})
    nRows = size(matrix, 1)
    result = Vector{Float64}(undef, nRows)
    @fastmath for i in 1:nRows
        @views result[i] = dot(vec, matrix[i, :])
    end
    return result
end

function cross(v1::Array{Float64, 1}, v2::Array{Float64, 1})
    x = v1[2]*v2[3] - v1[3]*v2[2]
    y = -(v1[1]*v2[3] - v1[3]*v2[1])
    z = v1[1]*v2[2] - v1[2]*v2[1]
    return [x,y,z]
end

function mag(vec)
    #Returns 2-norm of vector
    sqrSum = 0.0
    @fastmath for i = 1:size(vec,1)
        sqrSum += vec[i]*vec[i]
    end
    return sqrt(sqrSum)
end

function normalize(vec)
    #Turns a vector into a unit vector
    @fastmath return vec ./ mag(vec)
end


@testset "$(rpad("Lagrange Basis Tests",80))" begin

    b = LagrangeBasis([0.0, 1.0])

    @test evaluate(b, 1, 0.0) == 1.0
    @test evaluate(b, 1, 0.5) == 0.5
    @test evaluate(b, 1, 1.0) == 0.0

    @test evaluate(b, 2, 0.0) == 0.0
    @test evaluate(b, 2, 0.5) == 0.5
    @test evaluate(b, 2, 1.0) == 1.0

    @test derivative(b, 1, 0.0) == -1.0
    @test derivative(b, 1, 0.5) == -1.0
    @test derivative(b, 1, 1.0) == -1.0

    @test derivative(b, 2, 0.0) == 1.0
    @test derivative(b, 2, 0.5) == 1.0
    @test derivative(b, 2, 1.0) == 1.0

    @test derivative(b, 1, 1) == -1.0
    @test derivative(b, 1, 2) == -1.0

    @test derivative(b, 2, 1) == 1.0
    @test derivative(b, 2, 2) == 1.0

    @test integral(b, 1, 0.0) == 0.0
    @test integral(b, 1, 0.5) == 0.375
    @test integral(b, 1, 1.0) == 0.5

    @test integral(b, 2, 0.0) == 0.0
    @test integral(b, 2, 0.5) == 0.125
    @test integral(b, 2, 1.0) == 0.5

    @test integral(b, 1, 1) == 0.0
    @test integral(b, 1, 2) == 0.5

    @test integral(b, 2, 1) == 0.0
    @test integral(b, 2, 2) == 0.5

end


#    Problem : GROUP A
#    *********
#    Extended Cragg and Levy problem.
#    This problem is a sum of m  sets of 5 groups,
#    There are 2m+2 variables. The Hessian matrix is 7-diagonal.
#
#    Original SIF Source:
#    Ph. L. Toint,
#    "Test problems for partially separable optimization and results for the routine
#    PSPMIN", Report 83/4, Department of Mathematics, FUNDP (Namur, B), 1983.
#
#    See also Buckley#18
#
#    Implementation translated from Source:
#    http://eprints.tsu.ge/234/14/Tests%20collection-K-F.pdf
#
#    CRAGGLVY.SIF classification  OUR2-AY-V-0
#
#    Number of variables (variable, based on imput M, the number of group sets)

f = (x) -> begin
    fx = 1.0
    for i in firstindex(x):2:lastindex(x)-3
        item1   = exp(x[i]) - x[i+1]
        item2   = x[i+1] - x[i+2]
        element = x[i+2] - x[i+3]
        item3   = tan(element)+element
        item4   = x[i+3] - 1
        fx     += item1^4 + 100item2^6 + item3^4 + x[i]^8 + item4^2
    end
    return fx
end

g! = (g, x) -> begin
    for i in firstindex(x):2:lastindex(x)-3
        item1   = exp(x[i]) - x[i+1]
        item2   = x[i+1] - x[i+2]
        element = x[i+2] - x[i+3]
        item3   = tan(element)+element
        item4   = x[i+3] - 1
        g[i]   += 4*item1^3*exp(x[i]) + 8*x[i]^7
        g[i+1] += -4item1^3 + 600item2^5
        g[i+2] += -600item2^5 + 4item3^3 * (cos(element)^(-2) + 1)
        g[i+3] += -4.0item3^3*(cos(element)^(-2) + 1.0) + 2*item4
    end
    return g
end

fg! = (g, x) -> begin
    fx = 1.0
    for i in firstindex(x):2:lastindex(x)-3
        item1   = exp(x[i]) - x[i+1]
        item2   = x[i+1] - x[i+2]
        element = x[i+2] - x[i+3]
        item3   = tan(element)+element
        item4   = x[i+3] - 1
        fx     += item1^4 + 100item2^6 + item3^4 + x[i]^8 + item4^2
        g[i]   += 4*item1^3*exp(x[i]) + 8*x[i]^7
        g[i+1] += -4item1^3 + 600item2^5
        g[i+2] += -600item2^5 + 4item3^3 * (cos(element)^(-2) + 1)
        g[i+3] += -4.0item3^3*(cos(element)^(-2) + 1.0) + 2*item4
    end
    return fx, g
end

init = (n::Int=5000) -> begin
	n < 2 && @warn("CRAGGLVY: number of variables must be ≥ 2")
	n = max(n, 2)

    x0 = 2*ones(5000)
    x0[1] = 1.0
    return n, x0
end

@warn "TODO: Debug CRAGGLVY"
# TestSet["CRAGGLVY"] = UncProgram("CRAGGLVY",  f, g!, fg!, init)