using LightGraphs

function betweenness_centrality(c::Int, n::Int, k::Int)
    bc, elapsed = 0.0, 0.0
    for i in 1:c
        graph = barabasi_albert(n, k)
        elapsed += @elapsed begin
            bc += mean(LightGraphs.betweenness_centrality(graph))
            #  bc += mean(parallelbetweenness_centrality(graph))
        end
    end
    bc /= c
    elapsed /= c
    println("Betweenness Centrality:  $bc ($(elapsed)s/call)")
end

function clustering_coefficient(c::Int, n::Int, k::Int)
    cc, elapsed = 0.0, 0.0
    for i in 1:c
        graph = barabasi_albert(n, k)
        elapsed += @elapsed begin
            cc += mean(local_clustering_coefficient(graph))
        end
    end
    cc /= c
    elapsed /= c
    println("Average Clustering Coefficient:  $cc ($(elapsed)s/call)")
end

function main()
    n = parse(Int, ARGS[1])
    k = parse(Int, ARGS[2])
    if length(ARGS) >= 3
        srand(parse(Int, ARGS[3]))
    end

    betweenness_centrality(5, n, k)
    clustering_coefficient(10, n, k)
end

main()


@testset "Statistics" begin
    @testset "Statistics: Ordinary Least Squares" begin
        a = [10, 14,17,21,20, 18, 42, 51, 77, 11, 91]
        b = [0.11,0.7,0.2,0.19,0.09,0.8, 0.71,0.1, 0.6,0.3,0.81]
        OLSbeta(a,b)

        df = DataFrame(a=a, b=b)
        ols = GLM.lm(@formula(a~b), df)
        coef(ols) # print coefficients

        # Predict
        predict(ols) # predict the model
    end

end


module ParamLevelSet
using LinearAlgebra
using SparseArrays
using jInv.Mesh
using jInv.Utils
using NearestNeighbors

export centerHeaviside,deltaHeaviside,getDefaultHeaviside
centerHeavySide = 0.3;
deltaHeavySide = 0.05;

function getDefaultHeaviside()
	return (u,dsu)->heaviside!(u,dsu,ParamLevelSet.centerHeavySide,ParamLevelSet.deltaHeavySide);
end

## Other things to include: bf type in psi.
## Thresholding parameters for the Jacobians.

function getIdentityMat(n::Int64)
return Matrix(1.0I,n,n);

end
include("SpMatBuilder.jl");
include("RBFs.jl");
include("MeshFreeRBFs.jl");
include("RBFs_extended.jl");
include("heaviside.jl");
include("RBFRotation3D.jl");
include("SPD_regularization.jl");
include("rotation3D.jl")

end


# function fit(::Type{Gamma}, m::AbstractMoments)
#     # https://en.wikipedia.org/wiki/Gamma_distribution
#     n_moments(m) >= 1 || error("Need mean to estimate exponential")
#     return Gamma(mean(m))
# end

function fit(::Type{Gamma}, lower::QuantilePoint, upper::QuantilePoint)
    # https://www.johndcook.com/quantiles_parameters.pdf
    #    Shape-scale paras, shape α - cook:α - w:k, scale θ - cook:β - w:θ
    0 < lower.p < upper.p < 1 || error(
        "Expected 0 < lower.p < upper.p < 1, but got " *
        "lower.p = $(lower.p) and upper.p = $(upper.p)")
    0 < lower.q < upper.q || error(
        "Expected 0 < lower.q < upper.q, but got " *
        "lower.q = $(lower.q) and upper.q = $(upper.q)")
    logp1 = log(lower.p)
    logp2 = log(upper.p)
    lhs = upper.q/lower.q
    #α = 1
    fcost = (α) -> begin
        gamma1 = Gamma(α,1)
        rhs = invlogcdf(gamma1, logp2)/invlogcdf(gamma1, logp1)
        abs2(rhs - lhs)
    end
    resOpt = optimize(fcost, 1e-2, 1e8)
    resOpt.converged || error("Could not fit distribution to quantiles. resOpt=$resOpt")
    α_opt = resOpt.minimizer
    fbeta(α) = lower.q / invlogcdf(Gamma(α,1), log(lower.p))
    Gamma(α_opt, fbeta(α_opt))
end

# function fit_mean_quantile(::Type{Gamma}, mean::Real, qp::QuantilePoint)
#     # only fit to mean
#     fit(Type{Gamma}, AbstractMoments(mean))
# end

# function fit_mode_quantile(::Type{Gamma}, mode::Real, qp::QuantilePoint)
#     # ignore mode (its always at 0)
#     θ = -qp.q/log(1-qp.p)
#     Gamma(θ)
# end


#=
Doing dot product with a layer of neurons and multiple inputs
Associated YT NNFS tutorial: https://www.youtube.com/watch?v=tMrbN67U9d4
=#

inputs = [1.0, 2.0, 3.0, 2.5]
weights = [ 0.2   0.8  -0.5   1.0;
            0.5  -0.91  0.26 -0.5;
           -0.26 -0.27  0.17  0.87]

biases = [2.0, 3.0, 0.5]

output = weights * inputs + biases
println(output)


export LinearCongruentialGenerator

@doc raw"""
    LinearCongruentialGenerator(a, c, m, seed)

A linear congruential generator.

A linear congruential generator (LCG) is a primitive psueudo-random number
generator (PRNG), producing samples that approximate a ``\text{Uniform}(0, 1)``
distribution. Starting from a seed ``X_0``, a sequence is generated according
to the recurence relation,

```math
X_{n+1} = (aX_n + c) \mod m
```

which are then turned into samples ``Z_n = \frac{X_n}{m} \in [0, 1)``.

Although the generator is favoured for esoteric purposes due to its simplicity
and speed, in practice it is highly flawed and can result in samples that have
minimal random structure when viewed through an appropriate projection (as in
[Entacher](https://dl.acm.org/doi/10.1145/272991.273009)). The generator is
also highly sensitive to the choice of ``a``, ``c``, and ``m``.

# Arguments
* `a::Integer`: the "multiplier"; must be between 0 and m, exclusive.
* `c::Integer`: the "increment"; must be between 0 and m-1, inclusive.
* `m::Integer`: the "modulus"; must be non-negative
    proposal distribution.
* `seed::Integer`: the seed value; must be between 0 and m-1 inclusive

# Examples
```julia
# Generating 10 uniform random variables
# Parameters taken from "Numerical Recipes"
a = 1664525
c = 1013904223
m = 2^32
seed = 1729
s = LinearCongruentialGenerator(a, c, m, seed)
samples = sample(s, 10)
```

```julia
# Batch sampling
s = LinearCongruentialGenerator(a, c, m, seed)
samples1 = sample(s, 10)
samples2 = sample(s, 10)
# samples1 ≠ samples2 in general
```
"""
struct LinearCongruentialGenerator <: Sampler
    a::Integer
    c::Integer
    m::Integer
    seed::Integer
    state::Vector{Int}
    function LinearCongruentialGenerator(
        a::Integer,
        c::Integer,
        m::Integer,
        seed::Integer
    )
        # Validation
        if !(0 < m)
            error("m must be positive")
        elseif !(0 < a < m)
            error("a must be between 1 and m, exclusive")
        elseif !(0 <= c < m)
            error("c must be between 0 and m-1, inclusive")
        elseif !(0 ≤ seed < m)
            error("seed must be between 0 and m-1, inclusive")
        end
        state = [seed]
        new(a, c, m, seed, state)
    end
end

@doc raw"""
    sample(s::LinearCongruentialGenerator, N)

Generate samples using a linear congruential generator.

# Arguments
* `s::LinearCongruentialGenerator`: a linear congruential generator.
* `N::Integer`: the number of samples to generate.

# Returns
* `samples::Array{Float64}`: a 2-D array in which each column is a sample.
"""
function sample(s::LinearCongruentialGenerator, N::Integer)
    samples = Array{Float64}(undef, 1, N)
    for i in 1:N
        s.state[1] = (s.a * s.state[1] + s.c) % s.m
        samples[i] = s.state[1] / s.m
    end

    return samples
end


using JuMP, MathProgBase, MathOptInterface
using GLPK, GLPKMathProgInterface
using Cbc, Clp, Gurobi

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

function solveIt(gear::Vector{KPDSupply{T}}, capacity::Integer, solver::DataType) where T<:Integer
    w = getfield.(gear, :weight)
    v = getfield.(gear, :value)
    q = getfield.(gear, :quant)
    sol = mixintprog(-v, w', '<', capacity, :Int, 0, q, solver())
    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

gear = [KPDSupply("map", 9, 150, 1),
        KPDSupply("compass", 13, 35, 1),
        KPDSupply("water", 153, 200, 2),
        KPDSupply("sandwich", 50, 60, 2),
        KPDSupply("glucose", 15, 60, 2),
        KPDSupply("tin", 68, 45, 3),
        KPDSupply("banana", 27, 60, 3),
        KPDSupply("apple", 39, 40, 3),
        KPDSupply("cheese", 23, 30, 1),
        KPDSupply("beer", 52, 10, 3),
        KPDSupply("suntan cream", 11, 70, 1),
        KPDSupply("camera", 32, 30, 1),
        KPDSupply("T-shirt", 24, 15, 2),
        KPDSupply("trousers", 48, 10, 2),
        KPDSupply("umbrella", 73, 40, 1),
        KPDSupply("waterproof trousers", 42, 70, 1),
        KPDSupply("waterproof overclothes", 43, 75, 1),
        KPDSupply("note-case", 22, 80, 1),
        KPDSupply("sunglasses", 7, 20, 1),
        KPDSupply("towel", 18, 12, 2),
        KPDSupply("socks", 4, 50, 1),
        KPDSupply("book", 30, 10, 2)]
mySolver = CbcSolver
pack = solveIt(gear, 400, mySolver)
println("The hiker should pack: \n - ", join(pack, "\n - "))
println("\nPacked weight: ", sum(getfield.(pack, :weight)), " kg")
println("Packed value: ", sum(getfield.(pack, :value)), " €")


import PyCall, SymPy

using MLStyle

stats = PyCall.pyimport_conda("sympy.stats", "sympy")
SymPy.import_from(stats)

sym(x) = SymPy.symbols(x)

macro ℓ(expr)
    args = @match expr begin
        Expr(head, args...) => args
    end
    d = args[1]
    ps = args[2:end]
    quote
        L = SymPy.density(stats.$d(:foo, sym.($ps)...)).pdf(sym(:x))
        SymPy.sympy.expand_log(log(L), force=true)
    end
end

@ℓ Cauchy(μ, σ)


function ReadData(fname::AbstractString)
    lines = []

    xMax, xMin = 0, 1
    yMax, yMin = 0, 1

    for line in eachline(fname)
        push!(lines, map(y-> map(x -> parse(Int, x), split(y, ",")), split(line, " -> ")))

        x0, y0 = lines[length(lines)][1]
        x1, y1 = lines[length(lines)][2]

        if min(x0, x1) < xMin
            xMin = min(x0, x1)
        end
        if max(x0, x1) > xMax
            xMax = max(x0, x1)
        end
        if min(y0, y1) < yMin
            yMin = min(y0, y1)
        end
        if max(y0, y1) > yMax
            yMax = max(y0, y1)
        end
    end

    return lines, (xMin, xMax), (yMin, yMax)
end

function IsVertical(line::Array)
    return line[1][1] == line[2][1]
end

function IsHorizontal(line::Array)
    return line[1][2] == line[2][2]
end

function IsDiagonal(line::Array)
    return abs(line[1][1]-line[2][1]) == abs(line[1][2]-line[2][2])
end

function Part1(data)
    lines = data[1]
    xMin, xMax = data[2]
    yMin, yMax = data[3]

    grid = zeros(Int, yMax+1, xMax+1)

    for line in lines
        if IsVertical(line)
            y0, y1 = 0, 0
            if line[1][2] > line[2][2]
                y0, y1 = line[2][2], line[1][2]
            else
                y0, y1 = line[1][2], line[2][2]
            end

            grid[(y0+1):(y1+1), line[1][1]+1] += fill(1, y1-y0+1)
        end

        if IsHorizontal(line)
            x0, x1 = 0, 0
            if line[1][1] > line[2][1]
                x0, x1 = line[2][1], line[1][1]
            else
                x0, x1 = line[1][1], line[2][1]
            end

            grid[line[1][2]+1, (x0+1):(x1+1)] += fill(1, x1-x0+1)
        end
    end

    #for i in 1:(yMax+1)
    #    println(grid[i,:])
    #end

    return count(x -> x>=2, grid)
end

function Part2(data)
    lines = data[1]
    xMin, xMax = data[2]
    yMin, yMax = data[3]

    grid = zeros(Int, yMax+1, xMax+1)

    for line in lines
        if IsVertical(line)
            y0, y1 = 0, 0
            if line[1][2] > line[2][2]
                y0, y1 = line[2][2], line[1][2]
            else
                y0, y1 = line[1][2], line[2][2]
            end

            grid[(y0+1):(y1+1), line[1][1]+1] += fill(1, y1-y0+1)

        elseif IsHorizontal(line)
            x0, x1 = 0, 0
            if line[1][1] > line[2][1]
                x0, x1 = line[2][1], line[1][1]
            else
                x0, x1 = line[1][1], line[2][1]
            end

            grid[line[1][2]+1, (x0+1):(x1+1)] += fill(1, x1-x0+1)

        else
            x0, x1 = line[1][1], line[2][1]
            y0, y1 = line[1][2], line[2][2]

            dx = x1 > x0 ? 1 : -1
            dy = y1 > y0 ? 1 : -1

            for i = 0:(abs(x1-x0))
                grid[y0 + dy*i + 1, x0 + dx*i + 1] += 1
            end
        end

    end

    #for i in 1:yMax+1
    #    println(grid[i,:])
    #end

    return count(x -> x>=2, grid)
end

#data = ReadData("../data/test5.txt")
data = ReadData("../data/day5.txt")

#println(Part1(data))
println(Part2(data))

# 0,0 -> 8,8
# 8,0 -> 0,8
# NOT 6,4 -> 2,0


using Test

# Category
##########

A, B = Ob(FreeCategory, :A), Ob(FreeCategory, :B)
f, g = Hom(:f, A, B), Hom(:g, B, A)

# Expression types
@test isa(A, FreeCategory.Ob) && isa(f, FreeCategory.Hom)
@test isa(A, ObExpr) && isa(f, HomExpr)
@test isa(A, CategoryExpr) && isa(f, CategoryExpr)

# Domains and codomains
@test dom(f) == A
@test codom(f) == B
@test dom(compose(f,g)) == A
@test codom(compose(f,g)) == A
@test_throws SyntaxDomainError compose(f,f)

# Associativity
@test compose(compose(f,g),f) == compose(f,compose(g,f))

# Extra syntax
@test compose(f,g,f) == compose(compose(f,g),f)
@test compose([f,g,f]) == compose(compose(f,g),f)
@test f⋅g == compose(f,g)
@test f⋅g⋅f⋅g == compose(f,g,f,g)

# String format
@test string(A) == "A"
@test string(f) == "f"
@test string(compose(f,g)) == "compose(f,g)"
@test string(compose(f,g,f)) == "compose(f,g,f)"
@test string(Ob(FreeCategory, nothing)) != ""

# S-expressions
@test sexpr(A) == ":A"
@test sexpr(f) == ":f"
@test sexpr(compose(f,g)) == "(compose :f :g)"
@test sexpr(compose(f,g,f)) == "(compose :f :g :f)"

# Infix notation (Unicode)
@test unicode(A) == "A"
@test unicode(A, all=true) == "A"
@test unicode(f) == "f"
@test unicode(f, all=true) == "f: A → B"
@test unicode(id(A)) == "id{A}"
@test unicode(compose(f,g)) == "f⋅g"

# Infix notation (LaTeX)
@test latex(A) == "A"
@test latex(A, all=true) == raw"$A$"
@test latex(f) == "f"
@test latex(f, all=true) == raw"$f : A \to B$"
@test latex(id(A)) == "\\mathrm{id}_{A}"
@test latex(compose(f,g)) == "f \\cdot g"

@test latex(Ob(FreeCategory, "x")) == "x"
@test latex(Ob(FreeCategory, "sin")) == "\\mathrm{sin}"
@test latex(Ob(FreeCategory, "\\alpha")) == "\\alpha"

# (Co)presheaf
##############

A, B = Ob(FreePresheaf, :A, :B)
f = Hom(:f, A, B)
x = El(:x, B)
@test ob(x) == B
@test ob(coact(f, x)) == A
@test f ⋅ x == coact(f, x)

A, B = Ob(FreeCopresheaf, :A, :B)
f = Hom(:f, A, B)
x = El(:x, A)
@test ob(x) == A
@test ob(act(x, f)) == B
@test x ⋅ f == act(x, f)

# Infix notation (Unicode)
@test unicode(x) == "x"
@test unicode(x, all=true) == "x: A"
@test unicode(act(x, f)) == "x⋅f"
@test unicode(act(x, f), all=true) == "x⋅f: B"

# Infix notation (LaTeX)
@test latex(x) == "x"
@test latex(x, all=true) == raw"$x : A$"
@test latex(act(x, f)) == raw"x \cdot f"
@test latex(act(x, f), all=true) == raw"$x \cdot f : B$"


include("divisor.jl")

function compute()

	triangle=0
	for i=1:100000000
		triangle+=i;
		if num_divisors(triangle)>500
			return triangle
		end
	end
end

print(compute())


using DReal

import DReal: init_dreal!, Context, Var, add!, set_precision!, is_satisfiable, model

begin
# Blank slate
reset_ctx!()

# ## Example
# ## =======
init_dreal!()
ctx = Context(qf_nra)
set_precision!(ctx, 0.00001)
x = Var(ctx, Float64, "x", -3.141592, 3.141592)
y = Var(ctx, Float64, "y", -3.141592, 3.141592)
add!(sin(x) == cos(y))
res = is_satisfiable(ctx)
if res
  println("x = $(model(ctx,x))")
  println("y = $(model(ctx,y))")
end
end


# Independent Component Analysis

#### FastICA type

abstract type AbstractICAAlg end

struct FastICA <: AbstractICAAlg end

struct ICA{T<:Real}
    mean::Vector{T}   # mean vector, of length m (or empty to indicate zero mean)
    W::Matrix{T}      # component coefficient matrix, of size (m, k)
end

indim(M::ICA) = size(M.W, 1)
outdim(M::ICA) = size(M.W, 2)
mean(M::ICA) = fullmean(indim(M), M.mean)

transform(M::ICA, x::AbstractVecOrMat) = transpose(M.W) * centralize(x, M.mean)

#### core algorithm

# the abstract type for all g functions:
#
# Let f be an instance of such type, then
#
#   evaluate(f, x) --> (v, d)
#
# It returns a function value v, and derivative d
#
abstract type ICAGDeriv end

struct Tanh{T} <: ICAGDeriv
    a::T
end

evaluate(f::Tanh{T}, x::T) where T<:Real = (a = f.a; t = tanh(a * x); (t, a * (1 - t * t)))

function update_UE!(f::Tanh{T}, U::AbstractMatrix{T}, E1::AbstractVector{T}) where T
    n,k = size(U)
    _s = zero(T)
    a = f.a
    @inbounds for j = 1:k
        @inbounds @fastmath for i = 1:n
            t = tanh(a * U[i,j])
            U[i,j] = t
            _s += a * (1 - t^2)
        end
        E1[j] = _s / n
    end
end

struct Gaus <: ICAGDeriv end

evaluate(f::Gaus, x::T) where T<:Real = (x2 = x * x; e = exp(-x2/2); (x * e, (1 - x2) * e))

function update_UE!(f::Gaus, U::AbstractMatrix{T}, E1::AbstractVector{T}) where T
    n,k = size(U)
    _s = zero(T)
    @inbounds for j = 1:k
        for i = 1:n
            u = U[i,j]
            u2 = u^2
            e = exp(-u2/2)
            U[i,j] = u * e
            _s += (1 - u2) * e
        end
        E1[j] = _s / n
    end
end

# Fast ICA
#
# Reference:
#
#   Aapo Hyvarinen and Erkki Oja
#   Independent Component Analysis: Algorithms and Applications.
#   Neural Network 13(4-5), 2000.
#
function ica!(::FastICA, W::DenseMatrix{T},      # initialized component matrix, size (m, k)
                  X::DenseMatrix{T},      # (whitened) observation sample matrix, size(m, n)
                  fun::ICAGDeriv,         # approximate neg-entropy functor
                  maxiter::Int,           # maximum number of iterations
                  tol::Real) where T<:Real# convergence tolerance

    # argument checking
    m = size(W, 1)
    k = size(W, 2)
    size(X, 1) == m || throw(DimensionMismatch("Sizes of W and X mismatch."))
    n = size(X, 2)
    k <= min(m, n) || throw(DimensionMismatch("k must not exceed min(m, n)."))

    @debug "FastICA Algorithm" m=m n=n k=k

    # pre-allocated storage
    Wp = Matrix{T}(undef, m, k)    # to store previous version of W
    U  = Matrix{T}(undef, n, k)    # to store w'x & g(w'x)
    Y  = Matrix{T}(undef, m, k)    # to store E{x g(w'x)} for components
    E1 = Vector{T}(undef, k)       # store E{g'(w'x)} for components

    # normalize each column
    for j = 1:k
        w = view(W,:,j)
        rmul!(w, one(T) / sqrt(sum(abs2, w)))
    end

    # main loop
    chg = T(NaN)
    t = 0
    converged = false
    @inbounds while !converged && t < maxiter
        t += 1
        copyto!(Wp, W)

        # apply W of previous step
        mul!(U, transpose(X), W) # u <- w'x

        # compute g(w'x) --> U and E{g'(w'x)} --> E1
        update_UE!(fun, U, E1)

        # compute E{x g(w'x)} --> Y
        rmul!(mul!(Y, X, U), one(T) / n)

        # update w: E{x g(w'x)} - E{g'(w'x)} w := y - e1 * w
        for j = 1:k
            w = view(W,:,j)
            y = view(Y,:,j)
            e1 = E1[j]
            @. w = y - e1 * w
        end

        # symmetric decorrelation: W <- W * (W'W)^{-1/2}
        copyto!(W, W * _invsqrtm!(W'W))

        # compare with Wp to evaluate a conversion change
        chg = maximum(abs.(abs.(diag(W*Wp')) .- one(T)))
        converged = (chg < tol)

        @debug "Iteration $t" change=chg tolerance=tol
    end
    converged || throw(ConvergenceException(maxiter, chg, oftype(chg, tol)))
    return W
end

#### interface function

function fit(::Type{ICA}, X::AbstractMatrix{T},             # sample matrix, size (m, n)
                          k::Int;                           # number of independent components
                          alg::AbstractICAAlg,             # choice of algorithm
                          fun::ICAGDeriv=Tanh(one(T)),      # approx neg-entropy functor
                          do_whiten::Bool=true,             # whether to perform pre-whitening
                          maxiter::Integer=100,             # maximum number of iterations
                          tol::Real=1.0e-6,                 # convergence tolerance
                          mean=nothing,                     # pre-computed mean
                          winit::Matrix{T}=zeros(T,0,0)) where T<:Real  # init guess of W, size (m, k)

    # check input arguments
    m, n = size(X)
    n > 1 || error("There must be more than one samples, i.e. n > 1.")
    k <= min(m, n) || error("k must not exceed min(m, n).")
    maxiter > 1 || error("maxiter must be greater than 1.")
    tol > 0 || error("tol must be positive.")

    # preprocess data
    mv = preprocess_mean(X, mean)
    Z::Matrix{T} = centralize(X, mv)

    W0= zeros(T, 0, 0)  # whitening matrix
    if do_whiten
        C = rmul!(Z * transpose(Z), 1.0 / (n - 1))
        Efac = eigen(C)
        ord = sortperm(Efac.values; rev=true)
        (v, P) = extract_kv(Efac, ord, k)
        W0 = rmul!(P, Diagonal(1 ./ sqrt.(v)))
        Z = W0'Z
    end

    # initialize
    W = (isempty(winit) ? randn(T, size(Z,1), k) : copy(winit))

    # invoke core algorithm
    ica!(alg, W, Z, fun, maxiter, tol)

    # construct model
    if do_whiten
        W = W0 * W
    end
    return ICA(mv, W)
end

# function fastica!(W::DenseMatrix{T},      # initialized component matrix, size (m, k)
#                   X::DenseMatrix{T},      # (whitened) observation sample matrix, size(m, n)
#                   fun::ICAGDeriv{T},      # approximate neg-entropy functor
#                   maxiter::Int,           # maximum number of iterations
#                   tol::Real) where T<:Real# convergence tolerance
#
#     # argument checking
#     m = size(W, 1)
#     k = size(W, 2)
#     size(X, 1) == m || throw(DimensionMismatch("Sizes of W and X mismatch."))
#     n = size(X, 2)
#     k <= min(m, n) || throw(DimensionMismatch("k must not exceed min(m, n)."))
#
#     @debug "FastICA Algorithm" m=m n=n k=k
#
#     # pre-allocated storage
#     Wp = Matrix{T}(undef, m, k)    # to store previous version of W
#     U  = Matrix{T}(undef, n, k)    # to store w'x & g(w'x)
#     Y  = Matrix{T}(undef, m, k)    # to store E{x g(w'x)} for components
#     E1 = Vector{T}(undef, k)       # store E{g'(w'x)} for components
#
#     # normalize each column
#     for j = 1:k
#         w = view(W,:,j)
#         rmul!(w, one(T) / sqrt(sum(abs2, w)))
#     end
#
#     # main loop
#     chg = NaN
#     t = 0
#     converged = false
#     @inbounds while !converged && t < maxiter
#         t += 1
#         copyto!(Wp, W)
#
#         # apply W of previous step
#         mul!(U, transpose(X), W) # u <- w'x
#
#         # compute g(w'x) --> U and E{g'(w'x)} --> E1
#         _s = zero(T)
#         for j = 1:k
#             for i = 1:n
#                 u, v = evaluate(fun, U[i,j])
#                 U[i,j] = u
#                 _s += v
#             end
#             E1[j] = _s / n
#         end
#
#         # compute E{x g(w'x)} --> Y
#         rmul!(mul!(Y, X, U), one(T) / n)
#
#         # update w: E{x g(w'x)} - E{g'(w'x)} w := y - e1 * w
#         for j = 1:k
#             w = view(W,:,j)
#             y = view(Y,:,j)
#             e1 = E1[j]
#             @. w = y - e1 * w
#
#         end
#
#         # symmetric decorrelation: W <- W * (W'W)^{-1/2}
#         copyto!(W, W * _invsqrtm!(W'W))
#
#         # compare with Wp to evaluate a conversion change
#         chg = maximum(abs.(abs.(diag(W*Wp')) .- one(T)))
#         converged = (chg < tol)
#
#         @debug "Iteration $t" change=chg tolerance=tol
#     end
#     converged || throw(ConvergenceException(maxiter, chg, oftype(chg, tol)))
#     return W
# end


# Bayesian blocks
# Implementation by Michael P.H. Stumpf and T. Chan
# Based on the Python Code of Jake Vanderplas.

# This version implements Bayesian blocks for histograms, where the
# data are sorted, then treated as event data (see Scargle 2012).
# Defaults as suggested in Scargle 2012 are used.

# References:
# Scargle 2012: http://adsabs.harvard.edu/abs/2012arXiv1207.5578S
# Python implementation: https://github.com/astroML/astroML/blob/master/astroML/density_estimation/bayesian_blocks.py

type DiscretizeBayesianBlocks <: DiscretizationAlgorithm
end

function invVector(input::Array{Float64,})
	len = length(input)
	output = zeros(len)
	for i in 1:len
		output[len+1-i]=input[i]
	end
	return output
end

function binedges{N<:AbstractFloat}(alg::DiscretizeBayesianBlocks, data::AbstractArray{N})

	unique_data = unique(data)
	unique_data = sort(unique_data)

	n = length(unique_data) # Number of observations

	edges = zeros(n+1)
	edges[1] = unique_data[1]
	for i in 1 : (n-1)
		edges[i+1] = 0.5 * (unique_data[i]+unique_data[i+1])
	end
	edges[end] = unique_data[end]
	block_length = unique_data[end] - edges

	if length(unique_data) == length(data)
		nn_vec = ones(length(data))
	else
		nn_vec = convert(Array{Float64}, [length(findin(data, v)) for v in unique_data])
	end
	best = zeros(n)
	last = zeros(Int64,n)

	for K in 1 : n
		width = block_length[1 : K] - block_length[K+1]
		count_vec = invVector(cumsum(invVector(nn_vec[1 : K])))

		# Fitness function (eq. 19 from Scargle 2012)
		fit_vec = count_vec .* (log(count_vec) - log(width))
		# Prior (eq. 21 from Scargle 2012)
		fit_vec -= 4 - log(73.53 * 0.05 * ((K)^-0.478))
		fit_vec[2:end] += best[1 : K-1]

		i_max = indmax(fit_vec)
		last[K] = i_max
		best[K] = fit_vec[i_max]
	end

	change_points = zeros(Int64,n)
	i_cp = n+1
	ind = n+1
	while true
		i_cp -= 1
		change_points[i_cp] = ind
		if ind == 1
			break
		end
		ind = last[ind-1]
	end
	change_points = change_points[i_cp : end]
	edges[change_points]

end
function binedges{N<:Integer}(alg::DiscretizeBayesianBlocks, data::AbstractArray{N})

	data = convert(Array{typeof(0.0)}, data)
	binedges(alg, data)

end


using SolidModeling;
using Test;

# Test union of two cubes
function cubeUnion()
    c1 = cube(0.0, 0.0, 0.0, 1.0, 1.0, 1.0);
    c2 = cube(0.5, 0.5, 0.5, 2.0, 2.0, 2.0);
    c = bunion(c1, c2);

    return volume(c)
end

function cubeUnionReversed()
    c1 = cube(0.0, 0.0, 0.0, 1.0, 1.0, 1.0);
    c2 = cube(0.5, 0.5, 0.5, 2.0, 2.0, 2.0);
    c = bunion(c2, c1);

    return volume(c)
end

# Test subtraction of two cubes
function cubeSubtract()
    c1 = cube([0.5, 0.5, 0.5], 1.0, 1.0, 1.0)
    c2 = cube(0.5, 0.5, 0.5, 2.0, 2.0, 2.0)
    r = bsubtract(c1, c2)

    v = volume(r)

    return v
end

# Test intersection of two cubes
function cubeIntersect()
    c1 = cube(0.0, 0.0, 0.0, 1.0, 1.0, 1.0)
    c2 = cube(0.5, 0.5, 0.5, 2.0, 2.0, 2.0)

    v = volume(bintersect(c1, c2))

    return v
end

@testset begin
    @test isapprox(cubeUnion(), 4.25)                   # Test against known volume
    @test isapprox(cubeUnion(), cubeUnionReversed())    # Test if ordering doesnt matter
    @test isapprox(cubeSubtract(), (1-0.125))
    @test isapprox(cubeIntersect(), 0.125)
end


using SwitchTimeOpt

# Plotting settings
using PyPlot, PyCall
close("all")
# Import seaborn for nice plots
@pyimport seaborn as sns
sns.set_palette("hls", 8)
sns.set_context("paper", font_scale=1.5)
sns.set_style("whitegrid")
# Use Latex Labels in Plots
plt[:rc]("text", usetex=true)
plt[:rc]("font", family="serif")

maxiter = 20
using Ipopt
solver = IpoptSolver(
  print_level=0,
  max_iter=maxiter)

### Define system parameters
# Time vector
t0 = 0.0
tf = 12.0

# Size of the system
nx = 4

# Integer input
uvec = [repmat([0.0; 1.0], 4, 1); 0.0]'

# Number of switching times
N = size(uvec,2) - 1

# Cost funcction matrix
C = [1.0 0.0 -1.0 0.0;
     0.0 1.0 0.0  -1.0]
Q = C'*C

# Define initial state
x0 = [0.5; 0.7; 1; 1]

### Define system dynamics
function nldyn(x::Array{Float64,1}, u::Array{Float64,1})
  n = length(x)
  f = zeros(n)
  f[1] = x[1] - x[1]*x[2] - 0.4*x[1]*u[1]
  f[2] = -x[2] + x[1]*x[2] - 0.2*x[2]*u[1]
  return f

end

function nldyn_deriv(x::Array{Float64,1}, u::Array{Float64,1})
  df = [1.0-x[2]-0.4*u[1]       -x[1]                   0    0;
        x[2]                     -1+x[1]-0.2*u[1]       0    0;
        0                        0                      0    0;
        0                        0                      0    0]
end

### Generate and solve problems with different grid points
ngrid = [100; 150; 200; 250]

idx200 = (find(ngrid .== 200))[1]

# Preallocate vectors for results
objode45 = Array{Float64}(length(ngrid))
objlin = Array{Float64}(length(ngrid))
objiterates = Array{Float64}(maxiter+1, length(ngrid))
cputime = Array{Float64}(length(ngrid))
nobjeval = Array{Int}(length(ngrid))
ngradeval = Array{Int}(length(ngrid))
nhesseval = Array{Int}(length(ngrid))
tauopt = Array{Float64}(N, length(ngrid))
xsim = Array{Float64}(4, 10^4, length(ngrid))
xlinsim = Array{Float64}(4, 10^4, length(ngrid))
usim = Array{Float64}(1, 10^4, length(ngrid))

# Initialize the problem first
m = stoproblem(x0, nldyn, nldyn_deriv, uvec)

for i = 1:length(ngrid)  # Iterate over all grid points numbers

  m = stoproblem(
    x0,                 # Initial state
    nldyn,              # Nonlinear dynamics
    nldyn_deriv,        # Nonlinear dynamics derivative
    uvec,               # Vector of integer inputs
    ngrid = ngrid[i],   # Number of grid points
    t0 = t0,            # Initial time
    tf = tf,            # Final time
    Q = Q,              # Cost matrix
    solver = solver)

  # Solve optimization
  solve!(m)

  # Obtain values
  tauopt[:, i] = gettau(m)
  objlin[i] = getobjval(m)
  cputime[i] = getsoltime(m)
  nobjeval[i] = getnobjeval(m)
  ngradeval[i] = getngradeval(m)
  nhesseval[i] = getnhesseval(m)

  # Simulate system
  # Nonlinear simulation
  xsim[:, :, i], xpts, objode45[i], t = simulate(m, tauopt[:, i])
  usim[:, :, i], _ = simulateinput(m, t)

  # Linearized simulation
  xlinsim[:, :, i], _, Jlinsim, _ = simulatelinearized(m, tauopt[:, i], t)

  # Save objective function iterates
  objiterates[:, i] = m.STOev.obj[2:end]

end

###  Print results
@printf("RESULTS\n")
@printf("-------\n\n")

@printf("+==================================================================================+\n")
@printf("|  ngrid   |   objode45  |   objlin    | deltaobj [%%] |  nobjeval  |  cputime [s]  |\n")
@printf("+==================================================================================+\n")

for i = 1:length(ngrid)
  @printf("|  %7.i | %9.4f   | %9.4f   |   %6.3f     | %9.i  | %12.4f  | \n", ngrid[i], objode45[i], objlin[i], 100*norm(objode45[i]- objlin[i])/objode45[i], nobjeval[i], cputime[i])
  @printf("+----------------------------------------------------------------------------------+\n")
end

@printf("\nOptimal Switching Times for ngrid = 200\n")
@printf("--------------------------------------\n")
@printf("tauopt = "); show(round.(tauopt[:, idx200],3)); @printf("\n")

# Generate plots for ngrid = 25
t = linspace(t0, tf, 10000)

figure()
subplot(3,1,1)
plot(t, xlinsim[1,:, idx200], sns.xkcd_rgb["grass green"], linestyle = "dashdot")
plot(t, xsim[1,:, idx200], sns.xkcd_rgb["denim blue"])
ylim(0, 1.75)
yticks([0; 1; ])
ylabel(L"x_1")

subplot(3,1,2)
plot(t, xlinsim[2,:, idx200], sns.xkcd_rgb["grass green"], linestyle = "dashdot")
plot(t, xsim[2, :, idx200], sns.xkcd_rgb["denim blue"])
ylabel(L"x_2")
ylim(0, 1.75)
yticks([0; 1; ])

subplot(3,1,3)
plot(t, usim[1,:, idx200], sns.xkcd_rgb["denim blue"])
ylim(-0.2, 1.2)
yticks([0; 1])
ylabel(L"u")
xlabel(L"$\mathrm{Time}\; [s]$")

# Save figure
# tight_layout()
# savefig("fishing_problem.pdf")

# Do not return anything
nothing


# Code below is hard-copied from
# https://github.com/ahwillia/NonNegLeastSquares.jl
# See that repository for the license of this file.
# (Temporary stop-gap until the above package is registered)

using Distributed

function nonneg_lsq(A, B; alg::Symbol = :pivot, gram::Bool = false, kwargs...)

    if alg == :fnnls
        return fnnls(A, B; gram = gram, kwargs...)
    else
        error("Specified algorithm :", alg, " not recognized.")
    end
end

# If second input is a vector, convert it to a matrix
nonneg_lsq(A, b::AbstractVector; kwargs...) = nonneg_lsq(A, b[:, :]; kwargs...)

"""
x = fnnls(AtA, Atb; ...)
Returns x that solves A*x = b in the least-squares sense, subject to x >=0. The
inputs are the cross-products AtA = A'*A and Atb = A'*b. Uses the modified
active set method of Bro and De Jong (1997).
Optional arguments:
    tol: tolerance for nonnegativity constraints
    max_iter: maximum number of iterations (counts inner loop iterations)
References:
    Bro R, De Jong S. A fast non-negativitity-constrained least squares
    algorithm. Journal of Chemometrics. 11, 393–401 (1997)
"""
function fnnls(
    AtA,
    Atb::AbstractVector{T};
    tol::Float64 = 1e-8,
    max_iter = 30 * size(AtA, 2),
) where {T}

    n = size(AtA, 1)
    x = zeros(T, n)
    s = zeros(T, n)

    # P is a bool array storing positive elements of x
    # i.e., x[P] > 0 and x[~P] == 0
    P = x .> tol
    w = Atb - AtA * x

    # We have reached an optimum when either:
    #   (a) all elements of x are positive (no nonneg constraints activated)
    #   (b) ∂f/∂x = A' * (b - A*x) > 0 for all nonpositive elements of x
    iter = 0
    while sum(P) < n && any(w[(!).(P)] .> tol) && iter < max_iter

        # find i that maximizes w, restricting i to indices not in P
        # Note: the while loop condition guarantees at least one w[~P]>0
        i = argmax(w .* (!).(P))

        # Move i to P
        P[i] = true

        # Solve least-squares problem, with zeros for columns/elements not in P
        s[P] = AtA[P, P] \ Atb[P]
        s[(!).(P)] .= zero(eltype(s)) # zero out elements not in P

        # Inner loop: deal with negative elements of s
        while any(s[P] .<= tol)
            iter += 1

            # find indices in P where s is negative
            ind = @__dot__ (s <= tol) & P

            # calculate step size, α, to prevent any xᵢ from going negative
            α = minimum(x[ind] ./ (x[ind] - s[ind]))

            # update solution (pushes some xᵢ to zero)
            x += α * (s - x)

            # Remove all i in P where x[i] == 0
            for i = 1:n
                if P[i] && abs(x[i]) < tol
                    P[i] = false # remove i from P
                end
            end

            # Solve least-squares problem again, zeroing nonpositive columns
            s[P] = AtA[P, P] \ Atb[P]
            s[(!).(P)] .= zero(eltype(s)) # zero out elements not in P
        end

        # update solution
        x = deepcopy(s)
        w .= Atb - AtA * x
    end
    return x
end

function fnnls(
    A,
    B::AbstractMatrix;
    gram::Bool = false,
    use_parallel::Bool = false,
    kwargs...,
)

    n = size(A, 2)
    k = size(B, 2)

    if gram
        # A,B are actually Gram matrices
        AtA = A
        AtB = B
    else
        # cache matrix computations
        AtA = A' * A
        AtB = A' * B
    end

    if use_parallel && nprocs() > 1
        X = @distributed (hcat) for i = 1:k
            fnnls(AtA, AtB[:, i]; kwargs...)
        end
    else
        X = Array{eltype(B)}(undef, n, k)
        for i = 1:k
            X[:, i] = fnnls(AtA, AtB[:, i]; kwargs...)
        end
    end

    return X
end


### A Pluto.jl notebook ###
# v0.12.17

using Markdown
using InteractiveUtils

# ╔═╡ d02ecf8a-fdd0-11ea-0bec-b561da2f2a02
using DataFrames,CSV,Test,PovertyAndInequalityMeasures,Plots,Test,StatsBase

# ╔═╡ 97c87a0a-fde0-11ea-1419-2f9939bfa6e0
md"""

# Building A MicroSimulation Tax-Benefit Model of Scotland (in 25 minutes)

### Graham Stark
[graham.stark@virtual-worlds-research.com](mailto:graham.stark@virtual-worlds-research.com)

Things we need:

* data - Family Resources Survey(FRS)/LFS/Living Costs and Food Survey(LCF)/Understanding Society... This example uses FRS
* A programming language:
   - Spreadsheets, R, Fortran, Python ..
   - We use [Julia](https://julialang.org)
* Structures: we need to model:
   - people, families and households;
   - the fiscal system (taxes, benefits);
   - outcomes: incomes net of taxes and benefits, revenues raised, inequalities, poverty and so on.

Let's build one from scratch, in real time (with some 'here's some I prepared earlier' where needs be).

"""

# ╔═╡ d1e09cc6-fde1-11ea-07c7-0fce2411a060
md"""
## A quick intro to julia.

This web page is created automatically by Julia.

"""

# ╔═╡ 00f12bde-fde2-11ea-1026-c11c72cea702
2+2

# ╔═╡ 0651656c-fde2-11ea-251f-5747f4746430
x = 24

# ╔═╡ 0a7f7e3a-fde2-11ea-3825-37b7be64f5e2
x

# ╔═╡ 0c3b3004-fde2-11ea-0567-4bd9576443af
function f(x)
	x*x-1
end

# ╔═╡ 2b19845e-fde2-11ea-2353-7ddeb1182fc9
f(10)

# ╔═╡ 53b4ca1c-fde2-11ea-2955-f3c19026972b
struct Dog
	age
	sex
	barks
end

# ╔═╡ 708ab7c8-fde2-11ea-32ac-b56e9df87ebe
hope = Dog(4,"Female",true)

# ╔═╡ 83d83a9c-fde2-11ea-3512-9f140251a4e9
hope.barks

# ╔═╡ 940fd034-fde2-11ea-1fe2-97533b9dc40f
hope.age == 4

# ╔═╡ a8ff38e0-45c7-11eb-39ac-119d98bad749
	struct Pers
		age :: Int
		income :: Float64
		sex :: Int
	end

# ╔═╡ e8aa0fae-45c7-11eb-03b2-cf5021684e7e
g = Pers(62,25_000.0,1)

# ╔═╡ d871fa52-fde2-11ea-1e58-a9ddbea34508
md"""
## Data

this is the 'here's one I prepared earlier' bit... This is a large extract from the Family Resources Survey (pooled 2015-2018 surveys, Scotland only households).

Julia provides a structure called a [DataFrame](https://juliadata.github.io/DataFrames.jl/stable/) to hold large datasets like this.

"""

# ╔═╡ e8d7b7a4-fdd0-11ea-3e4e-d3cfcd789742
sp=CSV.File( "/home/graham_s/julia/vw/ScottishTaxBenefitModel/data/model_people_scotland.tab")|>DataFrame

# ╔═╡ 28ce666e-fdd1-11ea-2ae6-395356fc23e5
begin
    sp[((sp.age.>60).&(sp.sex.==1).&(sp.income_self_employment_income.>0)),[:age,:usual_hours_worked,:income_wages,:income_self_employment_income]]
	# sp = tsp[tsp.from_child_record.!=1,:]
end

# ╔═╡ d9eb5ebc-fdd2-11ea-28ab-2fa7c0cfaba9
names(sp)

# ╔═╡ 4f4ef062-fde3-11ea-1a36-1964d61271ca
md"""
## Structures

As I mentioned, we need structures for households, people,the tax and benefit system, and the results of our calculations. A good model is mostly structures and tests - get those right and the actual calculations are usually easy.

We'll skip most things for now; just:
* people (just age and income)
* simple Income Tax
* a results structure.

"""

# ╔═╡ 5ce6e028-fdd3-11ea-2924-897de20ca1c2
begin
	struct Person
		age  :: Int
		wage :: Real
	end

	struct TaxSystem
		rates :: Vector
		bands :: Vector
		allow :: Real
	end

	struct Result
		taxes :: Real
		net_income :: Real
	end

end

# ╔═╡ ba302158-fde3-11ea-2bae-939a6cab3511
md"""

### Initialise some tax systems

The current Scottish one and a flat-rate one.

"""

# ╔═╡ e118d6a8-fdd3-11ea-2d61-ad17c31e0d1a
begin
	sys1=TaxSystem(
		[0.190,0.20,0.21,0.41,0.46], # rates
		[2_049.0, 12_444.0, 30_930.0, 150_000.0], # thresholds
		12_500.00) # allowance
	# flat - no allowance, 1 rate
	sys2=TaxSystem([0.1475],[],0)
end

# ╔═╡ eff4a08e-fde3-11ea-2e58-9b4e4969819e

# ╔═╡ 055e7cc4-fde4-11ea-145b-c5261a647e10
md"""

### A very simple tax system

"""

# ╔═╡ e89d49e0-fdd3-11ea-199c-2d4bf8a5563b
begin

	"""
Tax due on `taxable` income, given rates and thresholds
rates can be one more than thresholds, in which case the last band is assumed infinite.
Rates should be (e.g.) 0.12 for 12%.
"""
function calctaxdue(
   taxable    :: Number,
   rates      :: Vector,
   thresholds :: Vector ) :: Real
   nthresholds = length(thresholds)[1]
   nrates = length(rates)[1]

   @assert (nrates >= 1) && ((nrates - nthresholds) in 0:1 ) # allow thresholds to be 1 less & just fill in the top if we need it
   due = 0.0
   remaining = taxable
   i = 0
   if nthresholds > 0
      maxv = typemax( typeof( thresholds[1] ))
      gap = thresholds[1]
   else
      maxv = typemax( typeof( taxable ))
      gap = maxv
   end
   while remaining > 0.0
      i += 1
      if i > 1
         if i < nrates
            gap = thresholds[i]-thresholds[i-1]
         else
            gap = maxv
         end
      end
      t = min( remaining, gap )
      due += t*rates[i]
      remaining -= gap
   end
   due
end

	function dotax( pers::Person, sys :: TaxSystem )::Result
		taxable = max(0.0, pers.wage-sys.allow)
		tax = calctaxdue( taxable, sys.rates, sys.bands )
		net = pers.wage - tax
		return Result(tax,net)
	end
end

# ╔═╡ 44f853e2-fdd4-11ea-1a13-d5740807f2d0
begin
	pers = Person(40,20_000.0)
	res = dotax(pers,sys1)
end

# ╔═╡ 6a698960-fdd5-11ea-3b8d-81aaf65a4d22
tt = (20_000-12500)*0.19

# ╔═╡ a5f30538-fde9-11ea-39c1-1f7f412d2c04
md"""
### Sample Weights

`target population/people (rows)` in the dataset.

"""

# ╔═╡ 7c9954a6-fdd5-11ea-32cc-819552e44bd2
begin
 	ss = size(sp)[1] # count of rows
	popn = 5_500_000 # approx popn of Scotland
	w = popn/ss
end

# ╔═╡ b265ebfa-fdd5-11ea-10fd-2f02021f86a0

function do_all(data::DataFrame, sys :: TaxSystem)::Matrix
	tot_tax = 0.0
	ss = size(data)[1]
	popn = 5_500_000
	w = popn/ss
	out = zeros(ss,3)
	inflation = 1.1
	i = 0
	for p in eachrow(data)
		i+=1
		pers = Person( p.age,p.income_wages*52*inflation)
		res = dotax(pers,sys)
		out[i,1]=w
		out[i,2]=res.net_income
		out[i,3]=res.taxes
	end
	out
end

# ╔═╡ a1f92cf4-fdd6-11ea-37ba-5b4efab11252
begin
	allr1=do_all(sp,sys1)
	allr2=do_all(sp,sys2)
end

# ╔═╡ d16e31fa-fdd6-11ea-3476-71dbfa596605
begin
	ineq1 = PovertyAndInequalityMeasures.make_inequality( allr1, 1, 2 )
	ineq2 = PovertyAndInequalityMeasures.make_inequality( allr2, 1, 2 )
end

# ╔═╡ 8fd8dc52-fdd8-11ea-0d46-db0fbd96cd90
begin
	dc1 = ineq1[:deciles]
	dc2 = ineq2[:deciles]
	m = hcat(dc1[:,1:2],dc2[:,2])
	plot(m,labels=["equality" "pre" "post"],title="Gini")
	#m
end

# ╔═╡ 8f1e6616-ffb2-11ea-0e84-f35d2f98221b
begin
	decs = (dc2[:,3] - dc1[:,3])./52
	bar(decs,labels="avg. gain",title="deciles")
end

# ╔═╡ 5667cce2-fdda-11ea-1eb2-130e04fd8cb3
begin
	r1=Int(trunc(sum(allr1[:,1].*allr1[:,3])/1_000_000))
	r2=Int(trunc(sum(allr2[:,1].*allr2[:,3])/1_000_000))
	cost = r2-r1
	(cost=cost,r1=r1,r2=r2)
end

# ╔═╡ 8f3045e0-fdda-11ea-2f94-0dec0b980c14
(ineq1[:gini],ineq2[:gini],ineq1[:gini]-ineq2[:gini])

# ╔═╡ f4a96ad2-fde9-11ea-28c3-f9376758d82f
@testset "X" begin
	@test 1 ≈ 1.0000000000000000000008
end

# ╔═╡ c985e5d6-0084-11eb-2569-c3d8af344837
combine(sp,:income_wages=>mean)[1,:income_wages_mean]*52|>trunc|>Integer

# ╔═╡ 20950768-0093-11eb-3e65-391d2057f808
last(select(sp, :income_wages => tiedrank,:income_wages => ordinalrank,:income_wages),9)

# ╔═╡ Cell order:
# ╠═d02ecf8a-fdd0-11ea-0bec-b561da2f2a02
# ╟─97c87a0a-fde0-11ea-1419-2f9939bfa6e0
# ╟─d1e09cc6-fde1-11ea-07c7-0fce2411a060
# ╠═00f12bde-fde2-11ea-1026-c11c72cea702
# ╠═0651656c-fde2-11ea-251f-5747f4746430
# ╠═0a7f7e3a-fde2-11ea-3825-37b7be64f5e2
# ╠═0c3b3004-fde2-11ea-0567-4bd9576443af
# ╠═2b19845e-fde2-11ea-2353-7ddeb1182fc9
# ╠═53b4ca1c-fde2-11ea-2955-f3c19026972b
# ╠═708ab7c8-fde2-11ea-32ac-b56e9df87ebe
# ╠═83d83a9c-fde2-11ea-3512-9f140251a4e9
# ╠═940fd034-fde2-11ea-1fe2-97533b9dc40f
# ╠═a8ff38e0-45c7-11eb-39ac-119d98bad749
# ╠═e8aa0fae-45c7-11eb-03b2-cf5021684e7e
# ╟─d871fa52-fde2-11ea-1e58-a9ddbea34508
# ╠═e8d7b7a4-fdd0-11ea-3e4e-d3cfcd789742
# ╠═28ce666e-fdd1-11ea-2ae6-395356fc23e5
# ╠═d9eb5ebc-fdd2-11ea-28ab-2fa7c0cfaba9
# ╟─4f4ef062-fde3-11ea-1a36-1964d61271ca
# ╠═5ce6e028-fdd3-11ea-2924-897de20ca1c2
# ╠═ba302158-fde3-11ea-2bae-939a6cab3511
# ╠═e118d6a8-fdd3-11ea-2d61-ad17c31e0d1a
# ╠═eff4a08e-fde3-11ea-2e58-9b4e4969819e
# ╟─055e7cc4-fde4-11ea-145b-c5261a647e10
# ╠═e89d49e0-fdd3-11ea-199c-2d4bf8a5563b
# ╠═44f853e2-fdd4-11ea-1a13-d5740807f2d0
# ╠═6a698960-fdd5-11ea-3b8d-81aaf65a4d22
# ╠═a5f30538-fde9-11ea-39c1-1f7f412d2c04
# ╠═7c9954a6-fdd5-11ea-32cc-819552e44bd2
# ╠═b265ebfa-fdd5-11ea-10fd-2f02021f86a0
# ╠═a1f92cf4-fdd6-11ea-37ba-5b4efab11252
# ╠═d16e31fa-fdd6-11ea-3476-71dbfa596605
# ╠═8fd8dc52-fdd8-11ea-0d46-db0fbd96cd90
# ╠═8f1e6616-ffb2-11ea-0e84-f35d2f98221b
# ╠═5667cce2-fdda-11ea-1eb2-130e04fd8cb3
# ╠═8f3045e0-fdda-11ea-2f94-0dec0b980c14
# ╠═f4a96ad2-fde9-11ea-28c3-f9376758d82f
# ╠═c985e5d6-0084-11eb-2569-c3d8af344837
# ╠═20950768-0093-11eb-3e65-391d2057f808


function compute()::Int
    months = [31, 0, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31]
    day = 2
    result = 0
    for year ∈ 1901:2000
        months[2] = 28 + (year % 4 == 0 && (year % 100 ≠ 0 || year % 400 == 0))
        for month ∈ 1:12
            day += months[month] % 7
            if day % 7 == 0 result += 1 end
        end
    end
    return result
end


"""
Use Floyd's algorithm to generate random index numbers given
total number of rows

    floydgen(nrows, pct=20)

"""
function floydgen(nrows, pct=20)
  nitems = floor(Int, nrows * (pct/100))

  s = Set{Int}()
  sizehint!(s, nitems)

  for id in (nrows-nitems + 1):nrows
    item = rand(1:id)
    if !(item in s)
      push!(s, item)
    else
      push!(s, id)
    end
  end

  g = Array{Int}(collect(s))
  return g
end


using Test
using CompScienceMeshes
using LinearAlgebra

verts = [
    point(0,0,0),
    point(1,0,0),
    point(0,1,0),
    point(1,1,0),]

nodes = [
    (1,),
    (2,),
    (3,),
    (4,)]

edges = [
    (1,2,),
    (2,3,),
    (3,1,),
    (2,4),
    (4,3),]

faces = [
    (1,2,3,),
    (-2,4,5)]

T = eltype(verts[1])
P = eltype(verts)
m = CompScienceMeshes.SComplex2D{T,P}(verts,nodes,edges,faces)

@test numcells(m) == 2

ch = chart(m, first(cells(m)))
@test ch.vertices[1] == verts[3]
@test ch.vertices[2] == verts[1]
@test ch.vertices[3] == verts[2]
@test dot(normal(center(ch)),point(0,0,1)) > 0

x1 = iterate(cells(m))
@test x1 != nothing
x2 = iterate(cells(m), x1[2])
@test x2 != nothing
c2 = x2[1]

ch2 = chart(m, c2)
@test ch2.vertices[1] == verts[4]
@test ch2.vertices[2] == verts[3]
@test ch2.vertices[3] == verts[2]
@test dot(normal(center(ch2)),point(0,0,1)) > 0

m0 = skeleton(m,0)
@test dimension(m0) == 0

m1 = skeleton(m,1)
@test dimension(m1) == 1

m2 = skeleton(m,2)
@test dimension(m2) == 2

D = connectivity(m1,m2)
@test sum(D,dims=1) == [1 0 1 1 1]

# b = CompScienceMeshes.SComplex1D{T,P}(verts, nodes, edges[[1,3,4,5]])
b = boundary(m2)
@test numcells(b) == 4
b0 = skeleton(b,0)
@test numcells(b0) == 4

@test inclosure_gpredicate(b).(verts) == [true, true, true, true]

mm2 = weld(m,m,seam=b)
mm1 = skeleton(mm2,1)
mm0 = skeleton(mm2,0)
@test numcells(mm2) == 2*numcells(m)
@test numcells(mm1) == 6
@test numcells(mm0) == 4

r_old = meshrectangle(1.0, 1.0, 0.5, 3)
r_new = CompScienceMeshes.SComplex2D(r_old)

@test numcells(r_new) == 8
@test numcells(skeleton(r_new,1)) == 16
@test numcells(skeleton(r_new,0)) == 9

b_new = boundary(r_new)
@test dimension(b_new) == 1
@test numcells(b_new) == 8

# r_new1 = skeleton(r_new,1)
# p = interior_tpredicate(r_new)
# for e in cells(r_new1)
#     println(cartesian(center(chart(r_new1,e))))
#     println(p(e))
# end

rr = weld(r_new, r_new, seam=boundary(r_new))
rr1 = skeleton(rr,1)
rr0 = skeleton(rr,0)
@test numcells(rr) == 16
@test numcells(rr1) == 24
@test numcells(rr0) == 10

fm = -m
C = collect(cells(fm))
ch1 = chart(fm,C[1])
ch2 = chart(fm,C[2])

@test dot(normal(center(ch1)), point(0,0,1)) < 0
@test dot(normal(center(ch2)), point(0,0,1)) < 0

dl = weld(m,-m,seam=boundary(m))
@test numcells(dl) == 4
@test numcells(skeleton(dl,1)) == 6
@test numcells(skeleton(dl,0)) == 4


n = 10 # rand(1:10)
@test matrixdepot("parter", n) == matrixdepot("parter", Float64, n)

A = matrixdepot("cauchy", [1.:n;] .+ 0.5, -[1.:n;])

@test matrixdepot("parter", n) ≈ A
println("'parter' passed test...")


using Test
using LinearAlgebraicRepresentation
Lar = LinearAlgebraicRepresentation

function BoxCalculation(Vertices)
	Minx=minimum(Vertices[1,:])
	Maxx=maximum(Vertices[1,:])
	Miny=minimum(Vertices[2,:])
	Maxy=maximum(Vertices[2,:])
	dx=Maxx-Minx
	dy=Maxy-Miny
	Box=dx*dy
	if size(Vertices,1)==3
		Minz=minimum(Vertices[3,:])
		Maxz=maximum(Vertices[3,:])
		dz=Maxz-Minz
		Box=Box*dz
	end
	return Box
end

@testset "circle" begin
	@test BoxCalculation(Lar.circle()()[1])==4
	@test BoxCalculation(Lar.circle(2., 2*pi)()[1])==16
	@test BoxCalculation(Lar.circle(3, 2*pi)()[1])==36
	@test BoxCalculation(Lar.circle(5, pi/2)()[1])==25
	#@test size(Lar.circle(3,2*pi)(60)[1],2)==60
	@test length(Lar.circle(3,2*pi)([60])[2])==60
end

@testset "helix" begin
	@test BoxCalculation(Lar.helix()()[1])==8
	@test BoxCalculation(Lar.helix(2, 2, 2)()[1])==64
	@test BoxCalculation(Lar.helix(1, 2, 5)()[1])==40
	@test BoxCalculation(Lar.helix(1, 1, 3)()[1])==12
	#=
	1, 1, 2, is contained
	in a box that has volume (radius*2)^2*pitch*nturns
	=#
	@test size(Lar.helix(5, 7, 9)()[1],2)==325
	@test length(Lar.helix(5, 7, 9)()[2])==324
end

@testset "disk" begin
	@test BoxCalculation(Lar.disk(1., 2*pi)([36, 1])[1])==4
	@test BoxCalculation(Lar.disk(2., 2*pi)()[1])==16
	@test BoxCalculation(Lar.disk(2., pi)()[1])==8
	@test BoxCalculation(Lar.disk(2., pi/2)()[1])==4
	@test size(Lar.disk(10, pi/7)()[1],2)==75
	@test length(Lar.disk(10, pi/7)()[2])==108
end

@testset "helicoid" begin
	@test BoxCalculation(Lar.helicoid()()[1])==8
	@test BoxCalculation(Lar.helicoid(2,1,2,2)()[1])==64
	@test BoxCalculation(Lar.helicoid(1,.5,2,5)()[1])==40
	@test BoxCalculation(Lar.helicoid(1,.3,1,3)()[1])==12
	#=
	1, 1, 2, is contained in a
	box that has volume (radius*2)^2*pitch*nturns
	=#
	@test size(Lar.helicoid(1.3,.7,1,3)()[1],2)==327
	@test length(Lar.helicoid(1.3,.7,1,3)()[2])==432
end

@testset "ring" begin
	@test BoxCalculation(Lar.ring(1.,3.,2*pi)()[1])==36
	@test BoxCalculation(Lar.ring(1.,2,pi)()[1])==8
	@test BoxCalculation(Lar.ring(1.,5,pi/2)()[1])==25
	#(radius*2)^2
	@test size(Lar.ring(1,5,pi/2)()[1],2)==74
	@test length(Lar.ring(5,10,pi/6)()[2])==72
end

@testset "cylinder" begin
	@test BoxCalculation(Lar.cylinder(1,5,2*pi)()[1])==20
	@test BoxCalculation(Lar.cylinder(2,2,pi)()[1])==16
	@test BoxCalculation(Lar.cylinder(1,4,pi)()[1])==8
	#((radius*2)^2)*height
	@test size(Lar.cylinder(3.4,20,pi/7)()[1],2)==74
	@test length(Lar.cylinder(3.4,20,pi/7)()[2])==72
end

@testset "sphere" begin
	@test BoxCalculation(Lar.sphere(2,pi,2*pi)()[1])==64
	@test BoxCalculation(Lar.sphere(6,pi,pi)()[1])==864
	@test BoxCalculation(Lar.sphere(4,pi,2*pi)()[1])==8^3
	@test size(Lar.sphere(2.5,pi/3,pi/5)()[1],2)==703
	@test length(Lar.sphere(2.5,pi/3,pi/5)()[2])==1296
end

@testset "toroidal" begin
	@test BoxCalculation(Lar.toroidal(1,3,2*pi,2*pi)()[1])==128
	@test BoxCalculation(Lar.toroidal(2,3,2*pi,2*pi)()[1])==400
	#(((R+r)*2)^2)*(r*2)
	@test size(Lar.toroidal(1.3,4.6,pi/4,pi/7)()[1],2)==925
	@test length(Lar.toroidal(1.3,4.6,pi/4,pi/7)()[2])==1728
end

@testset "crown" begin
	@test BoxCalculation(Lar.crown(1., 3., 2*pi)()[1])==128
	@test BoxCalculation(Lar.crown(2., 3., 2*pi)()[1])==400
	#(((R+r)*2)^2)*(r*2)
	@test size(Lar.crown(1.5, 5.6, pi/8)()[1],2)==481
	@test length(Lar.crown(1.5, 5.6, pi/8)()[2])==864
end

@testset "ball" begin
	@test BoxCalculation(Lar.ball(1, pi, 2*pi)()[1])==8
	@test BoxCalculation(Lar.ball(6, pi, pi)()[1])==864
	@test BoxCalculation(Lar.ball(3, pi, 2*pi)()[1])==6^3
	@test size(Lar.ball(2.6, pi/5, pi/9)()[1],2)==2813
	@test length(Lar.ball(2.6, pi/5, pi/9)()[2])==2592
end

@testset "rod" begin
	@test BoxCalculation(Lar.rod(1,5,2*pi)()[1])==20
	@test BoxCalculation(Lar.rod(2,2,pi)()[1])==16
	@test BoxCalculation(Lar.rod(1,4,pi)()[1])==8
	#((radius*2)^2)*height
	@test size(Lar.rod(3.7,8.9,pi/9)()[1],2)==74
	@test length(Lar.rod(3.7,8.9,pi/9)()[2])==1
end

@testset "hollowCyl" begin
	@test BoxCalculation(Lar.hollowCyl(0,1.,5,2*pi)()[1])==20
	@test BoxCalculation(Lar.hollowCyl(1,2.,4,pi)()[1])==32
	@test BoxCalculation(Lar.hollowCyl(1,4,3,pi/2)()[1])==48
	#((radius*2)^2)*height
	@test size(Lar.hollowCyl(3,4.,7.8,pi/5)()[1],2)==148
	@test length(Lar.hollowCyl(3,4.,7.8,pi/5)()[2])==36
end

@testset "hollowBall" begin
	@test BoxCalculation(Lar.hollowBall(1,2,pi,2*pi)([36,36,1])[1]) ==64
	@test BoxCalculation(Lar.hollowBall(1,6,pi,pi)([36,36,1])[1])== 864
	@test BoxCalculation(Lar.hollowBall(2,4,pi,2*pi)([36,36,1])[1])==8^3
	#(radius*2)^3
	@test size(Lar.hollowBall(1.5,6.7,pi/3,2*pi/3)()[1],2)==3700
	@test length(Lar.hollowBall(1.5,6.7,pi/3,2*pi/3)()[2])==2592
end

@testset "torus" begin
	@test BoxCalculation(Lar.torus(1.,2.,.5,2*pi,2*pi)()[1])==147
	@test BoxCalculation(Lar.torus(2,3,.5,2*pi,2*pi)()[1])==605.0
	#(((R+r)*2)^2)*(r*2)
	@test size(Lar.torus(5.2,7,.5,pi/3,pi/4)()[1],2)==4625
	@test length(Lar.torus(5.2,7,.5,pi/3,pi/4)()[2])==3456
end


input = readlines("day4.txt")
draws = split(input[1], ",") .|> x -> parse(Int64,x)

# Create a 3D array with the cards
A = zeros(Int64,5, 5, Int64((size(input,1)-1)/6))
i,j=1,1
for line in input[3:end]
    if line == ""
        i+=1
        j=1
    else
        A[j,:,i] = split(line).|> x -> parse(Int64,x)
        j+=1
    end
end

# Run the simulation
i=1
stop = 0
check = 0
dims_left = collect(1:size(A,3))
while stop == 0

    check = draws[i]
    A[findall(x->x==check, A)] .= -1
    dims = size(A,3)

    if size(dims_left,1) == 1
        for ind in 1:5
            if all(y->y==-1, A[:,ind,dims_left])
                stop = 1
            elseif all(y->y==-1, A[ind,:,dims_left])
                stop = 1
            end
        end
    else    for card in 1:dims
                for ind in 1:5
                    keep_ind = ind
                    if all(y->y==-1, A[:,ind,card])
                        A[:,:,card] .= -2
                        setdiff!(dims_left, card)
                    elseif all(y->y==-1, A[ind,:,card])
                        A[:,:,card] .= -2
                        setdiff!(dims_left, card)
                    end
                end
            end
        i+=1
    end
end

loser = A[:,:,dims_left]
loser[findall(x->x==-1, loser)] .= 0

sum(loser) * check


module TestReorderByModule
  using Base.Test
  using Brim

  # Test 1 - simple case

  A = [1 0 0; 0 0 1; 0 1 0]
  S = [0 1 0; 1 0 0; 0 0 1; 0 1 0; 0 0 1; 0 0 1]
  C = [1 0 0; 0 1 0; 0 0 1; 0 1 0; 0 0 1; 0 0 1]

  M = no_empty_modules!(Modular(A, S))

  reorder_by_module!(M)
  @test sum(abs(M.S .- C)) == 0

  # Test 2 - more complex case

  A = [0 0 0 1 1 1; 1 1 1 0 0 0; 0 0 0 1 1 1; 1 1 1 0 0 0; 0 0 0 1 1 1; 1 1 1 0 0 0]
  M = recursive_brim!(partition_random(A))
  reorder_by_module!(M)

  target = [1 0; 1 0; 1 0; 0 1; 0 1; 0 1; 1 0; 1 0; 1 0; 0 1; 0 1; 0 1]

  @test hash(M.S) == hash(target)

end


using DifferentialEquations, ParameterizedFunctions
van! = @ode_def VanDerPol begin
  dy = μ*((1-x^2)*y - x)
  dx = 1*y
end μ

prob = ODEProblem(van!,[0.0,2.0],(0.0,6.3),1e6)

sol = solve(prob,Tsit5())

sol = solve(prob,alg_hints = [:stiff])

sol = solve(prob)

using Plots; gr()
sol = solve(prob,alg_hints = [:stiff],reltol=1e-6)
plot(sol,denseplot=false)

plot(sol,ylims = (-10.0,10.0))

function lorenz!(du,u,p,t)
    σ,ρ,β = p
    du[1] = σ*(u[2]-u[1])
    du[2] = u[1]*(ρ-u[3]) - u[2]
    du[3] = u[1]*u[2] - β*u[3]
end
u0 = [1.0,0.0,0.0]
p = (10,28,8/3)
tspan = (0.0,100.0)
prob = ODEProblem(lorenz!,u0,tspan,p)

using BenchmarkTools
@btime solve(prob);

@btime solve(prob,alg_hints = [:stiff]);

using SciMLTutorials
SciMLTutorials.tutorial_footer(WEAVE_ARGS[:folder],WEAVE_ARGS[:file])


import Base: *, eltype, size
import LinearAlgebra: adjoint, mul!, rmul!, lmul!

# The reflector! code in stdlib has no underflow or accuracy protection

# These are translations of xLARFG from LAPACK
# LAPACK Copyright:
# Univ. of Tennessee
# Univ. of California Berkeley
# Univ. of Colorado Denver
# NAG Ltd.
function _reflector!(x::AbstractVector{T}) where {T<:Real}
    require_one_based_indexing(x)
    n = length(x)
    n <= 1 && return zero(T)
    sfmin = 2floatmin(T) / eps(T)
    @inbounds begin
        α = x[1]
        xnorm = _norm2(view(x,2:n))
        if iszero(xnorm)
            return zero(T)
        end
        β = -copysign(hypot(α, xnorm), α)
        kount = 0
        smallβ = abs(β) < sfmin
        if smallβ
            # recompute xnorm and β if needed for accuracy
            rsfmin = one(T) / sfmin
            while smallβ
                kount += 1
                for j in 2:n
                    x[j] *= rsfmin
                end
                β *= rsfmin
                α *= rsfmin
                # CHECKME: is 20 adequate for BigFloat?
                smallβ = (abs(β) < sfmin) && (kount < 20)
            end
            # now β ∈ [sfmin,1]
            xnorm = _norm2(view(x,2:n))
            β = -copysign(hypot(α, xnorm), α)
        end
        τ = (β - α) / β
        t = one(T) / (α - β)
        for j in 2:n
            x[j] *= t
        end
        for j in 1:kount
            β *= sfmin
        end
        x[1] = β
    end
    return τ
end

function _reflector!(x::AbstractVector{T}) where {T<:Complex}
    require_one_based_indexing(x)
    n = length(x)
    # we need to make subdiagonals real so the n=1 case is nontrivial for complex eltype
    n < 1 && return zero(T)
    RT = real(T)
    sfmin = floatmin(RT) / eps(RT)
    @inbounds begin
        α = x[1]
        αr, αi = reim(α)
        xnorm = _norm2(view(x,2:n))
        if iszero(xnorm) && iszero(αi)
            return zero(T)
        end
        β = -copysign(_hypot3(αr, αi, xnorm), αr)
        kount = 0
        smallβ = abs(β) < sfmin
        if smallβ
            # recompute xnorm and β if needed for accuracy
            rsfmin = one(real(T)) / sfmin
            while smallβ
                kount += 1
                for j in 2:n
                    x[j] *= rsfmin
                end
                β *= rsfmin
                αr *= rsfmin
                αi *= rsfmin
                smallβ = (abs(β) < sfmin) && (kount < 20)
            end
            # now β ∈ [sfmin,1]
            xnorm = _norm2(view(x,2:n))
            α = complex(αr, αi)
            β = -copysign(_hypot3(αr, αi, xnorm), αr)
        end
        τ = complex((β - αr) / β, -αi / β)
        t = one(T) / (α - β)
        for j in 2:n
            x[j] *= t
        end
        for j in 1:kount
            β *= sfmin
        end
        x[1] = β
    end
    return τ
end

# copied from Andreas Noack's GenericLinearAlgebra.jl, with trivial mods

"""
a Householder reflection represented as the essential part of the
vector and the normalizing factor
"""
struct Householder{T,S<:StridedVector}
    v::S
    τ::T
end

# warning; size -> length(v) in GLA, but this makes more sense to us:
size(H::Householder) = (length(H.v)+1, length(H.v)+1)
size(H::Householder, i::Integer) = i <= 2 ? length(H.v)+1 : 1

eltype(H::Householder{T})      where T = T

adjoint(H::Householder{T})      where {T} = Adjoint{T,typeof(H)}(H)

function lmul!(H::Householder, A::StridedMatrix)
    m, n = size(A)
    size(H,1) == m || throw(DimensionMismatch(""))
    v = view(H.v, 1:m - 1)
    τ = H.τ
    for j = 1:n
        va = A[1,j]
        Aj = view(A, 2:m, j)
        va += dot(v, Aj)
        va = τ*va
        A[1,j] -= va
        axpy!(-va, v, Aj)
    end
    A
end

function rmul!(A::StridedMatrix, H::Householder)
    m, n = size(A)
    size(H,1) == n || throw(DimensionMismatch(""))
    v = view(H.v, :)
    τ = H.τ
    a1 = view(A, :, 1)
    A1 = view(A, :, 2:n)
    x = A1*v
    axpy!(one(τ), a1, x)
    axpy!(-τ, x, a1)
    rankUpdate!(-τ, x, v, A1)
    A
end

function lmul!(adjH::Adjoint{<:Any,<:Householder}, A::StridedMatrix)
    H = parent(adjH)
    m, n = size(A)
    size(H,1) == m || throw(DimensionMismatch("A: $m,$n H: $(size(H))"))
    v = view(H.v, 1:m - 1)
    τ = H.τ
    for j = 1:n
        va = A[1,j]
        Aj = view(A, 2:m, j)
        va += dot(v, Aj)
        va = τ'va
        A[1,j] -= va
        axpy!(-va, v, Aj)
    end
    A
end

Base.convert(::Type{Matrix}, H::Householder{T}) where {T} = lmul!(H, Matrix{T}(I, size(H, 1), size(H, 1)))
Base.convert(::Type{Matrix{T}}, H::Householder{T}) where {T} = lmul!(H, Matrix{T}(I, size(H, 1), size(H, 1)))


"""
    LogLogistic(a,b)

The *log-logistic distribution* with shape `a` and scale `b` has probability density function

```math
f(x; a, b)=\\frac{(a / b )(x/b)^{a-1}}{1+(x/b)^a)^2}}
```

```julia
LogLogistic()       # LogLogistic distribution with zero location and unit scale, i.e. LogLogistic(0, 1)
LogLogistic(a)      # LogLogistic distribution with location a and unit scale, i.e. LogLogistic(a, 1)
LogLogistic(a, b)   # LogLogistic distribution with location a and scale b

params(d)       # Get the parameters, i.e. (a, b)
shape(d)     # Get the location parameter, i.e. a
scale(d)        # Get the scale parameter, i.e. b
```

External links

* [Log-Logistic distribution on Wikipedia](https://en.wikipedia.org/wiki/Log-logistic_distribution)

"""
struct LogLogistic <: ContinuousUnivariateDistribution
    a
    b
end

#### Parameters

shape(d::LogLogistic) = d.a
scale(d::LogLogistic) = d.b

params(d::LogLogistic) = (d.a, d.b)
#@inline partype(d::LogLogistic{T}) where {T<:Real} = T

#### Statistics

function mean(d::LogLogistic)
    a, b = params(d)
    if a>1
        return (b*pi/a)/sin(pi/a)
    else
        return missing
    end
end
median(d::Logistic) = d.b
#mode(d::Logistic) = d.b
#	{\displaystyle \alpha \left({\frac {\beta -1}{\beta +1}}\right)^{1/\beta }}\alpha\left(\frac{\beta-1}{\beta+1}\right)^{1/\beta}

# std(d::Logistic) = π * d.θ / sqrt3
# var(d::Logistic) = (π * d.θ)^2 / 3
# skewness(d::Logistic{T}) where {T<:Real} = zero(T)
# kurtosis(d::Logistic{T}) where {T<:Real} = T(6)/5

# entropy(d::Logistic) = log(d.θ) + 2

#### Evaluation

# zval(d::Logistic, x::Real) = (x - d.μ) / d.θ
# xval(d::Logistic, z::Real) = d.μ + z * d.θ

function pdf(d::LogLogistic, x)
    a, b = params(d)
    return ((a/b)*(x/b )^(a-1))/((1+(x/b)^a))^2
end

function logpdf(d::LogLogistic, x)
    a, b = params(d)
    return log(a)-log(b)+(a-1)*log(x/b) - 2*log1p((x/b)^a)
end

function cdf(d::LogLogistic, x)
    a, b = params(d)
    return (x^a) / (b^a +x^a)
end

function ccdf(d::LogLogistic, x)
    a, b = params(d)
    return one(x) - (x^a) / (b^a +x^a)
end

# logcdf(d::Logistic, x::Real) = -log1pexp(-zval(d, x))
# logccdf(d::Logistic, x::Real) = -log1pexp(zval(d, x))
function quantile(d::LogLogistic,p)
    a, b = params(d)
    return b * ((p)/(1-p))^inv(a)
end

function rand(rng::AbstractRNG, d::LogLogistic)
    p = rand(rng)
    return quantile(d, p)
end

# quantile(d::Logistic, p::Real) = xval(d, logit(p))
# cquantile(d::Logistic, p::Real) = xval(d, -logit(p))
# invlogcdf(d::Logistic, lp::Real) = xval(d, -logexpm1(-lp))
# invlogccdf(d::Logistic, lp::Real) = xval(d, logexpm1(-lp))

# function gradlogpdf(d::Logistic, x::Real)
#     e = exp(-zval(d, x))
#     ((2e) / (1 + e) - 1) / d.θ
# end

# mgf(d::Logistic, t::Real) = exp(t * d.μ) / sinc(d.θ * t)

# function cf(d::Logistic, t::Real)
#     a = (π * t) * d.θ
#     a == zero(a) ? complex(one(a)) : cis(t * d.μ) * (a / sinh(a))
# end


using MathProgBase, Gurobi
using Distributions, DataFrames

"""
Computes the optimal q value via the cross validation procedure.
Args:
    data (array):       vector of binary outcomes.
    tr_ind (array):     Indices for training sample.
    test_ind (array):   Indices for validation sample.
    focus_ind (array):  Indices for focus covariates.
    aux_ind (array):    Indices for auxiliary covariates.
    beta0 (int):        Coefficient taking value either 1 or -1 to normalize the scale for the first
                        first covariate in x_foc.
    q (int):            Cardinality constraint for the covariate selection.
    T (int):            Time limit specified for the MIO solver.
    bnd (array):        (((k-1)+d) by 2) matrix where the first and second columns respectively
                        store the lower and upper bounds of the unknown coefficients. First (k-1)
                        rows correspond to the bounds of the focused covariates excluding the first one.
                        Remaining d rows correspond to the bounds of the auxiliary covariates.
    mio (int) :         MIO formulation of the best subset maximum score problem
                        set mio = 1 for Method 1 of the paper
                        set mio = 2 for Method 2 of the paper
    tau (int):          Tuning parameter for enlarging the estimated bounds.
Yields:
    best_q:             Best q value.
    bhat (array):       Maximum score estimates for the unknown coefficients.
    score (array):      Value of maximum score objective function.
    gap   (array):      MIO optimization gap value in case of early termination
                        gap = 0 ==> optimal solution is found within the time limit.
    rtime (array):      Time used by the MIO solver in the estimation procedure.
    ncount (array):     Node count.
"""
function CVMaxScore(tr_ind::AbstractArray,test_ind::AbstractArray,data::AbstractArray,focus_ind::AbstractArray,aux_ind::AbstractArray,beta0::Integer,q_range::AbstractMatrix,T::Real,tol::Real,bnd::AbstractArray,mio::Integer)
    q_num = len(q_range)
    fold=size(tr_ind)[1]
    score=zeros(fold,q_num)
    gap=zeros(fold,q_num)
    rtime=zeros(fold,q_num)
    ncount=zeros(fold,q_num)
    bhat=zeros(len(focus_ind)+len(aux_ind)-1,fold,q_num)
    val_score=zeros(q_num,1)

    for q=1:q_num
        for i=1:fold
            println("(q,fold) : ",q_range[q]," ",i+1)
            y=data[tr_ind[:,i],1]
            datax=data[tr_ind[:,i],2:size(data)[2]]

            bhat[:,i,q],score[i,q],gap[i,q],rtime[i,q],ncount[i,q]  = MaxScore(y,datax[:,focus_ind],datax[:,aux_ind],beta0,q_range[q],T,tol[q],bnd,mio)

            y_v=data[test_ind[:,i],1]
            datax_v=data[test_ind[:,i],2:size(data)[2]]
            val_score[q] = val_score[q] + mean(Int.(y_v .== (datax_v*[beta0;bhat[:,i,q]].>=0)))
        end
        val_score[q]=val_score[q]/fold
    end
    best_q = maximum(val_score)
    return best_q, bhat,score,gap,rtime,ncount
end

export CVMaxScore


module Ex7_3
using DescriptorSystems
using LinearAlgebra
using Polynomials
using Test

# Example 7.3 - Nullspace-based synthesis
# Uses only DescriptorSystems.jl
println("Example 7.3")

A = diagm([ -2, -3, -1, 2, 3]);
Bu = [1 1 0 0 0]'; Bd = [0 0 2 0 0]';
Bf = [0 0 0 2 2;0 0 0 0 0]';
C = [-1 0 -1 1.5 0; 0 -1 0 0 2.5];
Du = [ 1 1]'; Dd = [1 0]'; Df = [1 0; 1 1];
p = 2; mu = 1; md = 1; mf = 2;       # enter dimensions
sys = dss(A,[Bu Bd Bf],C,[Du Dd Df]); # define system

# compute [Q Rf], where Q is a left nullspace basis of
# [Gu Gd;I 0] and Rf = Q*[Gf;0];
Q_Rf = glnull([sys;eye(mu,mu+md+mf)],mf)[1];

# stabilize using left coprime factorization
Q_Rf, M = glcf(Q_Rf, sdeg = -3, smarg = -2);

# compute Q and Rf; scale to meet example
Q = -sqrt(2)*Q_Rf[:,1:p+mu]; Rf = -sqrt(2)*Q_Rf[:,p+mu+1:end];

# display results
println("Q = $(dss2rm(Q))")
println("Rf = $(dss2rm(Rf))")
println("M = $(dss2rm(M))")

end # module


include("euler/euler.jl")
using .Calculus: fibonacci_index, fibonacci_numbers

function compute(n::Int)::Int
    index = fibonacci_index(n)
    fibonacci = fibonacci_numbers(index, Int)
    last_sum, new_sum = 0, 0
    result = sum(fibonacci[1:5]) - 1
    for i ∈ 6:index + 1
        last_sum, new_sum = new_sum, new_sum + last_sum + fibonacci[i - 3]
        result += new_sum + fibonacci[i]
    end
    return result
end


for N in [Float64, Rational{Int}, Float32]

    # ==================================
    # Constructor and interface methods
    # ==================================

    B = BallInf(zeros(N, 3), N(1))
    v = N[1, 0, 0] # translation along dimension 1
    M = Diagonal(N[1, 2, 3])
    am = AffineMap(M, B, v)

    # dimension check
    @test dim(am) == 3

    # dimension assertion
    @test_throws AssertionError AffineMap(M, B, N[0, 0])
    @test_throws AssertionError AffineMap(M, B × B, v)

    # linear map of an affine map is automatically simplified to an affine map
    Mam = M * am
    @test Mam isa AffineMap && Mam.M == M * am.M && Mam.v == M * am.v

    # scaling of an affine map is an affine map
    α = N(2)
    αam = α * am
    @test αam isa AffineMap && αam.M == α * am.M && αam.v == α * am.v

    # support vector
    @test σ(N[1, 0, 0], am) == N[2, 2, 3]

    # support function
    @test ρ(N[1, 0, 0], am) == N(2)

    # boundedness
    @test isbounded(am)
    @test !isbounded(AffineMap(M, Universe{N}(3), v))

    # function to get an element
    @test (an_element(am) - am.v) ∈ (am.M * am.X)
    @test an_element(am) ∈ am.M * am.X ⊕ am.v

    # emptiness check
    @test !isempty(am)
    # @test isempty(AffineMap(M, EmptySet{N}(), v)) # requires #1201

    # ==================================
    # Type-specific methods
    # ==================================

    # an affine map of the form I*X + b where I is the identity matrix is a pure translation
    #v = N[1, 0, 2]
    #am_tr = AffineMap(I, B, v) # crashes, see #1544
    #@test am_tr isa Translation && am_tr.v == v

    # two-dimensional case
    B2 = BallInf(zeros(N, 2), N(1))
    M = N[1 0; 0 2]
    v = N[-1, 0]
    am = AffineMap(M, B2, v)

    # list of vertices check
    vlist = vertices_list(am)
    @test ispermutation(vlist, [N[0, 2], N[-2, 2], N[0, -2], N[-2, -2]])

    # inclusion check
    h = Hyperrectangle(N[-1, 0], N[1, 2])
    @test h ⊆ am && am ⊆ h
end

# tests that only work with Float64 and Float32
for N in [Float64, Float32]
    B = BallInf(zeros(N, 3), N(1))

    # the translation is the origin and the linear map is the identity => constraints remain unchanged
    Id3 = Matrix(one(N) * I, 3, 3)
    @test ispermutation(constraints_list(AffineMap(Id3, B, zeros(N, 3))),
                        constraints_list(B))
end


@testset "Indexing" begin
    @testset "Linear getindex() on SVector" begin
        sv = SVector{4}(4,5,6,7)

        # Tuple
        @test (@inferred getindex(sv, (4,3,2,1))) === SVector((7,6,5,4))

        # Colon
        @test (@inferred getindex(sv,:)) === sv
    end

    @testset "Linear getindex()/setindex!() on MVector" begin
        vec = @SVector [4,5,6,7]

        # Tuple
        mv = MVector{4,Int}()
        @test (mv[(1,2,3,4)] = vec; (@inferred getindex(mv, (4,3,2,1)))::MVector{4,Int} == MVector((7,6,5,4)))

        # Colon
        mv = MVector{4,Int}()
        @test (mv[:] = vec; (@inferred getindex(mv, :))::MVector{4,Int} == MVector((4,5,6,7)))
    end

    @testset "Linear getindex()/setindex!() with a tuple on an Array" begin
        v = [11,12,13]
        m = [1.0 2.0; 3.0 4.0]

        @test v[(2,3)] === SVector(12, 13)
        @test m[(2,3)] === SVector(3.0, 2.0)

        @test (v[(2,3)] = [22,23]; (v[2] == 22) & (v[3] == 23))

    end

    @testset "2D getindex() on SMatrix" begin
        sm = @SMatrix [1 3; 2 4]

        # Scalar
        @test sm[1,1] === 1
        @test sm[2,1] === 2
        @test sm[1,2] === 3
        @test sm[2,2] === 4

        # Tuple, scalar
        @test (@inferred getindex(sm, (2,1), (2,1))) === @SMatrix [4 2; 3 1]
        @test (@inferred getindex(sm, 1, (1,2))) === @SVector [1,3]
        @test (@inferred getindex(sm, (1,2), 1)) === @SVector [1,2]

        # Colon
        @test (@inferred getindex(sm, :, :)) === @SMatrix [1 3; 2 4]
        @test (@inferred getindex(sm, (2,1), :)) === @SMatrix [2 4; 1 3]
        @test (@inferred getindex(sm, :, (2,1))) === @SMatrix [3 1; 4 2]
        @test (@inferred getindex(sm, 1, :)) === @SVector [1,3]
        @test (@inferred getindex(sm, :, 1)) === @SVector [1,2]
    end

    @testset "2D getindex()/setindex! on MMatrix" begin
        sm = @MMatrix [1 3; 2 4]

        # Tuple, scalar
        @test (mm = MMatrix{2,2,Int}(); mm[(2,1),(2,1)] = sm[(2,1),(2,1)]; (@inferred getindex(mm, (2,1), (2,1)))::MMatrix == @MMatrix [4 2; 3 1])
        @test (mm = MMatrix{2,2,Int}(); mm[1,(1,2)] = sm[1,(1,2)]; (@inferred getindex(mm, 1, (1,2)))::MVector == @MVector [1,3])
        @test (mm = MMatrix{2,2,Int}(); mm[(1,2),1] = sm[(1,2),1]; (@inferred getindex(mm, (1,2), 1))::MVector == @MVector [1,2])

        # Colon
        @test (mm = MMatrix{2,2,Int}(); mm[:,:] = sm[:,:]; (@inferred getindex(mm, :, :))::MMatrix == @MMatrix [1 3; 2 4])
        @test (mm = MMatrix{2,2,Int}(); mm[(2,1),:] = sm[(2,1),:]; (@inferred getindex(mm, (2,1), :))::MMatrix == @MMatrix [2 4; 1 3])
        @test (mm = MMatrix{2,2,Int}(); mm[:,(2,1)] = sm[:,(2,1)]; (@inferred getindex(mm, :, (2,1)))::MMatrix == @MMatrix [3 1; 4 2])
        @test (mm = MMatrix{2,2,Int}(); mm[1,:] = sm[1,:]; (@inferred getindex(mm, 1, :))::MVector == @MVector [1,3])
        @test (mm = MMatrix{2,2,Int}(); mm[:,1] = sm[:,1]; (@inferred getindex(mm, :, 1))::MVector == @MVector [1,2])
    end

    @testset "2D getindex() with tuples on an Array" begin
        m = [1.0 2.0; 3.0 4.0]

        @test m[(1,2), (1,2)] === @SMatrix [1.0 2.0; 3.0 4.0]
        @test m[1, (1,2)] === @SVector [1.0, 2.0]
        @test m[(1,2), 1] === @SVector [1.0, 3.0]
    end

    @testset "3D scalar indexing" begin
        sa = SArray{(2,2,2), Int}([i*j*k for i = 1:2, j = 2:3, k=3:4])

        @test sa[1,1,2] === 8
        @test sa[1,2,1] === 9
        @test sa[2,1,1] === 12

        ma = MArray{(2,2,2), Int}()
        @test (ma[1,1,2] = 8; ma[1,1,2] === 8)
        @test (ma[1,2,1] = 9; ma[1,2,1] === 9)
        @test (ma[2,1,1] = 12; ma[2,1,1] === 12)
    end

    @testset "4D scalar indexing" begin
        sa = SArray{(2,2,2,2), Int}([i*j*k*l for i = 1:2, j = 2:3, k=3:4, l=4:5])

        @test sa[1,1,1,2] === 30
        @test sa[1,1,2,1] === 32
        @test sa[1,2,1,1] === 36
        @test sa[2,1,1,1] === 48

        ma = MArray{(2,2,2,2), Int}()
        @test (ma[1,1,1,2] = 30; ma[1,1,1,2] === 30)
        @test (ma[1,1,2,1] = 32; ma[1,1,2,1] === 32)
        @test (ma[1,2,1,1] = 36; ma[1,2,1,1] === 36)
        @test (ma[2,1,1,1] = 48; ma[2,1,1,1] === 48)
    end

end


"""
    function var_maxent(X,p)
Fits Vector AR model (VAR) to multi-trial, multivariate time series data
NOTE: May be inneficient!!! Direct translation from MATLAB code

### Arguments

* X - multi-trial time series data
      [n,m,N] = size(X); for n = number of variables (e.g.
       channels), m = number of sample per trial, N = number of trials.
* p  - model order (number of lags)

### output

* A          VAR coefficients matrix
* SIGMA      residuals covariance matrix
* E          residuals time series

Returns VAR coefficients |A| and (optionally) residuals covariance matrix
|SIG| and serially uncorrelated residuals |E| for the |p|-lag autoregression

(where  [[ii_Sigma.png]] = |SIG|) of a stationary multivariate process
|X|. |X| may contain single- or multi-trial multivariate time series
data. LWR algorithm [1].

[1] M. Morf, A. Viera, D. T. L. Lee and T. Kailath, "Recursive Multichannel
Maximum Entropy Spectral Estimation", _IEEE Trans. Geosci. Elec._, 16(2), 1978.
This function is based on the MVGC toolbox (Barnett and Seth, 2014)
"""
function var_maxent(X,p)

    n,m,N = size(X);
    assert(p < m);
    p1 = p+1;

    A   = NaN; # assure a "bad" return value if anything goes wrong (see routine "isbad")
    SIGMA = NaN; # assure a "bad" return value if anything goes wrong (see routine "isbad")
    E   = NaN; # assure a "bad" return value if anything goes wrong (see routine "isbad")

    X = demean_time(X); # no constant term

    q1n = p1*n;

    I = eye(n);

    # store lags
    XX = zeros(n,p1,m+p,N);
    for k = 0:p
        XX[:,k+1,k+1:k+m,:] = X; # k-lagged observations
    end

    # initialise recursion
    AF = zeros(n,q1n); # forward  AR coefficients
    AB = zeros(n,q1n); # backward AR coefficients (reversed compared with Morf"s treatment)

    k  = 1;            # model order is k-1
    kn = k*n;
    M  = N*(m-k);
    kf = 1:kn;         # forward  indices
    kb = q1n-kn+1:q1n; # backward indices

    XF = reshape(XX[:,1:k,k+1:m,:],kn,M);
    XB = reshape(XX[:,1:k,k:m-1,:],kn,M);

    CXF = chol(XF*XF');
    # if cholp, return; end # show-stopper! MATLAB check for A positive-definite. Julia?

    CXB= chol(XB*XB');
    # if cholp, return; end # show-stopper! MATLAB check for A positive-definite. Julia?

    AF[:,kf] = CXF'\I;
    AB[:,kb] = CXB'\I;

    AFPREV = zeros(size(AF))
    EF = zeros(kn, M)

    # and loop
    while k <= p

        EF = AF[:,kf]*reshape(XX[:,1:k,k+1:m,:],kn,M); # forward  prediction errors
        EB = AB[:,kb]*reshape(XX[:,1:k,k:m-1,:],kn,M); # backward prediction errors

        CEF = chol(EF*EF');
    #     if cholp, return; end  # show-stopper!

        CEB = chol(EB*EB');
    #     if cholp, return; end  # show-stopper!

        R = CEF'\(EF*EB')/CEB; # normalised reflection coefficients

        RF = chol(I-R*R');
    #     if cholp, return; end  # show-stopper!

        RB = chol(I-R'*R);
    #     if cholp, return; end  # show-stopper!

        k  = k+1;
        kn = k*n;
        M  = N*(m-k);
        kf = 1:kn;
        kb = q1n-kn+1:q1n;

        AFPREV = AF[:,kf];
        ABPREV = AB[:,kb];

        AF[:,kf] = RF'\(AFPREV-R*ABPREV);
        AB[:,kb] = RB'\(ABPREV-R'*AFPREV);

    end

    E   = AFPREV[:,1:n]\EF;   # residuals
    SIGMA = (E*E')/(M-1);       # residuals covariance matrix
    E   = reshape(E,n,m-p,N); # put residuals back into per-trial form
    A = reshape(-AF[:,1:n]\AF[:,n+1:end],n,n,p); # so A(:,:,k) is the k-lag coefficients matrix

    return A,SIGMA,E

end

"""
    function demean(X; normalise=false)

Temporal demean of time series data

### Arguments

* X - multi-trial time series data
* normalise::Bool  - normalise (temporal) variance of each variable to 1 (default: false)

### output

* Y - demeaned time series data
"""
function demean_time(X::Array{Float64,3}; normalise=false)
    n,m,N = size(X);
    U = ones(1,N*m);
    Y = X.-mean(X,2);
    if normalise
        Y = Y./std(Y,2);
    end
    return Y
end


using CausalInference
using LightGraphs
using Test
using Random
using Statistics
include("plotdag.jl")

# After examples discussed in chapter 2 of
# Pearl, Judea. Causality. Cambridge University Press, 2009.

Random.seed!(123)
N = 1000
p = 0.01
x = randn(N)
v = x + randn(N)*0.25
w = x + randn(N)*0.25
z = v + w + randn(N)*0.25
s = z + randn(N)*0.25

X = [x v w z s]
C = cor(X)
df = (x=x, v=v, w=w, z=z, s=s)

println("Running Gaussian tests")
@time est_g = pcalg(df, p, gausscitest)

variables = [String(k) for k in keys(df)]
tp = plot_dag(est_g, variables)
save(PDF("estdag"), tp)

# This should be the true DAG

g = DiGraph(5)
d = nv(g)
for (i,j) in [(1,2), (1,3), (2,4), (3,4), (4,5)]
   add_edge!(g, i, j)
end

tp = plot_dag(g, variables)
save(PDF("truedag"), tp)

# Use the oracle to tell about a graph what can be learned from observations
dg = pcalg(d, dseporacle, g)

tp = plot_dag(dg, variables)
save(PDF("equivalencedag"), tp)

@testset "pctest" begin
    @test collect(LightGraphs.edges(est_g)) == collect(LightGraphs.edges(dg))
end


#== # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
#
# Description
#
#   Many auxiliary functions for sun synchronous orbit computations.
#
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # ==#

export compute_ss_orbit_by_ang_vel
export compute_ss_orbit_by_inclination
export compute_ss_orbit_by_semi_major_axis

"""
    function compute_ss_orbit_by_ang_vel(n::Number, e::Number)

Compute the Sun-synchronous orbit given the angular velocity `n` [rad/s] and the
eccentricity `e`.

# Returns

* The semi-major axis [m].
* The inclination [rad].
* The residues of the two functions.
* A boolean variable that indicates if the numerical algorithm converged.

"""
function compute_ss_orbit_by_ang_vel(n::Number, e::Number)
    # Check if the arguments are valid.
    (n <= 0)          && throw(ArgumentError("The angular velocity must be greater than 0."))
    !( 0. <= e < 1. ) && throw(ArgumentError("The eccentricity must be within the interval 0 <= e < 1."))

    # Tolerance for the Newton-Raphson method.
    tol = 1e-18

    # Maximum number of iterations for the Newton-Raphson method.
    maxIt = 30

    # Auxiliary variables.
    sqrt_m0 = sqrt(m0)
    aux_e   = 1-e^2
    sqrt_e2 = sqrt(aux_e)

    # Auxiliary constant to compute the functions.
    K1 = 3R0^2*J2*sqrt_m0/(4aux_e^2)

    # Solve for zeros of f1 and f2 using Newton-Raphson method
    # ==========================================================================
    #
    # The function `f1` is the residue related to the time derivative of RAAN,
    # and the function `f2` is the residue related to the angular velocity.

    # Initial guess based on the unperturbed model.
    a_k = (m0/n^2)^(1/3)
    i_k = acos( -ne*a_k^(3.5)/(2K1) )

    # By setting the initial values of `f1` and `f2` to `10tol`, we assure that
    # the loop will be executed at least one time.
    f1 = 10tol
    f2 = 10tol

    # Loop
    it = 0
    converged = true

    while (abs(f1) > tol) || (abs(f2) > tol)
        a_k_1 = a_k
        i_k_1 = i_k

        # Auxiliary variables.
        sin_i, cos_i = sincos(i_k_1)
        sin_i²       = sin_i^2
        cos_i²       = cos_i^2
        a_pm1d5      = a_k_1^(-1.5)
        a_pm2d5      = a_pm1d5*a_k_1^(-1) # --> a_k_1^(-2.5)
        a_pm3d5      = a_pm2d5*a_k_1^(-1) # --> a_k_1^(-3.5)
        a_pm4d5      = a_pm3d5*a_k_1^(-1) # --> a_k_1^(-4.5)
        aux_1        = K1*sqrt_e2*(3cos_i²-1)
        aux_2        = K1*(5cos_i²-1)

        # Compute the Jacobian.
        df1da = -7K1*a_pm4d5*cos_i
        df1di = -2K1*a_pm3d5*sin_i
        df2da = -1.5*sqrt_m0*a_pm2d5 - 3.5aux_1*a_pm4d5 - 3.5aux_2*a_pm4d5
        df2di = -6K1*sqrt_e2*a_pm3d5*cos_i*sin_i - 10K1*a_pm4d5*cos_i*sin_i

        J_k_1 = SMatrix{2,2}(df1da, df1di,
                             df2da, df2di)'

        # Compute the functions.
        f1 = ne + 2K1*a_pm3d5*cos_i
        f2 = sqrt_m0*a_pm1d5 + aux_1*a_pm3d5 + aux_2*a_pm3d5 - n

        # Compute the new estimate.
        vf    = SVector{2}(f1,f2)
        v_k_1 = SVector{2}(a_k_1, i_k_1)
        v_k   = v_k_1 - inv(J_k_1)*vf
        a_k   = v_k[1]
        i_k   = v_k[2]

        # Check if the maximum number of iterations has been reached.
        it += 1

        # If the maximum number of iterations allowed has been reached, then
        # indicate that the solution did not converged and exit loop.
        if (it >= maxIt)
            converged = false
            break
        end
    end

    # Return.
    a_k, i_k, f1, f2, converged
end

"""
    function compute_ss_orbit_by_inclination(i::Number, e::Number)

Compute the Sun-synchronous orbit given the inclination `i` [rad] and the
eccentricity `e`.

# Returns

The semi-major axis of the Sun-synchronous orbit [m].

"""
function compute_ss_orbit_by_inclination(i::Number, e::Number)
    !( 0. <= e < 1. ) && throw(ArgumentError("The eccentricity must be within the interval 0 <= e < 1."))

    # Auxiliary variables.
    sqrt_m0 = sqrt(m0)

    # Auxiliary constant.
    K1 = 3.0*R0^2*J2*sqrt_m0/(4.0*(1-e^2)^2)

    # Compute the semi-major axis.
    a = (-2*cos(i)*K1/ne)^(2.0/7.0)

    # Check if the orbit is valid.
    ( a*(1-e) <  R0 ) && throw(ErrorException("It was not possible to find a valid Sun-synchronous orbit with the inclination given."))

    # Return.
    a
end

"""
    function compute_ss_orbit_by_semi_major_axis(a::Number, e::Number)

Compute the Sun-synchronous orbit given the semi-major axis `a` [m] and the
eccentricity `e`.

# Returns

The inclination of the Sun-synchronous orbit [rad].

"""
function compute_ss_orbit_by_semi_major_axis(a::Number, e::Number)
    # Check if the arguments are valid.
    (a*(1-e) <= R0)   && throw(ArgumentError("The perigee must be larger than the Earth radius."))
    !( 0. <= e < 1. ) && throw(ArgumentError("The eccentricity must be within the interval 0 <= e < 1."))

    # Auxiliary variables.
    sqrt_m0 = sqrt(m0)

    # Auxiliary constant.
    K1 = 3R0^2*J2*sqrt_m0/(4(1-e^2)^2)

    # Compute the inclination.
    cos_i = -ne*a^3*sqrt(a)/(2K1)

    # Check if -1 <= cos_i <= 1.
    ( (cos_i < -1) || (cos_i > 1) ) && throw(ErrorException("It was not possible to find a Sun-synchronous orbit with the semi-major axis given."))

    # Return.
    acos(cos_i)
end


using LinearAlgebra
using SparseArrays

export Laplacian2D,sparse_corr

struct Laplacian2D
    lpx::Laplacian1D
    lpy::Laplacian1D
    nx::Int
    ny::Int
    Lx::Float64
    Ly::Float64
    bc::NTuple{2,NTuple{2,BoundaryCondition}}
    allneumann::Bool
    function Laplacian2D(nx,ny,Lx,Ly,bcleft,bcright,bcbottom,bctop)
        bc=((bcleft,bcright),(bcbottom,bctop))
        allneumann = bc[1][1]==bc[1][1]==bc[1][1]==bc[1][1]==neumann
        allneumann && println("allneumann !!")
        new(Laplacian1D(nx,Lx,bcleft,bcright),Laplacian1D(ny,Ly,bcbottom,bctop),nx,ny,Lx,Ly,bc,allneumann)
    end

end
function push_element!(Is,Js,Vs,I,J,v)
    push!(Is,I)
    push!(Js,J)
    push!(Vs,v)
end

function SparseArrays.sparse(a::Laplacian2D)
    Is=Int[]
    Js=Int[]
    Vs=Float64[]
    lpx,lpy=a.lpx,a.lpy
    nx,ny=lpx.n_,lpy.n_

    Is=Int[]
    Js=Int[]
    Vs=Float64[]

    for i=1:nx
        for j=1:ny
            I=i+nx*(j-1)
            push_element!(Is,Js,Vs,I,I,lpx[i,i]+lpy[j,j])# sp[I,I]=Lx[i,i]

            i>1  && push_element!(Is,Js,Vs,I,I-1,lpx[i,i-1])#(sp[I,I-1]=Lx[i,i-1])
            i<nx && push_element!(Is,Js,Vs,I,I+1,lpx[i,i+1])#(sp[I,I+1]=Lx[i,i+1])
            j>1 &&  push_element!(Is,Js,Vs,I,I-ny,lpy[j,j-1])#(sp[I,I-ny]+=Ly[j,j-1])
            j<ny && push_element!(Is,Js,Vs,I,I+ny,lpy[j,j+1])#(sp[I,I+ny]+=Ly[j,j+1])

        end
    end
    sp=sparse(Is,Js,Vs)
    sp
end

function sparse_corr(a::Laplacian2D)
    sp=SparseArrays.sparse(a::Laplacian2D)
    if a.allneumann
        sp[1,1]=(3/2)sp[1][1]
    end
    sp
end


ADCME.options.newton_raphson.verbose = true
Δt = 0.01
mmesh = Mesh(30, 30, 1/30)
bdnode = bcnode(mmesh)
bdnode = [bdnode; bdnode .+ mmesh.ndof]
nu = constant(0.0001ones(get_ngauss(mmesh)))
nodes = fem_nodes(mmesh)
u = @. sin(2π * nodes[:,1])
v = @. cos(2π * nodes[:,2])
u0 = [u;v]
u0[bdnode] .= 0.0
us = solve_burgers(u0, 10)

sess = Session(); init(sess)
U = run(sess, us)
figure(figsize=(10,5))
close("all")
subplot(121)
visualize_scalar_on_fem_points(U[end,1:mmesh.nnode], mmesh)
subplot(122)
visualize_scalar_on_fem_points(U[end,mmesh.ndof + 1:mmesh.ndof + mmesh.nnode], mmesh)
savefig("test_mfem.png")

close("all")
visualize_vector_on_fem_points(U[end,1:mmesh.nnode], U[end,mmesh.ndof + 1:mmesh.ndof + mmesh.nnode], mmesh)
savefig("mfem_quiver.png")
# function ff(x)
#     unext = constant(x[1:mmesh.ndof])
#     vnext = constant(x[mmesh.ndof+1:end])
#     r, J = calc_residual_and_jacobian(unext, vnext, u, v, mmesh)
#     run(sess, r), run(sess, J)
# end

# ff(rand(2mmesh.ndof))
# sess = Session(); init(sess)
# test Jacobian
# test_jacobian(ff, rand(2mmesh.ndof))


@testset "solveMAP" begin

M  = [1 1 1; 1 1 0; 1 0 1; 1 0 0; 0 1 1; 0 1 0; 0 0 1;]
(m,n) = size(M)
R     = StrainRecon.sdiag(ones(m)*100);
atrue = [.6; 0.3; 0.1]
Data  = M*atrue

param = getStrainReconParam(Data,n,R=R)

Mmap,amap = solveMAP(param,maxTry=100,maxIterQP=10)
@test norm(amap - atrue)/norm(atrue) < 1e-8
@test norm(Mmap*amap-Data) < 1e-7

Msqp,Asqp = solveMAP(param,storeInterm=true,maxIterQP=20)
# Asqp is n-by-history of each BCD iteration-by-MaxTry
# Msqp is m-by-n-by-history of each BCD iteration-by-MaxTry

Jc = zeros(size(Asqp,3)) # Misfit for MaxTry number of iterations

for k=1:size(Asqp,3)
    # for each Asqp iteration, find column where norm(w_old - w_new) = 0, use column number - 1 to find frame for Msqp
    iter = size(Asqp,2)
    for i = 1:size(Asqp,2)
        # println(i)
        if sum(Asqp[:,i,k]) == 0
            iter = i-1
            # println("found column of all zeros ", i-1)
            break
        end
    end
    @test all(Asqp[:,iter,k].>0)
	Jc[k] = misfit(param,Msqp[:,:,iter-1,k],Asqp[:,iter,k])[1]
end
@test all(diff(Jc).>=0)
@test norm(sum(Asqp[:,1],1)-1,Inf) < 1e-7 # Asqp is n-by-history of iterations
# @test all(Asqp[:,:].>0)
end