"""
    x=hardsphere(y,H,Nc)

  Solve the problem argmin_x ||y-Hx||.  The input vector y is of
  length N, with H of dimension N by M, and the returned vector x of
  length M.  If Nc is even the elements of x are integers from
  [0:Nc-1]*2-(Nc-1); odd Nc is not yet supported. For Nc=2, we're
  searching the 2PAM constellation of [-1,1], and for nInt=4 we're
  searching [-3,-1,1,3].

  Digital communication researchers often call the Fincke-Pohst search
  algorithm used in this function as the "sphere-decoder" because of its use
  in decoding digital signals and for the use of a multi-dimensional sphere
  to constrain search complexity. This function uses only hard-decision
  decoding, hence the term "hardsphere".  To complete the vocabulary lesson,
  the problem that number theorists call the "closest vector problem" is
  labeled "integer least squares" by many GPS researchers.

X=hardsphere(Y,H,Nc)

  The input may be a matrix Y of dimension N by Ns, in which case the
  problem is solved for each column of Y, with the solutions in the
  columns of X.

# Examples:
```jldoctest
julia> X = hardsphere([1. 1]', [1. 2; 3 4],2)
2×1 Array{Int64,2}:
 -1
  1

```
"""
function hardsphere(Y::AbstractArray{Td,2},H::AbstractArray{Td,2},Nc::Integer) where {Td}
    (N,M) = size(H);

    Qc = ones(M)*Nc;
    if iseven(Nc)
        xoffset = Qc.-1
        xmult = 2
    else
        @error("Odd Nc is not yet supported.")
        # To find a failing case, remove @error above, and compare w full
        # search. The fix is likely minor (once it is found).
        xoffset = floor.(Qc/2)
        xmult = 1
    end
    Q,R = qr(H);
    N,Ns = size(Y);
    Xh = zeros(Int,M,Ns);
    for ns = 1:Ns
        yp = (Q'*Y[:,ns] + R*ones(M,1).*xoffset)/xmult;
        xp = algIIsmart(yp,R,Qc);
        Xh[:,ns] = xp*xmult - xoffset;
    end

    return Xh
end

"""
    hard_sphere(...) = hardsphere(...)

See hardsphere; in a future version hard_sphere will be removed.
"""
hard_sphere(x...) = hardsphere(x...)

"""
    xh = algIIsmart(yp,R,Qc)

Find the closest xh for yp=R*xh + w, where w is some unknown noise and x(i)
is from the range [0,...,Qc(i)-1]. This implements the "Alg II-smart"
hard-decision sphere decoder as in Damen, El Gamal, and Caire, Trans It
03. This function is not exported from LLLplus at present and may disappear
in the future.

# Examples:
```jldoctest
julia> X = algIIsmart([5. 8]', [1. 2; 0 4],[4,4])
2×1 Array{Float64,2}:
 1.0
 2.0

```
"""
function algIIsmart(yp,R,Qc)
    (N,M) = size(R);

    xh = zeros(M,1); # the best solution
    xp = zeros(M,1); # work vector (a possible solution)
    ksi = zeros(M,1);
    TT = zeros(M,1);
    delta = zeros(M,1);

    # Step 1: initialization
    ix = M;
    TT[ix] = 0;
    ksi[ix] = 0;
    dc = Inf;
    state = 2;
    terminate= false;

    # show(Qc)
    # println(" ")
    #    @printf("state=%d, ix=%d, xp[ix] = %6.3f, xh[ix] = %6.3f, "*
    #       "delta[ix] = %6.3f\n",state,ix,xp[ix],xh[ix],delta[ix])
    while ~terminate

        if state==2 # Step 2: DFE
            xp[ix] = round((yp[ix]-ksi[ix])/R[ix,ix]);
            delta[ix] = sign(yp[ix]-ksi[ix] - R[ix,ix]*xp[ix]);
            state = 3;
        elseif state== 3 # Step 3: Main loop
            ytmp = yp[ix]-ksi[ix] - R[ix,ix]*xp[ix];
            # @printf("ytmp'*ytmp = %6.3f, TT[ix] = %6.3f, "*
            #   "TT[ix] + ytmp'*ytmp = %6.3f, dc=%6.3f\n",
            #   ytmp'*ytmp,TT[ix],TT[ix] + ytmp'*ytmp,dc)
            if TT[ix] + ytmp'*ytmp >= dc
                # We're outside the sphere
                state = 4;
                # elseif !(0<=real(xp[ix])<Qc[ix]) || !(0<=imag(xp[ix])<Qc[ix])
            elseif !(0 <= xp[ix] < Qc[ix])
                # inside sphere but outside signal set boundary
                state = 6;
            else # inside sphere and inside signal set
                if ix>1
                    # ksi[ix-1] = (conj(R[ix-1,ix:M])*xp[ix:M])[1];
                    ksi[ix-1] = (R[ix-1,ix:M]'*xp[ix:M])[1];
                    TT[ix-1] = TT[ix] + ytmp'*ytmp;
                    ix = ix-1;
                    state = 2;
                else
                    state = 5;
                end
            end
        elseif state==4 # Step 4: Check termination criteria
            if ix==M
                terminate=true;
            else
                ix = ix+1;
                state = 6;
            end
        elseif state==5 # Step 5: A valid point is found
            ytmp = yp[1] -ksi[1] - R[1,1]*xp[1];
            dc = TT[1] + ytmp'*ytmp;
            xh[:] = xp[:];
            ix = ix+1;
            state = 6;
        elseif state==6 # Step 6: Schnorr-Euchner
            xp[ix] = xp[ix] + delta[ix];
            delta[ix] = -delta[ix] -sign(delta[ix]);
            state = 3;
        else
            error("Unknown state");
        end
        #@printf("state=%d, ix=%d, xp = [%1.0f %1.0f %1.0f %1.0f], "*
        #    xh = [%1.0f %1.0f %1.0f %1.0f], δ[ix] = %2.0f\n",
        #    state,ix,xp[1],xp[2],xp[3],xp[4],xh[1],xh[2],xh[3],xh[4],delta[ix])
        #    @printf("state=%d, ix=%d, xp = [%1.0f %1.0f], xh = [%1.0f "*
        #      "%1.0f],  δ[ix] = %2.0f\n",state,ix,xp[1],xp[2],xh[1],xh[2],delta[ix])
    end
    #println("xh=$(xh)")
    return xh
end


import Primes: primes

function solve()
    n = parse(Int, readline())

    m = floor(Int, sqrt(n))

    s = Set{Int}()
    for a=2:m
        for b=2:n
            x = a^b
            if x > n
                break
            end
            push!(s, x)
        end
    end

    n - length(s)
end

println(solve())


"""
`Transition{T<:AbstractFloat}`

The transition equation of the state-space model takes the form

    `s_t = TTT*s_{t-1} + RRR*ϵ_t + CCC`

The `Transition` type stores the coefficient `Matrix{T}`s (`TTT`, `RRR`) and constant `Vector{T} CCC`.
"""
mutable struct Transition{T<:AbstractFloat}
    TTT::Matrix{T}
    RRR::Matrix{T}
    CCC::Vector{T}
end
function Transition(TTT::Matrix{T}, RRR::Matrix{T}) where T<:AbstractFloat
    CCC = zeros(eltype(TTT), size(TTT, 1))
    Transition{T}(TTT, RRR, CCC)
end
function Transition(TTT::Matrix{T}, RRR::Matrix{T}, CCC::Matrix{T}) where T<:AbstractFloat
    Transition{T}(TTT, RRR, collect(CCC))
end
function Base.getindex(eq::Transition, d::Symbol)
    if d in (:TTT, :RRR, :CCC)
        return getfield(eq, d)
    else
        throw(KeyError(d))
    end
end

"""
`Measurement{T<:AbstractFloat}`

The measurement equation of the state-space model takes the form

   `y_t = ZZ*s_t + DD + u_t`

where the error `u_t` is the measurement error, which is uncorrelated with the
shocks in the transition equation `ϵ_t`.

### Fields

If `Ns` is the number of states `s_t`, `Ny` is the number of
observables `y_t`, and `Ne` is the number of shocks `ϵ_t`:

- `ZZ`: the `Ny` x `Ns`  measurement matrix
- `DD`: the `Ny` x 1 constant vector
- `QQ`: the `Ne` x `Ne` covariance matrix for the shocks `ϵ_t`
- `EE`: the `Ny` x `Ny` covariance matrix for the measurement error `η_t`
"""
mutable struct Measurement{T<:AbstractFloat}
    ZZ::Matrix{T}
    DD::Vector{T}
    QQ::Matrix{T}
    EE::Matrix{T}
end

function Base.getindex(M::Measurement, d::Symbol)
    if d in (:ZZ, :DD, :QQ, :EE)
        return getfield(M, d)
    else
        throw(KeyError(d))
    end
end

function measurement(m::AbstractDSGEModel, trans::Transition; shocks::Bool=true)
    TTT = trans[:TTT]
    RRR = trans[:RRR]
    CCC = trans[:CCC]
    measurement(m, TTT, RRR, CCC; shocks=shocks)
end

"""
```
PseudoMeasurement{T<:AbstractFloat}
```

The pseudo-measurement equation of the state-space model takes the form

   `x_t = ZZ_pseudo*s_t + DD_pseudo`

### Fields

Let `Ns` be the number of states `s_t` and `Nx` be the number of
pseudo-observables `x_t`:

- `ZZ_pseudo`: the `Nx` x `Ns` pseudo-measurement matrix
- `DD_pseudo`: the `Nx` x 1 constant vector
"""
mutable struct PseudoMeasurement{T<:AbstractFloat}
    ZZ_pseudo::Matrix{T}
    DD_pseudo::Vector{T}
end

function Base.getindex(M::PseudoMeasurement, d::Symbol)
    if d in (:ZZ_pseudo, :DD_pseudo)
        return getfield(M, d)
    else
        throw(KeyError(d))
    end
end

"""
`System{T<:AbstractFloat}`

A mutable struct containing the transition and measurement equations for a
state-space model. The matrices may be directly indexed: `sys[:TTT]`
returns `sys.transition.TTT`, etc.
"""
mutable struct System{T<:AbstractFloat}
    transition::Transition{T}
    measurement::Measurement{T}
    pseudo_measurement::PseudoMeasurement{T}
end

function System(transition::Transition{T}, measurement::Measurement{T}) where T<:AbstractFloat
    # Initialize empty pseudo-measurement equation
    _n_states = size(transition.TTT, 1)
    _n_pseudo = 0
    ZZ_pseudo = zeros(_n_pseudo, _n_states)
    DD_pseudo = zeros(_n_pseudo)
    pseudo_measurement = PseudoMeasurement(ZZ_pseudo, DD_pseudo)

    return System(transition, measurement, pseudo_measurement)
end

function Base.getindex(system::System, d::Symbol)
    if d in (:transition, :measurement, :pseudo_measurement)
        return getfield(system, d)
    elseif d in fieldnames(typeof(system.transition))
        return getfield(system.transition, d)
    elseif d in fieldnames(typeof(system.measurement))
        return getfield(system.measurement, d)
    elseif d in fieldnames(typeof(system.pseudo_measurement))
        return getfield(system.pseudo_measurement, d)
    else
        throw(KeyError(d))
    end
end

function Base.copy(system::System)
    trans = Transition(system[:TTT], system[:RRR], system[:CCC])
    meas  = Measurement(system[:ZZ], system[:DD], system[:QQ], system[:EE])
    pseudo_meas = PseudoMeasurement(system[:ZZ_pseudo], system[:DD_pseudo])
    return System(trans, meas, pseudo_meas)
end

"""
```
compute_system(m; apply_altpolicy = false)
```

Given the current model parameters, compute the state-space system
corresponding to model `m`. Returns a `System` object.
"""
function compute_system(m::AbstractDSGEModel{T}; apply_altpolicy = false,
                        verbose::Symbol = :high) where T<:AbstractFloat
    # Solve model
    TTT, RRR, CCC = solve(m; apply_altpolicy = apply_altpolicy, verbose = verbose)
    transition_equation = Transition(TTT, RRR, CCC)

    # Solve measurement equation
    measurement_equation = measurement(m, TTT, RRR, CCC)

    type_tuple = (typeof(m), Matrix{T}, Matrix{T}, Vector{T})
    if hasmethod(pseudo_measurement, type_tuple)
        # Solve pseudo-measurement equation
        pseudo_measurement_equation = pseudo_measurement(m, TTT, RRR, CCC)
        return System(transition_equation, measurement_equation, pseudo_measurement_equation)
    else
        return System(transition_equation, measurement_equation)
    end
end

"""
```
compute_system(m::PoolModel{T})
```

Given the current model parameters, compute the state-space system
corresponding to the PoolModel model `m`.

Outputs

```
Φ: state transition function
Ψ: likelihood function, given weights on underlying models (the states) and predictive densities
F_ϵ: structural shock distribution
F_u: likelihood function measurement error distribution
F_λ: initial distribution of λ for state transition function
"""
function compute_system(m::PoolModel{T};
                        verbose::Symbol = :high) where T<:AbstractFloat
    Φ, F_ϵ, F_λ = transition(m)
    Ψ, F_u = measurement(m)
    return Φ, Ψ, F_ϵ, F_u, F_λ
end

"""
```
compute_system_function(system::System{S}) where S<:AbstractFloat
```

### Inputs

- `system::System`: The output of compute_system(m), i.e. the matrix outputs from solving a given model, m.

### Outputs

- `Φ::Function`: transition equation
- `Ψ::Function`: measurement equation
- `F_ϵ::Distributions.MvNormal`: shock distribution
- `F_u::Distributions.MvNormal`: measurement error distribution
"""
function compute_system_function(system::System{S}) where S<:AbstractFloat
    # Unpack system
    TTT    = system[:TTT]
    RRR    = system[:RRR]
    CCC    = system[:CCC]
    QQ     = system[:QQ]
    ZZ     = system[:ZZ]
    DD     = system[:DD]
    EE     = system[:EE]

    # Define transition and measurement functions
    @inline Φ(s_t1::Vector{S}, ϵ_t::Vector{S}) = TTT*s_t1 + RRR*ϵ_t + CCC
    @inline Ψ(s_t::Vector{S}) = ZZ*s_t + DD

    # Define shock and measurement error distributions
    nshocks = size(QQ, 1)
    nobs    = size(EE, 1)
    F_ϵ = Distributions.MvNormal(zeros(nshocks), QQ)
    F_u = Distributions.MvNormal(zeros(nobs),    EE)

    return Φ, Ψ, F_ϵ, F_u
end

function zero_system_constants(system::System{S}) where S<:AbstractFloat
    system = copy(system)

    system.transition.CCC = zeros(size(system[:CCC]))
    system.measurement.DD = zeros(size(system[:DD]))
    system.pseudo_measurement.DD_pseudo = zeros(size(system[:DD_pseudo]))

    return system
end


using Postprocessing
using Test
using LinearAlgebra, DelimitedFiles

@testset "Fourier Padé reconstruction" begin
  println(devnull, reference(fourier_pade))

  for T in (Float32, Float64)
    data = readdlm(joinpath(@__DIR__, "test_u_piecewise_constant.txt"), comments=true)
    x = T.(data[:, 1])
    u = T.(data[:, 2])
    u_true = @. ifelse(abs(x - 0.6) < 0.2, one(T), zero(T))

    u_reconstructed = fourier_pade(u, 50, 50)
    @test norm(u_reconstructed - u_true) < norm(u - u_true)
    @test total_variation(u_reconstructed) < total_variation(u)

    u_reconstructed = fourier_pade(u, 50, 45)
    @test norm(u_reconstructed - u_true) < norm(u - u_true)
    @test total_variation(u_reconstructed) < total_variation(u)

    # this choice of the degrees leads to increased oscillations
    # near the discontinuity but should still be computable
    u_reconstructed = fourier_pade(u, 50, 52)

    N = length(u)
    @test_throws ArgumentError fourier_pade(u[1:end-1], N-(N÷2)+1, N÷2)
  end
end


@testset "$TEST $G" begin

if !@isdefined(test_find_path_types)
    # Test the types of the values returned by fetch_path
    function test_find_path_types(residual_graph, s, t, flow_matrix, capacity_matrix)
        v, P, S, flag = Erdos.fetch_path(residual_graph, s, t, flow_matrix, capacity_matrix)
        @test typeof(P) == Vector{Int}
        @test typeof(S) == Vector{Int}
        @test typeof(flag) == Int
        @test typeof(v) <: Integer
    end

    # Test the value of the flags returned.
    function test_find_path_disconnected(residual_graph, s, t, flow_matrix, capacity_matrix)
        h = copy(residual_graph)
        for dst in collect(neighbors(residual_graph, s))
            rem_edge!(residual_graph, s, dst)
        end
        v, P, S, flag = Erdos.fetch_path(residual_graph, s, t, flow_matrix, capacity_matrix)
        @test flag == 1
        for dst in collect(neighbors(h, t))
            rem_edge!(h, t, dst)
        end
        v, P, S, flag = Erdos.fetch_path(h, s, t, flow_matrix, capacity_matrix)
        @test flag == 0
        for i in collect(in_neighbors(h, t))
            rem_edge!(h, i, t)
        end
        v, P, S, flag = Erdos.fetch_path(h, s, t, flow_matrix, capacity_matrix)
        @test flag == 2
    end
end

# Construct DiGraph
flow_graph = DG(8)

# Load custom dataset
flow_edges = [
    (1,2,10),(1,3,5),(1,4,15),(2,3,4),(2,5,9),
    (2,6,15),(3,4,4),(3,6,8),(4,7,16),(5,6,15),
    (5,8,10),(6,7,15),(6,8,10),(7,3,6),(7,8,10)
]

capacity_matrix = zeros(Int,8,8)

for e in flow_edges
    u,v,f = e
    add_edge!(flow_graph,u,v)
    capacity_matrix[u,v] = f
end

residual_graph = complete(flow_graph)

# Test with default distances
@test Erdos.edmonds_karp_impl(residual_graph, 1, 8, Erdos.DefaultCapacity(residual_graph))[1] == 3

# Test with capacity matrix
@test Erdos.edmonds_karp_impl(residual_graph,1,8,capacity_matrix)[1] == 28

flow_matrix = zeros(Int, nv(residual_graph), nv(residual_graph))
test_find_path_types(residual_graph, 1,8, flow_matrix, capacity_matrix)
test_find_path_disconnected(residual_graph, 1, 8, flow_matrix, capacity_matrix)

end # testset


using Primes: nextprime

################################################################
function fed_tau_by_process_time(T::Float64, M::Int64, tau_max::Float64,
                            reordering::Bool)
    ## All cycles have the same fraction of the stopping time
    fed_tau_by_cycle_time(T/M, tau_max, reordering)
end

################################################################
function fed_tau_by_cycle_time(t::Float64, tau_max::Float64,
                               reordering::Bool)

    n = 0          ## Number of time steps
    scale = 0.0    ## Ratio of t we search to maximal t

    ## Compute necessary number of time steps
    # n = ceil(Int64, sqrt(3.0*t/tau_max+0.25)-0.5-1.0e-8)

    n = floor(Int64, ceil(sqrt(3.0 * t / tau_max + 0.25) - 0.5 - 1.0e-8)+ 0.5)

    scale = 3.0*t/(tau_max*Float64(n*(n+1)))

    ## Call internal FED time step creation routine
    fed_tau_internal(n,scale,tau_max,reordering)
end

################################################################
function fed_tau_internal(n::Int64,  scale::Float64, tau_max::Float64,
                          reordering::Bool)

    if (n <= 0)
        return 0, nothing
    end

    #### Allocate memory for the time step size
    tau = zeros(n)

    if (reordering)
        tauh = zeros(n)
    end

    ## Compute time saver
    c = 1.0 / (4.0 * n + 2.0)
    d = scale * tau_max / 2.0

    ## Set up originally ordered tau vector
    for k in 1:n
        h = cos(pi * (2.0 * k - 1.0) * c)

        if reordering
            tauh[k] = d / (h * h);
        else
            tau[k] = d / (h * h);
        end
    end

    ## Permute list of time steps according to chosen reordering function
    if reordering
        ## Choose kappa cycle with k = n/2
        ## This is a heuristic. We can use Leja ordering instead!!
        kappa = n ÷ 2

        ## Get modulus for permutation
        prime = nextprime(n + 1)

        ## Perform permutation
        k = 0
        l = 0
        while l < n
            index = ((k+1)*kappa) % prime - 1
            while (index >= n)
                k+=1
                index = ((k+1)*kappa) % prime - 1
            end

            tau[l+1] = tauh[index+1]

            k += 1
            l += 1
        end
    end

    return n, tau
end


# binomial.jl
# Binomial tests
#
# Copyright (C) 2013   Simon Kornblith
#
# Permission is hereby granted, free of charge, to any person obtaining
# a copy of this software and associated documentation files (the
# "Software"), to deal in the Software without restriction, including
# without limitation the rights to use, copy, modify, merge, publish,
# distribute, sublicense, and/or sell copies of the Software, and to
# permit persons to whom the Software is furnished to do so, subject to
# the following conditions:
#
# The above copyright notice and this permission notice shall be
# included in all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

export BinomialTest, SignTest

## BINOMIAL TEST

struct BinomialTest <: HypothesisTest
    p::Float64
    x::Int
    n::Int

    BinomialTest(x::Real, n::Real, p::Real=0.5) = new(p, x, n)
end

"""
    BinomialTest(x::Integer, n::Integer, p::Real = 0.5)
    BinomialTest(x::AbstractVector{Bool}, p::Real = 0.5)

Perform a binomial test of the null hypothesis that the distribution from which `x`
successes were encountered in `n` draws (or alternatively from which the vector `x` was
drawn) has success probability `p` against the alternative hypothesis that the success
probability is not equal to `p`.

Computed confidence intervals ([`confint`](@ref)) by default are Clopper-Pearson intervals.

Implements: [`pvalue`](@ref), [`confint(::BinomialTest)`](@ref)
"""
BinomialTest(x::AbstractVector{Bool}, p=0.5) =
    BinomialTest(sum(x), length(x), p)

"""
    testname(::HypothesisTest)

Returns the string value, e.g. "Binomial test" or "Sign Test".
"""
testname(::BinomialTest) = "Binomial test"
population_param_of_interest(x::BinomialTest) = ("Probability of success", x.p, x.x/x.n) # parameter of interest: name, value under h0, point estimate
default_tail(test::BinomialTest) = :both

function show_params(io::IO, x::BinomialTest, ident="")
    println(io, ident, "number of observations: $(x.n)")
    println(io, ident, "number of successes:    $(x.x)")
end

pvalue(x::BinomialTest; tail=:both) = pvalue(Binomial(x.n, x.p), x.x; tail=tail)

# Confidence interval
"""
    confint(test::BinomialTest, alpha = 0.05; tail = :both, method = :clopper_pearson)

Compute a confidence interval with coverage 1-`alpha` for a binomial proportion using one
of the following methods. Possible values for `method` are:

  - `:clopper_pearson` (default): Clopper-Pearson interval is based on the binomial
    distribution. The empirical coverage is never less than the nominal coverage of
    1-`alpha`; it is usually too conservative.
  - `:wald`: Wald (or normal approximation) interval relies on the standard approximation of
    the actual binomial distribution by a normal distribution. Coverage can be erratically
    poor for success probabilities close to zero or one.
  - `:wilson`: Wilson score interval relies on a normal approximation. In contrast to `:wald`,
    the standard deviation is not approximated by an empirical estimate, resulting in good
    empirical coverages even for small numbers of draws and extreme success probabilities.
  - `:jeffrey`: Jeffreys interval is a Bayesian credible interval obtained by using a
    non-informative Jeffreys prior. The interval is very similar to the Wilson interval.
  - `:agresti_coull`: Agresti-Coull interval is a simplified version of the Wilson interval;
    both are centered around the same value. The Agresti Coull interval has higher or equal
    coverage.
  - `:arcsine`: Confidence interval computed using the arcsine transformation to make
    ``var(p)`` independent of the probability ``p``.

# References

  * Brown, L.D., Cai, T.T., and DasGupta, A. Interval estimation for a binomial proportion.
    Statistical Science, 16(2):101–117, 2001.

# External links

  * [Binomial confidence interval on Wikipedia](https://en.wikipedia.org/wiki/
    Binomial_proportion_confidence_interval)
"""
function StatsBase.confint(x::BinomialTest, alpha::Float64=0.05; tail=:both, method=:clopper_pearson)
    check_alpha(alpha)

    if tail == :left
        (0.0, StatsBase.confint(x, alpha*2, method=method)[2])
    elseif tail == :right
        (StatsBase.confint(x, alpha*2, method=method)[1], 1.0)
    elseif tail == :both
        if method == :clopper_pearson
            ci_clopper_pearson(x, alpha)
        elseif method == :wald
            ci_wald(x, alpha)
        elseif method == :wilson
            ci_wilson(x, alpha)
        elseif method == :jeffrey
            ci_jeffrey(x, alpha)
        elseif method == :agresti_coull
            ci_agresti_coull(x, alpha)
        elseif method == :arcsine
            ci_arcsine(x, alpha)
        else
            throw(ArgumentError("method=$(method) is not implemented yet"))
        end
    else
        throw(ArgumentError("tail=$(tail) is invalid"))
    end
end

# Clopper-Pearson interval (confidence interval by inversion)
function ci_clopper_pearson(x::BinomialTest, alpha::Float64=0.05)
    (x.x == 0 ? 0.0 : quantile(Beta(x.x, x.n - x.x + 1), alpha/2),
     x.x == x.n ? 1.0 : quantile(Beta(x.x + 1, x.n - x.x), 1-alpha/2))
end

# Wald interval / normal approximation interval
function ci_wald(x::BinomialTest, alpha::Float64=0.05)
    μ = x.x / x.n
    σ = sqrt(μ*(1-μ)/x.n)
    (quantile(Normal(μ, σ), alpha/2), quantile(Normal(μ, σ), 1-alpha/2))
end

# Jeffreys interval
function ci_jeffrey(x::BinomialTest, alpha::Float64=0.05)
    (quantile(Beta(x.x + 1/2, x.n - x.x + 1/2), alpha/2), quantile(Beta(x.x + 1/2, x.n - x.x + 1/2), 1-alpha/2))
end

# Agresti-Coull interval
function ci_agresti_coull(x::BinomialTest, alpha::Float64=0.05)
    q = quantile(Normal(), 1-alpha/2)
    n = x.n + q^2
    μ = (x.x + q^2/2)/n
    σ = sqrt(μ*(1-μ)/n)
    (μ-q*σ, μ+q*σ)
end

# Wilson score interval
function ci_wilson(x::BinomialTest, alpha::Float64=0.05)
    q = quantile(Normal(), 1-alpha/2)
    p = x.x / x.n
    denominator = 1 + q^2/x.n
    μ = p + q^2/(2*x.n)
    μ /= denominator
    σ = sqrt(p*(1-p)/x.n + q^2/(4x.n^2))
    σ /= denominator
    (μ-q*σ, μ+q*σ)
end

# Arcsine transformation interval as based on Cohen's H: https://en.wikipedia.org/wiki/Binomial_proportion_confidence_interval#Arcsine_transformation
function ci_arcsine(x::BinomialTest, alpha::Float64=0.05)
    q = quantile(Normal(), 1-alpha/2)
    p = x.x / x.n
    z = q/(2*sqrt(x.n))
    (sin(asin(sqrt(p))-z)^2, sin(asin(sqrt(p))+z)^2)
end

## SIGN TEST

struct SignTest <: HypothesisTest
    median::Float64
    x::Int
    n::Int
    data
end

"""
    SignTest(x::AbstractVector{T<:Real}, median::Real = 0)
    SignTest(x::AbstractVector{T<:Real}, y::AbstractVector{T<:Real}, median::Real = 0)

Perform a sign test of the null hypothesis that the distribution from which `x`
(or `x - y` if `y` is provided) was drawn has median `median` against the alternative
hypothesis that the median is not equal to `median`.

Implements: [`pvalue`](@ref), [`confint`](@ref)
"""
SignTest(x::AbstractVector{T}, median::Real=0) where {T<:Real} =
    SignTest(median, sum(x .> median), sum(x .!= median), sort(x))
SignTest(x::AbstractVector{T}, y::AbstractVector{S}) where {T<:Real, S<:Real} =
    SignTest(x - y, 0.0)

testname(::SignTest) = "Sign Test"
population_param_of_interest(x::SignTest) = ("Median", x.median, median(x.data)) # parameter of interest: name, value under h0, point estimate
default_tail(test::SignTest) = :both

function show_params(io::IO, x::SignTest, ident="")
    text1 = "number of observations:"
    text2 = "observations larger than $(x.median): "
    maxlen = length(text2)

    println(io, ident, text1, repeat(" ", maxlen-length(text1)), x.n)
    println(io, ident, text2, x.x)
end

pvalue(x::SignTest; tail=:both) = pvalue(Binomial(x.n, 0.5), x.x; tail=tail)

function StatsBase.confint(x::SignTest, alpha::Float64=0.05; tail=:both)
    check_alpha(alpha)

    if tail == :left
        q = Int(quantile(Binomial(x.n, 0.5), alpha))
        (x.data[q+1], x.median)
    elseif tail == :right
        q = Int(quantile(Binomial(x.n, 0.5), alpha))
        (x.median, x.data[end-q])
    elseif tail == :both
        q = Int(quantile(Binomial(x.n, 0.5), alpha/2))
        (x.data[q+1], x.data[end-q])
    else
        throw(ArgumentError("tail=$(tail) is invalid"))
    end
end


using IntegerSmithNormalForm: negateRow, negateCol, swapRows, swapCols, addRow,
addCol, SNF!, SNF, SNFWithoutTransform
using Base.Test

@testset "Negating rows and columns" begin
    A = [[1 2];[3 4]]
    B = [[1 2 3];[4 5 6];[7 8 9]]
    C = [[1 2];[3 4];[5 6]]

    (S,Tmp,T) = negateRow(A,1)
    @test Tmp == [[-1 -2];[3 4]]
    @test S*A*T == Tmp
    @inferred negateRow(A,1)

    (S,Tmp,T) = negateRow(A,2)
    @test Tmp == [[1 2];[-3 -4]]
    @test S*A*T == Tmp
    @inferred negateRow(A,2)

    (S,Tmp,T) = negateRow(B,3)
    @test Tmp == [[1 2 3];[4 5 6];[-7 -8 -9]]
    @test S*B*T == Tmp
    @inferred negateRow(B,3)

    (S,Tmp,T) = negateRow(C,2)
    @test Tmp == [[1 2];[-3 -4];[5 6]]
    @test S*C*T == Tmp
    @inferred negateRow(C,2)

    (S,Tmp,T) = negateCol(A,1)
    @test Tmp == [[-1 2];[-3 4]]
    @test S*A*T == Tmp
    @inferred negateCol(A,1)

    (S,Tmp,T) = negateCol(A,2)
    @test Tmp == [[1 -2];[3 -4]]
    @test S*A*T == Tmp
    @inferred negateCol(A,2)

    (S,Tmp,T) = negateCol(B,3)
    @test Tmp == [[1 2 -3];[4 5 -6];[7 8 -9]]
    @test S*B*T == Tmp
    @inferred negateCol(B,3)

    (S,Tmp,T) = negateCol(C,2)
    @test Tmp == [[1 -2];[3 -4];[5 -6]]
    @test S*C*T == Tmp
    @inferred negateCol(C,2)
end

@testset "Swapping rows and columns" begin
    A = [[1 2];[3 4];[5 6]]

    (S,B,T) = swapRows(A,1,2)
    @test B == [[3 4];[1 2];[5 6]]
    @test S*A*T == B
    @inferred swapRows(A,1,2)

    (S,B,T) = swapRows(A,3,2)
    @test B == [[1 2];[5 6];[3 4]]
    @test S*A*T == B
    @inferred swapRows(A,3,2)

    A = [[1 2 3];[4 5 6]]
    (S,B,T) = swapCols(A,1,2)
    @test B == [[2 1 3];[5 4 6]]
    @test S*A*T == B
    @inferred swapCols(A,1,2)

    (S,B,T) = swapCols(A,3,2)
    @test B == [[1 3 2];[4 6 5]]
    @test S*A*T == B
    @inferred swapCols(A,3,2)
end

@testset "Adding rows and columns" begin
    A = [[1 2];[3 4];[5 6]]

    (S,B,T) = addRow(A,1,2,10)
    @test B == [[31 42];[3 4];[5 6]]
    @test S*A*T == B
    @inferred addRow(A,1,2,10)

    A = [[1 2 3];[4 5 6]]

    (S,B,T) = addCol(A,2,3,10)
    @test B == [[1 32 3];[4 65 6]]
    @test S*A*T == B
    @inferred addCol(A,2,3,10)

end

function getMatrix(
                   k1,k2,k3, a
                  )

  b = 32

  if a == 0
    return [b k1 k2;
            0 b k3;
            0 0 b]
  elseif a == 1
    return [b 0 k2;
            k1 b k3;
            0 0 b]
  elseif a == 2
    return [b k1 0;
            0 b k3;
            k2 0 b]
  elseif a == 3
    return [b k1 k2;
            0 b 0;
            0 k3 b]
  elseif a == 4
    return [b 0 0;
            k1 b k3;
            k2 0 b]
  elseif a == 5
    return [b 0 k2;
            k1 b 0;
            0 k3 b]
  elseif a == 6
    return [b k1 0;
            0 b 0;
            k2 k3 b]
  elseif a == 7
    return [b 0 0;
            k1 b 0;
            k2 k3 b]
  end
end

function testSNF(A)
    B = copy(A)
    @inferred SNF!(B)
    B = copy(A)
    (S,B,T) = SNF!(B)
    @test B == S*A*T
    @test isdiag(B)
    d = diag(B)
    for i in 1:length(d)
        @test d[i] >= 0
        if d[i] != 0
            for j in i:length(d)
                @test mod(d[j],d[i]) == 0
            end
        end
    end
end

@testset "Test Smith Normal Form" begin

    testSNF([16 -16 -16; -16 16 -2; 9 -9 16])
    testSNF([[1 2 3];[4 5 6];[1 4 9]])
    testSNF([[1 2];[4 5];[1 4]])

    aRange = 0:7
    k1Range = [-24:1:24..., 99,100,101]

    simulations = length(aRange)*length(k1Range)^3

    for a in aRange
        for k1 in k1Range
            for k2 in k1Range
                for k3 in k1Range
                    M = getMatrix(k1,k2,k3,a)
                    testSNF(M)
                end
            end
        end
    end

    (S,B,T) = SNF([[1 2 3];[4 5 6];[1 4 9]])
    @inferred SNF([[1 2 3];[4 5 6];[1 4 9]])
    @test B == S*[[1 2 3];[4 5 6];[1 4 9]]*T
    @test isdiag(B)
    d = diag(B)
    for i in 1:length(d)
        @test d[i] >= 0
        for j in i:length(d)
            @test mod(d[j],d[i]) == 0
        end
    end

    B = SNFWithoutTransform([[1 2 3];[4 5 6];[1 4 9]])
    @inferred SNFWithoutTransform([[1 2 3];[4 5 6];[1 4 9]])
    @test isdiag(B)
    d = diag(B)
    for i in 1:length(d)
        @test d[i] >= 0
        for j in i:length(d)
            @test mod(d[j],d[i]) == 0
        end
    end
end


# ---
# title: 504. Base 7
# id: problem504
# author: Tian Jun
# date: 2020-10-31
# difficulty: Easy
# categories:
# link: <https://leetcode.com/problems/base-7/description/>
# hidden: true
# ---
#
# Given an integer, return its base 7 string representation.
#
# **Example 1:**
#
#
#
#     Input: 100
#     Output: "202"
#
#
# **Example 2:**
#
#
#
#     Input: -7
#     Output: "-10"
#
#
# **Note:** The input will be in range of [-1e7, 1e7].
#
#
## @lc code=start
using LeetCode

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


# ---
# title: 104. Maximum Depth of Binary Tree
# id: problem104
# author: zhwang
# date: 2022-02-20
# difficulty: Easy
# categories: Tree, Depth-first Search, Recursion
# link: <https://leetcode.com/problems/maximum-depth-of-binary-tree/description/>
# hidden: true
# ---
#
# Given the `root` of a binary tree, return _its maximum depth_.
#
# A binary tree's **maximum depth**  is the number of nodes along the longest
# path from the root node down to the farthest leaf node.
#
#
#
# **Example 1:**
#
# ![](https://assets.leetcode.com/uploads/2020/11/26/tmp-tree.jpg)
#
#
#
#     Input: root = [3,9,20,null,null,15,7]
#     Output: 3
#
#
# **Example 2:**
#
#
#
#     Input: root = [1,null,2]
#     Output: 2
#
#
# **Example 3:**
#
#
#
#     Input: root = []
#     Output: 0
#
#
# **Example 4:**
#
#
#
#     Input: root = [0]
#     Output: 1
#
#
#
#
# **Constraints:**
#
#   * The number of nodes in the tree is in the range `[0, 104]`.
#   * `-100 <= Node.val <= 100`
#
#
## @lc code=start
using LeetCode

maximum_depth_of_btree(::Nothing) = 0
function maximum_depth_of_btree(root::TreeNode)
    return maximum(maximum_depth_of_btree, [root.left, root.right]) + 1
end
## @lc code=end


# The first task is to find the variables to make the mean vector:
find_variables(x) = find_variables!(Symbol[], x) #this is so that we can call the function without the exclamation
function find_variables!(var_names, x::Number) #if the variable name is a number then just return it without doing anything
    return var_names
end
function find_variables!(var_names, x::Symbol) # if the variable name is a symbol then push it to the list of var_names because its a name of a column
    push!(var_names, x)
end

function find_variables!(var_names, x::Expr) # if the variable is a expression object then we have to crawl through each argument of the expression
    # safety checking
    if x.head == :call  # check for + symbol bc we are summing linear combinations within each expression argument
      # pass the remaining expression
      for argument in x.args[2:end] # since the first argument is the :+ call
        find_variables!(var_names, argument) #recursively find the names given in each argument so check if number, if symbol, if expression etc.again agian
    end
end
return var_names
end

function search_variables!(x::Expr, var::Symbol)
    for i in eachindex(x.args)
        if x.args[i] == var # if the argument is one of the variables given then just put it in the right format df[:x1]
            x.args[i] = Meta.parse(string(:input_data_from_user,"[", ":", var, "]"))
        elseif x.args[i] isa Expr # else if the argument is an expression (i.e not a varaible (symbol) or a number) then
            search_variables!(x.args[i], var) #go through this function recursively on each of the arguments of the expression object
        end
    end
    return x
end

function search_variables!(x::Expr, vars...) # this is for when you have more than one variable name found in the string
    for var in vars #goes through each of the variables in the vector vars
        x = search_variables!(x, var) #runs the recursion on each variable in vars
    end
    return x
end

function mean_formula(user_formula_string::String, df::DataFrame)
    global input_data_from_user = df

    users_formula_expression = Meta.parse(user_formula_string)
    if(users_formula_expression isa Expr)
        found_markers = find_variables(users_formula_expression) #store the vector of symbols of the found variables

        dotted_args = map(Base.Broadcast.__dot__, users_formula_expression.args) # adds dots to the arguments in the expression
        dotted_expression = Expr(:., dotted_args[1], Expr(:tuple, dotted_args[2:end]...)) #reformats the exprssion arguments by changing the variable names to tuples of the variable names to keep the dot structure of julia

        julia_interpretable_expression = search_variables!(dotted_expression, found_markers...) #gives me the julia interpretable exprsesion with the dataframe provided

        mean_vector = eval(Meta.parse(string(julia_interpretable_expression))) #evaluates the julia interpretable expression on the dataframe provided
    else
        mean_vector = [users_formula_expression for i in 1:size(df, 1)]
    end
    return mean_vector
end


module KernelPerf
import Perftests: @perf, meta

abstract List{T}

type Nil{T} <: List{T}
end

type Cons{T} <: List{T}
    head::T
    tail::List{T}
end

function listn1n2(n1::Int,n2::Int)
    l1 = Nil{Int}()
    for i=n2:-1:n1
        l1 = Cons{Int}(i,l1)
    end
    l1
end

@perf listn1n2(1,10^6) meta("cons", "List concatenation")

# issue #1211
include("ziggurat.jl")
a = Array(Float64, 1000000)
@perf randn_zig!(a) meta("randn_zig", "Ziggurat gaussian number generator", issue=1211)

# issue #950
include("gk.jl")
@perf gk(350,[0.1]) meta("gk", "Grigoriadis Khachiyan matrix games", issue=950)

# issue #942
x = sparse(ones(280,280));
@perf (x*x) meta("sparsematmul", "normal", "Sparse matrix multiplication", issue=942)
s2 = sparse(rand(1:2000,10^5), kron([1:10^4;],ones(Int,10)), ones(Int,10^5), 2000, 10^4);
@perf s2*s2' meta("sparsematmul", "fillin", "Sparse matrix multiplication with fill-in", issue=942)

# issue #938
x = 1:600000;
@perf sparse(x,x,x) meta("sparserange", "Construction of a sparse matrix from ranges", issue=938)

# issue 4707
include("getdivgrad.jl")
A = getDivGrad(64,64,64)
v = rand(64^3)
@perf A*v meta("sparsemat_densevec", "Sparse matrix - dense vector multiplication", issue=4707)

# issue #939
y = [500000:-1:1;]
@perf sortperm(y) meta("sortperm", "Sorting of a worst-case vector", issue=939)

# issue #445
include("stockcorr.jl")
@perf stockcorr() meta("stockcorr", "Correlation analysis of random matrices", issue=445)

include("bench_eu.jl")
@perf bench_eu_vec(10000) meta("finance", "vectorized", "Vectorized Monte Carlo financial simulation")
@perf bench_eu_devec(10000) meta("finance", "devectorized", "Devectorized Monte Carlo financial simulation")

# issue #1163
include("actor_centrality.jl")
@perf actor_centrality() meta("actorgraph", "Centrality of actors in IMDB database", issue=1163)

# issue #1168
include("laplace.jl")
@perf laplace_vec() meta("laplace", "vectorized", "Vectorized Laplacian", issue=1168)
@perf laplace_devec() meta("laplace", "devectorized", "Devectorized Laplacian", issue=1168)

# issue #1169
include("go_benchmark.jl")
@perf benchmark(10) meta("go","Simulation of random games of Go", issue=1169)

# issue #3142
include("simplex.jl")
@perf doTwoPassRatioTest() meta("simplex", "Dual simplex algorithm for Linear Programming", issue=3142)

# issue #3811
include("raytracer.jl")
@perf Raytracer(5, 256, 4) meta("raytracer", "Simple native Julia raytracer", issue=3811)

function cmp_with_func(x::Vector, f::Function)
    count::Int = 0
    for i = 1:length(x)-1
      if f(x[i], x[i+1]) count += 1 end
    end
    count
end
x = randn(200_000)
@perf cmp_with_func(x, isless) meta("funarg", "Function argument benchmark")

arith_vectorized(b,c,d) = b.*c + d .+ 1.0
len = 1_000_000
b = randn(len)
c = randn(len)
d = randn(len)
@perf arith_vectorized(b,c,d) meta("vectorize", "Vectorized arithmetic")

open("random.csv","w") do io
    writecsv(io, rand(100000,4))
end
function parsecsv()
    for line in EachLine(open("random.csv"))
        split(line, ',')
    end
end
@perf parsecsv() meta("splitline", "CSV parsing")
rm("random.csv")

include("json.jl")
@perf parse_json(_json_data) meta("json", "JSON parsing")

include("indexing.jl")
x = [1:100_000;]
y = filter(iseven, 1:length(x))
logical_y = map(iseven, 1:length(x))
@perf add1!(x,y) meta("indexing", "add1", "Increment vector x at locations y")
@perf devec_add1!(x,y) meta("indexing", "devectorized_add1", "Devectorized increment vector x at locations y")
@perf add1!(x,logical_y) meta("indexing", "add1_logical", "Increment x_i if y_i is true")
@perf devec_add1_logical!(x,logical_y) meta("indexing", "devectorized_add1_logical", "Devectorized increment x_i if y_i is true")

end #module


# ---
# title: 1621. Number of Sets of K Non-Overlapping Line Segments
# id: problem1621
# author: Tian Jun
# date: 2020-10-31
# difficulty: Medium
# categories: Dynamic Programming
# link: <https://leetcode.com/problems/number-of-sets-of-k-non-overlapping-line-segments/description/>
# hidden: true
# ---
#
# Given `n` points on a 1-D plane, where the `ith` point (from `0` to `n-1`) is
# at `x = i`, find the number of ways we can draw **exactly** `k` **non-
# overlapping** line segments such that each segment covers two or more points.
# The endpoints of each segment must have **integral coordinates**. The `k` line
# segments **do not** have to cover all `n` points, and they are **allowed** to
# share endpoints.
#
# Return _the number of ways we can draw_`k` _non-overlapping line segments_ _._
# Since this number can be huge, return it **modulo** `109 + 7`.
#
#
#
# **Example 1:**
#
# ![](https://assets.leetcode.com/uploads/2020/09/07/ex1.png)
#
#
#
#     Input: n = 4, k = 2
#     Output: 5
#     Explanation: The two line segments are shown in red and blue.
#     The image above shows the 5 different ways {(0,2),(2,3)}, {(0,1),(1,3)}, {(0,1),(2,3)}, {(1,2),(2,3)}, {(0,1),(1,2)}.
#
# **Example 2:**
#
#
#
#     Input: n = 3, k = 1
#     Output: 3
#     Explanation: The 3 ways are {(0,1)}, {(0,2)}, {(1,2)}.
#
#
# **Example 3:**
#
#
#
#     Input: n = 30, k = 7
#     Output: 796297179
#     Explanation: The total number of possible ways to draw 7 line segments is 3796297200. Taking this number modulo 109 + 7 gives us 796297179.
#
#
# **Example 4:**
#
#
#
#     Input: n = 5, k = 3
#     Output: 7
#
#
# **Example 5:**
#
#
#
#     Input: n = 3, k = 2
#     Output: 1
#
#
#
# **Constraints:**
#
#   * `2 <= n <= 1000`
#   * `1 <= k <= n-1`
#
#
## @lc code=start
using LeetCode

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


@testset "simple broadcasting" begin

    b = rand(2)
    c = rand(3)
    bc = rand(2,3)

    ## compare to ordinary broadcasting
    Z = zeros(3,2)
    A = bc' .+ b' .* c .^ 3
    @cast B[j,i] := bc[i,j] .+ b[i] .* c[j]^3
    @cast Z[j,i] = bc[i,j] .+ b[i] .* c[j]^3
    @test A ==B == Z

    Z1 = zeros(3,2,1)
    @cast Z1[j,i,1] = bc[i,j] .+ b[i] .* c[j]^3
    @test A == Z1[:,:,1]

    A = @. exp(-bc + 2*c') / sqrt(b)
    @cast B[i,j] := exp(-bc[i,j] + 2*c[j]) / sqrt(b[i])
    @test A == B

    ## test permutedims in orient function
    bcdef = rand(2,3,4,5,6);
    @cast A[d,e,c,f,b] := 10 + bcdef[b,c,d,e,f];
    @test A[3,4,2,5,1] == 10 + bcdef[1,2,3,4,5]

    ## check that f(A) is allowed as array name
    exp_all(A) = similar(A) .= exp.(A)
    A = exp_all(bc) .* sqrt.(b .+ 3)
    @cast B[i,j] := exp_all(bc)[i,j] * sqrt(b[i] + 3)
    @test A == B

    ## check that log(2) doesn't get broadcast
    global log_n = 0
    two=2.0
    log_count(x) = (global log_n += 1; log(x))
    A = log.(bc) ./ log_count(2)
    @cast B[i,j] := log(bc[i,j]) / log_count(2)
    @cast C[i,j] := log(bc[i,j]) / log_count(two)
    @test A == B == C
    @test log_n == 3

end
@testset "broadcast reduction" begin

    b = rand(2)
    c = rand(3)
    bc = rand(2,3)

    Z = zeros(3)
    A = sum( bc' .+ b' .* c ; dims=2) |> vec
    @reduce B[j] := sum(i) bc[i,j] .+ b[i] .* c[j]
    @reduce Z[j] = sum(i)  bc[i,j] .+ b[i] .* c[j]
    @test A ==B == Z

    ## complete reduction
    @reduce S[] := sum(i,j) bc[j,i] + 1
    T = @reduce sum(i,j) bc[j,i] + 1
    @test S[] ≈ sum(bc .+ 1) ≈ T

    ## with output shaping / fixing
    bcde = rand(2,3,4,5);
    @reduce A[d⊗c,_,b] := sum(e) bcde[-b,c,d,e]
    B = similar(A);
    @reduce B[d⊗c,_,b] = sum(e) bcde[-b,c,d,e]
    @test A ≈ B

    @reduce A[_,d⊗b,_] := sum(e,c) bcde[b,c,d,e]
    B = similar(A);
    @reduce B[_,d⊗b,_] = sum(c,e) bcde[b,c,d,e]
    @test A ≈ B

end
@testset "complex" begin

    C = rand(3,3) .+ im .* rand(3,3)
    R = rand(1:33, 3,3)

    @cast A[i,j] := R[i,j] + C[j,i]'
    @test A == R .+ C'

    @cast A[i,j] = R[j,i] + C[i,j]' + 10im
    @test A == R' .+ conj.(C) .+ 10im

    @cast B[i,j] := R[i,j] + C'[i,j]
    @test B == R .+ C'

end
@testset "diag" begin

    B = rand(4,5)
    @test TensorCast.diagview(B) == TensorCast.diag(B) # really LinearAlgebra!

    R = rand(2,2)
    @cast V[i] := R[i,i]
    @cast V2[i] := R[i,i] lazy=false

    @test V[1] == R[1,1]
    @test V[2] == R[2,2]
    @test V == V2

    # R4 = rand(1:10, 4)
    # @cast W[i] := R4[i⊗i]^2 # diag(reshape(R4, (sz_i, sz_i))) with sz_i = (:)
    # @test W[1] := R4[1]^2
    # @test W[2] := R4[4]^2

    M = [repeat([10i+j],2) for i=1:3, j=1:3]
    @cast A[i,k] := M[i,i][k]

    @test A[1,1] == A[1,2] == 11
    @test A[3,1] == A[3,2] == 33

end
@testset "mapslices etc" begin

    M = rand(-10:10, 3,4)
    g(x) = x ./ sum(abs,x)
    @cast N[i,j] := g(M[:,j])[i]
    N2 = mapslices(g, M, dims=1)
    @test N == N2

    # V = [ rand(1:99, 2) for _=1:5, _=1:1 ]
    # @cast W[i,j] := (v->v .// 2)(V[j,_])[i] # TODO
    # W2 = hcat(V...) .// 2
    # @test W == W2

    fun(m::AbstractMatrix, a=0) = vec(sum(m,dims=1)) .+ a
    X = rand(2,3,4)
    Y = rand(4)
    @cast Z[i,j] := fun(X[:,i,:],42)[j] + Y[j]^2
    Z2 = dropdims(sum(X, dims=1), dims=1) .+ 42 .+ transpose(Y).^2
    @test Z ≈ Z2

    A = rand(2:2:10, 3,2,4)
    ff(v::AbstractVector) = v .^2
    B = rand(-1000:1000:1000, 2,1,4)
    CM = rand(2,3)
    C = eachcol(CM) |> collect
    @cast Y[i,j,k] := ff(A[j,:,k])[i] + B[i,_,k] + C[j][i]
    Y2 = permutedims(A, (2,1,3)) .^2 .+ B .+ CM
    @test Y ≈ Y2

end


export Geometry

struct Geometry

    n1 :: Int64
    n2 :: Int64

    x1min  :: Float64
    x2min  :: Float64

    delta1 :: Float64
    delta2 :: Float64

    x1grid :: Array{Float64,1}
    x2grid :: Array{Float64,1}

    bc :: Symbol

"""
initialize geometry from origin cordinates,
space steps and number of points
"""
    function Geometry(n1, n2, x1min, x2min, delta1, delta2)

        x1grid = collect(range(x1min, length=n1, step=delta1))
        x2grid = collect(range(x2min, length=n2, step=delta2))

        new(n1, n2, x1min, x2min, delta1, delta2, x1grid, x2grid, :none)

    end

"""
initialize geometry from origin and end coordinates and
number of points
"""
    function Geometry(n1, n2, x1, x2, bc::Symbol)

	x1min, x1max = x1
	x2min, x2max = x2

        if ( (bc == :perxy) || (bc == :perx) )
           x1grid = collect(range(x1min, stop=x1max, length=n1+1)[1:end-1])
        else
           x1grid = collect(range(x1min, stop=x1max, length=n1))
        end

        if ( (bc == :perxy) || (bc == :pery) )
           x2grid = collect(range(x2min, stop=x2max, length=n2+1)[1:end-1])
        else
           x2grid = collect(range(x2min, stop=x2max, length=n2))
        end

        delta1 = x1grid[2] - x1grid[1]
        delta2 = x2grid[2] - x2grid[1]

        new(n1, n2, x1min, x2min, delta1, delta2, x1grid, x2grid, bc)

    end

end


# The original license on the t-tests from HypothesisTests.jl
# is as follows. We're including it here because we're mostly copying the
# documentation of the functions.
#
# jarque_bera.jl
# Jarque-Bera goodness-of-fit test
#
# Copyright (C) 2017   Benjamin Born
#
# Permission is hereby granted, free of charge, to any person obtaining
# a copy of this software and associated documentation files (the
# "Software"), to deal in the Software without restriction, including
# without limitation the rights to use, copy, modify, merge, publish,
# distribute, sublicense, and/or sell copies of the Software, and to
# permit persons to whom the Software is furnished to do so, subject to
# the following conditions:
#
# The above copyright notice and this permission notice shall be
# included in all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

import HypothesisTests.JarqueBeraTest

"""
    JarqueBeraTest(d::AbstractUncertainValue, n::Int = 1000) -> JarqueBeraTest

Compute the Jarque-Bera statistic to test the null hypothesis that an uncertain
value is normally distributed.
"""
function JarqueBeraTest(d::AbstractUncertainValue, n::Int = 1000)
    x = resample(d, n)
    JarqueBeraTest(x)
end

"""
    JarqueBeraTestPooled(ud::UncertainDataset, n::Int = 1000) -> JarqueBeraTest

First, draw `n` realisations of each uncertain value in `ud` and pool them
together. Then, compute the Jarque-Bera statistic to test the null hypothesis
that the values of the pool are normally distributed.
"""
function JarqueBeraTestPooled(ud::UncertainDataset, n::Int = 1000)
    x = vcat(resample(ud, n)...,)
    JarqueBeraTest(x)
end

"""
    OneSampleADTestElementWise(ud::UncertainDataset,
        n::Int = 1000) -> Vector{JarqueBeraTest}

First, draw `n` realisations of each uncertain value in `ud`, keeping one
pool of values for each uncertain value.

Then, compute the Jarque-Bera statistic to test the null hypothesis that each
value pool is normally distributed.
"""
function JarqueBeraTestElementWise(ud::UncertainDataset, n::Int = 1000)
    [JarqueBeraTest(resample(ud[i], n)) for i = 1:length(ud)]
end

export
JarqueBeraTest,
JarqueBeraTestPooled,
JarqueBeraTestElementWise


#
# This file contains various distributions in unconstrained, centered, and scaled form.
# Sometimes ad hoc, via autodiff.
# The purpose is for finding error-minimizing (Optimal) quadrature rules.
#

## Normal

## Gamma - low var

## Gamma - medium var

## Gamma - high var

## Inv-Gamma - low var

## Inv-Gamma - medium var

## Inv-Gamma - high var

## T - small nu

## T - medium nu

## T - large nu


module Constraint

using LinearAlgebra

using MutableArithmetics
const MA = MutableArithmetics

using MathOptInterface
const MOI = MathOptInterface
const MOIU = MOI.Utilities
const MOIB = MOI.Bridges

import MultivariatePolynomials
const MP = MultivariatePolynomials
import MultivariateBases
import SemialgebraicSets
import MultivariateMoments
import PolyJuMP
import SumOfSquares
const SOS = SumOfSquares
const Certificate = SOS.Certificate

# Symmetric PSD matrix bridges
include("empty.jl")
include("psd2x2.jl")
include("diagonally_dominant.jl")
include("scaled_diagonally_dominant.jl")

# SOS polynomial bridges
include("utilities.jl")
include("sos_polynomial.jl")
include("sos_polynomial_in_semialgebraic_set.jl")

# TODO bridges should redirect to `MOI.get_fallback` as well so that
# we can just use `Union{MOI.ConstraintIndex,MOI.Bridges.AbstractBridge}` in the `get_fallback` in `attributes.jl`
function MOI.get(
    model::MOI.ModelLike,
    attr::SOS.SOSDecompositionAttribute,
    bridge::Union{SOSPolynomialBridge,SOSPolynomialInSemialgebraicSetBridge},
)
    gram = MOI.get(model, SOS.GramMatrixAttribute(attr.result_index), bridge)
    return SOS.SOSDecomposition(gram, attr.ranktol, attr.dec)
end

end


"""
This module represents an explicit, direct sparse 2D finite-difference Poisson solver for heterogeneous media,
i.e. media having spatially varying (space-dependent) medium parameters.
The following functionality is currently available in this module:
* 'Forward' problem: given the source, and the conductivity distribution, solve for the electrical potential ψ:
	 Example problem: solve Aψ=∇⋅j; A=∇⋅(σ(x,z)∇), for ψ.
* 'Inverse' problem: given wave propagation related pore pressure, and mechanical medium properties, calculate the source term:
	 Example problem: solve Aψ=-∇⋅j; A=∇⋅([Q*k/η](x,z)∇B*P), for ∇⋅j;
* The Boundary Value Problem that is currently implemented assumes Neumann boundary conditions at all boundaries.
Developed by:\\\n
Niels Grobbe, Massachusetts Institute of Technology, USA.\\\n
In collaboration with: Aimé Fournier & Laurent Demanet, Massachusetts Institute of Technology, USA.\\\n
Date: October, 2017 \\\n
Contact: ngrobbe@gmail.com
"""
module Poisson

include("core.jl")
include("expt.jl")
include("expt_born.jl")
include("getprop.jl")
include("inversion.jl")

end # end module Poisson


# =================================================================================================
# =                                         Hyland83a.jl                                          =
# =================================================================================================

#=
Testing the virial coefficients. Table 1 from reference [2].
=#
@testset "Hyland83a" begin
@testset "Virial Coeffients" begin

@testset "Baa" begin
@test -55.94 ≈ MoistAir.Baa(173.15)*1e6 atol=0.01
@test -13.15 ≈ MoistAir.Baa(273.15)*1e6 atol=0.01
@test 3.72 ≈ MoistAir.Baa(373.15)*1e6 atol=0.01
@test 12.31 ≈ MoistAir.Baa(473.15)*1e6 atol=0.01
end

@testset "dBaa/dT" begin
@test 0.7240 ≈ MoistAir.dBaa(173.15)*1e6 atol=0.0001
@test 0.2460 ≈ MoistAir.dBaa(273.15)*1e6 atol=0.0001
@test 0.1146 ≈ MoistAir.dBaa(373.15)*1e6 atol=0.0001
@test 0.0640 ≈ MoistAir.dBaa(473.15)*1e6 atol=0.0001
end

@testset "Caaa" begin
@test 2267.0 ≈ MoistAir.Caaa(173.15)*1e12 atol=1.0
@test 1409.0 ≈ MoistAir.Caaa(273.15)*1e12 atol=1.0
@test 1202.0 ≈ MoistAir.Caaa(373.15)*1e12 atol=1.0
@test 1139.0 ≈ MoistAir.Caaa(473.15)*1e12 atol=1.0
end

@testset "dCaaa/dT" begin
@test -18.00 ≈ MoistAir.dCaaa(173.15)*1e12 atol=0.01
@test -3.65 ≈ MoistAir.dCaaa(273.15)*1e12 atol=0.01
@test -1.06 ≈ MoistAir.dCaaa(373.15)*1e12 atol=0.01
@test -0.34 ≈ MoistAir.dCaaa(473.15)*1e12 atol=0.01
end

@testset "Baw" begin
@test -93.3 ≈ MoistAir.Baw(173.15)*1e6 atol=0.1
@test -36.4 ≈ MoistAir.Baw(273.15)*1e6 atol=0.1
@test -14.5 ≈ MoistAir.Baw(373.15)*1e6 atol=0.1
@test -3.1 ≈ MoistAir.Baw(473.15)*1e6 atol=0.1
end

@testset "dBaw/dT" begin
@test 1.01 ≈ MoistAir.dBaw(173.15)*1e6 atol=0.01
@test 0.318 ≈ MoistAir.dBaw(273.15)*1e6 atol=0.001
@test 0.151 ≈ MoistAir.dBaw(373.15)*1e6 atol=0.001
@test 0.0869 ≈ MoistAir.dBaw(473.15)*1e6 atol=0.0001
end

@testset "Caaw" begin
@test 1023 ≈ MoistAir.Caaw(173.15)*1e12 atol=1.0
@test 861 ≈ MoistAir.Caaw(273.15)*1e12 atol=1.0
@test 696 ≈ MoistAir.Caaw(373.15)*1e12 atol=1.0
@test 627 ≈ MoistAir.Caaw(473.15)*1e12 atol=1.0
end

@testset "dCaaw/dT" begin
@test  5.56 ≈ MoistAir.dCaaw(173.15)*1e12 atol=0.01
@test -2.44 ≈ MoistAir.dCaaw(273.15)*1e12 atol=0.01
@test -1.02 ≈ MoistAir.dCaaw(373.15)*1e12 atol=0.01
@test -0.457 ≈ MoistAir.dCaaw(473.15)*1e12 atol=0.001
end

@testset "Caww" begin
@test -2.0e7 ≈ MoistAir.Caww(173.15)*1e12 atol=0.1e7
@test -2.2e5 ≈ MoistAir.Caww(273.15)*1e12 atol=0.1e5
@test -3.0e4 ≈ MoistAir.Caww(373.15)*1e12 atol=0.1e4
@test -8.4e3 ≈ MoistAir.Caww(473.15)*1e12 atol=0.1e3
end

@testset "dCaww/dT" begin
@test 1.61e6 ≈ MoistAir.dCaww(173.15)*1e12 atol=0.01e6
@test 6.05e3 ≈ MoistAir.dCaww(273.15)*1e12 atol=0.01e3
@test    456 ≈ MoistAir.dCaww(373.15)*1e12 atol=1.0
@test   86.9 ≈ MoistAir.dCaww(473.15)*1e12 atol=0.1
# Note that on table 1, the last line is 8.69 instead of 86.9. Probably a typo in the paper.
end
end

@testset "Specific volume of dry air" begin
# Table 3
P = 0.1e6
@test 0.49510 ≈ MoistAir.volumeair(173.15, P) atol=0.00003
@test 0.78364 ≈ MoistAir.volumeair(273.15, P) atol=0.0001
@test 1.0713 ≈ MoistAir.volumeair(373.15, P) atol=0.0001
@test 1.3586 ≈ MoistAir.volumeair(473.15, P) atol=0.0001

P = 0.5e6
@test 0.09747 ≈ MoistAir.volumeair(173.15, P) atol=0.00003
@test 0.15637 ≈ MoistAir.volumeair(273.15, P) atol=0.00003
@test 0.21436 ≈ MoistAir.volumeair(373.15, P) atol=0.00003
@test 0.27207 ≈ MoistAir.volumeair(473.15, P) atol=0.00003

P = 1e6
@test 0.04775 ≈ MoistAir.volumeair(173.15, P) atol=0.00003
@test 0.07797 ≈ MoistAir.volumeair(273.15, P) atol=0.00003
@test 0.10725 ≈ MoistAir.volumeair(373.15, P) atol=0.00003
@test 0.13625 ≈ MoistAir.volumeair(473.15, P) atol=0.00003

P = 5e6
@test 0.00795 ≈ MoistAir.volumeair(173.15, P) atol=0.00003
@test 0.01533 ≈ MoistAir.volumeair(273.15, P) atol=0.00003
@test 0.02162 ≈ MoistAir.volumeair(373.15, P) atol=0.00003
@test 0.02763 ≈ MoistAir.volumeair(473.15, P) atol=0.00003
end

@testset "Specific enthalpy of dry air" begin
# Table 3
P = 0.1e6
@test -100.63 ≈ MoistAir.enthalpyair(173.15, P)/1000 atol=0.01
@test    0.004≈ MoistAir.enthalpyair(273.15, P)/1000 atol=0.01
@test  100.79 ≈ MoistAir.enthalpyair(373.15, P)/1000 atol=0.01
@test  202.55 ≈ MoistAir.enthalpyair(473.15, P)/1000 atol=0.01

P = 0.5e6
@test -103.17 ≈ MoistAir.enthalpyair(173.15, P)/1000 atol=0.01
@test   -1.10 ≈ MoistAir.enthalpyair(273.15, P)/1000 atol=0.01
@test  100.26 ≈ MoistAir.enthalpyair(373.15, P)/1000 atol=0.01
@test  202.30 ≈ MoistAir.enthalpyair(473.15, P)/1000 atol=0.01

P = 1e6
@test -106.42 ≈ MoistAir.enthalpyair(173.15, P)/1000 atol=0.01
@test   -2.48 ≈ MoistAir.enthalpyair(273.15, P)/1000 atol=0.01
@test   99.60 ≈ MoistAir.enthalpyair(373.15, P)/1000 atol=0.01
@test  202.00 ≈ MoistAir.enthalpyair(473.15, P)/1000 atol=0.01

P = 5e6
@test -135.55 ≈ MoistAir.enthalpyair(173.15, P)/1000 atol=0.01
@test  -13.15 ≈ MoistAir.enthalpyair(273.15, P)/1000 atol=0.01
@test   94.63 ≈ MoistAir.enthalpyair(373.15, P)/1000 atol=0.01
@test  199.8 ≈ MoistAir.enthalpyair(473.15, P)/1000 atol=0.1
end

@testset "Specific entropy of dry air" begin
P = 0.1e6
@test -0.4550 ≈ MoistAir.entropyair(173.15, P)/1000 atol=0.0001
@test  0.0038 ≈ MoistAir.entropyair(273.15, P)/1000 atol=0.0001
@test  0.3182 ≈ MoistAir.entropyair(373.15, P)/1000 atol=0.0001
@test  0.5597 ≈ MoistAir.entropyair(473.15, P)/1000 atol=0.0001
end

# Table 4, appendix of reference [2]
@testset "Volume of saturated moist air" begin
volmoist(Tk, P) = MoistAir.volumemoist(Tk, P, MoistAir.efactor(Tk,P)*MoistAir.Pws(Tk)/P)
P = 0.1e6
@test 0.49510 ≈ volmoist(173.15, P) atol=0.00005
@test 0.78846 ≈ volmoist(273.15, P) atol=0.00005
@test 3.4974 ≈ volmoist(363.15, P) atol=0.0002 #fail
P = 0.5e6
@test 0.097466 ≈ volmoist(173.15, P) atol=0.000006
@test 0.15657 ≈ volmoist(273.15, P) atol=0.00005
@test 0.24273 ≈ volmoist(363.15, P) atol=0.00003
@test 0.26904 ≈ volmoist(373.15, P) atol=0.00003
P=1e6
@test 0.047752 ≈ volmoist(173.15, P) atol=0.000006
@test 0.078022 ≈ volmoist(273.15, P) atol=0.000005
@test 0.11226 ≈ volmoist(363.15, P) atol=0.00001
@test 0.11942 ≈ volmoist(373.15, P) atol=0.00001
@test 0.23281 ≈ volmoist(423.15, P) atol=0.00002 #fail
@test 0.32711 ≈ volmoist(433.15, P) atol=0.00002
@test 0.61489 ≈ volmoist(443.15, P) atol=0.00005 #fail

P=5e6
@test 0.007951 ≈ volmoist(173.15, P) atol=0.000006
@test 0.015331 ≈ volmoist(273.15, P) atol=0.000005
@test 0.021310 ≈ volmoist(363.15, P) atol=0.00001
@test 0.022073 ≈ volmoist(373.15, P) atol=0.00001
@test 0.02727  ≈ volmoist(423.15, P) atol=0.00001
@test 0.02885  ≈ volmoist(433.15, P) atol=0.00001
@test 0.03077  ≈ volmoist(443.15, P) atol=0.00001
@test 0.03315  ≈ volmoist(453.15, P) atol=0.00001
@test 0.0402  ≈ volmoist(473.15, P) atol=0.0001
end

@testset "enthalpy of saturated moist air" begin
hmoist(Tk, P) = MoistAir.enthalpymoist(Tk, P, MoistAir.efactor(Tk,P)*MoistAir.Pws(Tk)/P)/1000
P = 0.1e6
@test -100.627 ≈ hmoist(173.15, P) atol=0.005
@test    9.602 ≈ hmoist(273.15, P) atol=0.005
@test   4035.0 ≈ hmoist(363.15, P) atol=0.3 # In the paper the number is 4305! #fail

P = 0.5e6
@test -103.168 ≈ hmoist(173.15, P) atol=0.005
@test    0.831 ≈ hmoist(273.15, P) atol=0.005
@test   365.55 ≈ hmoist(363.15, P) atol=0.03
@test   533.27 ≈ hmoist(373.15, P) atol=0.03
@test   35957 ≈ hmoist(423.15, P) atol=2.0 #fail

P = 1e6
@test -106.415 ≈ hmoist(173.15, P) atol=0.005
@test   -1.498 ≈ hmoist(273.15, P) atol=0.005
@test   217.77 ≈ hmoist(363.15, P) atol=0.03
@test   293.17 ≈ hmoist(373.15, P) atol=0.03
@test   1788.8 ≈ hmoist(423.15, P) atol=0.5 #fail
@test   3113.3 ≈ hmoist(433.15, P) atol=0.5
@test   7206.0 ≈ hmoist(443.15, P) atol=2.0 #fail

P = 5e6
@test -135.547 ≈ hmoist(173.15, P) atol=0.005
@test  -12.929 ≈ hmoist(273.15, P) atol=0.005
@test   109.89 ≈ hmoist(363.15, P) atol=0.03
@test   132.42 ≈ hmoist(373.15, P) atol=0.03
@test   347.3 ≈ hmoist(423.15, P) atol=0.5
@test   428.8 ≈ hmoist(433.15, P) atol=0.5
@test   534.1 ≈ hmoist(443.15, P) atol=0.5
@test   672.0 ≈ hmoist(453.15, P) atol=1.0
@test   1113.0 ≈ hmoist(473.15, P) atol=1.0
end

@testset "Entropy of saturated moist air" begin
# Table 4 in the appendix of reference [2].
smoist(Tk, P) = MoistAir.entropymoist(Tk, P, MoistAir.efactor(Tk,P)*MoistAir.Pws(Tk)/P)/1000
P = 0.1e6
@test  -0.45499 ≈ smoist(173.15, P) atol=0.00003
@test   0.04070 ≈ smoist(273.15, P) atol=0.00003
@test  11.7287 ≈ smoist(363.15, P) atol=0.0005 #fail

P = 0.5e6
@test  -0.92718 ≈ smoist(173.15, P) atol=0.00005
@test  -0.45417 ≈ smoist(273.15, P) atol=0.00005
@test   0.64517 ≈ smoist(363.15, P) atol=0.0005
@test   1.1102 ≈ smoist(373.15, P) atol=0.0005
@test  90.01 ≈ smoist(423.15, P) atol=0.05 #fail

P = 1e6
@test  -1.13929 ≈ smoist(173.15, P) atol=0.00005 # In the table the signal is +
@test  -0.66104 ≈ smoist(273.15, P) atol=0.00005
@test   0.00805 ≈ smoist(363.15, P) atol=0.00005
@test   0.21676 ≈ smoist(373.15, P) atol=0.0005
@test   4.0478  ≈ smoist(423.15, P) atol=0.0005 #fail
@test   7.3103  ≈ smoist(433.15, P) atol=0.0005
@test   17.221  ≈ smoist(443.15, P) atol=0.005 #fail

P = 5e6
@test  -1.72380 ≈ smoist(173.15, P) atol=0.0001 # In the table the signal is +
@test  -1.15924 ≈ smoist(273.15, P) atol=0.00005
@test  -0.77519 ≈ smoist(363.15, P) atol=0.00005
@test  -0.71331 ≈ smoist(373.15, P) atol=0.00005
@test  -0.1629  ≈ smoist(423.15, P) atol=0.0002
@test   0.0364  ≈ smoist(433.15, P) atol=0.0002
@test   0.2901  ≈ smoist(443.15, P) atol=0.0002
@test   0.6185  ≈ smoist(453.15, P) atol=0.0002
@test   1.642  ≈ smoist(473.15, P) atol=0.002
end
end


#=

  Euler #43 in Julia.

  """
  The number, 1406357289, is a 0 to 9 pandigital number because it is made up of
  each of the digits 0 to 9 in some order, but it also has a rather interesting
  sub-string divisibility property.

  Let d1 be the 1st digit, d2 be the 2nd digit, and so on. In this way, we
  note the following:

      * d2d3d4=406 is divisible by 2
      * d3d4d5=063 is divisible by 3
      * d4d5d6=635 is divisible by 5
      * d5d6d7=357 is divisible by 7
      * d6d7d8=572 is divisible by 11
      * d7d8d9=728 is divisible by 13
      * d8d9d10=289 is divisible by 17

  Find the sum of all 0 to 9 pandigital numbers with this property.
  """

  This Julia model was created by Hakan Kjellerstrand, hakank@gmail.com
  See also my Julia page: http://www.hakank.org/julia/

=#

include("Euler.jl")

# Using all_permutations:
# 1.95482710s
function euler43a()
    primes = [2,3,5,7,11,13,17];
    perms = all_permutations(Array(0:9))
    sum = 0
    for p in perms
        i = 0;
        found = true
        while i < 7 && found == true
            if (100*p[i+2] + 10*p[i+3] + p[i+4]) % primes[i+1] != 0
                found = false
                break
            end
            i += 1
        end

        if found
            sum += parse(Int,join(p,""))
        end
    end

    return sum;
end

# Using next_permutation
# 0.05306371s
function euler43b()
    primes = [2,3,5,7,11,13,17]
    p = [1,0,2,3,4,5,6,7,8,9]
    sum = 0
    rev = Array(9:-1:0)
     while p != rev
        i = 0
        found = true
        while i < 7 && found == true
            if (100*p[i+2] + 10*p[i+3] + p[i+4]) % primes[i+1] != 0
                found = false
                break
            end
            i += 1
        end

        if found
            sum += parse(Int,join(p,""))
        end
        p = next_permutation(p)
    end

    return sum
end

# run_euler(euler43a)
run_euler(euler43b)


module Euler
  export EulerLaw, γ

  import ..Atum
  using ..Atum: avg, logavg, roe_avg
  using StaticArrays
  using LinearAlgebra: I

  struct EulerLaw{γ, FT, D, S} <: Atum.AbstractBalanceLaw{FT, D, S}
    function EulerLaw{FT, D}(; γ = 7 // 5) where {FT, D}
      S = 2 + D
      new{FT(γ), FT, D, S}()
    end
  end

  γ(::EulerLaw{_γ}) where {_γ} = _γ

  function varsindices(law::EulerLaw)
    S = Atum.numberofstates(law)
    ix_ρ = 1
    ix_ρu⃗ = StaticArrays.SUnitRange(2, S - 1)
    ix_ρe = S
    return ix_ρ, ix_ρu⃗, ix_ρe
  end

  function unpackstate(law::EulerLaw, q)
    ix_ρ, ix_ρu⃗, ix_ρe = varsindices(law)
    @inbounds q[ix_ρ], q[ix_ρu⃗], q[ix_ρe]
  end

  function pressure(law::EulerLaw, ρ, ρu⃗, ρe)
    (γ(law) - 1) * (ρe - ρu⃗' * ρu⃗ / 2ρ)
  end
  function energy(law::EulerLaw, ρ, ρu⃗, p)
    p / (γ(law) - 1) + ρu⃗' * ρu⃗ / 2ρ
  end
  function soundspeed(law::EulerLaw, ρ, p)
    sqrt(γ(law) * p / ρ)
  end
  function soundspeed(law::EulerLaw, ρ, ρu⃗, ρe)
    soundspeed(law, ρ, pressure(law, ρ, ρu⃗, ρe))
  end

  function Atum.flux(law::EulerLaw, q, _)
    ρ, ρu⃗, ρe = unpackstate(law, q)

    u⃗ = ρu⃗ / ρ
    p = pressure(law, ρ, ρu⃗, ρe)

    fρ = ρu⃗
    fρu⃗ = ρu⃗ * u⃗' + p * I
    fρe = u⃗ * (ρe + p)

    hcat(fρ, fρu⃗, fρe)
  end

  function Atum.wavespeed(law::EulerLaw, n⃗, q, x⃗)
    ρ, ρu⃗, ρe = unpackstate(law, q)

    u⃗ = ρu⃗ / ρ
    abs(n⃗' * u⃗) + soundspeed(law, ρ, ρu⃗, ρe)
  end

  function Atum.surfaceflux(::Atum.RoeFlux, law::EulerLaw, n⃗, x⃗, q⁻, q⁺)
    f⁻ = Atum.flux(law, q⁻, x⃗)
    f⁺ = Atum.flux(law, q⁺, x⃗)

    ρ⁻, ρu⃗⁻, ρe⁻ = unpackstate(law, q⁻)
    u⃗⁻ = ρu⃗⁻ / ρ⁻
    e⁻ = ρe⁻ / ρ⁻
    p⁻ = pressure(law, ρ⁻, ρu⃗⁻, ρe⁻)
    h⁻ = e⁻ + p⁻ / ρ⁻
    c⁻ = soundspeed(law, ρ⁻, p⁻)

    ρ⁺, ρu⃗⁺, ρe⁺ = unpackstate(law, q⁺)
    u⃗⁺ = ρu⃗⁺ / ρ⁺
    e⁺ = ρe⁺ / ρ⁺
    p⁺ = pressure(law, ρ⁺, ρu⃗⁺, ρe⁺)
    h⁺ = e⁺ + p⁺ / ρ⁺
    c⁺ = soundspeed(law, ρ⁺, p⁺)

    ρ = sqrt(ρ⁻ * ρ⁺)
    u⃗ = roe_avg(ρ⁻, ρ⁺, u⃗⁻, u⃗⁺)
    h = roe_avg(ρ⁻, ρ⁺, h⁻, h⁺)
    c = roe_avg(ρ⁻, ρ⁺, c⁻, c⁺)

    uₙ = u⃗' * n⃗

    Δρ = ρ⁺ - ρ⁻
    Δp = p⁺ - p⁻
    Δu⃗ = u⃗⁺ - u⃗⁻
    Δuₙ = Δu⃗' * n⃗

    c⁻² = 1 / c^2
    w1 = abs(uₙ - c) * (Δp - ρ * c * Δuₙ) * c⁻² / 2
    w2 = abs(uₙ + c) * (Δp + ρ * c * Δuₙ) * c⁻² / 2
    w3 = abs(uₙ) * (Δρ - Δp * c⁻²)
    w4 = abs(uₙ) * ρ

    fp_ρ = (w1 + w2 + w3) / 2
    fp_ρu⃗ = (w1 * (u⃗ - c * n⃗) +
             w2 * (u⃗ + c * n⃗) +
             w3 * u⃗ +
             w4 * (Δu⃗ - Δuₙ * n⃗)) / 2
    fp_ρe = (w1 * (h - c * uₙ) +
             w2 * (h + c * uₙ) +
             w3 * u⃗' * u⃗ / 2 +
             w4 * (u⃗' * Δu⃗ - uₙ * Δuₙ)) / 2

    (f⁻ + f⁺)' * n⃗ / 2 - SVector(fp_ρ, fp_ρu⃗..., fp_ρe)
  end

  function Atum.twopointflux(::Atum.EntropyConservativeFlux,
                             law::EulerLaw,
                             q₁, _, q₂, _)
      FT = eltype(law)
      ρ₁, ρu⃗₁, ρe₁ = unpackstate(law, q₁)
      ρ₂, ρu⃗₂, ρe₂ = unpackstate(law, q₂)

      u⃗₁ = ρu⃗₁ / ρ₁
      p₁ = pressure(law, ρ₁, ρu⃗₁, ρe₁)
      b₁ = ρ₁ / 2p₁

      u⃗₂ = ρu⃗₂ / ρ₂
      p₂ = pressure(law, ρ₂, ρu⃗₂, ρe₂)
      b₂ = ρ₂ / 2p₂

      ρ_avg = avg(ρ₁, ρ₂)
      u⃗_avg = avg(u⃗₁, u⃗₂)
      b_avg = avg(b₁, b₂)

      u²_avg = avg(u⃗₁' * u⃗₁, u⃗₂' * u⃗₂)
      ρ_log = logavg(ρ₁, ρ₂)
      b_log = logavg(b₁, b₂)

      fρ = u⃗_avg * ρ_log
      fρu⃗ = u⃗_avg * fρ' + ρ_avg / 2b_avg * I
      fρe = (1 / (2 * (γ(law) - 1) * b_log) - u²_avg / 2) * fρ + fρu⃗ * u⃗_avg

      hcat(fρ, fρu⃗, fρe)
  end
end


using OrdinaryDiffEq, DiffEqSensitivity, DiffEqFlux, LinearAlgebra, Flux
nn = FastChain(FastDense(1,16),FastDense(16,16,tanh),FastDense(16,2))
initial = initial_params(nn)

function ode2!(u, p, t)
    f1, f2 = nn([t],p)
    [-f1^2; f2]
end

tspan = (0.0, 10.0)
prob = ODEProblem(ode2!, Complex{Float64}[0;0], tspan, initial)

function loss(p)
  sol = last(solve(prob, Tsit5(), p=p, sensealg=BacksolveAdjoint(autojacvec=ZygoteVJP())))
  return norm(sol)
end

result_ode = DiffEqFlux.sciml_train(loss, initial, ADAM(0.1), maxiters = 100)


mango(quantity, price) = (quantity - quantity ÷ 3) * price


using FFTW

export Poisson

"""
    Poisson( grid )

Derived type to solve the Poisson equation on 2d regular
cartesian mesh with periodic boundary conditions on both sides
- kx   : wave number in x
- ky   : wave number in y
- k2   : ``k_x^2 + k_y^2``
- nc_x : cells number in x
- nc_y : cells number in y
- dx   : x step size
- dy   : y step size
- rht  : fft(rho)
"""
struct Poisson

    grid::Grid
    kx::Array{Float64,2}
    ky::Array{Float64,2}
    k2::Array{Float64,2}
    rht::Array{ComplexF64,2}

    function Poisson(grid::Grid)

        nx = grid.nx
        ny = grid.ny

        rht = zeros(ComplexF64, (div(nx, 2) + 1, ny))

        kx = zeros(nx ÷ 2 + 1, ny)
        ky = zeros(nx ÷ 2 + 1, ny)
        k2 = zeros(nx ÷ 2 + 1, ny)

        kx0 = 2π / grid.lx
        ky0 = 2π / grid.ly

        for ik = 1:nx÷2+1
            kx1 = (ik - 1) * kx0
            for jk = 1:ny÷2
                kx[ik, jk] = kx1
                ky[ik, jk] = (jk - 1) * ky0
            end
            for jk = ny÷2+1:ny
                kx[ik, jk] = kx1
                ky[ik, jk] = (jk - 1 - ny) * ky0
            end
        end

        kx[1, 1] = 1.0
        k2 .= kx .* kx .+ ky .* ky
        kx .= kx ./ k2
        ky .= ky ./ k2

        new(grid, kx, ky, k2, rht)

    end

end

export compute_psi!

"""
    compute_psi!( poisson, psi, omega )

computes ``\\psi`` from ``\\omega``

```math
-\\Delta \\psi(x,y) = \\omega(x,y)
```

"""
function compute_psi!(psi, poisson::Poisson, omega)

    poisson.rht .= rfft(omega)
    poisson.rht ./= poisson.k2
    psi .= irfft(poisson.rht, poisson.grid.nx)

end

export compute_velocities!

"""
    compute_velocities!( ux, uy, poisson, omega )

solves Poisson equation to compute velocity fields

```math
\\Delta \\psi(x,y) = - \\omega(x,y) \\\\
u(x,y) = \\nabla \\times \\psi(x,y)
```

"""
function compute_velocities!(ux, uy, poisson::Poisson, omega)

    poisson.rht .= rfft(omega)

    poisson.rht[1, 1] = 0.0
    poisson.rht .*= -1im .* poisson.ky

    ux .= irfft(poisson.rht, poisson.grid.nx)

    poisson.rht .= rfft(omega)

    poisson.rht[1, 1] = 0.0
    poisson.rht .*= 1im .* poisson.kx

    uy .= irfft(poisson.rht, poisson.grid.nx)

end


"""
More general version of hypergeometric2F1 function than is provided by GSL library, based on 'Computation of Hypergeometric Functions' (Pearson 2009)
"""
function hypergeom_2F1_expanded(a::G, b::G, c::G, z::G) where G <: AbstractFloat
    if z < -1.0
        return (1.0 - z) ^ (-1.0 * a) * hypergeom_2F1_expanded(a, c - b, c, z / (z - 1.0))
    elseif z < 0.5
        return hypergeom([a, b], c, z)
    elseif z < 1.0
        coef1 = gamma(c) * gamma(c - a - b) / (gamma(c - a) * gamma(c - b))
        hypergeom1 = hypergeom_2F1_expanded(a, b, a + b - c + 1.0, 1.0 - z)
        coef2 = (1.0 - z) ^ (c - a - b) * gamma(c) * gamma(a + b - c) / (gamma(a) * gamma(b))
        hypergeom2 = hypergeom_2F1_expanded(c - a, c - b, c - a - b + 1.0, 1.0 - z)
        return coef1 * hypergeom1 + coef2 * hypergeom2
    else #if z >= 1.0
        coef1 = z ^ (-1.0 * a) * gamma(c) * gamma(c - a - b) / (gamma(c - a) * gamma(c - b))
        hypergeom1 = hypergeom_2F1_expanded(a, a - c + 1.0, a + b - c + 1.0, 1.0 - 1.0 / z)
        coef2 = z ^ (a - c) * (1.0 - z) ^ (c - a - b) * gamma(c) * gamma(a + b - c) / (gamma(a) * gamma(b))
        hypergeom2 = hypergeom_2F1_expanded(c - a, 1.0 - a, c - a - b + 1.0, 1.0 - 1.0 / z)
        return coef1 * hypergeom1 + coef2 * hypergeom2
    end
end

"""
Dark matter Density profile given in units of ρs
"""
function density_profile(x::G) where G <: AbstractFloat
    return (x * (1.0 + x) ^ 2) ^ -1
end

function density_profile(x::G, p::P) where {G <: AbstractFloat, P <: SFWParameters}
    return (x ^ -p.γ) * (1.0 + x ^ p.α) ^ ((p.γ - p.β) / p.α)
end

"""
Define gravitationla potential function, results are in units of Φs (i.e. are often multiplied by it)
"""
function gravitational_potential(x::G) where G <: AbstractFloat
    return 1.0 - (log(1.0 + x) / x)
end

function gravitational_potential(x::G, p::NFWParameters{G}) where G <: AbstractFloat
    return p.Φs * (1.0 - (log(1.0 + x) / x))
end

"""
Slower version of function gravitational_potential0() but works in cases where a analytic form for solution does not exist
"""
function gravitational_potential_integral(x::G, p::P) where {G <: AbstractFloat, P <: SFWParameters}
    function integrand1(xi::G)
        return  density_profile(xi, p) * xi * xi
    end
    function integrand2(xi::G)
        return density_profile(xi, p) * xi
    end
    Φ1 = quadgk(integrand1, 0.0, x)[1]
    Φ2 = quadgk(integrand2, x, Inf)[1]

    return p.Φs * (1.0 - (Φ1 / x + Φ2))
end

"""
Gravitational potential for SFW profile where center of galaxy is not forced to zero, use to compute normalization factor Φ0
"""
function gravitational_potential0(x::G, p::P) where {G <: AbstractFloat, P <: SFWParameters}
    if ((3.0 - p.β) / p.α % 1.0) == 0.0 || ((p.γ - p.β) / p.α % 1.0) == 0.0
        return gravitational_potential_integral(x, p)
    end
    p1b = hypergeom_2F1_expanded((3.0 - p.γ) / p.α, (p.β - p.γ) / p.α, (3.0 + p.α - p.γ) / p.α, -x ^ p.α)
    I1 = -(x ^ (3.0 - p.γ) * p1b) / (x * (p.γ - 3.0))
    p2 = hypergeom_2F1_expanded((p.β - 2.0) / p.α, (p.β - p.γ) / p.α, (p.α + p.β - 2.0) / p.α, -x ^ (-p.α))
    I2 = x ^ (2.0 - p.β) * p2 / (p.β - 2.0)
    return p.Φs * (1.0 - (I1 + I2))
end

#complete if re-encouter the error with hypergeom...
function gravitational_potential0_robust(x::G, p::P) where {G <: AbstractFloat, P <: SFWParameters}
    return try
        gravitational_potential0(x, p) #determine if should be 0 or not
    catch
        gravitational_potential_integral(x, p)
    end
end

"""
Gravitational potential for SFW profile where center of galaxy is forced to have a potential of 0.
"""
function gravitational_potential(x::G, p::P) where {G <: AbstractFloat, P <: SFWParameters}
    return gravitational_potential0(x, p) -  p.Φ0
end


using ForwardDiff

include(string(pwd(),"/src/likelihood_and_diff_old_kalman.jl"))

to_unit(x) = 1/(1+exp(-x))
to_positive(x) = exp(x)
to_one_inf(x) = exp(x) + 1

d_to_unit(x) = exp(-x)/(exp(-x)+1)^2
d_to_positive(x) = exp(x)
d_to_one_inf(x) = exp(x)

d2_to_unit(x) = ForwardDiff.derivative(xx->abs(d_to_unit(xx)),x)
d2_to_positive(x) = exp(x)
d2_to_one_inf(x) = exp(x)

function renormalization(par)

    J_1 = d_to_unit(par[2])
    J_2 = d_to_positive(par[3])
    J_3 = d_to_unit(par[4])
    J_4 = d_to_positive(par[5])
    J_5 = d_to_positive(par[6])
    J_6 = d_to_positive(par[7])
    J_7 = d_to_one_inf(par[8])
    J_8 = d_to_positive(par[9])
    J_9 = d_to_unit(par[10])

    return([J_1;J_2;J_3;J_4;J_5;J_6;J_7;J_8;J_9])
end

function dens(par,data)
    alfa = par[1]
    bet = to_unit(par[2])
    epsilon = to_positive(par[3])
    theta = to_unit(par[4])
    sig = to_positive(par[5])
    s2 = to_positive(par[6])
    phi = to_positive(par[7])
    phi_pi = to_one_inf(par[8])
    phi_y = to_positive(par[9])
    rho_v = to_unit(par[10])

    renorm_par = [alfa;bet;epsilon;theta;sig;s2;phi;phi_pi;phi_y;rho_v]

    renorm = renormalization(par)
    jacob = abs(prod(renorm))

    log_p_bet(bet) = logpdf(prior_bet,bet)
    log_p_epsilon(epsilon) = logpdf(prior_epsilon,epsilon)
    log_p_theta(theta) = logpdf(prior_theta,theta)
    log_p_sig(sig) = logpdf(prior_sig,sig)
    log_p_s2(s2) = logpdf(prior_s2,s2)
    log_p_phi(phi) = logpdf(prior_phi,phi)
    log_p_phi_pi(phi_pi) = logpdf(prior_phi_pi,phi_pi)
    log_p_phi_y(phi_y) = logpdf(prior_phi_y,phi_y)
    log_p_rho_v(rho_v) = logpdf(prior_rho_v,rho_v)
    llh,dll = log_like_dsge(renorm_par,data)
    llh = llh + log(jacob) + log_p_bet(bet) + log_p_epsilon(epsilon) + log_p_theta(theta) + log_p_sig(sig) + log_p_s2(s2) + log_p_phi(phi) + log_p_phi_pi(phi_pi) + log_p_phi_y(phi_y) + log_p_rho_v(rho_v) + sum(log.(renorm))
    return llh
end

function dens_and_grad(par,data)
    alfa = par[1]
    bet = to_unit(par[2])
    epsilon = to_positive(par[3])
    theta = to_unit(par[4])
    sig = to_positive(par[5])
    s2 = to_positive(par[6])
    phi = to_positive(par[7])
    phi_pi = to_one_inf(par[8])
    phi_y = to_positive(par[9])
    rho_v = to_unit(par[10])

    nobs = size(data,1)

    renorm_par = [alfa;bet;epsilon;theta;sig;s2;phi;phi_pi;phi_y;rho_v]

    renorm = renormalization(par)
    jacob = abs(prod(renorm))

    renorm_diff = ForwardDiff.gradient(x->abs(prod(renormalization(x))),par) #derivate of abs det of Jacobian matrix

    diff_correction = 1/jacob*renorm_diff
    diff_correction = diff_correction[2:10]

    log_p_bet(bet) = logpdf(prior_bet,bet)
    log_p_epsilon(epsilon) = logpdf(prior_epsilon,epsilon)
    log_p_theta(theta) = logpdf(prior_theta,theta)
    log_p_sig(sig) = logpdf(prior_sig,sig)
    log_p_s2(s2) = logpdf(prior_s2,s2)
    log_p_phi(phi) = logpdf(prior_phi,phi)
    log_p_phi_pi(phi_pi) = logpdf(prior_phi_pi,phi_pi)
    log_p_phi_y(phi_y) = logpdf(prior_phi_y,phi_y)
    log_p_rho_v(rho_v) = logpdf(prior_rho_v,rho_v)

    ## Derivates for the priors

    diff_prior = [ForwardDiff.derivative(log_p_bet,bet); ForwardDiff.derivative(log_p_epsilon,epsilon); ForwardDiff.derivative(log_p_theta,theta); ForwardDiff.derivative(log_p_sig,sig); ForwardDiff.derivative(log_p_s2,s2); ForwardDiff.derivative(log_p_phi,phi); ForwardDiff.derivative(log_p_phi_pi,phi_pi); ForwardDiff.derivative(log_p_phi_y,phi_y); ForwardDiff.derivative(log_p_rho_v,rho_v)]

    #diff_prior_cor = [d2_to_unit(par[2]);d2_to_positive(par[3]);d2_to_unit(par[4]);d2_to_positive(par[5]);d2_to_positive(par[6]);d2_to_positive(par[7]);d2_to_one_inf(par[8]);d2_to_positive(par[9]);d2_to_unit(par[10])]

    #pdf_prior = [pdf(prior_bet,bet);pdf(prior_epsilon,epsilon);pdf(prior_theta,theta);pdf(prior_sig,sig);pdf(prior_s2,s2);pdf(prior_phi,phi);pdf(prior_phi_pi,phi_pi);pdf(prior_phi_y,phi_y);pdf(prior_rho_v,rho_v)]

    llh,dll = log_like_dsge(renorm_par,data)
    llh = llh +log(jacob) + log_p_bet(bet) + log_p_epsilon(epsilon) + log_p_theta(theta) + log_p_sig(sig) + log_p_s2(s2) + log_p_phi(phi) + log_p_phi_pi(phi_pi) + log_p_phi_y(phi_y) + log_p_rho_v(rho_v) + sum(log.(abs.(renorm)))

    dll = dll[2:10]

    dll = dll .*renorm + diff_correction + diff_prior.*renorm

    #dll = dll/nobs

    return llh,dll
end


module Nystrom

using DocStringExtensions
using StaticArrays
using LinearAlgebra
using SparseArrays
using IterativeSolvers
using Base.Threads
using SpecialFunctions
using OrderedCollections

using WaveProp.Utils
using WaveProp.Geometry
using WaveProp.Integration
using WaveProp.Integration
using WaveProp.Mesh
using WaveProp.PhysicalProblem

import WaveProp.Geometry: geometric_dimension, ambient_dimension, domain, range
import WaveProp.Integration: singular_weights, qnodes, qweights, qnormals
import WaveProp.Mesh: etypes, elements

export
    Laplace,
    Helmholtz,
    Elastostatic,
    Stokes,
    Maxwell,
    SingleLayerKernel,
    DoubleLayerKernel,
    IntegralOperator,
    NystromMesh,
    Density,
    γ₀,
    γ₁,
    SingleLayerPotential,
    DoubleLayerPotential,
    SingleLayerOperator,
    DoubleLayerOperator,
    AdjointDoubleLayerOperator,
    HyperSingularOperator,
    GreensCorrection,
    singular_weights_dim,
    single_double_layer,
    isinside,
    NystromMesh,
    dom2dof,
    assemble

include("nystrommesh.jl")
include("greensformulae.jl")
include("lebedev.jl")
include("kernels.jl")
include("laplace.jl")
include("helmholtz.jl")
include("elastostatic.jl")
include("maxwell.jl")
include("density.jl")
include("potential.jl")
include("integraloperators.jl")
include("dim.jl")
include("ldim.jl")
include("assemble.jl")
include("martensen_kussmaul.jl")

end # module


@testset "Ideals in algebras" begin

  @testset "Left / Right" begin
    A = matrix_algebra(GF(3), 2)
    I = A[2]*A

    @test isleft_ideal(I) == false
    @test isright_ideal(I) == true
  end

  @testset "Quotients" begin
    Qx, x = FlintQQ["x"]
    # f = x^2 + 1
    # g = x^3 + 3x^2 + 5x - 5
    f2g3 = x^13 + 9x^12 + 44x^11 + 120x^10 + 205x^9 + 153x^8 + 32x^7 - 168x^6 - 5x^5 - 485x^4 + 500x^3 - 400x^2 + 375x - 125 # = f^2*g^3
    A = AlgAss(f2g3)
    J = radical(A)
    Q, AtoQ = quo(A, J)

    @test dim(Q) == 5
    @test iszero(radical(Q))

    @test iszero(AtoQ(zero(A)))
    @test AtoQ\zero(Q) in J
    @test isone(AtoQ(one(A)))
    @test mod(one(A), J) == mod(AtoQ\one(Q), J)

    for i = 1:5
      c = rand(A, -10:10)
      d = rand(A, -10:10)
      @test mod(AtoQ\(AtoQ(c)), J) == mod(c, J)
      @test mod(AtoQ\(AtoQ(d)), J) == mod(d, J)
      @test AtoQ(c + d) == AtoQ(c) + AtoQ(d)
      @test AtoQ(c*d) == AtoQ(c)*AtoQ(d)

      e = rand(Q, -10:10)
      f = rand(Q, -10:10)
      @test AtoQ(AtoQ\e) == e
      @test AtoQ(AtoQ\f) == f
      @test mod(AtoQ\(e + f), J) == mod((AtoQ\e + AtoQ\f), J)
      @test mod(AtoQ\(e*f), J) == mod(((AtoQ\e)*(AtoQ\f)), J)
    end

    J2 = J^2
    Q, AtoQ = quo(J, J2)

    @test dim(Q) == 5

    @test iszero(AtoQ(zero(A)))
    @test AtoQ\zero(Q) in J2
    @test_throws ErrorException AtoQ(one(A))

    for i = 1:5
      c = rand(J, -10:10)
      d = rand(J, -10:10)
      @test mod(AtoQ\(AtoQ(c)), J2) == mod(c, J2)
      @test mod(AtoQ\(AtoQ(d)), J2) == mod(d, J2)
      @test AtoQ(c + d) == AtoQ(c) + AtoQ(d)

      e = rand(Q, -10:10)
      f = rand(Q, -10:10)
      @test AtoQ(AtoQ\e) == e
      @test AtoQ(AtoQ\f) == f
      @test mod(AtoQ\(e + f), J2) == mod((AtoQ\e + AtoQ\f), J2)
    end
  end

end


#########################################
####        JLMS and BC index        ####
#########################################

#? --------------- Truncated Normal --------------

function jlmsbc(::Type{Trun}, PorC::Int64, pos::NamedTuple, coef::Array{Float64, 1},
                Y::Matrix, X::Matrix, Z::Matrix, q, W::Matrix, V::Matrix, # dum1,
                dum2)

    x_pre = X*coef[pos.begx : pos.endx]
    z_pre = Z*coef[pos.begz : pos.endz]  # mu
  # q_pre = Q*coef[pos.begq : pos.endq]
    w_pre = W*coef[pos.begw : pos.endw] # log_σᵤ²
    v_pre = V*coef[pos.begv : pos.endv] # log_σᵥ²

    μ   = z_pre
    σᵤ² = @. exp(w_pre)
    σᵤ  = @. exp(0.5 * w_pre)
    σᵥ² = @. exp(v_pre)
    σ²  = σᵤ² + σᵥ²
    ϵ   = PorC*(Y - x_pre)
    μₛ  = @. (σᵥ² * μ - σᵤ² * ϵ) / σ²
    σₛ  = @. sqrt((σᵥ² * σᵤ²) / σ²)

   jlms = @. (σₛ * normpdf(μₛ / σₛ )) / normcdf(μₛ / σₛ) + μₛ
     bc = @. exp( -μₛ + 0.5 * (σₛ)^2  ) * ( normcdf( (μₛ / σₛ) - σₛ ) / normcdf(μₛ / σₛ)  )

   return jlms, bc
end

#? ------------------ Half normal model -------------

function jlmsbc(::Type{Half}, PorC::Int64, pos::NamedTuple, coef::Array{Float64, 1},
                Y::Matrix, X::Matrix, z, q, W::Matrix, V::Matrix, # dum1,
                dum2)

    x_pre = X*coef[pos.begx : pos.endx]
  # z_pre = Z*coef[pos.begz : pos.endz]  # mu
  # q_pre = Q*coef[pos.begq : pos.endq]
    w_pre = W*coef[pos.begw : pos.endw] # log_σᵤ²
    v_pre = V*coef[pos.begv : pos.endv] # log_σᵥ²

    μ   = 0.0
    σᵤ² = @. exp(w_pre)
    σᵥ² = @. exp(v_pre)
    σ²  = σᵤ² + σᵥ²
    ϵ   = PorC*(Y - x_pre)
    μₛ  = @. (σᵥ² * μ - σᵤ² * ϵ) / σ²
    σₛ  = @. sqrt((σᵥ² * σᵤ²) / σ²)

   jlms = @. (σₛ * normpdf(μₛ / σₛ )) / normcdf(μₛ / σₛ) + μₛ
     bc = @. exp( -μₛ + 0.5 * (σₛ)^2  ) * ( normcdf( (μₛ / σₛ) - σₛ ) / normcdf(μₛ / σₛ)  )

   return jlms, bc
end

#? ------------------ Exponential model -------------

function jlmsbc(::Type{Expo}, PorC::Int64, pos::NamedTuple, coef::Array{Float64, 1},
                Y::Matrix, X::Matrix, z, q, W::Matrix, V::Matrix, # dum1,
                dum2)

    x_pre = X*coef[pos.begx : pos.endx]
  # z_pre = Z*coef[pos.begz : pos.endz]  # mu
  # q_pre = Q*coef[pos.begq : pos.endq]
    w_pre = W*coef[pos.begw : pos.endw] # log_σᵤ²
    v_pre = V*coef[pos.begv : pos.endv] # log_σᵥ²

    λ   = @. exp(0.5*w_pre)
    σᵥ² = @. exp(v_pre)
    σᵥ  = @. exp(0.5*v_pre)

    ϵ   = PorC*(Y - x_pre)
    μₛ  = (- ϵ) -(σᵥ² ./ λ) # don't know why this line cannot use @.

    jlms = @. (σᵥ * normpdf(μₛ / σᵥ )) / normcdf(μₛ / σᵥ) + μₛ
      bc = @. exp( -μₛ + 0.5 * σᵥ²  ) * ( normcdf( (μₛ / σᵥ) - σᵥ ) / normcdf(μₛ / σᵥ)  )

    return jlms, bc
end

#? -------------- Scaling Property Model ------------

function jlmsbc(::Type{Trun_Scale}, PorC::Int64, pos::NamedTuple, coef::Array{Float64, 1},
                Y::Matrix, X::Matrix, Z::Matrix, Q::Matrix, W::Matrix, V::Matrix, # dum1,
                dum2)

    x_pre = X*coef[pos.begx : pos.endx]
    z_pre = Z*coef[pos.begz : pos.endz]  # mu
    q_pre = Q*coef[pos.begq : pos.endq]
    w_pre = W*coef[pos.begw : pos.endw] # log_σᵤ²
    v_pre = V*coef[pos.begv : pos.endv] # log_σᵥ²

    μ   = @. (z_pre) * exp(q_pre)             # Q is _cons; μ here is the after-mutiplied-by-scaling function
    σᵤ² = @. exp(w_pre + 2*q_pre)           # The notation of σᵤ² is different from the paper. W is _cons
    σᵥ² = @. exp(v_pre)
    σ²  = σᵤ² + σᵥ²
    ϵ   = PorC*(Y - x_pre)
    μₛ  = @. (σᵥ² * μ - σᵤ² * ϵ) / σ²
    σₛ  = @. sqrt((σᵥ² * σᵤ²) / σ²)

   jlms = @. (σₛ * normpdf(μₛ / σₛ )) / normcdf(μₛ / σₛ) + μₛ
     bc = @. exp( -μₛ + 0.5 * (σₛ)^2  ) * ( normcdf( (μₛ / σₛ) - σₛ ) / normcdf(μₛ / σₛ)  )

   return jlms, bc
end

#? ---------- panel FE Wang and Ho (2010 JoE), Half normal -----------

function jlmsbc(::Type{PFEWHH}, PorC::Int64, pos::NamedTuple, coef::Array{Float64, 1},
                Y::Vector, X::Matrix, z, Q::Matrix, W::Matrix, V::Matrix, idt::Matrix{Any})

    x_pre = X*coef[pos.begx : pos.endx]
  # z_pre = Z*coef[pos.begz : pos.endz]  # mu
    q_pre = Q*coef[pos.begq : pos.endq]
    w_pre =   coef[pos.begw] # log_σᵤ²
    v_pre =   coef[pos.begv] # log_σᵥ²

    ϵ̃   = PorC*(Y - x_pre)
    h   = exp.(q_pre)
    σᵤ² = exp(w_pre)
    σᵥ² = exp(v_pre)

    μ = 0.0

    nofobs = length(Y)
    N = size(idt)[1]

    jlms = zeros(nofobs, 1)
      bc = zeros(nofobs, 1)

    for i = 1:N
      # @views Π⁻  = INVtrM[i]*(1/σᵥ²)
        @views ind = idt[i,1]
      # @views trM = Matrix(I, idt[i,2], idt[i,2]) .- (1/idt[i,2])
        @views h̃   = sf_demean(h[ind]) # trM*h[ind]
              σₛₛ² = σₛₛ² = 1.0/(h̃'*h̃*(1/σᵥ²) + 1/σᵤ²)  # h̃ is already de-meaned, so skip Π⁻ but mind 1/σᵥ² # 1.0/(h̃'*Π⁻*h̃ + 1/σᵤ²)
              σₛₛ  = sqrt(σₛₛ²)
        @views μₛₛ = (μ/σᵤ² - ϵ̃[ind]'*h̃*(1/σᵥ²)) * σₛₛ² # (μ/σᵤ² - ϵ̃[ind]'*Π⁻*h̃) * σₛₛ²

        jlms[ind] = @. h[ind] * (μₛₛ + normpdf(μₛₛ/σₛₛ)*σₛₛ/normcdf(μₛₛ/σₛₛ))
        bc[ind]   = @. ((normcdf(μₛₛ/σₛₛ - h[ind]*σₛₛ))/normcdf(μₛₛ/σₛₛ))*exp(-h[ind]*μₛₛ + 0.5*(h[ind]^2)*σₛₛ²)
    end # for_i

    return jlms, bc
end

#? ---------- panel FE Wang and Ho (2010 JoE), truncated normal -----------

function jlmsbc(::Type{PFEWHT}, PorC::Int64, pos::NamedTuple, coef::Array{Float64, 1},
    Y::Vector, X::Matrix, Z::Matrix, Q::Matrix, W::Matrix, V::Matrix, idt::Matrix{Any})

    x_pre = X*coef[pos.begx : pos.endx]
    z_pre =   coef[pos.begz]  # mu, a scalar
    q_pre = Q*coef[pos.begq : pos.endq]
    w_pre =   coef[pos.begw] # log_σᵤ²
    v_pre =   coef[pos.begv] # log_σᵥ²

    ϵ̃   = PorC*(Y - x_pre)
    μ   = z_pre
    h   = exp.(q_pre)
    σᵤ² = exp(w_pre)
    σᵥ² = exp(v_pre)

    nofobs = length(Y)
         N = size(idt)[1]

    jlms = zeros(nofobs, 1)
      bc = zeros(nofobs, 1)

    for i = 1:N
      # @views Π⁻  = INVtrM[i]*(1/σᵥ²)
        @views ind = idt[i,1]
      # @views trM = Matrix(I, idt[i,2], idt[i,2]) .- (1/idt[i,2])
        @views h̃   = sf_demean(h[ind]) # trM*h[ind]
              σₛₛ² = 1.0/(h̃'*h̃*(1/σᵥ²) + 1/σᵤ²)  # 1.0/(h̃'*Π⁻*h̃ + 1/σᵤ²)
              σₛₛ  = sqrt(σₛₛ²)
        @views μₛₛ = (μ/σᵤ² - ϵ̃[ind]'*h̃*(1/σᵥ²)) * σₛₛ² # (μ/σᵤ² - ϵ̃[ind]'*Π⁻*h̃) * σₛₛ²

        jlms[ind] = @. h[ind] * (μₛₛ + normpdf(μₛₛ/σₛₛ)*σₛₛ/normcdf(μₛₛ/σₛₛ))
        bc[ind]   = @. ((normcdf(μₛₛ/σₛₛ - h[ind]*σₛₛ))/normcdf(μₛₛ/σₛₛ))*exp(-h[ind]*μₛₛ + 0.5*(h[ind]^2)*σₛₛ²)
    end # for_i

    return jlms, bc
end

#? ---------- panel time decay model of Battese and Coelli (1992) -----------

function jlmsbc(::Type{PanDecay}, PorC::Int64, pos::NamedTuple, coef::Array{Float64, 1},
  Y::Matrix, X::Matrix, Z::Matrix, Q::Matrix, W::Matrix, V::Matrix, # dum1,
  idt::Matrix{Any})

  x_pre = X*coef[pos.begx : pos.endx]
  z_pre = Z*coef[pos.begz : pos.endz]
  q_pre = Q*coef[pos.begq : pos.endq]
  w_pre =   coef[pos.begw] # log_σᵤ²
  v_pre =   coef[pos.begv] # log_σᵥ²

  nofobs = length(Y)
  N = size(idt)[1]

  σᵤ² = exp(w_pre)    # should be W*log_σᵤ² where W is a _cons =1; make a short cut here
  σᵥ² = exp(v_pre)
  σₛ² = σᵤ² + σᵥ²

  jlms_ui  = zeros(nofobs, 1)
  jlms_uit = zeros(nofobs, 1)
  bc_uit   = zeros(nofobs, 1)

  @inbounds for i = 1:N
      @views ind = idt[i,1]
      @views Tᵢ  = idt[i,2]
      @views ε   = PorC*(Y[ind] - x_pre[ind])
      @views μ   = (z_pre[ind])[1]  # ok becuase it is time-invariant
      @views Gₜ  = exp.(q_pre[ind])
      Gε  = Gₜ .* ε

      ΣGε  = sum_kbn(Gε)
      ΣGₜ² = sum_kbn(Gₜ.^2)

      μₛ   = (μ * σᵥ² - ΣGε * σᵤ²) / (σᵥ² + σᵤ² * ΣGₜ²)
      σₛ²  =  σᵥ² * σᵤ² / (σᵥ² + σᵤ² * ΣGₜ²)
      σₛ   = sqrt(σₛ²)

             jlms_ui[ind] .= μₛ + σₛ*(normpdf(μₛ/σₛ) / normcdf(μₛ/σₛ))  # identical within the panel
      @views jlms_uit[ind] = Gₜ .* jlms_ui[ind]

      bc_uit[ind] = (normcdf.(μₛ/σₛ .- Gₜ*σₛ  ) ./ normcdf( μₛ/σₛ )) .* exp.(-Gₜ.*μₛ + 0.5*((Gₜ).^2)*σₛ²)

  end

  return jlms_uit, bc_uit
end

#? ---------- panel Kumbhakar 1990 -----------

function jlmsbc(::Type{PanKumb90}, PorC::Int64, pos::NamedTuple, coef::Array{Float64, 1},
  Y::Matrix, X::Matrix, Z::Matrix, Q::Matrix, W::Matrix, V::Matrix, # dum1,
  idt::Matrix{Any})

  x_pre = X*coef[pos.begx : pos.endx]
  z_pre = Z*coef[pos.begz : pos.endz]
  q_pre = Q*coef[pos.begq : pos.endq]
  w_pre =   coef[pos.begw] # log_σᵤ²
  v_pre =   coef[pos.begv] # log_σᵥ²

  nofobs = length(Y)
  N = size(idt)[1]

  σᵤ² = exp(w_pre)    # should be W*log_σᵤ² where W is a _cons =1; make a short cut here
  σᵥ² = exp(v_pre)
  σₛ² = σᵤ² + σᵥ²

  jlms_ui  = zeros(nofobs, 1)
  jlms_uit = zeros(nofobs, 1)
  bc_uit   = zeros(nofobs, 1)

  @inbounds for i = 1:N
      @views ind = idt[i,1]
      @views Tᵢ  = idt[i,2]
      @views ε   = PorC*(Y[ind] - x_pre[ind])
      @views μ   = (z_pre[ind])[1]  # ok becuase it is time-invariant
      @views Gₜ  = 2 ./ (1 .+ exp.(q_pre[ind])  ) # Gₜ  = exp.(q_pre[ind])
      Gε  = Gₜ .* ε

      ΣGε  = sum_kbn(Gε)
      ΣGₜ² = sum_kbn(Gₜ.^2)

      μₛ   = (μ * σᵥ² - ΣGε * σᵤ²) / (σᵥ² + σᵤ² * ΣGₜ²)
      σₛ²  =  σᵥ² * σᵤ² / (σᵥ² + σᵤ² * ΣGₜ²)
      σₛ   = sqrt(σₛ²)

             jlms_ui[ind] .= μₛ + σₛ*(normpdf(μₛ/σₛ) / normcdf(μₛ/σₛ))  # identical within the panel
      @views jlms_uit[ind] = Gₜ .* jlms_ui[ind]

      bc_uit[ind] = (normcdf.(μₛ/σₛ .- Gₜ*σₛ  ) ./ normcdf( μₛ/σₛ )) .* exp.(-Gₜ.*μₛ + 0.5*((Gₜ).^2)*σₛ²)

  end

  return jlms_uit, bc_uit
end

#? ---------- panel FE CSW (JoE 2014), Half normal -----------

function jlmsbc(::Type{PFECSWH}, PorC::Int64, pos::NamedTuple, coef::Array{Float64, 1},
  Y::Vector, X::Matrix, z, q, w, v, idt::Matrix{Any})

   nofobs = length(Y)
   N = size(idt,1)
  #=
    for i in 1:N
        ind = idt[i,1]
        Y[ind] = Y[ind] .+ idt[i,3][1]  # add back the mean for the panel
        X[ind] = X[ind] .+ idt[i,3][2:end]
    end
  =#

    x_pre = X*coef[pos.begx : pos.endx]
    # z_pre = Z*coef[pos.begz : pos.endz]  # mu
    # q_pre = Q*coef[pos.begq : pos.endq]
    w_pre =   coef[pos.begw] # log_σᵤ²
    v_pre =   coef[pos.begv] # log_σᵥ²

    αϵ   = (Y - x_pre)  # αᵢ + ϵᵢ
    σᵤ² = exp(w_pre)
    σᵥ² = exp(v_pre)
    σᵤ = exp(0.5*w_pre)

    μ = 0.0

    jlms = zeros(nofobs, 1)
    bc = zeros(nofobs, 1)

    for i = 1:N

        @views ind = idt[i,1]
        @views αᵢᴹ = mean(αϵ[ind]) + PorC*( sqrt(2/π)*σᵤ )
        @views ϵ   = αϵ[ind] .- αᵢᴹ
               μₛ  = -(σᵤ²*ϵ) ./ (σᵤ² + σᵥ²)
               σₛ² = (σᵤ² * σᵥ²)/(σᵤ² + σᵥ²)
               r   =  μₛ ./ sqrt(σₛ²)
         jlms[ind] =  (sqrt.(σₛ²) * normpdf.(r) ./ normcdf.(r)) .+μₛ
         bc[ind]   =  exp.( -μₛ .+ 0.5 .* σₛ²  ) .* ( normcdf.( r .- sqrt.(σₛ²) ) ./ normcdf.(r)  )

    end # for_i

    return jlms, bc
end

#? ----- Panel TRE, Half Normal --------------

function jlmsbc(::Type{PTREH}, PorC::Int64, pos::NamedTuple, coef::Array{Float64, 1},
  Y::Matrix, X::Matrix, z, q, w, v, idt::Matrix{Any})

   #! only BC, no JLMS

    nofobs = length(Y)

    β   = coef[1:pos.endx]

    σₐ² = exp(coef[pos.begq])
    σᵤ² = exp(coef[pos.begw])
    σᵥ² = exp(coef[pos.begv])

    σᵤ  = exp(0.5*coef[pos.begw])
    σᵥ  = exp(0.5*coef[pos.begv])

    ε   = PorC*(Y - X * β)

  # μ   = (z[ind, :] * δ) #! truncated normal
    μ   = zeros(nofobs, 1)

    bc  = zeros(nofobs, 1)

    nofid = size(idt, 1)

    for i in 1:nofid
        @views ind  = idt[i, 1]
        @views T    = idt[i, 2]

              Σ     = ones(T,T)*σₐ² + Matrix(σᵥ²*I,T,T)
              Σᵤ    = Matrix(σᵤ²*I,T,T)
              invΣ  = (1/σᵥ²) * (Matrix(I,T,T) - σₐ²/(σᵥ² + T*σₐ²)*ones(T,T))
              invΣᵤ = (1/σᵤ²) * Matrix(I,T,T)
        @views K    = ε[ind]'*invΣ - μ[ind]'*invΣᵤ

      # analytic form of G = inv(inv(Σ)+inv(Σᵤ))
        γ²   = 1/(1/σᵤ²+1/σᵥ²)
        g    = -T*1/(1+σₐ²/σᵥ²*T)*σₐ²/σᵥ²^2*1/(1/σᵤ²+1/σᵥ²)
        ρ²   = 1/(1+g) * 1/(1/σᵤ²+1/σᵥ²)^2 * 1/(1 + σₐ²/σᵥ²*T) *(σₐ²/σᵥ²^2)
        G    = γ² * Matrix(I,T,T) + ρ² * ones(T,T)

        B    = (-K*G)'
        γ    = sqrt(γ²)
        ρ    = sqrt(ρ²)

        MCDF1 = quadgk(u -> normpdf(u)*prod(normcdf.((B.-ρ*u)/γ)), -Inf, Inf, rtol=1e-8)

        h1 = MCDF1[1]*exp(0.5*(B'*(invΣ + invΣᵤ)*B))

        bc_i = zeros(T)
        for j in 1:T
           l_T    = zeros(T)
           l_T[j] = 1.0
           K2 = K + l_T'
           B2 = (-K2*G)'
           MCDF2 = quadgk(u -> normpdf(u)*prod(normcdf.((B2.-ρ*u)/γ)), -Inf, Inf, rtol=1e-8)
           h2 = MCDF2[1]*exp(0.5*(B2'*(invΣ + invΣᵤ)*B2))
           bc_i[j] = h2/h1
        end
           bc[ind] = bc_i

      end
           jlms = 0

    return jlms, bc
end

#? ----- Panel TRE, truncated normal ---------------

function jlmsbc(::Type{PTRET}, PorC::Int64, pos::NamedTuple, coef::Array{Float64, 1},
  Y::Matrix, X::Matrix, Z::Matrix, q, w, v, idt::Matrix{Any})

    #! only bc, no jlms

    nofobs = length(Y)

    β   = coef[1 : pos.endx]
    δ   = coef[pos.begz : pos.endz]

    σₐ² = exp(coef[pos.begq])
    σᵤ² = exp(coef[pos.begw])
    σᵥ² = exp(coef[pos.begv])

    ε   = PorC*(Y - X * β)

    μ   = (Z * δ)

    bc  = zeros(nofobs, 1)

    nofid = size(idt, 1)

    for i in 1:nofid
        @views ind  = idt[i, 1]
        @views T    = idt[i, 2]
              invΣ  = (1/σᵥ²) * (Matrix(I,T,T) - σₐ²/(σᵥ² + T*σₐ²)*ones(T,T))
              invΣᵤ = (1/σᵤ²) * Matrix(I,T,T)
        @views K     = ε[ind]'*invΣ - μ[ind]'*invΣᵤ

      # the analytic form of G = inv(inv(Σ)+inv(Σᵤ))
        γ²   = 1/(1/σᵤ²+1/σᵥ²)
        g    = -T*1/(1+σₐ²/σᵥ²*T)*σₐ²/σᵥ²^2*1/(1/σᵤ²+1/σᵥ²)
        ρ²   = 1/(1+g) * 1/(1/σᵤ²+1/σᵥ²)^2 * 1/(1 + σₐ²/σᵥ²*T) *(σₐ²/σᵥ²^2)
        G    = γ² * Matrix(I,T,T) + ρ² * ones(T,T)

        B    = (-K*G)'
        γ    = sqrt(γ²)
        ρ    = sqrt(ρ²)

        MCDF1 = quadgk(u -> normpdf(u)*prod(normcdf.((B.-ρ*u)/γ)), -Inf, Inf, rtol=1e-8)

        h1 = MCDF1[1]*exp(0.5*(B'*(invΣ + invΣᵤ)*B))

        bc_i = zeros(T)
        for j in 1:T
           l_T    = zeros(T)
           l_T[j] = 1.0
           K2 = K + l_T'
           B2 = (-K2*G)'
           MCDF2 = quadgk(u -> normpdf(u)*prod(normcdf.((B2.-ρ*u)/γ)), -Inf, Inf, rtol=1e-8)
           h2 = MCDF2[1]*exp(0.5*(B2'*(invΣ + invΣᵤ)*B2))
           bc_i[j] = h2/h1
        end
           bc[ind] = bc_i

      end
           jlms = 0

    return jlms, bc
end


# TEST:
function priorsample(y=range(-8, 4, length=200))
  tau = rand(Gamma(.5, 1))
  omega = rand(InverseGamma(2.5, tau))
  psi = rand(Normal(-1, .5))
  mu = rand(Normal(1, 1))
  sigma = CDE.Util.scalefromaltskewt(sqrt(omega), psi)
  phi = CDE.Util.skewfromaltskewt(sqrt(omega), psi)
  nu = rand(LogNormal(3, .5))
  println("(tau,omega,psi): $((tau,omega,psi))")
  println("(sigma,phi): $((sigma,phi))")
  return y, pdf.(CDE.Util.SkewT(mu, sigma, nu, phi), y)
end
dopriorsample() = let
  _y, ypdf = priorsample()
  plot(_y, ypdf, label=nothing); savefig("results/tmp.pdf"); closeall()
end


#function to be called when calculating wigner 3j-symbols
#17/12/2015

function tfunction3j(tt,aa,bb,cc,alpha,beta)
return xoft3j = factorial(tt)*factorial(cc-bb+tt+alpha)*factorial(cc-aa+tt-beta)*factorial(aa+bb-cc-tt)*factorial(aa-tt-alpha)*factorial(bb-tt+beta)
end


struct KPDSupply{T<:Integer}
    item::String
    weight::T
    value::T
    quant::T
end

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

using MathProgBase, Cbc
function solve(gear::Vector{<:KPDSupply}, capacity::Integer)
    w = getfield.(gear, :weight)
    v = getfield.(gear, :value)
    sol = mixintprog(-v, w', '<', capacity, :Bin, 0, 1, CbcSolver())
    gear[sol.sol .≈ 1]
end


# les N reines

# renvoie l'entier 00...0011..11 (n bits faibles à n)
# c'est-à-dire l'ensemble {0,1,...,n-1}
# version inefficace de ^(^0 << n)
function full(n)
    if n == 0
	0
    else
	full(n-1) * 2 + 1
    end
end

# le plus petit élément de x
# version inefficace de x & -x
function lowestbit(x)
    if x == 1
	1
    else
	2 * lowestbit(div(x, 2))
    end
end

# différence ensembliste
# version inefficace de x &^ y
function andnot(x, y)
    if x == 0
	return 0
    end
    z = 2 * andnot(div(x, 2), div(y, 2))
    if x % 2 == 1 && y % 2 == 0
	z + 1
    else
	z
    end
end

function t(a, b, c)
    if a == 0
	return 1
    end
    f = 0;
    e = andnot(andnot(a, b), c)
    while e > 0
	d = lowestbit(e)
	f = f + t(a - d, (b+d)*2, div(c+d,2))
	e = e - d
    end
    f
end

function queens(n)
    t(full(n), 0, 0)
end

for n = 0:10
    print(n)
    print(" ")
    print(queens(n))
    print("\n")
end


using FastPow, Test

@testset "integer powers" begin
    let x = 3.7, z = 5.2, u = 0.988
        y = sin(x)^6 + z^8.3 + u^9
        @fastpow y2 = sin(x)^6 + z^8.3 + u^9
        @test y ≈ y2
    end

    @test @fastpow(0.73^25) ≈ big"0.73"^25 rtol=3e-15
    @test @fastpow(0.6^-7) ≈ big"0.6"^-7 rtol=1e-15
    @test @fastpow(0.6^0) === 1.0
    @test @fastpow(0.6^1) === 0.6
end

struct DummyType; end
Base.:^(x::Int, ::DummyType) = "$x^dummy"
const dummy = DummyType()

@testset "1^p" begin
    for p in (-10,-9,0,9,10,0.2,2//3,0.3+0.4im)
        @test @fastpow(1^p) === one(p)
    end
    @test @fastpow(1^dummy) == "1^dummy"
end

@testset "(-1)^p" begin
    for n in (-10,-9,0,9,10)
        correct = iseven(n) ? 1 : -1
        @test @fastpow((-1)^n) === correct
        @test @fastpow((-1)^float(n)) == correct
        @test @fastpow((-1)^Rational(n)) == correct
    end
    for p in (0.2, 2//3)
        @test_throws DomainError @fastpow((-1)^p)
    end
    z = 0.3 + 0.4im
    @test @fastpow((-1)^z) ≈ (-1)^z ≈ 0.16728929223461353 + 0.23025395732014076im
    @test @fastpow((-1)^dummy) == "-1^dummy"
end

@testset "2^p" begin
    for p in Any[Int64.(0:65); big(100)]
        @test @fastpow(2^p)::typeof(2^p) == 2^p
    end
    p = -2
    @test_throws DomainError @fastpow(2^p)
    @test @fastpow(2^-2) === 0.25
    @test @fastpow(2^-100) === 7.888609052210118e-31
    for p in (0.3, -0.5, 4//3, 0.3+4im)
        @test @fastpow(2^p) == exp2(p)
    end
    @test @fastpow(2^dummy) == "2^dummy"
end

@testset "10^p" begin
    for p in Int64.(0:30)
        @test @fastpow(10^p) === 10^p
    end
    p = -2
    @test_throws DomainError @fastpow(10^p)
    @test @fastpow(10^-2) == 0.01
    for p in (0.3, -0.5, 4//3, 0.3+4im)
        @test @fastpow(10^p) == exp10(p)
    end
    @test @fastpow(10^dummy) == "10^dummy"
end