@doc raw"""

Consider a symplectic pair of tableaus $(a^{1}, b^{1}, c^{1})$ and $(a^{3}, b^{3}, c^{3})$, i.e., satsifying $b^{1}_{i} b^{3}_{j} = b^{1}_{i} a^{3}_{ij} + b^{3}_{j} a^{1}_{ji}$, with an arbitrary number of stages $s$.
Use the same tableaus for $\tilde{a}^{1}$ and $\tilde{a}^{3}$, so that $\tilde{s} = s$, as well as

```math
\begin{aligned}
\begin{array}{c|cc}
& \tfrac{1}{2} b^{1} \\
& \vdots \\
& \tfrac{1}{2} b^{1} \\
\hline
a^{2} & \\
\end{array}
&&
\begin{array}{c|cc}
& \tfrac{1}{2} b^{3} \\
& \vdots \\
& \tfrac{1}{2} b^{3} \\
\hline
a^{4} & \\
\end{array}
&&
\begin{array}{c|cc}
& \tfrac{1}{2} b^{1} \\
c^{1} & \vdots \\
& \tfrac{1}{2} b^{1} \\
\hline
\tilde{a}^{2} & \tfrac{1}{2} (1 + R(\infty)) \, b^{1} \\
\end{array}
&&
\begin{array}{c|cc}
& \tfrac{1}{2} b^{3} \\
c^{3} & \vdots \\
& \tfrac{1}{2} b^{3} \\
\hline
\tilde{a}^{4} & \tfrac{1}{2} (1 + R(\infty)) \, b^{3} \\
\end{array}
\end{aligned}
```

Set $\omega = [0, ..., 0, 1]$ and
```math
\delta_{ij} = \begin{cases}
+1 & j = i , \\
-1 & j = \tilde{s} , \\
 0 & \text{else} ,
\end{cases}
```
so that $\Lambda_{1} = \Lambda_{2} = ... = \Lambda_{\tilde{s}}$.

This methods is constructed to satisfy the constraint on the projective stages, $\phi(\tilde{Q}_{n,i}, \tilde{P}_{n,i}) = 0$ for $i = 1, \, ..., \, \tilde{s}$.
Note, however, that it violates the symplecticity conditions $b^{1}_{i} b^{4}_{j} = b^{1}_{i} a^{4}_{ij} + b^{4}_{j} \tilde{a}^{1}_{ji}$ and $b^{2}_{i} b^{3}_{j} = b^{2}_{i} \tilde{a}^{3}_{ij} + b^{3}_{j} a^{2}_{ji}$.

"""
function TableauVSPARKInternalProjection(name, q::Tableau{T}, p::Tableau{T}, d=[]; R∞=1) where {T}

    @assert q.s == p.s

    s = q.s
    o = min(q.o, p.o)

    α_q = zeros(T, s, s)
    α_p = zeros(T, s, s)

    for i in 1:s
        α_q[i,:] .= q.b ./ 2
        α_p[i,:] .= p.b ./ 2
    end

    β_q = q.b .* (1 + R∞) / 2
    β_p = p.b .* (1 + R∞) / 2

    c_λ = q.c
    d_λ = q.b
    ω_λ = zeros(T, 1, s+1)
    δ_λ = zeros(T, s-1, s)

    ω_λ[1,s+1] = 1

    for i in 1:s-1
        δ_λ[i,i] = +1
        δ_λ[i,s] = -1
    end

    if length(d) == 0
        return TableauVSPARKprimary(name, o,
                            q.a, p.a, α_q, α_p,
                            q.a, p.a, α_q, α_p,
                            q.b, p.b, β_q, β_p,
                            q.c, p.c, c_λ, d_λ,
                            ω_λ, δ_λ)
    else
        @assert length(d) == q.s == p.s

        return TableauVSPARKprimary(name, o,
                            q.a, p.a, α_q, α_p,
                            q.a, p.a, α_q, α_p,
                            q.b, p.b, β_q, β_p,
                            q.c, p.c, c_λ, d_λ,
                            ω_λ, δ_λ, d)
    end

end

"Tableau for Gauss-Legendre method with s stages and symplectic projection."
function TableauVSPARKGLRKpInternal(s)
    glrk = TableauGauss(s)
    TableauVSPARKInternalProjection(Symbol("GLRK($s)pInternal"), glrk, glrk; R∞=(-1)^s)
end

@doc raw"""

Consider a symplectic pair of tableaus $(a^{1}, b^{1}, c^{1})$ and $(a^{3}, b^{3}, c^{3})$, i.e., satsifying $b^{1}_{i} b^{3}_{j} = b^{1}_{i} a^{3}_{ij} + b^{3}_{j} a^{1}_{ji}$, with an arbitrary number of stages $s$, and set

```math
\begin{aligned}
\begin{array}{c|cc}
& \tfrac{1}{2} b^{1} \\
& \vdots \\
& \tfrac{1}{2} b^{1} \\
\hline
a^{2} & \\
\end{array}
&&
\begin{array}{c|cc}
& \tfrac{1}{2} b^{4} \\
& \vdots \\
& \tfrac{1}{2} b^{4} \\
\hline
a^{4} & \\
\end{array}
&&
\begin{array}{c|cc}
& \tfrac{1}{2} b^{1} \\
c^{1} & \vdots \\
& \tfrac{1}{2} b^{1} \\
\hline
\tilde{a}^{2} & \tfrac{1}{2} (1 + R(\infty)) \, b^{1} \\
\end{array}
&&
\begin{array}{c|cc}
& \tfrac{1}{2} b^{3} \\
c^{3} & \vdots \\
& \tfrac{1}{2} b^{3} \\
\hline
\tilde{a}^{4} & \tfrac{1}{2} (1 + R(\infty)) \, b^{3} \\
\end{array}
\end{aligned}
```

Note that by this definition $\tilde{s} = s$.
The coefficients $\tilde{a}^{1}$ and $\tilde{a}^{3}$ are determined by the (modified) symplecticity conditions, specifically $a^{4}_{ij} = b^{3}_{j} ( b^{1}_{i} - \tilde{a}^{1}_{ji}) / b^{1}_{i}$ and $a^{2}_{ij} = b^{1}_{j} ( b^{3}_{i} - \tilde{a}^{3}_{ji} ) / b^{3}_{i}$, where $b^{2}$ has been replaced with $b^{1}$ and $b^{4}$ with $b^{3}$, respectively.
Set $\omega = [0, ..., 0, 1]$ and
```math
\delta_{ij} = \begin{cases}
+1 & j = i , \\
-1 & j = \tilde{s} , \\
 0 & \text{else} ,
\end{cases}
```
so that $\Lambda_{1} = \Lambda_{2} = ... = \Lambda_{\tilde{s}}$.

Note that this method satisfies the symplecticity conditions $b^{1}_{i} b^{4}_{j} = b^{1}_{i} a^{4}_{ij} + b^{4}_{j} \tilde{a}^{1}_{ji}$ and $b^{2}_{i} b^{3}_{j} = b^{2}_{i} \tilde{a}^{3}_{ij} + b^{3}_{j} a^{2}_{ji}$ only if $R(\infty) = 1$ due to the definitions of $b^{2}$ and $b^{4}$.
Moreover, it does usually not satisfy the constraint on the projective stages, $\phi(\tilde{Q}_{n,i}, \tilde{P}_{n,i}) = 0$ for $i = 1, \, ..., \, \tilde{s}$, exactly, but only approximately with bounded error, thus implying a residual in the symplecticity equation even if $R(\infty) = 1$.

"""
function TableauVSPARKModifiedInternalProjection(name, q, p, d=[]; R∞=1)
    T = eltype(q.a)

    @assert q.s == p.s

    s = q.s
    o = min(q.o, p.o)

    α_q = zeros(T, s, s)
    α_p = zeros(T, s, s)

    for i in 1:s
        α_q[i,:] .= q.b ./ 2
        α_p[i,:] .= p.b ./ 2
    end

    β_q = q.b .* (1 + R∞) / 2
    β_p = p.b .* (1 + R∞) / 2

#    a_q̃, a_p̃ = get_ã_vspark_primary(α_q, β_q, q.b, α_p, β_p, p.b)
    a_q̃, a_p̃ = get_ã_vspark_primary(α_q, q.b, q.b, α_p, p.b, p.b)

    c_λ = q.c
    d_λ = q.b
    ω_λ = zeros(T, 1, s+1)
    δ_λ = zeros(T, s-1, s)

    ω_λ[1,s+1] = 1

    for i in 1:s-1
        δ_λ[i,i] = +1
        δ_λ[i,s] = -1
    end

    if length(d) == 0
        return TableauVSPARKprimary(name, o,
                            q.a, p.a, α_q, α_p,
                            a_q̃, a_p̃, α_q, α_p,
                            q.b, p.b, β_q, β_p,
                            q.c, p.c, c_λ, d_λ,
                            ω_λ, δ_λ)
    else
        @assert length(d) == q.s == p.s

        return TableauVSPARKprimary(name, o,
                            q.a, p.a, α_q, α_p,
                            a_q̃, a_p̃, α_q, α_p,
                            q.b, p.b, β_q, β_p,
                            q.c, p.c, c_λ, d_λ,
                            ω_λ, δ_λ, d)
    end

end

"Tableau for Gauss-Legendre method with s stages and symplectic projection."
function TableauVSPARKGLRKpModifiedInternal(s)
    glrk = TableauGauss(s)
    TableauVSPARKModifiedInternalProjection(Symbol("GLRK($s)pModifiedInternal"), glrk, glrk; R∞=(-1)^s)
end

@doc raw"""

Consider a symplectic pair of tableaus $(a^{1}, b^{1}, c^{1})$ and $(a^{3}, b^{3}, c^{3})$, i.e., satsifying $b^{1}_{i} b^{3}_{j} = b^{1}_{i} a^{3}_{ij} + b^{3}_{j} a^{1}_{ji}$, with an arbitrary number of stages $s$.
For the projection, choose the tableau with $\tilde{s} = 1$ and $\rho = 0$, such that $\tilde{Q}_{n,1} = \tfrac{1}{2} ( q_{n} + q_{n+1})$, $\tilde{P}_{n,1} = \tfrac{1}{2} ( p_{n} + p_{n+1})$, i.e.,

```math
\begin{aligned}
\begin{array}{c|cc}
& \tfrac{1}{2} \\
& \vdots \\
& \tfrac{1}{2} \\
\hline
a^{2} & \\
\end{array}
&&
\begin{array}{c|cc}
& \tfrac{1}{2} \\
& \vdots \\
& \tfrac{1}{2} \\
\hline
a^{4} & \\
\end{array}
&&
\begin{array}{c|cc}
\tfrac{1}{2} & \tfrac{1}{2} \\
\hline
\tilde{a}^{2} & \tfrac{1}{2} (1 + R(\infty))\\
\end{array}
&&
\begin{array}{c|cc}
\tfrac{1}{2} & \tfrac{1}{2} \\
\hline
\tilde{a}^{4} & \tfrac{1}{2} (1 + R(\infty))\\
\end{array}
\end{aligned}
```

The coefficients $\tilde{a}^{1}$ and $\tilde{a}^{3}$ are determined by the symplecticity conditions, specifically $a^{4}_{ij} = b^{4}_{j} ( b^{1}_{i} - \tilde{a}^{1}_{ji}) / b^{1}_{i}$ and $a^{2}_{ij} = b^{2}_{j} ( b^{3}_{i} - \tilde{a}^{3}_{ji} ) / b^{3}_{i}$, and $\omega = [0, 1]$.

"""
function TableauVSPARKMidpointProjection(name, q::Tableau{T}, p::Tableau{T}, d=[]; R∞=1) where {T}
    @assert q.s == p.s
    s = q.s

    g = TableauGauss(1)
    α̃ = g.a
    β = g.b
    γ = g.c

    α = 0.5 * ones(T, s, 1)

    q_ã = zeros(T, 1, s)
    for i in 1:s
        q_ã[1,i] = q.b[i] / β[1] * ( β[1] - α[i,1] )
    end

    p_ã = zeros(T, 1, s)
    for i in 1:s
        p_ã[1,i] = p.b[i] / β[1] * ( β[1] - α[i,1] )
    end

    β .= (1 + R∞) / 2
    dλ = ones(T, 1)
    ω  = reshape(T[0  1], (1,2))
    δ  = zeros(T, 0, 1)

    if length(d) == 0
        return TableauVSPARKprimary(name, min(q.o, p.o),
                            q.a, p.a, α, α,
                            q_ã, p_ã, α̃, α̃,
                            q.b, p.b, β, β,
                            q.c, p.c, γ, dλ,
                            ω, δ)
    else
        @assert length(d) == q.s == p.s

        return TableauVSPARKprimary(name, min(q.o, p.o),
                            q.a, p.a, α, α,
                            q_ã, p_ã, α̃, α̃,
                            q.b, p.b, β, β,
                            q.c, p.c, γ, dλ,
                            ω, δ, d)
    end
end

"Tableau for Gauss-Lobatto IIIA-IIIB method with s stages and midpoint projection."
function TableauVSPARKLobattoIIIAIIIBpMidpoint(s)
    TableauVSPARKMidpointProjection(Symbol("LobattoIIIAIIIB($s)pMidpoint"), TableauLobattoIIIA(s), TableauLobattoIIIB(s), get_lobatto_nullvector(s); R∞=-1^(s+1))
end

"Tableau for Gauss-Lobatto IIIB-IIIA method with s stages and midpoint projection."
function TableauVSPARKLobattoIIIBIIIApMidpoint(s)
    TableauVSPARKMidpointProjection(Symbol("LobattoIIIBIIIA($s)pMidpoint"), TableauLobattoIIIB(s), TableauLobattoIIIA(s), get_lobatto_nullvector(s); R∞=-1^(s+1))
end

"Tableau for Gauss-Legendre method with s stages and midpoint projection."
function TableauVSPARKGLRKpMidpoint(s)
    glrk = TableauGauss(s)
    TableauVSPARKMidpointProjection(Symbol("GLRK($s)pMidpoint"), glrk, glrk; R∞=(-1)^s)
end

@doc raw"""

Consider a symplectic pair of tableaus $(a^{1}, b^{1}, c^{1})$ and $(a^{3}, b^{3}, c^{3})$, i.e., satsifying $b^{1}_{i} b^{3}_{j} = b^{1}_{i} a^{3}_{ij} + b^{3}_{j} a^{1}_{ji}$, with an arbitrary number of stages $s$.
For the projection, choose the tableau with $\tilde{s} = 1$ and $\rho = 0$, such that $\tilde{Q}_{n,1} = \tfrac{1}{2} ( q_{n} + q_{n+1})$, $\tilde{P}_{n,1} = \tfrac{1}{2} ( p_{n} + p_{n+1})$, i.e.,

```math
\begin{aligned}
\begin{array}{c|cc}
\tfrac{1}{2} & \tfrac{1}{2} b^{1} \\
\hline
\tilde{a}^{1} & \\
\end{array}
&&
\begin{array}{c|cc}
\tfrac{1}{2} & \tfrac{1}{2} \\
\hline
\tilde{a}^{2} & \tfrac{1}{2} ( 1 + R (\infty) ) \\
\end{array}
&&
\begin{array}{c|cc}
\tfrac{1}{2} & \tfrac{1}{2} b^{3} \\
\hline
\tilde{a}^{3} & \\
\end{array}
&&
\begin{array}{c|cc}
\tfrac{1}{2} & \tfrac{1}{2} \\
\hline
\tilde{a}^{4} & \tfrac{1}{2} ( 1 + R (\infty) ) \\
\end{array}
\end{aligned}
```

The coefficients $a^{2}$ and $a^{4}$ are determined by the symplecticity conditions, specifically $a^{4}_{ij} = b^{4}_{j} ( b^{1}_{i} - \tilde{a}^{1}_{ji}) / b^{1}_{i}$ and $a^{2}_{ij} = b^{2}_{j} ( b^{3}_{i} - \tilde{a}^{3}_{ji} ) / b^{3}_{i}$, and $\omega = [0, 1]$.

"""
function TableauVSPARKModifiedMidpointProjection(name, q::Tableau{T}, p::Tableau{T}, d=[]; R∞=1) where {T}
    @assert q.s == p.s
    s = q.s

    g = TableauGauss(1)
    α̃ = g.a
    β = g.b
    γ = g.c

    q_ã = reshape(q.b ./ 2, 1, s)
    p_ã = reshape(p.b ./ 2, 1, s)

    α_q = zeros(T, s, 1)
    α_p = zeros(T, s, 1)

    for i in 1:s
        α_q[i,1] = β[1] / p.b[i] * ( p.b[i] - p_ã[1,i] )
        α_p[i,1] = β[1] / q.b[i] * ( q.b[i] - q_ã[1,i] )
    end

    β .= (1 + R∞) / 2
    dλ = ones(T, 1)
    ω  = reshape(T[0  1], (1,2))
    δ  = zeros(T, 0, 1)

    if length(d) == 0
        return TableauVSPARKprimary(name, min(q.o, p.o),
                            q.a, p.a, α_q, α_p,
                            q_ã, p_ã, α̃, α̃,
                            q.b, p.b, β, β,
                            q.c, p.c, γ, dλ,
                            ω, δ)
    else
        @assert length(d) == q.s == p.s

        return TableauVSPARKprimary(name, min(q.o, p.o),
                            q.a, p.a, α_q, α_p,
                            q_ã, p_ã, α̃, α̃,
                            q.b, p.b, β, β,
                            q.c, p.c, γ, dλ,
                            ω, δ, d)
    end
end

"Tableau for Gauss-Lobatto IIIA-IIIB method with s stages and midpoint projection."
function TableauVSPARKLobattoIIIAIIIBpModifiedMidpoint(s)
    TableauVSPARKModifiedMidpointProjection(Symbol("LobattoIIIAIIIB($s)pModifiedMidpoint"), TableauLobattoIIIA(s), TableauLobattoIIIB(s), get_lobatto_nullvector(s); R∞=-1^(s+1))
end

"Tableau for Gauss-Lobatto IIIB-IIIA method with s stages and midpoint projection."
function TableauVSPARKLobattoIIIBIIIApModifiedMidpoint(s)
    TableauVSPARKModifiedMidpointProjection(Symbol("LobattoIIIBIIIA($s)pModifiedMidpoint"), TableauLobattoIIIB(s), TableauLobattoIIIA(s), get_lobatto_nullvector(s); R∞=-1^(s+1))
end

"Tableau for Gauss-Legendre method with s stages and midpoint projection."
function TableauVSPARKGLRKpModifiedMidpoint(s)
    glrk = TableauGauss(s)
    TableauVSPARKModifiedMidpointProjection(Symbol("GLRK($s)pModifiedMidpoint"), glrk, glrk; R∞=(-1)^s)
end

@doc raw"""

Consider a symplectic pair of tableaus $(a^{1}, b^{1}, c^{1})$ and $(a^{3}, b^{3}, c^{3})$, i.e., satsifying $b^{1}_{i} b^{3}_{j} = b^{1}_{i} a^{3}_{ij} + b^{3}_{j} a^{1}_{ji}$, with an arbitrary number of stages $s$.
For the projection, choose the tableau with $\tilde{s} = 2$ and $\rho = 1$, such that $\tilde{Q}_{n,1} = q_{n}$, $\tilde{Q}_{n,2} = q_{n+1}$, $\tilde{P}_{n,1} = p_{n}$, $\tilde{P}_{n,2} = p_{n+1}$, i.e.,

```math
\begin{aligned}
\begin{array}{c|cc}
0 & 0 \\
1 & b^{1} \\
\hline
\tilde{a}^{1} & \\
\end{array}
&&
\begin{array}{c|cc}
& 0 & 0 \\
& \tfrac{1}{2} & \tfrac{1}{2} \\
\hline
\tilde{a}^{2} & \tfrac{1}{2} & \tfrac{1}{2} \\
\end{array}
&&
\begin{array}{c|cc}
0 & 0 \\
1 & b^{3} \\
\hline
\tilde{a}^{3} & \\
\end{array}
&&
\begin{array}{c|cc}
& 0 & 0 \\
& \tfrac{1}{2} & \tfrac{1}{2} \\
\hline
\tilde{a}^{4} & \tfrac{1}{2} & \tfrac{1}{2} \\
\end{array}
\end{aligned}
```

The coefficients $a^{2}$ and $a^{4}$ are determined by the symplecticity conditions, specifically $a^{4}_{ij} = b^{4}_{j} ( b^{1}_{i} - \tilde{a}^{1}_{ji}) / b^{1}_{i}$ and $a^{2}_{ij} = b^{2}_{j} ( b^{3}_{i} - \tilde{a}^{3}_{ji} ) / b^{3}_{i}$.
Further choose $\omega = [1, 1, 0]$ and $\delta = [-1, R_{\infty}]$, so that $\tilde{\Lambda}_{n,1} = R_{\infty} \tilde{\Lambda}_{n,2}$ and
```math
\tilde{P}_{n,1} - \vartheta (\tilde{Q}_{n,1}) + R_{\infty} ( \tilde{P}_{n,2} - \vartheta (\tilde{Q}_{n,2}) ) = 0 .
```
Due to the particular choice of projective stages, this is equivalent to
```math
p_{n} - \vartheta (q_{n}) + R_{\infty} ( p_{n+1} - \vartheta (q_{n+1}) ) = 0 ,
```
so that the constraint $\phi(q_{n+1}, p_{n+1}) = 0$ is satisfied if $\phi(q_{n}, p_{n}) = 0$.
Note that the choice of $\tilde{a}^{2}$ and $\tilde{a}^{4}$ violates the symplecticity condition $b^{2}_{i} b^{4}_{j} = b^{2}_{i} \tilde{a}^{4}_{ij} + b^{4}_{j} \tilde{a}^{2}_{ji}$.

"""
function TableauVSPARKSymmetricProjection(name, q::Tableau{T}, p::Tableau{T}, d=[]; R∞=1) where {T}

    @assert q.s == p.s

    o = min(q.o, p.o)

    a_q = q.a
    a_p = p.a

    α_q = zeros(T, q.s, 2)
    α_q[:,1] .= 0.5

    α_p = zeros(T, p.s, 2)
    α_p[:,1] .= 0.5

    a_q̃ = vcat(zero(q.b)', q.b')
    a_p̃ = vcat(zero(p.b)', p.b')

    α_q̃ = [[0.0  0.0]
           [0.5  0.5]]
    α_p̃ = [[0.0  0.0]
           [0.5  0.5]]

    b_q = q.b
    b_p = p.b
    β_q = [0.5, 0.5]
    β_p = [0.5, 0.5]

    c_q = q.c
    c_p = p.c
    c_λ = [ 0.0, 1.0]
    d_λ = [ 0.5, 0.5*R∞]

    ω_λ = reshape(T[1  1  0], (1,3))
    δ_λ = reshape(T[-1   R∞], (1,2))

    if length(d) == 0
        return TableauVSPARKprimary(name, o,
                            a_q, a_p, α_q, α_p,
                            a_q̃, a_p̃, α_q̃, α_p̃,
                            b_q, b_p, β_q, β_p,
                            c_q, c_p, c_λ, d_λ,
                            ω_λ, δ_λ)
    else
        @assert length(d) == q.s == p.s

        return TableauVSPARKprimary(name, o,
                            a_q, a_p, α_q, α_p,
                            a_q̃, a_p̃, α_q̃, α_p̃,
                            b_q, b_p, β_q, β_p,
                            c_q, c_p, c_λ, d_λ,
                            ω_λ, δ_λ, d)
    end
end

"Tableau for Gauss-Lobatto IIIA-IIIB method with s stages and symmetric projection."
function TableauVSPARKLobattoIIIAIIIBpSymmetric(s)
    TableauVSPARKSymmetricProjection(Symbol("LobattoIIIAIIIB($s)pSymmetric"), TableauLobattoIIIA(s), TableauLobattoIIIB(s), get_lobatto_nullvector(s); R∞=-1^(s+1))
end

"Tableau for Gauss-Lobatto IIIB-IIIA method with s stages and symmetric projection."
function TableauVSPARKLobattoIIIBIIIApSymmetric(s)
    TableauVSPARKSymmetricProjection(Symbol("LobattoIIIBIIIA($s)pSymmetric"), TableauLobattoIIIB(s), TableauLobattoIIIA(s), get_lobatto_nullvector(s); R∞=-1^(s+1))
end

"Tableau for Gauss-Legendre method with s stages and symplectic projection."
function TableauVSPARKGLRKpSymmetric(s)
    glrk = TableauGauss(s)
    TableauVSPARKSymmetricProjection(Symbol("GLRK($s)pSymmetric"), glrk, glrk; R∞=(-1)^s)
end

@doc raw"""

Consider a symplectic pair of tableaus $(a^{1}, b^{1}, c^{1})$ and $(a^{3}, b^{3}, c^{3})$, i.e., satsifying $b^{1}_{i} b^{3}_{j} = b^{1}_{i} a^{3}_{ij} + b^{3}_{j} a^{1}_{ji}$, with an arbitrary number of stages $s$.
For the projection, choose the Lobatto-IIIA and IIIB tableaus with $\tilde{s} = 2$ stages for $(\tilde{a}^{4}, b^{4})$ and $(\tilde{a}^{2}, b^{2})$, respectively, and choose $\tilde{a}^{1}$ and $\tilde{a}^{3}$ such that the projective stages correspond to the initial condition and the solution, i.e.,

```math
\begin{aligned}
\begin{array}{c|cc}
0 & 0 \\
1 & b^{1} \\
\hline
\tilde{a}^{1} & \\
\end{array}
&&
\begin{array}{c|cc}
0 & \tfrac{1}{2} & 0 \\
1 & \tfrac{1}{2} & 0 \\
\hline
\tilde{a}^{2} & \\
\end{array}
&&
\begin{array}{c|cc}
0 & 0 \\
1 & b^{3} \\
\hline
\tilde{a}^{3} & \\
\end{array}
&&
\begin{array}{c|cc}
0 & 0 & 0 \\
1 & \tfrac{1}{2} & \tfrac{1}{2} \\
\hline
\tilde{a}^{4} & \\
\end{array}
\end{aligned}
```

and compute $a^{2}$ and $a^{4}$ by the symplecticity conditions, that is $a^{2}_{ij} = b^{2}_{j} ( b^{3}_{i} - \tilde{a}^{3}_{ji} ) / b^{3}_{i}$ and $a^{4}_{ij} = b^{4}_{j} ( b^{1}_{i} - \tilde{a}^{1}_{ji}) / b^{1}_{i}$.
Finally choose $\omega = [0, 0, 1]$ and $\delta = [-1, R_{\infty}]$, implying that $\rho = 1$.
By construction, this method satisfies all symplecticity conditions, but the constraint on the projection stages, $\phi(\tilde{Q}_{n,i}, \tilde{P}_{n,i}) = 0$ for $i = 1, \, ..., \, \tilde{s}$, is not satisfied exactly, but only approximately, although with bounded error.

"""
function TableauVSPARKLobattoIIIAIIIBProjection(name, q::Tableau{T}, p::Tableau{T}, d=[]; R∞=1) where {T}

    @assert q.s == p.s

    o = min(q.o, p.o)

    loba = TableauLobattoIIIA(2)
    lobb = TableauLobattoIIIB(2)

    a_q = q.a
    a_p = p.a
    b_q = q.b
    b_p = p.b

    a_q̃ = vcat(zero(q.b)', q.b')
    a_p̃ = vcat(zero(p.b)', p.b')

    α_q̃ = loba.a
    α_p̃ = lobb.a

    β_q = loba.b
    β_p = lobb.b

    α_q, α_p = get_α_vspark_primary(a_q̃, b_q, β_q, a_p̃, b_p, β_p)

    c_q = q.c
    c_p = p.c
    c_λ = [ 0.0, 1.0]
    d_λ = [ 0.5, 0.5]

    ω_λ = reshape(T[0  0  1], (1,3))
    δ_λ = reshape(T[-1   R∞], (1,2))

    if length(d) == 0
        return TableauVSPARKprimary(name, o,
                            a_q, a_p, α_q, α_p,
                            a_q̃, a_p̃, α_q̃, α_p̃,
                            b_q, b_p, β_q, β_p,
                            c_q, c_p, c_λ, d_λ,
                            ω_λ, δ_λ)
    else
        @assert length(d) == q.s == p.s

        return TableauVSPARKprimary(name, o,
                            a_q, a_p, α_q, α_p,
                            a_q̃, a_p̃, α_q̃, α_p̃,
                            b_q, b_p, β_q, β_p,
                            c_q, c_p, c_λ, d_λ,
                            ω_λ, δ_λ, d)
    end

end

"Tableau for Gauss-Lobatto IIIA-IIIB method with s stages and Lobatto-IIIA-IIIB projection."
function TableauVSPARKLobattoIIIAIIIBpLobattoIIIAIIIB(s)
    TableauVSPARKLobattoIIIAIIIBProjection(Symbol("LobattoIIIAIIIB($s)pLobattoIIIAIIIB(2)"), TableauLobattoIIIA(s), TableauLobattoIIIB(s), get_lobatto_nullvector(s); R∞=-1^(s+1))
end

"Tableau for Gauss-Lobatto IIIB-IIIA method with s stages and Lobatto-IIIA-IIIB projection."
function TableauVSPARKLobattoIIIBIIIApLobattoIIIAIIIB(s)
    TableauVSPARKLobattoIIIAIIIBProjection(Symbol("LobattoIIIBIIIA($s)pLobattoIIIAIIIB(2)"), TableauLobattoIIIB(s), TableauLobattoIIIA(s), get_lobatto_nullvector(s); R∞=-1^(s+1))
end

"Tableau for Gauss-Legendre method with s stages and Lobatto-IIIA-IIIB projection."
function TableauVSPARKGLRKpLobattoIIIAIIIB(s)
    glrk = TableauGauss(s)
    TableauVSPARKLobattoIIIAIIIBProjection(Symbol("GLRK($s)pLobattoIIIAIIIB(2)"), glrk, glrk; R∞=(-1)^s)
end

@doc raw"""

This methods is the same as `TableauVSPARKLobattoIIIAIIIBProjection`, except for using Lobatto-IIIA and IIIB tableaus with $\tilde{s} = 2$ stages for $(\tilde{a}^{2}, b^{2})$, and $(\tilde{a}^{4}, b^{4})$ respectively, instead of the other way around.

"""
function TableauVSPARKLobattoIIIBIIIAProjection(name, q::Tableau{T}, p::Tableau{T}, d=[]; R∞=1) where {T}

    @assert q.s == p.s

    o = min(q.o, p.o)

    loba = TableauLobattoIIIA(2)
    lobb = TableauLobattoIIIB(2)

    a_q = q.a
    a_p = p.a
    b_q = q.b
    b_p = p.b

    a_q̃ = vcat(zero(q.b)', q.b')
    a_p̃ = vcat(zero(p.b)', p.b')

    α_q̃ = lobb.a
    α_p̃ = loba.a

    β_q = lobb.b
    β_p = loba.b

    α_q, α_p = get_α_vspark_primary(a_q̃, b_q, β_q, a_p̃, b_p, β_p)

    c_q = q.c
    c_p = p.c
    c_λ = [ 0.0, 1.0]
    d_λ = [ 0.5, 0.5]

    ω_λ = reshape(T[0  0  1], (1,3))
    δ_λ = reshape(T[-1   R∞], (1,2))

    if length(d) == 0
        return TableauVSPARKprimary(name, o,
                            a_q, a_p, α_q, α_p,
                            a_q̃, a_p̃, α_q̃, α_p̃,
                            b_q, b_p, β_q, β_p,
                            c_q, c_p, c_λ, d_λ,
                            ω_λ, δ_λ)
    else
        @assert length(d) == q.s == p.s

        return TableauVSPARKprimary(name, o,
                            a_q, a_p, α_q, α_p,
                            a_q̃, a_p̃, α_q̃, α_p̃,
                            b_q, b_p, β_q, β_p,
                            c_q, c_p, c_λ, d_λ,
                            ω_λ, δ_λ, d)
    end

end

"Tableau for Gauss-Lobatto IIIA-IIIB method with s stages and Lobatto-IIIB-IIIA projection."
function TableauVSPARKLobattoIIIAIIIBpLobattoIIIBIIIA(s)
    TableauVSPARKLobattoIIIBIIIAProjection(Symbol("LobattoIIIAIIIB($s)pLobattoIIIBIIIA(2)"), TableauLobattoIIIA(s), TableauLobattoIIIB(s), get_lobatto_nullvector(s); R∞=-1^(s+1))
end

"Tableau for Gauss-Lobatto IIIB-IIIA method with s stages and Lobatto-IIIB-IIIA projection."
function TableauVSPARKLobattoIIIBIIIApLobattoIIIBIIIA(s)
    TableauVSPARKLobattoIIIBIIIAProjection(Symbol("LobattoIIIBIIIA($s)pLobattoIIIBIIIA(2)"), TableauLobattoIIIB(s), TableauLobattoIIIA(s), get_lobatto_nullvector(s); R∞=-1^(s+1))
end

"Tableau for Gauss-Legendre method with s stages and Lobatto-IIIB-IIIA projection."
function TableauVSPARKGLRKpLobattoIIIBIIIA(s)
    glrk = TableauGauss(s)
    TableauVSPARKLobattoIIIBIIIAProjection(Symbol("GLRK($s)pLobattoIIIBIIIA(2)"), glrk, glrk; R∞=(-1)^s)
end

@doc raw"""

Consider a symplectic pair of tableaus $(a^{1}, b^{1}, c^{1})$ and $(a^{3}, b^{3}, c^{3})$, i.e., satsifying $b^{1}_{i} b^{3}_{j} = b^{1}_{i} a^{3}_{ij} + b^{3}_{j} a^{1}_{ji}$, with an arbitrary number of stages $s$.
For the projection, choose the Lobatto-IIIA and IIIB tableaus with $\tilde{s} = 2$ stages for $(\tilde{a}^{4}, b^{4})$ and $(\tilde{a}^{2}, b^{2})$, respectively.

The coefficients $\tilde{a}^{1}$ and $\tilde{a}^{3}$ are determined by the relations
```math
\begin{aligned}
\sum \limits_{j=1}^{s} \tilde{a}^{1}_{ij} (c_{j}^{1})^{k-1} &= \frac{(c_{i}^{2})^k}{k} , \qquad &
\sum \limits_{j=1}^{s} \tilde{a}^{3}_{ij} (c_{j}^{3})^{k-1} &= \frac{(c_{i}^{4})^k}{k} , \qquad &
i &= 1 , \, ... , \, \tilde{s} , \qquad &
k &= 1 , \, ... , \, s .
\end{aligned}
```

The coefficients $a^{2}$ and $a^{4}$ by the symplecticity conditions, that is $a^{2}_{ij} = b^{2}_{j} ( b^{3}_{i} - \tilde{a}^{3}_{ji} ) / b^{3}_{i}$ and $a^{4}_{ij} = b^{4}_{j} ( b^{1}_{i} - \tilde{a}^{1}_{ji}) / b^{1}_{i}$.
Finally choose $\omega = [0, 0, 1]$ and $\delta = [-1, R_{\infty}]$, implying that $\rho = 1$.
By construction, this method satisfies all symplecticity conditions, but the constraint on the projection stages, $\phi(\tilde{Q}_{n,i}, \tilde{P}_{n,i}) = 0$ for $i = 1, \, ..., \, \tilde{s}$, is not satisfied exactly, but only approximately, although with bounded error.

"""
function TableauVSPARKModifiedLobattoIIIAIIIBProjection(name, q::Tableau{T}, p::Tableau{T}, d=[]; R∞=1) where {T}

    @assert q.s == p.s

    s = q.s
    o = min(q.o, p.o)

    loba = TableauLobattoIIIA(2)
    lobb = TableauLobattoIIIB(2)

    a_q = q.a
    a_p = p.a
    b_q = q.b
    b_p = p.b

    β_q = loba.b
    β_p = lobb.b

    a_q̃ = get_lobatto_glrk_coefficients(s, 2, T).a
    a_p̃ = get_lobatto_glrk_coefficients(s, 2, T).a

    α_q̃ = loba.a
    α_p̃ = lobb.a

    α_q, α_p = get_α_vspark_primary(a_q̃, b_q, β_q, a_p̃, b_p, β_p)

    c_q = q.c
    c_p = p.c
    c_λ = loba.c
    d_λ = loba.b

    ω_λ = reshape(T[0  0  1], (1,3))
    δ_λ = reshape(T[-1   R∞], (1,2))

    if length(d) == 0
        return TableauVSPARKprimary(name, o,
                            a_q, a_p, α_q, α_p,
                            a_q̃, a_p̃, α_q̃, α_p̃,
                            b_q, b_p, β_q, β_p,
                            c_q, c_p, c_λ, d_λ,
                            ω_λ, δ_λ)
    else
        @assert length(d) == q.s == p.s

        return TableauVSPARKprimary(name, o,
                            a_q, a_p, α_q, α_p,
                            a_q̃, a_p̃, α_q̃, α_p̃,
                            b_q, b_p, β_q, β_p,
                            c_q, c_p, c_λ, d_λ,
                            ω_λ, δ_λ, d)
    end

end

"Tableau for Gauss-Lobatto IIIA-IIIB method with s stages and Lobatto-IIIA-IIIB projection."
function TableauVSPARKLobattoIIIAIIIBpModifiedLobattoIIIAIIIB(s)
    TableauVSPARKModifiedLobattoIIIAIIIBProjection(Symbol("LobattoIIIAIIIB($s)pModifiedLobattoIIIAIIIB(2)"), TableauLobattoIIIA(s), TableauLobattoIIIB(s), get_lobatto_nullvector(s); R∞=-1^(s+1))
end

"Tableau for Gauss-Lobatto IIIB-IIIA method with s stages and Lobatto-IIIA-IIIB projection."
function TableauVSPARKLobattoIIIBIIIApModifiedLobattoIIIAIIIB(s)
    TableauVSPARKModifiedLobattoIIIAIIIBProjection(Symbol("LobattoIIIBIIIA($s)pModifiedLobattoIIIAIIIB(2)"), TableauLobattoIIIB(s), TableauLobattoIIIA(s), get_lobatto_nullvector(s); R∞=-1^(s+1))
end

"Tableau for Gauss-Legendre method with s stages and symplectic projection."
function TableauVSPARKGLRKpModifiedLobattoIIIAIIIB(s)
    glrk = TableauGauss(s)
    TableauVSPARKModifiedLobattoIIIAIIIBProjection(Symbol("GLRK($s)pModifiedLobattoIIIAIIIB(2)"), glrk, glrk; R∞=(-1)^s)
end

@doc raw"""

This methods is the same as `TableauVSPARKModifiedLobattoIIIAIIIBProjection`, except for using Lobatto-IIIA and IIIB tableaus with $\tilde{s} = 2$ stages for $(\tilde{a}^{2}, b^{2})$, and $(\tilde{a}^{4}, b^{4})$ respectively, instead of the other way around.

"""
function TableauVSPARKModifiedLobattoIIIBIIIAProjection(name, q::Tableau{T}, p::Tableau{T}, d=[]; R∞=1) where {T}

    @assert q.s == p.s

    s = q.s
    o = min(q.o, p.o)

    loba = TableauLobattoIIIA(2)
    lobb = TableauLobattoIIIB(2)

    a_q = q.a
    a_p = p.a
    b_q = q.b
    b_p = p.b

    β_q = loba.b
    β_p = lobb.b

    a_q̃ = get_lobatto_glrk_coefficients(s, 2, T).a
    a_p̃ = get_lobatto_glrk_coefficients(s, 2, T).a

    α_q̃ = lobb.a
    α_p̃ = loba.a

    α_q, α_p = get_α_vspark_primary(a_q̃, b_q, β_q, a_p̃, b_p, β_p)

    c_q = q.c
    c_p = p.c
    c_λ = loba.c
    d_λ = loba.b

    ω_λ = reshape(T[0  0  1], (1,3))
    δ_λ = reshape(T[-1   R∞], (1,2))

    if length(d) == 0
        return TableauVSPARKprimary(name, o,
                            a_q, a_p, α_q, α_p,
                            a_q̃, a_p̃, α_q̃, α_p̃,
                            b_q, b_p, β_q, β_p,
                            c_q, c_p, c_λ, d_λ,
                            ω_λ, δ_λ)
    else
        @assert length(d) == q.s == p.s

        return TableauVSPARKprimary(name, o,
                            a_q, a_p, α_q, α_p,
                            a_q̃, a_p̃, α_q̃, α_p̃,
                            b_q, b_p, β_q, β_p,
                            c_q, c_p, c_λ, d_λ,
                            ω_λ, δ_λ, d)
    end

end

"Tableau for Gauss-Lobatto IIIA-IIIB method with s stages and Lobatto-IIIB-IIIA projection."
function TableauVSPARKLobattoIIIAIIIBpModifiedLobattoIIIBIIIA(s)
    TableauVSPARKModifiedLobattoIIIBIIIAProjection(Symbol("LobattoIIIAIIIB($s)pModifiedLobattoIIIBIIIA(2)"), TableauLobattoIIIA(s), TableauLobattoIIIB(s), get_lobatto_nullvector(s); R∞=-1^(s+1))
end

"Tableau for Gauss-Lobatto IIIB-IIIA method with s stages and Lobatto-IIIB-IIIA projection."
function TableauVSPARKLobattoIIIBIIIApModifiedLobattoIIIBIIIA(s)
    TableauVSPARKModifiedLobattoIIIBIIIAProjection(Symbol("LobattoIIIBIIIA($s)pModifiedLobattoIIIBIIIA(2)"), TableauLobattoIIIB(s), TableauLobattoIIIA(s), get_lobatto_nullvector(s); R∞=-1^(s+1))
end

"Tableau for Gauss-Legendre method with s stages and symplectic projection."
function TableauVSPARKGLRKpModifiedLobattoIIIBIIIA(s)
    glrk = TableauGauss(s)
    TableauVSPARKModifiedLobattoIIIBIIIAProjection(Symbol("GLRK($s)pModifiedLobattoIIIBIIIA(2)"), glrk, glrk; R∞=(-1)^s)
end

function TableauVSPARKSymplecticProjection(name, q::Tableau{T}, p::Tableau{T}, d=[]; R∞=+1) where {T}

    la = TableauLobattoIIIA(2)
    lb = TableauLobattoIIIB(2)

    @assert q.s == p.s
    @assert q.b == p.b
    @assert q.c == p.c

    @assert la.s == lb.s
    @assert la.b == lb.b

    s = q.s
    σ = la.s
    ρ = 0

    a_q = q.a
    a_p = p.a
    b_q = q.b
    b_p = p.b

    β_q = [0.5, R∞*0.5]
    β_p = [0.5, R∞*0.5]

    α_q = zeros(T, s, 2)
    α_q[:,1] .= 0.5

    α_p = zeros(T, s, 2)
    α_p[:,1] .= 0.5

    a_q̃ = zeros(T, σ, s)
    a_p̃ = zeros(T, σ, s)
    for i in 1:σ
        for j in 1:s
            a_q̃[i,j] = b_q[j] / β_p[i] * (β_p[i] - α_p[j,i])
            a_p̃[i,j] = b_p[j] / β_q[i] * (β_q[i] - α_q[j,i])
        end
    end

    α_q̃ = la.a
    α_p̃ = lb.a

    c_q = q.c
    c_p = p.c
    c_λ = la.c
    d_λ = [0.5, 0.5]

    ω_λ = [0.5 0.5 0.0
           0.0 0.0 1.0]

    δ_λ = zeros(T, ρ, σ)

    if length(d) == 0
        return TableauVSPARKprimary(name, min(q.o, p.o),
                            a_q, a_p, α_q, α_p,
                            a_q̃, a_p̃, α_q̃, α_p̃,
                            b_q, b_p, β_q, β_p,
                            c_q, c_p, c_λ, d_λ,
                            ω_λ, δ_λ)
    else
        @assert length(d) == s

        return TableauVSPARKprimary(name, min(q.o, p.o),
                            a_q, a_p, α_q, α_p,
                            a_q̃, a_p̃, α_q̃, α_p̃,
                            b_q, b_p, β_q, β_p,
                            c_q, c_p, c_λ, d_λ,
                            ω_λ, δ_λ, d)
    end

end

function TableauVSPARKGLRKpSymplectic(s)
    glrk = TableauGauss(s)
    TableauVSPARKSymplecticProjection(Symbol("GLRK($s)pSymplectic"), glrk, glrk; R∞=(-1)^s)
end

function TableauVSPARKLobABCCD(s=2, T=Float64)
    lob  = TableauLobattoIIIC̄(s)
    loba = TableauLobattoIIIA(s)
    lobb = TableauLobattoIIIB(s)
    lobc = TableauLobattoIIIC(s)
    lobd = TableauLobattoIIID(s)

    a_q = lobd.a
    a_p = lobd.a

    α_q = lobb.a
    α_p = lobb.a

    a_q̃ = loba.a
    a_p̃ = loba.a

    α_q̃ = lob.a
    α_p̃ = lobc.a

    b_q = lobd.b
    b_p = lobd.b

    β_q̃ = lobd.b
    β_p̃ = lobd.b

    c_q = lobd.c
    c_p = lobd.c

    γ = lobd.c

    d = ones(T, s)
    ω = get_lobatto_ω_matrix(s)
    δ = zeros(T, 0, s)

    return TableauVSPARKprimary(Symbol("LobABCCD($s)"), 2*s-2,
                        a_q, a_p, α_q, α_p,
                        a_q̃, a_p̃, α_q̃, α_p̃,
                        b_q, b_p, β_q̃, β_p̃,
                        c_q, c_p, γ, d,
                        ω, δ)
end

function TableauVSPARKLobABCCE(s=2, T=Float64)
    lob  = TableauLobattoIIIC̄(s)
    loba = TableauLobattoIIIA(s)
    lobb = TableauLobattoIIIB(s)
    lobc = TableauLobattoIIIC(s)
    lobd = TableauLobattoIIID(s)
    lobe = TableauLobattoIIIE(s)

    a_q = lobe.a
    a_p = lobe.a

    α_q = lobb.a
    α_p = lobb.a

    a_q̃ = loba.a
    a_p̃ = loba.a

    α_q̃ = lob.a
    α_p̃ = lobc.a

    b_q = lobd.b
    b_p = lobd.b

    β_q̃ = lobd.b
    β_p̃ = lobd.b

    c_q = lobd.c
    c_p = lobd.c

    γ = lobd.c

    d = ones(T, s)
    ω = get_lobatto_ω_matrix(s)
    δ = zeros(T, 0, s)

    return TableauVSPARKprimary(Symbol("LobABCCE($s)"), 2*s-2,
                        a_q, a_p, α_q, α_p,
                        a_q̃, a_p̃, α_q̃, α_p̃,
                        b_q, b_p, β_q̃, β_p̃,
                        c_q, c_p, γ, d,
                        ω, δ)
end

function TableauVSPARKLobABED(s=2, T=Float64)
    lob  = TableauLobattoIIIC̄(s)
    loba = TableauLobattoIIIA(s)
    lobb = TableauLobattoIIIB(s)
    lobc = TableauLobattoIIIC(s)
    lobd = TableauLobattoIIID(s)
    lobe = TableauLobattoIIIE(s)

    a_q = lobe.a
    a_p = lobe.a

    α_q = lobb.a
    α_p = lobb.a

    a_q̃ = loba.a
    a_p̃ = loba.a

    α_q̃ = lobd.a
    α_p̃ = lobd.a

    b_q = lob.b
    b_p = lob.b

    β_q̃ = lob.b
    β_p̃ = lob.b

    c_q = lob.c
    c_p = lob.c

    γ   = lob.c

    d = ones(T, s)
    ω = get_lobatto_ω_matrix(s)
    δ = zeros(T, 0, s)

    return TableauVSPARKprimary(Symbol("LobABED($s)"), 2*s-2,
                        a_q, a_p, α_q, α_p,
                        a_q̃, a_p̃, α_q̃, α_p̃,
                        b_q, b_p, β_q̃, β_p̃,
                        c_q, c_p, γ, d,
                        ω, δ)
end

function TableauVSPARKLobABDE(s=2, T=Float64)
    lob  = TableauLobattoIIIC̄(s)
    loba = TableauLobattoIIIA(s)
    lobb = TableauLobattoIIIB(s)
    lobc = TableauLobattoIIIC(s)
    lobd = TableauLobattoIIID(s)
    lobe = TableauLobattoIIIE(s)

    a_q = lobd.a
    a_p = lobd.a

    α_q = lobb.a
    α_p = lobb.a

    a_q̃ = loba.a
    a_p̃ = loba.a

    α_q̃ = lobe.a
    α_p̃ = lobe.a

    b_q = lob.b
    b_p = lob.b

    β_q̃ = lob.b
    β_p̃ = lob.b

    c_q = lob.c
    c_p = lob.c

    γ   = lob.c

    d = ones(T, s)
    ω = get_lobatto_ω_matrix(s)
    δ = zeros(T, 0, s)

    return TableauVSPARKprimary(Symbol("LobABDE($s)"), 2*s-2,
                        a_q, a_p, α_q, α_p,
                        a_q̃, a_p̃, α_q̃, α_p̃,
                        b_q, b_p, β_q̃, β_p̃,
                        c_q, c_p, γ, d,
                        ω, δ)
end

function TableauVSPARKLobABD(s=2, T=Float64)
    lob  = TableauLobattoIIIC̄(s)
    loba = TableauLobattoIIIA(s)
    lobb = TableauLobattoIIIB(s)
    lobc = TableauLobattoIIIC(s)
    lobd = TableauLobattoIIID(s)
    lobe = TableauLobattoIIIE(s)

    a_q = lobd.a
    a_p = lobd.a

    α_q = lobb.a
    α_p = lobb.a

    a_q̃ = loba.a
    a_p̃ = loba.a

    α_q̃ = loba.a
    α_p̃ = lobb.a

    b_q = lob.b
    b_p = lob.b

    β_q̃ = lob.b
    β_p̃ = lob.b

    c_q = lob.c
    c_p = lob.c

    γ   = lob.c

    d = ones(T, s)
    ω = get_lobatto_ω_matrix(s)
    δ = zeros(T, 0, s)

    return TableauVSPARKprimary(Symbol("LobABD($s)"), 2*s-2,
                        a_q, a_p, α_q, α_p,
                        a_q̃, a_p̃, α_q̃, α_p̃,
                        b_q, b_p, β_q̃, β_p̃,
                        c_q, c_p, γ, d,
                        ω, δ)
end

function TableauVSPARKLobABE(s=2, T=Float64)
    lob  = TableauLobattoIIIC̄(s)
    loba = TableauLobattoIIIA(s)
    lobb = TableauLobattoIIIB(s)
    lobc = TableauLobattoIIIC(s)
    lobd = TableauLobattoIIID(s)
    lobe = TableauLobattoIIIE(s)

    a_q = lobe.a
    a_p = lobe.a

    α_q = lobb.a
    α_p = lobb.a

    a_q̃ = loba.a
    a_p̃ = loba.a

    α_q̃ = loba.a
    α_p̃ = lobb.a

    b_q = lob.b
    b_p = lob.b

    β_q̃ = lob.b
    β_p̃ = lob.b

    c_q = lob.c
    c_p = lob.c

    γ   = lob.c

    d = ones(T, s)
    ω = get_lobatto_ω_matrix(s)
    δ = zeros(T, 0, s)

    return TableauVSPARKprimary(Symbol("LobABE($s)"), 2*s-2,
                        a_q, a_p, α_q, α_p,
                        a_q̃, a_p̃, α_q̃, α_p̃,
                        b_q, b_p, β_q̃, β_p̃,
                        c_q, c_p, γ, d,
                        ω, δ)
end

function TableauVSPARKLobDE(s=2, T=Float64)
    lob  = TableauLobattoIIIC̄(s)
    loba = TableauLobattoIIIA(s)
    lobb = TableauLobattoIIIB(s)
    lobc = TableauLobattoIIIC(s)
    lobd = TableauLobattoIIID(s)
    lobe = TableauLobattoIIIE(s)

    a_q = lobd.a
    a_p = lobd.a

    α_q = lobe.a
    α_p = lobe.a

    a_q̃ = lobe.a
    a_p̃ = lobe.a

    α_q̃ = lobd.a
    α_p̃ = lobd.a

    b_q = lob.b
    b_p = lob.b

    β_q̃ = lob.b
    β_p̃ = lob.b

    c_q = lob.c
    c_p = lob.c

    γ   = lob.c

    d = ones(T, s)
    ω = get_lobatto_ω_matrix(s)
    δ = zeros(T, 0, s)

    return TableauVSPARKprimary(Symbol("LobDE($s)"), 2*s-2,
                        a_q, a_p, α_q, α_p,
                        a_q̃, a_p̃, α_q̃, α_p̃,
                        b_q, b_p, β_q̃, β_p̃,
                        c_q, c_p, γ, d,
                        ω, δ)
end

function TableauVSPARKLobED(s=2, T=Float64)
    lob  = TableauLobattoIIIC̄(s)
    loba = TableauLobattoIIIA(s)
    lobb = TableauLobattoIIIB(s)
    lobc = TableauLobattoIIIC(s)
    lobd = TableauLobattoIIID(s)
    lobe = TableauLobattoIIIE(s)

    a_q = lobe.a
    a_p = lobe.a

    α_q = lobd.a
    α_p = lobd.a

    a_q̃ = lobd.a
    a_p̃ = lobd.a

    α_q̃ = lobe.a
    α_p̃ = lobe.a

    b_q = lob.b
    b_p = lob.b

    β_q̃ = lob.b
    β_p̃ = lob.b

    c_q = lob.c
    c_p = lob.c

    γ   = lob.c

    d = ones(T, s)
    ω = get_lobatto_ω_matrix(s)
    δ = zeros(T, 0, s)

    return TableauVSPARKprimary(Symbol("LobED($s)"), 2*s-2,
                        a_q, a_p, α_q, α_p,
                        a_q̃, a_p̃, α_q̃, α_p̃,
                        b_q, b_p, β_q̃, β_p̃,
                        c_q, c_p, γ, d,
                        ω, δ)
end


using DynamicPolynomials
# include("E:\\Programs\\NCTSSOS\\src\\NCTSSOS.jl")
using NCTSSOS

n = 3
@ncpolyvar x[1:3]
f = x[1]^2-x[1]*x[2]-x[2]*x[1]+3x[2]^2-2x[1]*x[2]*x[1]+2x[1]*x[2]^2*x[1]-x[2]*x[3]-x[3]*x[2]+
6x[3]^2+9x[2]^2*x[3]+9x[3]*x[2]^2-54x[3]*x[2]*x[3]+142x[3]*x[2]^2*x[3]

opt,data = nctssos_first(f, x, newton=true, reducebasis=true, TS="MD", obj="eigen", QUIET=true)
# optimum = -0.003551211274204682

opt,data = nctssos_first(f, x, newton=true, TS="MD", obj="trace", QUIET=true)
# optimum = -0.003551219665210936

n = 2
@ncpolyvar x[1:2]
f = 2-x[1]^2+x[1]*x[2]^2*x[1]-x[2]^2
g1 = 4-x[1]^2-x[2]^2
g2 = x[1]*x[2]+x[2]*x[1]-2
pop = [f, g1, g2]

opt,data = nctssos_first(pop, x, 2, numeq=1, TS="MD", obj="eigen", QUIET=true)
# optimum = -0.9999999892683991

opt,data = nctssos_higher!(data, TS="MD", QUIET=true)
# optimum = -0.9999999673495177

opt,data = nctssos_first(pop, x, 2, numeq=1, TS="MD", obj="trace", QUIET=true)
# optimum = -0.9999999992878111

opt,data = nctssos_higher!(data, TS="MD", QUIET=true)
# optimum = -0.9999999980730225

n = 10
@ncpolyvar x[1:n]
f = 0.0
for i = 1:n
    jset = max(1,i-5):min(n,i+1)
    jset = setdiff(jset,i)
    global f += (2x[i]+5*x[i]^3+1)^2
    global f -= sum([4x[i]*x[j]+10x[i]^3*x[j]+2x[j]+4x[i]*x[j]^2+10x[i]^3*x[j]^2+2x[j]^2 for j in jset])
    global f += sum([x[j]*x[k]+2x[j]^2*x[k]+x[j]^2*x[k]^2 for j in jset for k in jset])
end

opt,data = cs_nctssos_first(f, x, TS="MD", obj="trace", QUIET=true)
# optimum = 2.6202683948677496e-9

opt,data = cs_nctssos_higher!(data, TS="MD", QUIET=true)
# optimum = 6.069573675277303e-8

pop = [f]
for i = 1:n
    push!(pop, 1-x[i]^2)
    push!(pop, x[i]-1/3)
end

opt,data = cs_nctssos_first(pop, x, 3, TS="MD", obj="eigen", QUIET=true)
# optimum = 3.011288257024969

opt,data = cs_nctssos_first(pop, x, 3, TS="MD", obj="trace", QUIET=true)
# optimum = 3.0112882337979103

println("Run successfully!")


using Base.Test
# morlet1d.jl
import WaveletScattering: gauss

# gauss
@test_approx_eq gauss(0.0, 1.0) 1.0
for ω in 1.0:10.0
    for den in logspace(0, 3, 4)
        g = gauss(ω, den)
        @test g >= 0.0
        @test_approx_eq g gauss(-ω, den)
        @test_approx_eq sqrt(-log(g) * den) ω
    end
end


# Version over exact field:
function row_echelon_form!(A::AbstractMatrix{T}) where {T<:Union{FiniteFields.GF, Cyclotomic{<:Rational}, Rational}}
    pivots = Int[]
    pos = 0
    for i = 1:size(A, 2)
        j = findnext(x -> !iszero(x), @view(A[:, i]), pos+1)
        j === nothing && continue
        pos += 1
        push!(pivots, i)

        # swap the rows so that
        if (pos != j)
            for k in 1:size(A, 2)
                A[pos, k], A[j, k] = A[j, k], A[pos, k]
            end
        end
        # A[pos, :] is the row with leading nz in i-th column

        w = inv(A[pos, i])
        # all columns to the left of i are zeroed (below pivots) so we start at i
        for colidx in i:size(A, 2)
            A[pos, colidx] *= w
        end
        # A[pos, i] is 1 now

        # zero the whole i-th column above and below pivot:
        for rowidx = 1:size(A, 1)
            rowidx == pos && continue # don't zero the active row
            v = A[rowidx, i]
            iszero(v) && continue
            for k in i:size(A, 2) # to the left of pivot everything is zero
                A[rowidx, k] -= v * A[pos, k]
            end
        end
    end
    return A, pivots
end

function _findmax(f, A::AbstractArray)
    mval, midx = f(first(A)), firstindex(A)
    for idx in eachindex(A)
        if (v = f(A[idx])) > mval
            mval, midx = v, idx
        end
    end
    return mval, midx
end

function row_echelon_form!(A::AbstractMatrix{T}, atol=eps(T)*max(size(A)...)) where {T<:AbstractFloat}
    pivots = Int[]
    pos = 0
    sgn = 1.0
    for i = 1:size(A, 2)
        # find the largest entry in the column below the last pivot
        mval, midx = _findmax(abs, @view(A[pos+1:end, i]))
        # j = findnext(x -> !iszero(x), , pos+1)
        if mval < atol # the largest entry is below threshold so we zero everything in the column
            A[pos+1:end, i] .= zero(T)
            continue
        end
        j = midx+pos
        pos += 1
        push!(pivots, i)

        # swap the rows so that
        if (pos != j)
            for k in 1:size(A, 2)
                A[pos, k], A[j, k] = A[j, k], A[pos, k]
            end
        end
        # A[pos, :] is the row with leading nz in i-th column

        w = inv(A[pos, i])
        sgn *= sign(w)
        # all columns to the left of i are zeroed (below pivots) so we start at i
        for colidx in i:size(A, 2)
            A[pos, colidx] *= w
        end
        # A[pos, i] is 1 now

        # zero the whole i-th column above and below pivot:
        for rowidx = 1:size(A, 1)
            rowidx == pos && continue # don't zero the active row
            v = A[rowidx, i]
            iszero(v) && continue
            for k in i:size(A, 2) # to the left of pivot everything is zero
                A[rowidx, k] -= v * A[pos, k]
            end
        end
    end
    if sgn < 0
        A .*= sgn
    end
    return A, pivots
end

row_echelon_form(A::AbstractMatrix) = row_echelon_form!(deepcopy(A))


println("\n\n\nStarting runtests.jl ...")

using ExtremelyRandomizedTrees
using FactCheck

doPlot = false

if doPlot
	using PyPlot
end

doDummyClassification = true
doDummyRegression = true
doClassification = true
doRegression1to1 = true
doRegression1to2 = true
doRegression2to2 = true
testIndivProbPrediction = true

if doDummyClassification
    data = [-2 -1 1 2]
    labels = [1 1 2 2]
    a = ExtraTrees(float(data), labels)

    testdata = linspace(-5,5,100)'

    result, votes = predict(a, testdata, returnvotes = true)
    if doPlot
        figure()
        plot(testdata', [votes' result'])
    end
end

if doDummyRegression
    trainingData = collect(-10:10)'
    trainingLabels = trainingData+100
    testData = trainingData

    a = ExtraTrees(trainingData, trainingLabels; regression = true)
    result = predict(a,testData)

    if doPlot
        figure()
        plot(testData', result', "b-")
    end
end

if doClassification
    trainingData = hcat(rand(2,100), rand(2,100)+1, rand(2,100)+2)
    trainingLabels = hcat(ones(1,100), 2*ones(1,100), 3*ones(1,100))

    a = ExtraTrees(trainingData, trainingLabels)

    X = [x for x in -5:0.1:10, y in -5:0.1:10]
    Y = [y for x in -5:0.1:10, y in -5:0.1:10]

    result = predict(a, [vec(X) vec(Y)]');

    if doPlot
        figure()
        imshow(reshape(result, size(X)))
    end
end

####################################
# univariate regression
# 1 feature, 1 target
####################################

if doRegression1to1
    trainingData = linspace(0,2*pi,100)'
    testData = linspace(0,2*pi,1000)'
    trainingLabels = 10 + sin(trainingData) + (0.3*rand(size(trainingData))-0.15)

    a = ExtraTrees(trainingData, trainingLabels; regression = true)
    result = predict(a,testData)

	if doPlot
        figure()
		plot(trainingData, trainingLabels,"b.", hold = true)
		plot(testData', result',"g-", hold = true)
		title("univariate regression - 1 feature, 1 target")
	end
end

if testIndivProbPrediction
    trainingData = hcat(rand(2,100), rand(2,100)+1, rand(2,100)+2)
    trainingLabels = hcat(ones(1,100), 2*ones(1,100), 3*ones(1,100))

    a = ExtraTrees(trainingData, trainingLabels)

    X = [x for x in -5:0.1:10, y in -5:0.1:10]
    Y = [y for x in -5:0.1:10, y in -5:0.1:10]

    result, votes = predict(a, [vec(X) vec(Y)]', returnvotes = true)
    indivvotes = Array(Any,3)
    for i = 1:3
        indivvotes[i] = predict(a, [vec(X) vec(Y)]'; votesfor=i)
        #assert(all(votes[i,:]/sum(votes[i,:]) == indivvotes[i]))
    end
	if doPlot
        figure()
		for i = 1:3
			subplot(1,3,i); imshow(reshape(indivvotes[i], size(X)))
		end
	end
end

####################################
# multivariate regression
# 1 feature, 2 target values
####################################

if doRegression1to2
    trainingData = linspace(0,2*pi,100)'
    testData = linspace(0,2*pi,1000)'
    trainingLabels = vcat(3 + sin(trainingData) + (0.3*rand(size(trainingData))-0.15),
        10 + trainingData.^2 + (0.3*rand(size(trainingData))-0.15))

    a = ExtraTrees(trainingData, trainingLabels; regression = true)
    result = predict(a,testData)

	if doPlot
        figure()
		plot(trainingData, trainingLabels[1,:],"b.", hold = true)
		plot(trainingData, trainingLabels[2,:],"b.", hold = true)
		plot(testData', result[1,:]',"g-", hold = true)
		plot(testData', result[2,:]',"g-", hold = true)
		title("multivariate regression - 1 feature, 2 target values")
	end
end

####################################
# multivariate regression
# 2 features, 2 target values
####################################

if doRegression2to2
    trainingData = linspace(0,2*pi,100)'
    testData = linspace(0,2*pi,1000)'
    trainingTargets = vcat(10 + sin(trainingData) + (0.3*rand(size(trainingData))-0.15),
    10 + cos(trainingData) + (0.3*rand(size(trainingData))-0.15));
    testTargets = vcat(10 + sin(testData),
    10 + cos(testData));

    a = ExtraTrees(trainingData, trainingTargets; regression = true)
    result = predict(a,testData)

    assert(maximum(abs(result-testTargets))<0.3)
end

println("  ... runtests.jl done!")


twoj(j) = twice(Integer,j)
function twojp1(j)
	t = twoj(j)
	t + one(t)
end

function filledelements(j)
	N = twojp1(j)
	Np = (N+1) >> 1
	return (1 + N - Np)*Np
end

struct WignerdMatrixContainer{T} <: AbstractMatrix{T}
	j :: HalfInt
	v :: Vector{T}

	function WignerdMatrixContainer{T}(j::HalfInt, v::Vector{T}) where {T}

		j >= zero(j) || throw(ArgumentError("j must be ≥ 0"))

		if length(v) < filledelements(j)
			throw(ArgumentError("array provided is not large enough"*
			" to store all values. Need an array of "*
			"length $(filledelements(j)) whereas the array provided "*
			"has length = $(length(v))"))
		end

		new{T}(j, v)
	end
end

function WignerdMatrixContainer{T}(::UndefInitializer, j) where {T}
	jh = HalfInt(j)
	N = filledelements(jh)
	v = Vector{T}(undef, N)
	WignerdMatrixContainer{T}(jh, v)
end

Base.size(d::WignerdMatrixContainer) = (twojp1(d.j), twojp1(d.j))
Base.fill!(d::WignerdMatrixContainer, val) = fill!(d.v, val)
Base.IndexStyle(::Type{<:WignerdMatrixContainer}) = IndexCartesian()

function Base.:(==)(d1::WignerdMatrixContainer, d2::WignerdMatrixContainer)
	d1.j == d2.j || return false
	N = filledelements(d1.j)
	for i = 1:N
		d1.v[i] == d2.v[i] || return false
	end
	return true
end

function flatind(j, m_ind, n_ind)
	# This is m-major, so m increases faster than n
	# Store only the left triangular quadrant
	N = twojp1(j)
	indskip = (2 + N - n_ind)*(n_ind - 1)
	indskip + (m_ind - n_ind) + 1
end

function flatind_phase(j, m_ind, n_ind)
	N = twojp1(j)
	Np = (N + 1) >> 1

	neg = (-1)^(m_ind - n_ind)
	phase = one(neg)

	# The left quadrant (m,n <= 0, n <= m <= -n) is stored
	# We evaluate the others using symmetry relations
	if n_ind > Np && N - n_ind < m_ind <= n_ind
		# right
		# (m, n) → (-m, -n)
		n_ind, m_ind = N - n_ind + 1, N - m_ind + 1
		phase = neg
	elseif m_ind < n_ind
		# top
		# (m, n) → (n, m)
		m_ind, n_ind = n_ind, m_ind
		phase = neg
	elseif n_ind < m_ind && m_ind + n_ind > N + 1
		# bottom
		# (m, n) → (-n ,-m)
		m_ind, n_ind = N - n_ind + 1, N - m_ind + 1
	end

	ind = flatind(j, m_ind, n_ind)

	return ind, phase
end

@propagate_inbounds function Base.getindex(d::WignerdMatrixContainer, m_ind::Int, n_ind::Int, I...)
	@boundscheck checkbounds(d, m_ind, n_ind, I...)
	ind, phase = flatind_phase(d.j, m_ind, n_ind)
	d.v[ind]*phase
end

@propagate_inbounds function Base.setindex!(d::WignerdMatrixContainer, val, m_ind::Int, n_ind::Int, I...)
	@boundscheck checkbounds(d, m_ind, n_ind, I...)
	ind, phase = flatind_phase(d.j, m_ind, n_ind)
	d.v[ind] = val*phase
	d
end

abstract type AbstractWignerMatrix{T} <: AbstractHalfIntegerMatrix{T} end

Base.size(w::AbstractWignerMatrix) = size(parent(w))
Base.axes(w::AbstractWignerMatrix) = axes(parent(w))
Base.axes(w::AbstractWignerMatrix, d) = axes(parent(w), d)

"""
	WignerdMatrix(j, beta::Real)

Compute the Wigner d-matrix in the z-y-z convention for the
angular momentum `j` and the angle `beta`.
The resulting matrix has a size of `(2j+1, 2j+1)` and axes `(-j:j, -j:j)`.
The angular momentum `j` needs to be either an `Integer` or a half-integer.
The matrix elements are real in this convention.

# Examples

```jldoctest
julia> d = WignerdMatrix(0.5, π)
2×2 WignerdMatrix{Float64} for j = 1/2 and beta = π with indices -1/2:1/2×-1/2:1/2:
  0.0  1.0
 -1.0  0.0

julia> d[0.5, 0.5]
0.0
```
"""
struct WignerdMatrix{T, B<:Real} <: AbstractWignerMatrix{T}
	beta :: B
	dj :: SpinMatrix{T,WignerdMatrixContainer{T}}
end

Base.parent(d::WignerdMatrix) = d.dj
Base.collect(d::WignerdMatrix) = collect(parent(d))

@propagate_inbounds function Base.getindex(d::WignerdMatrix{T}, m::Real, n::Real) where {T}
	parent(d)[m,n]
end

@propagate_inbounds function Base.getindex(d::WignerdMatrix{T, <:Union{ScaledPi,Irrational{:π}}}, m::Real, n::Real) where {T}
	@boundscheck checkbounds(d, m, n)
	wignerdmatrixelement(T, sphericaldegree(d), m, n, d.beta)
end

#= Linear indexing has to be done in a roundabout way through Cartesian indexing
to avoid undefined references.
=#
@inline function Base.getindex(d::WignerdMatrix, i::Int)
	@boundscheck checkbounds(d, i)
	mn = Tuple(@inbounds eachindex(d)[i])
	@inbounds d[mn...]
end

function Base.:(==)(d1::WignerdMatrix, d2::WignerdMatrix)
	d1.beta == d2.beta && parent(d1.dj) == parent(d2.dj)
end

function Base.:(==)(d1::WignerdMatrix{<:Any,<:ScaledPi}, d2::WignerdMatrix{<:Any,<:ScaledPi})
	(d1.beta == d2.beta && sphericaldegree(d1) == sphericaldegree(d2)) || return false

	j = sphericaldegree(d1)
	for n = -j:zero(j)
		m = nonzerofirstindex(d1.beta, n)
		d1[m,n] == d2[m,n] || return false
	end
	return true
end

"""
	WignerDMatrix(j, α::Real, β::Real, γ::Real)

Construct the Wigner D-matrix in the z-y-z convention for the angular momentum `j`
and the Euler angles `(α, β, γ)`. The angular momentum `j` needs to be an `Integer`
or a half-integer. The resulting matrix has a size of `(2j+1, 2j+1)` and axes
`(-j:j, -j:j)`.

# Examples

```jldoctest
julia> D = WignerDMatrix(0.5, 0, 0, 0)
2×2 WignerDMatrix{Complex{Float64}} for j = 1/2, alpha = 0, beta = 0 and gamma = 0 with indices -1/2:1/2×-1/2:1/2:
 1.0+0.0im  0.0-0.0im
 0.0+0.0im  1.0+0.0im

julia> D[-0.5, 0.5]
-0.0 + 0.0im
```
"""
struct WignerDMatrix{T<:Complex,A<:Real,W<:WignerdMatrix,G<:Real} <: AbstractWignerMatrix{T}
	alpha :: A
	dj :: W
	gamma :: G
end

function WignerDMatrix{T}(α::A, dj::W, γ::G) where {W<:WignerdMatrix,A<:Real,G<:Real,T<:Complex}
	WignerDMatrix{T,A,W,G}(α, dj, γ)
end

function WignerDMatrix(α::A, dj::WignerdMatrix{R}, γ::G) where {R,A<:Real,G<:Real}
	Tphase = Complex{promote_type(HalfInt,promote_type(A,G))}
	T = promote_type(Tphase, R)
	WignerDMatrix{T}(α, dj, γ)
end

Base.parent(D::WignerDMatrix) = D.dj

WignerDphase(m, α::Real) = cis(-m * α)
WignerDphase(m, α::SpecialAngle) = cis_special(-m, α)
WignerDphase(m, α::ZeroRadians) = 1 # return an integer to avoid complex multiplication

@inline function Base.getindex(D::WignerDMatrix{T}, m::Real, n::Real) where {T}
	@boundscheck checkbounds(D, m, n)
	dmn = @inbounds D.dj[m,n]
	val = dmn * WignerDphase(m, D.alpha) * WignerDphase(n, D.gamma)
	convert(T, val)
end

# Linear indexing forces Cartesian indexing
@inline function Base.getindex(D::WignerDMatrix, i::Int)
	@boundscheck checkbounds(D, i)
	mn = Tuple(@inbounds eachindex(D)[i])
	@inbounds D[mn...]
end

function Base.:(==)(d1::WignerDMatrix, d2::WignerDMatrix)
	d1.alpha == d2.alpha && d1.gamma == d2.gamma && d1.dj == d2.dj
end

eulerangles(d::WignerdMatrix) = (zero(d.beta), d.beta, zero(d.beta))
eulerangles(D::WignerDMatrix) = (D.alpha, D.dj.beta, D.gamma)

sphericaldegree(d::WignerdMatrixContainer) = d.j
sphericaldegree(d::SpinMatrix) = d.j
sphericaldegree(d::Union{WignerdMatrix, WignerDMatrix}) = sphericaldegree(d.dj)

Base.IndexStyle(d::Type{<:AbstractWignerMatrix}) = IndexCartesian()

function Base.similar(d::WignerdMatrix, ::Type{T}) where {T}
	jh = HalfInt(sphericaldegree(d))
	w = WignerdMatrixContainer{T}(undef, jh)
	s = SpinMatrix(w, jh)
	WignerdMatrix(d.beta, s)
end

function Base.similar(D::WignerDMatrix, ::Type{T}) where {T}
	d = similar(D.dj)
	WignerDMatrix{T}(D.alpha, d, D.gamma)
end

HalfIntegerArrays.SpinMatrix(d::AbstractWignerMatrix) = SpinMatrix(collect(d))

Base.dataids(d::WignerdMatrixContainer) = Base.dataids(d.v)
Base.dataids(d::AbstractWignerMatrix) = Base.dataids(parent(d))
function Broadcast.broadcast_unalias(dest::AbstractWignerMatrix, src::AbstractWignerMatrix)
	parent(dest) === parent(src) ? src : Broadcast.unalias(dest, src)
end

# Display

function Base.showarg(io::IO, d::WignerdMatrix, toplevel)
	j = sphericaldegree(d)
	if toplevel
		print(io,"WignerdMatrix{$(eltype(d))} for j = ", j, " and beta = ", d.beta)
	else
		print(io,"WignerdMatrix{$(eltype(d))}(",j,", ",d.beta,")")
	end
end
function Base.showarg(io::IO, D::WignerDMatrix, toplevel)
	α, β, γ = eulerangles(D)
	j = sphericaldegree(D)
	if toplevel
		print(io,"WignerDMatrix{$(eltype(D))} for j = ", j,
			", alpha = ", α,", beta = ", β, " and gamma = ", γ)
	else
		print(io,"WignerDMatrix{$(eltype(D))}(", j,
			", ", α, ", ", β, ", ", γ,")")
	end
end

function Base.replace_in_print_matrix(d::WignerdMatrix{<:Any, <:ScaledPi}, m::HalfInteger, n::HalfInteger, s::AbstractString)
    nonzerocheck(d.beta, m, n, sphericaldegree(d)) ? s : Base.replace_with_centered_mark(s)
end

function Base.replace_in_print_matrix(D::WignerDMatrix{<:Any, <:Any, <:WignerdMatrix{<:Any, <:ScaledPi}},
	m::HalfInteger, n::HalfInteger, s::AbstractString)

	_,β,_ = eulerangles(D)
	nonzerocheck(β, m, n, sphericaldegree(D)) ? s : Base.replace_with_centered_mark(s)
end


################################
### crossvalidation routines ###
################################

# container object for XV results
immutable OMPCrossvalidationResults{T <: Float}
    mses :: Vector{T}
    path :: Vector{Int}
    b    :: Vector{T}
    bidx :: Vector{Int}
    k    :: Int
end

# subroutine to refit preditors after crossvalidation
function refit_omp{T <: Float}(
    x     :: DenseMatrix{T},
    y     :: DenseVector{T},
    k     :: Int;
    quiet :: Bool = true,
)
    # first use OMP to extract model
    betas = omp(x, y, k, quiet=quiet)

    # which components of β are nonzero?
    # cannot use binary indices here since we need to return Int indices
    bidx = find(betas[:,end])

    # allocate the submatrix of x corresponding to the inferred model
    x_inferred = x[:,bidx]

    # now estimate β with the ordinary least squares estimator β = inv(x'x)x'y
    # return it with the vector of MSEs
    xty = BLAS.gemv('T', x_inferred, sdata(y)) :: Vector{T}
    xtx = At_mul_B(x_inferred, x_inferred) :: Matrix{T}
    b   = zeros(T, length(bidx))
    try
        b = (xtx \ xty) :: Vector{T}
    catch e
        warn("caught error: ", e, "\nSetting returned values of b to -Inf")
        fill!(b, -Inf)
    end

    return b, bidx
end

"""
    one_fold(x, y, k, folds, fold) -> Vector

For a regularization path given by the integer `k`,
this function performs orthogonal matching pursuit on `x` and `y` and computes an out-of-sample error based on the indices given in `folds`.

Arguments:
- `x` is the `n` x `p` design matrix.
- `y` is the `n`-vector of responses.
- `k` is an integer that specifies the maximum desired sparsity level to test.
- `folds` is an `Int` vector indicating which component of `y` goes to which fold, e.g. `folds = IHT.cv_get_folds(n,nfolds)`
- `fold` is the current fold to compute.

Optional Arguments:
- `quiet` is a Boolean to activate output. Defaults to `true` (no output).

Output:
- `errors` is a vector of out-of-sample errors (MSEs) for the current fold.
"""
function one_fold{T <: Float}(
    x     :: DenseMatrix{T},
    y     :: DenseVector{T},
    k     :: Int,
    folds :: DenseVector{Int},
    fold  :: Int;
    quiet :: Bool  = true,
)

    # make vector of indices for folds
    test_idx = folds .== fold
    test_size = sum(test_idx)

    # train_idx is the vector that indexes the TRAINING set
    train_idx = !test_idx

    # allocate the arrays for the training set
    x_train = x[train_idx,:]
    y_train = y[train_idx]

    # compute the regularization path on the training set
    betas = omp(x_train, y_train, k, quiet=quiet)

    # compute the mean out-of-sample error for the TEST set
    #errors = vec(sumabs2(broadcast(-, y[test_idx], x[test_idx,:] * betas), 1)) ./ (2*test_size)
    xb = view(x, test_idx, :) * betas
    r  = broadcast(-, y[test_idx], xb)
    errors = vec(sumabs2(r, 1)) ./ (2*test_size)

    return errors :: Vector{T}
end

"""
    pfold(x, y, k, folds, q [, pids=procs()]) -> Vector

This function is the parallel execution kernel in `cv_omp`. It is not meant to be called outside of `cv_omp`.
For floating point data `x` and `y` and an integer `k`, `pfold` will distribute `q` crossvalidation folds across the processes supplied by the optional argument `pids`.
It calls `one_fold` for each fold, then collects the vectors of MSEs each process, applys a reduction, and finally returns the average MSE vector across all folds.
"""
function pfold{T <: Float}(
    x        :: DenseMatrix{T},
    y        :: DenseVector{T},
    k        :: Int,
    folds    :: DenseVector{Int},
    q        :: Int;
    pids     :: Vector{Int} = procs(),
    quiet    :: Bool  = true,
)
    # how many CPU processes can pfold use?
    np = length(pids)

    # report on CPU processes
    quiet || println("pfold: np = ", np)
    quiet || println("pids = ", pids)

    # set up function to share state (indices of folds)
    i = 1
    nextidx() = (idx=i; i+=1; idx)

    # preallocate cell array for results
    results = SharedArray(T, (k,q), pids=pids) :: SharedMatrix{T}

    # master process will distribute tasks to workers
    # master synchronizes results at end before returning
    @sync begin

        # loop over all workers
        for worker in pids

            # exclude process that launched pfold, unless only one process is available
            if worker != myid() || np == 1

                # asynchronously distribute tasks
                @async begin
                    while true

                        # grab next fold
                        current_fold = nextidx()

                        # if current fold exceeds total number of folds then exit loop
                        current_fold > q && break

                        # report distribution of fold to worker and device
                        quiet || print_with_color(:blue, "Computing fold $current_fold on worker $worker.\n\n")

                        # launch job on worker
                        # worker loads data from file paths and then computes the errors in one fold
                        r = remotecall_fetch(worker) do
                                one_fold(x, y, k, folds, current_fold, quiet=quiet)
                        end # end remotecall_fetch()
                        setindex!(results, r, :, current_fold)
                    end # end while
                end # end @async
            end # end if
        end # end for
    end # end @sync

    # return reduction (row-wise sum) over results
    return (vec(sum(results, 2) ./ q)) :: Vector{T}
end

"""
    cv_omp(x,y) -> Vector

This function will perform `q`-fold cross validation for the ideal model size in OMP least squares regression using the `n` x `p` design matrix `x` and the response vector `y`.
Each path is asynchronously spawned using any available processor.
For each path, one fold is held out of the analysis for testing, while the rest of the data are used for training.
The function to compute each path, `one_fold()`, will return a vector of out-of-sample errors (MSEs).
Arguments:
- `x` is the `n` x `p` design matrix.
- `y` is the `n`-vector of responses.
Optional Arguments:
- `q` is the number of folds to compute. Defaults to `max(3, min(CPU_CORES, 5))`, where `CPU_CORES`is the Julia variable to query the number of available CPU cores.
- `path` is an `Int` vector that specifies which model sizes to include in the path. Defaults to `path = collect(1:min(p,20))`.
- `folds` is the partition of the data. Defaults to `IHT.cv_get_folds(n,q)`.
- `pids`, a vector of process IDs. Defaults to `procs()`, which recruits all available processes.
- `quiet` is a Boolean to activate output. Defaults to `true` (no output).
   *NOTA BENE*: each processor outputs feed to the console without regard to the others,
   so setting `quiet = false` can yield very messy output!
Output:
An `OMPCrossvalidationResults` object with the following fields:
- `mses` is the averaged MSE over all folds.
- `k` is the best crossvalidated model size.
- `path` is the regularization path used in the crossvalidation.
- `b`, a vector of `k` floats
- `bidx`, a vector of `k` indices indicating the support of the best model.
"""
function cv_omp{T <: Float}(
    x     :: DenseMatrix{T},
    y     :: DenseVector{T};
    q     :: Int  = cv_get_num_folds(3,5),
    k     :: Int  = min(size(x,2), 20),
    folds :: DenseVector{Int} = cv_get_folds(sdata(y),q),
    pids  :: Vector{Int} = procs(),
    quiet :: Bool = true,
)
    # do not allow crossvalidation with fewer than 3 folds
    q > 2 || throw(ArgumentError("Number of folds q = $q must be at least 3."))

    # problem dimensions?
    n,p = size(x)

    # check for conformable arrays
    n == length(y) || throw(DimensionMismatch("Row dimension of x ($n) must match number of rows in y ($(length(y)))"))

    # want to compute a path for each fold
    # the folds are computed asynchronously over processes enumerated by pids
    # master process then reduces errors across folds and returns MSEs
    mses = pfold(x, y, k, folds, q, pids=pids, quiet=quiet)

    # what is the best model size?
    path = collect(1:k)
    kbest = path[indmin(mses)] :: Int

    # print results
    !quiet && print_cv_results(mses, path, kbest)

    # refit best model
    #b, bidx = refit_omp(x, y, kbest, quiet=quiet)
    betas = omp(x, y, kbest, quiet=quiet)
    bidx  = find(betas[:,end])
    b     = full(betas[bidx,end])

    return OMPCrossvalidationResults{T}(mses, path, b, bidx, kbest)
end


# ## Brusselator

# ## Model

# The Brusselator is a two ODEs theoretical model for autocatalytic reactions,
# where $x$ and $y$ are chemical concentrations:

module brusselator      #jl

using ReachabilityAnalysis

const A = 1.0
const B = 1.5
const B1 = B + 1

@taylorize function brusselator!(du, u, p, t)
    x, y = u
    x² = x * x
    aux = x² * y
    du[1] = A + aux - B1*x
    du[2] = B*x - aux
    return du
end

function model(X0)
    S = @system(x' = brusselator!(x), dim:2)
    return IVP(S, X0)
end

end #jl

# ## Reachability settings

# ## Results

# ## References


# Methods for absolute and relative abundances

abundancetable(df::DataFrame) = ComMatrix(
    convert(Matrix{Float64}, df[2:end]),
        string.(df[1]),
        string.(names(df[2:end]))
    )

abundancetable(table::AbstractArray{T,2},
    site = ["sample_$x" for x in axes(table, 2)],
    species = ["feature_$x" for x in axes(table, 1)]
    ) where T<:Real = ComMatrix(Float64.(table), species, site)

"""
Filter an abundance table to the top `n` features accross all samples

This function also adds a row for "other", which sums the abundances of the
remaining features.
"""
function filterabund(abun::AbstractComMatrix, n::Int=minimum(10, nfeatures(abun)))
    # TODO: add prevalence filter

    totals = featuretotals(abun)

    srt = sortperm(totals, rev=true)

    newabun = getfeature(abun, srt[1:n])

    remainder = [sum(occurrences(abun)[srt[n+1:end], i]) for i in 1:size(abun, 2)]'
    newabun = vcat(newabun, remainder)
    newrows = cat(featurenames(abun)[srt[1:n]], ["other"], dims=1)

    return abundancetable(newabun, samplenames(abun), newrows)
end

filterabund(df::DataFrame, n::Int=10) = filterabund(abundancetable(df), n)

function rownormalize!(abt::AbstractComMatrix)
    for i in 1:size(abt, 1)
        rowmax = maximum(occurrences(abt)[i,:])
        for j in 1:size(abt, 2)
            occurrences(abt)[i,j] /= rowmax
        end
    end
end

function rownormalize(abt::AbstractComMatrix)
    abt = deepcopy(abt)
    rownormalize!(abt)
    return abt
end

function colnormalize!(abt::AbstractComMatrix)
    for j in 1:size(abt, 2)
        colmax = maximum(occurrences(abt)[:,j])
        for i in 1:size(abt, 1)
            occurrences(abt)[i,j] /= colmax
        end
    end
end

function colnormalize(abt::AbstractComMatrix)
    abt = deepcopy(abt)
    rownormalize!(abt)
    return abt
end

function relativeabundance!(a::AbstractComMatrix; kind::Symbol=:fraction)
    in(kind, [:percent, :fraction]) || error("Invalid kind: $kind")
    if eltype(a.occurrences) != Float64; a.occurrences = Float64.(a.occurrences) end
    for i in 1:nsamples(a)
        s = sum(getsample(a,i))
        s == 0 && continue
        for x in 1:nfeatures(a)
            kind == :fraction ? occurrences(a)[x,i] /= s : occurrences(a)[x,i] /= (s / 100.)
        end
    end
end

function relativeabundance(a::AbstractComMatrix; kind::Symbol=:fraction)
    relab = deepcopy(a)
    relativeabundance!(relab, kind=kind)
    return relab
end


"""
    initialize_σ_Ising(L::Int, start_type::Symbol, rng::AbstractRNG)

Initialize the Ising model lattice `σ` with values ∈ {-1, 1}.

Arguments
* `L`         : Side length of the square lattice
* `start_type`: Either `:hot` or `:cold` to initialize the lattice with a hot or cold start
* `rng`       : Random number generator
Returns
* `σ`         : 2D array representing a square lattice
"""
function initialize_σ_Ising(L::Int, start_type::Symbol, rng::AbstractRNG)
    if start_type == :cold
        σ = ones(Int8, (L, L))
    elseif start_type == :hot
        σ = rand(rng, Int8[-1, 1], (L, L))
    else
        error("invalid start_type provided, valid choices are [:cold, :hot]")
    end

    return σ
end

"""
    initialize_σ_Potts(q::Int, L::Int, start_type::Symbol, rng::AbstractRNG)

Initialize the Potts model lattice `σ` with values ∈ {1, ..., q}.

Arguments
* `q`: Number of possible spin values
* `L`         : Side length of the square lattice
* `start_type`: Either `:hot` or `:cold` to initialize the lattice with a hot or cold start
* `rng`       : Random number generator
Returns
* `σ`         : 2D array representing a square lattice
"""
function initialize_σ_Potts(q::Int, L::Int, start_type::Symbol, rng::AbstractRNG)
    if start_type == :cold
        σ = ones(Int8, (L, L))
    elseif start_type == :hot
        σ = rand(rng, UnitRange{Int8}(1, q), (L, L))
    else
        error("invalid start_type provided, valid choices are [:cold, :hot]")
    end

    return σ
end

"""
    initialize_σ_XY(L::Int, start_type::Symbol, rng::AbstractRNG)

Initialize the XY model lattice `σ` with unit vectors represented by `NTuple{2, Float64}`.

Arguments
* `L`         : Side length of the square lattice
* `start_type`: Either `:hot` or `:cold` to initialize the lattice with a hot or cold start
* `rng`       : Random number generator
Returns
* `σ`         : 2D array representing a square lattice
    represented by a two-tuple
"""
function initialize_σ_XY(L::Int, start_type::Symbol, rng::AbstractRNG)
    if start_type == :cold
        σ = [(0.0, 1.0) for i = 1:L, j = 1:L]
    elseif start_type == :hot
        σ = [random_XYVector(rng) for i = 1:L, j = 1:L]
    else
        error("invalid start_type provided, valid choices are [:cold, :hot]")
    end

    return σ
end

"""
    random_XYVector(rng::AbstractRNG)

Generates a random unit vector represented by `NTuple{2, Float64}`.

Arguments
* `rng` : Random number generator
Returns
* A two-tuple representing a unit-vector with random angular orientation
"""
function random_XYVector(rng::AbstractRNG)
    θ = rand(rng) * 2π
    return (cos(θ), sin(θ))
end


function Jdouble(x::Vector{Float64},beta::Vector{Float64},rho::Float64)
    J = zeros(6,6);

    J[1,1] = 1.;
    J[2,1] = rho.*x[1]./(x[2].^2);
    J[3,1] = 1.;
    J[4,1] = 1./x[2];

    J[2,2] = (0.5.*beta[1].*x[2].^-0.5 .- rho.*x[1].^2.*x[2].^-3);
    J[3,2] = J[2,2];
    J[4,2] = -x[1].*x[2].^-2 .+ sqrt.(0.5.*beta[1]./rho).*x[2].^-0.75;

    J[1,3] = -1.;
    J[2,3] = -rho.*x[3]./(x[4].^2);
    J[5,3] = 1./x[4];

    J[2,4] = (-0.5.*beta[2].*x[4].^-0.5 .+ rho.*x[3].^2.*x[4].^-3);
    J[5,4] = -x[3].*x[4].^-2 .- sqrt.(0.5.*beta[2]./rho).*x[4].^-0.75;

    J[1,5] = -1.;
    J[3,5] = -rho.*x[5]./(x[6].^2);
    J[6,5] = 1./x[6];

    J[3,6] = (-0.5.*beta[3].*x[6].^-0.5 .+ rho.*x[5].^2.*x[6].^-3);
    J[6,6] = -x[5].*x[6].^-2 .- sqrt.(0.5.*beta[3]./rho).*x[6].^-0.75;

    return J
end


using VectorSphericalWaves
using StaticArrays
using ComplexOperations

import VectorSphericalWaves.M_mn_wave_SeparateRealImag_SMatrix
m, n, kr_r, kr_i, θ, ϕ = 1, 1, 1e5, 1e2, 0.2, 0.3
kr = complex(kr_r, kr_i)

M_mn_wave_SeparateRealImag(m, n, kr_r, kr_i, θ, ϕ, "regular");
N_mn_wave_SeparateRealImag(m, n, kr_r, kr_i, θ, ϕ, "regular");

M_mn_wave_SeparateRealImag_SMatrix(m, n, kr_r, kr_i, θ, ϕ, "regular");
N_mn_wave_SeparateRealImag_SMatrix(m, n, kr_r, kr_i, θ, ϕ, "regular");

# BigFloat
m, n, kr_r, kr_i, θ, ϕ = 1, 1, BigFloat(1e6), BigFloat(1e2), BigFloat(0.2), BigFloat(0.3);
kr = complex(kr_r, kr_i);

M_mn_wave(m, n, kr, θ, ϕ; kind="regular");
N_mn_wave(m, n, kr, θ, ϕ; kind="regular");


@defcomp biodiversity begin
    # Number of species
    nospecies = Variable(index=[time])

    # additive parameter
    bioloss = Parameter(default = 0.003)

    # multiplicative parameter
    biosens = Parameter(default = 0.001)

    # Temperature
    temp = Parameter(index=[time])

    # benchmark temperature change
    dbsta = Parameter()

    # Number of species in the year 2000
    nospecbase = Parameter()

    function run_timestep(p, v, d, t)

        if gettime(t) > 2000
            dt = abs(p.temp[t] - p.temp[t - 1])

            v.nospecies[t] = max(
            p.nospecbase / 100,
            v.nospecies[t - 1] * (1.0 - p.bioloss - p.biosens * dt * dt / p.dbsta / p.dbsta)
            )
        else
            v.nospecies[t] = p.nospecbase
        end
    end
end


#=
  Lagrange Interpolation
  Newton forward difference interpolation
  Trapezoidal Quadrature
  Simpsons Quadrature
  Composite Trapezoidal Rule Quadrature
  Composite Simpsons Rule Quadrature
=#

function Lagrange(n,x,y)
  #accepts a 2 by n matrix
  #evalates at x
  for i = [2,1]:[2,n]
    a = 1
    k = i
    for j = 1:
      if k == j
        continue
      else
        a *= ((x-x[j]) / (x[k]-x[j]))
      end
    end
    f[i] = a * y[i]
    for i = 1:n-1
      sum += f[i]
    end
  end
  return sum

function Newton(n,x,y)
  #accepts x,y vectors
  for j = 1:n-1
    for i = n-1:-1:j
      y[i] = (y[i]-y[i-1]) / (x[i]-x[i-j-1])
    end
    for i = n-1:-1:0
      a = 1;
      for j= 1:i-1
          a *= a - x[j]
      end
      a *= y[j]
      sum += a
    end
  end
  return sum

function trapezoidalRule(p,a,b)
  #equally spaced nodes
  return h * (y[1]/2 + l1(y[2:end-1]) + y[end]/2)

function simpsonsRule(p,a,b)
  #even number eqaually spaced nodes
  a = 0
  b = 0
  for i = 2:2:end-1
    a += 2 * y[i]
  end
  for j = 3:2:end-1
    b += 4 * y[i]
  end
  return h/3 * (y[1] + a + b + y[end])

function compositeTrapezoidal(p,h,a,b)
  #equally-spaced nodes
  return trapezoidalRule(p,a,b) + (h * l1(y[1:end-1])

function compositeSimpsons(p,n,h,a,b)
  #even number n equally spaced nodes
  a = 0
  for i = 1:n/2
    a += y[2i - 2] + 4y[2i-1] + y[2i])
  end
  return h/3 * a


include("cg_sem.jl")
using ForwardDiff: derivative
using LinearAlgebra: eigen

function mms_test(N, K, z0, z1, cfl)
    elem = element_operators(N)
    mesh = create_mesh(K, elem, z0, z1)

    problem = Isothermal{Float64}()

    # mms solution
    f(z, t) = cos(t) * cos(2π * (z - z0) / (z1 - z0))
    ρ′(z, t) = f(1z, 4t)
    w′(z, t) = f(2z, 2t)
    E′(z, t) = f(4z, 1t)

    ∂tρ′(z, t) = derivative(t -> ρ′(z, t), t)
    ∂tw′(z, t) = derivative(t -> w′(z, t), t)
    ∂tE′(z, t) = derivative(t -> E′(z, t), t)

    ∂zρ̄w(z, t) = derivative(z -> ρ̄_(problem, z) * w′(z, t), z)
    ∂zδp(z, t) = derivative(z -> δp_(problem, z, E′(z, t), ρ′(z, t)), z)
    ∂zĒ_p̄w′(z, t) =
        derivative(z -> (Ē_(problem, z) + p̄_(problem, z)) * w′(z, t), z)

    # Forcing for each term
    fρ(z, t) = ∂tρ′(z, t) + ∂zρ̄w(z, t)

    grav_source(z, t) = problem.g * ρ′(z, t)
    fw(z, t) = ∂tw′(z, t) + (grav_source(z, t) + ∂zδp(z, t)) / ρ̄_(problem, z)

    fE(z, t) = ∂tE′(z, t) + ∂zĒ_p̄w′(z, t)

    forcing = (ρ = fρ, w = fw, E = fE)

    # Matrices for upwinding the boundary conditions
    A0 = A_(problem, z0)
    (λ, V) = eigen(A0)
    A0⁺ = V * Diagonal(max.(0, λ)) / V
    A0⁻ = V * Diagonal(min.(0, λ)) / V

    A1 = -A_(problem, z1)
    (λ, V) = eigen(A1)
    A1⁺ = V * Diagonal(max.(0, λ)) / V
    A1⁻ = V * Diagonal(min.(0, λ)) / V

    bc = (
        (A = A0, A⁺ = A0⁺, A⁻ = A0⁻, δw = t -> w′(z0, t)),
        (A = A1, A⁺ = A1⁺, A⁻ = A1⁻, δw = t -> w′(z1, t)),
    )

    # Calculate the wave speed
    c̄ = maximum(c̄_(problem, mesh.zdg))

    # estimate time step
    dt = cfl * mesh.Δz / (N^2 * c̄)

    # Make sure we end at the right time
    tspan = (0, 11π)
    steps = ceil(Int, (tspan[2] - tspan[1]) / dt)
    dt = (tspan[2] - tspan[1]) / steps

    # Initial conditions
    q = (
        δρ = ρ′.(mesh.zdg, tspan[1]),
        δw = w′.(mesh.zdg, tspan[1]),
        δE = E′.(mesh.zdg, tspan[1]),
    )

    # advance in time
    rhs!(∂q, q, t) = tendency!(∂q, q, t, problem, mesh, elem, bc, forcing)
    timestep!(q, rhs!, dt, tspan)

    # Exact solution at the final time
    qe = (
        δρ = ρ′.(mesh.zdg, tspan[2]),
        δw = w′.(mesh.zdg, tspan[2]),
        δE = E′.(mesh.zdg, tspan[2]),
    )

    # Calculate the L2 error
    ϵ = zeros(3)
    for i in 1:3
        ϵ[i] = sqrt(sum(mesh.J .* elem.ω .* (q[i] - qe[i]) .^ 2))
    end

    return ϵ
end

function mms_run(N, Ks; cfl = 1)
    ϵ = zeros(3, length(Ks))
    for (lvl, K) in enumerate(Ks)
        @show lvl, K
        ϵ[:, lvl] .= mms_test(N, K, 0, 100π, cfl)
        @show ϵ[:, lvl]
        if lvl > 1
            rate =
                (log.(ϵ[:, lvl]) - log.(ϵ[:, lvl - 1])) ./
                (log.(Ks[lvl - 1]) - log.(Ks[lvl]))
            @show rate
        end
        println()
    end
end


#=
main:
- Julia version: 1.3.1
- Author: abd_alazez_ahmed
- Date: 2020-04-02
=#
using Plots
pyplot()
thetaRange=range(-pi,stop=pi,length=100)
println("ϵₐ = ")
ep_a = 1200
println("ϵᵦ = ")
ep_b = 600
println("ϵᵪ = ")
ep_c = -700
println("θₐ = ")
theta_a = -60
println("θᵦ = ")
theta_ab = 60
println("θᵪ = ")
theta_ac = 120
ep = inv([sind(theta_ab)^2 0.5*sind(2*theta_ab); sind(theta_ac)^2 0.5*sind(2*theta_ac)])*[ep_b - ep_a*cosd(theta_ab)^2 ; ep_c - ep_a*cosd(theta_ab)^2]
final_ep = [cosd(theta_a)^2 sind(theta_a)^2 sind(2*theta_a);sind(theta_a)^2 cosd(theta_a)^2 -sind(2*theta_a);-0.5*sind(2*theta_a) 0.5*sind(2*theta_a) cosd(2*theta_a)]*[ep_a;ep[1];ep[2]/2]
println(ep)
final_ep[3]*=2
println(final_ep)
E=2e11
v=0.3
sigMat=E/(1-v^2)*[1 v 0;v 1 0;0 0 (1-v)/2]*final_ep
sigma_x = sigMat[1]
sigma_y = sigMat[2]
tau = sigMat[3]
r = sqrt((sigma_x-sigma_y)^2 /4 + tau^2)
plot((sigma_x+sigma_y)/2 .+ r*cos.(thetaRange), r.*sin.(thetaRange),aspect_ratio=1,framestyle = :origin)
plot!([sigma_x,sigma_x],[0,-tau])
hline!([-tau])
plot!([sigma_y,(sigma_x+sigma_y)/2 + 1.1*r],[-tau,0.1*r*(tau)/((sigma_x+sigma_y)/2 + r-sigma_y)])
plot!([sigma_y,(sigma_x+sigma_y)/2],[-tau,-r])
savefig("Stress.svg")
r = sqrt((final_ep[1]-final_ep[2])^2 /4 + final_ep[3]^2)
plot((final_ep[1]+final_ep[2])/2 .+ r*cos.(thetaRange), r.*sin.(thetaRange),aspect_ratio=1,framestyle = :origin)
plot!([final_ep[1],final_ep[1]],[0,-final_ep[3]])
hline!([-final_ep[3]])
plot!([final_ep[2],(final_ep[1]+final_ep[2])/2 + 1.1*r],[-final_ep[3],0.1*r*(final_ep[3])/((final_ep[1]+final_ep[2])/2 + r-final_ep[2])])
plot!([final_ep[2],(final_ep[1]+final_ep[2])/2],[-final_ep[3],-r])
savefig("Strain.svg")


function psiNextLebesgueMeasure(p::Vector,psi::Vector)
    N = length(psi)
    psik = zero(psi)
    if length(p)==1
        p = cat(1,zero(p[1]),p)
        c = sqrt(1-abs2(p[end]))
    else
        c = sqrt((1-abs2(p[end]))/(1-abs2(p[end-1])))
    end
    for j=2:(N)
        psik[j] = c*sum([p[end]^(j-i)*(psi[i-1]-conj(p[end-1])*psi[i]) for i=2:j])
    end
    return psik
end

function psiBasis(p::Vector,u::Vector)
    N = length(u)
    B = zeros(Complex128,(N,length(p)+1))
    B[:,1] = u
    for k=1:length(p)
        B[:,k+1] = psiNextLebesgueMeasure([p[1:k]],B[:,k])
    end
    return B[2:end,2:end]
end

function dictObjective(x::Vector,g::Vector,target::Vector,u::Vector)
    p = x[1]*exp(im*2*pi*x[2])
    trial = psiNextLebesgueMeasure([p],u)[2:end]
    return abs2(dot(target,trial))
end

function unitStep(k)
    return k>=0 ? 1 : 0
end

function invBlaschkeElem(p::Complex128,K::Int)
    return [((conj(p)^(-i))*unitStep(i-1)-conj(p)^(-i-1))::Complex128 for i=0:(K-1)]
end

function unitDiskGrid(n::Int,delta::Float64)
    N = nextpow2(n)
    M = N>>1
    v = linspace(delta,one(Float64)-delta,M)
    Zjk = zeros(Complex128,N,N)
    for j=1:(M)
             #bottom, running counter-clockwise
            Zjk[(M+j),((M-(j-1)):(M+1+(j-1)))] = v[j]*exp(im*linspace(5*pi/4,7*pi/4,2*j))
            #top, running clockwise
            Zjk[(M-j+1),((M-(j-1)):(M+1+(j-1)))] = v[j]*exp(im*linspace(3*pi/4,1*pi/4,2*j))
    end
    for j=1:(M)
            #right, running clockwise
            Zjk[((M-(j-1)):(M+1+(j-1))),M+j] = v[j]*exp(im*linspace(pi/4,-pi/4,2*j)) #right
            #left, running counter-clockwise
            Zjk[((M-(j-1)):(M+1+(j-1))),M-j+1] = v[j]*exp(im*linspace(3*pi/4,5*pi/4,2*j)) #left
    end
    return reshape(Zjk,N^2)
end

function afdT(target::Vector,u::Vector,Npoles::Int,Nstarts::Int,lmethod,ltol)
    Zstarts = unitDiskGrid(int(sqrt(Nstarts)),0.3)
    opt = Opt(lmethod,2)
    ftol_rel!(opt, ltol) #local stopping criteria
    lower_bounds!(opt,[0.01; 0])
    upper_bounds!(opt,[0.9;  1-eps(Float64)])
    p = zeros(Complex128,Npoles)
    R = copy(target)
    for j=1:Npoles
        ovec = zeros(Float64,length(Zstarts))
        avec = zeros(Float64,(length(Zstarts),2))
        max_objective!(opt,(x,g)->dictObjective(x,g,R,u))
        for k=1:length(Zstarts)
            beta1 = abs(Zstarts[k]);
            beta2 = (angle(Zstarts[k])+pi)/(2*pi);
            beta0 = [beta1; beta2]
            (ovec[k],avec[k,:],e) = optimize(opt,beta0)
        end
        aopt = avec[indmax(ovec),:]
        p[j] = aopt[1]*exp(im*2*pi*aopt[2])
        B = psiBasis([p[1:j]],u)
        R = (eye(length(target))-B*pinv(B))*target
        Q = invBlaschkeElem(p[j],(length(target))*4)
        R = oneSidedConv(R,Q)[1:length(target)]
    end
    return p
end

function oneSidedConv(u::Vector,v::Vector)
    U = fft(u)
    V = fft(v)
    if length(U)<=length(V)
        return ifft(V.*cat(1,U,zeros(Complex128,length(V)-length(U))))
    else
        return ifft(U.*cat(1,V,zeros(Complex128,length(U)-length(V))))
    end
end


julia> [1 1 ; 1 0]^10
2x2 Array{Int64,2}:
 89  55
 55  34


const PWGRID_VERSION = 2

mutable struct GVectorsW
    Ngwx::Int
    idx_gw2r::Array{Int}
end

mutable struct GVectors
    Ng::Int
    G::Array{Float64,2}
    G2::Array{Float64}
end

mutable struct PWGrid
    Ns::Array{Int64}
    LatVecs::Array{Float64,2}
    RecVecs::Array{Float64,2}
    Ω::Float64
    r::Array{Float64,2}
    gvec::GVectors
    gvecw::GVectorsW
end

function PWGrid( Ns, LatVecs::Array{Float64,2} )

    if any( Ns .% 2 .== 1 )
        @printf("Error: Ns must be even numbers\n")
        exit()
    end

    RecVecs = 2*pi*inv(LatVecs')
    Ω = det(LatVecs)
    #
    LatVecsLen = Array{Float64}(undef,3)
    LatVecsLen[1] = norm(LatVecs[:,1])
    LatVecsLen[2] = norm(LatVecs[:,2])
    LatVecsLen[3] = norm(LatVecs[:,3])

    Npoints = prod(Ns)
    r = init_grid_R( Ns, LatVecs )

    gvec = init_grid_G( Ns, RecVecs )
    gvecw = init_gvecw( Ns, gvec.G2 )

    return PWGrid( Ns, LatVecs, RecVecs, Ω, r, gvec, gvecw )
end

function mm_to_nn(mm::Int,S::Int)
    if mm > S/2
        return mm - S
    else
        return mm
    end
end

function init_grid_G( Ns, RecVecs )

    Ng = prod(Ns)

    G  = Array{Float64}(undef,3,Ng)
    G2 = Array{Float64}(undef,Ng)

    ig = 0
    for k in 0:Ns[3]-1
    for j in 0:Ns[2]-1
    for i in 0:Ns[1]-1
        ig = ig + 1
        gi = mm_to_nn( i, Ns[1] )
        gj = mm_to_nn( j, Ns[2] )
        gk = mm_to_nn( k, Ns[3] )
        G[1,ig] = RecVecs[1,1]*gi + RecVecs[1,2]*gj + RecVecs[1,3]*gk
        G[2,ig] = RecVecs[2,1]*gi + RecVecs[2,2]*gj + RecVecs[2,3]*gk
        G[3,ig] = RecVecs[3,1]*gi + RecVecs[3,2]*gj + RecVecs[3,3]*gk
        G2[ig] = G[1,ig]^2 + G[2,ig]^2 + G[3,ig]^2
    end
    end
    end

    return GVectors( Ng, G, G2 )
end

function init_gvecw( Ns, G2 )
    edges = find_edges( Ns )
    G2mx = minimum( G2[edges] )
    #@printf("G2mx = %f\n", G2mx)
    @printf("ecutwfc_Ry = %f\n", G2mx/4)
    idx_gw2r = findall( G2 .< G2mx/4 )
    Ngwx = length(idx_gw2r)
    return GVectorsW( Ngwx, idx_gw2r )
end

function find_edges( Ns )
    eS = Ns/2 .+ 0.5
    edges = []
    ip = 1
    for k in 0:Ns[3]-1
    for j in 0:Ns[2]-1
    for i in 0:Ns[1]-1
        ei = abs(i - eS[1])
        ej = abs(j - eS[2])
        ek = abs(k - eS[3])
        # if any of i, j, or k is equal to Ns/2 or Ns/2+1
        if ei < 1.0 || ej < 1.0 || ek < 1.0
            push!(edges,ip)
        end
        ip = ip + 1
    end
    end
    end
    return edges
end

function init_grid_R( Ns, LatVecs )
    #
    Npoints = prod(Ns)
    #
    R = Array{Float64}(undef,3,Npoints)
    ip = 0
    for k in 0:Ns[3]-1
    for j in 0:Ns[2]-1
    for i in 0:Ns[1]-1
        ip = ip + 1
        R[1,ip] = LatVecs[1,1]*i/Ns[1] + LatVecs[1,2]*j/Ns[2] + LatVecs[1,3]*k/Ns[3]
        R[2,ip] = LatVecs[2,1]*i/Ns[1] + LatVecs[2,2]*j/Ns[2] + LatVecs[2,3]*k/Ns[3]
        R[3,ip] = LatVecs[3,1]*i/Ns[1] + LatVecs[3,2]*j/Ns[2] + LatVecs[3,3]*k/Ns[3]
    end
    end
    end
    #
    return R
end


using MultivariateStats
using LinearAlgebra
using Test
using StableRNGs
using Statistics: mean, cov
using StatsBase: ConvergenceException

@testset "ICA" begin

    rng = StableRNG(15678)

    function generatetestdata(rng, n, k, m)
        t = range(0.0, step=10.0, length=n)
        s1 = sin.(t * 2)
        s2 = s2 = 1.0 .- 2.0 * Bool[isodd(floor(Int, x / 3)) for x in t]
        s3 = Float64[mod(x, 5.0) for x in t]

        s1 += 0.1 * randn(rng, n)
        s2 += 0.1 * randn(rng, n)
        s3 += 0.1 * randn(rng, n)

        S = hcat(s1, s2, s3)'
        @assert size(S) == (k, n)

        A = randn(rng, m, k)

        X = A * S
        mv = vec(mean(X, dims=2))
        @assert size(X) == (m, n)
        C = cov(X, dims=2)
        return X, mv, C
    end

    @testset "Auxiliary" begin

        f = MultivariateStats.Tanh(1.0)
        u, v = ones(1,1), zeros(1)
        MultivariateStats.update!(f, u, v)
        @test u[] ≈ 0.7615941559557649
        @test v[] ≈ 0.41997434161402614

        f = MultivariateStats.Tanh(1.0f0)
        u, v = ones(Float32,1,1), zeros(Float32,1)
        MultivariateStats.update!(f, u, v)
        @test u[] ≈ 0.7615942f0
        @test v[] ≈ 0.41997433f0

        f = MultivariateStats.Gaus()
        u, v = ones(1,1), ones(1)
        MultivariateStats.update!(f, u, v)
        @test u[] ≈ 0.6065306597126334
        @test v[] ≈ 0.0

        u, v = ones(Float32,1,1), ones(Float32,1)
        MultivariateStats.update!(f, u, v)
        @test u[] ≈ 0.60653067f0
        @test v[] ≈ 0.0f0

    end

    ## data
    @testset "Algorithm" begin

        # sources
        n = 1000
        k = 3
        m = 8
        X, μ, C = generatetestdata(rng, n, k, m)

        # FastICA

        M = fit(ICA, X, k; do_whiten=false, tol=Inf)
        @test isa(M, ICA)
        @test size(M) == (m,k)
        @test mean(M) == μ
        W = M.W
        @test predict(M, X) ≈ W' * (X .- μ)
        @test W'W ≈ Matrix(I, k, k)

        M = fit(ICA, X, k; do_whiten=true, tol=Inf)
        @test isa(M, ICA)
        @test size(M) == (m,k)
        @test mean(M) == μ
        W = M.W
        @test W'C * W ≈ Matrix(I, k, k)

        @test_throws ConvergenceException fit(ICA, X, k; do_whiten=true, tol=1e-8, maxiter=2)

        # Use data of different type
        XX = convert(Matrix{Float32}, X)

        MM = fit(ICA, XX, k; do_whiten=true, tol=Inf)
        @test eltype(mean(MM)) == Float32
        @test eltype(MM.W) == Float32

        MM = fit(ICA, XX, k; do_whiten=false, tol=Inf)
        @test isa(MM, ICA)
        @test eltype(mean(MM)) == Float32
        @test eltype(MM.W) == Float32
        W = MM.W
        @test predict(MM, X) ≈ W' * convert(Matrix{Float32}, (X .- μ))
        @test predict(M, XX) ≈ M.W' * (X .- μ) atol=1e-4
        @test W'W ≈ Matrix{Float32}(I, k, k)

        # input as view
        M = fit(ICA, view(XX, :, 1:400), k; do_whiten=true, tol=Inf)
        @test eltype(mean(MM)) == Float32
        @test eltype(MM.W) == Float32
    end

end


using LinearAlgebra
using AdFem
using DelimitedFiles

A = readdlm("fenics/A.txt")
A1 = readdlm("fenics/B.txt")

A = [A A1]
mesh = Mesh(8, 8, 1. /8, degree=1)
B = compute_interaction_matrix(mesh)

@show norm(A - B)

mesh = Mesh(8, 8, 1. / 8, degree=2)

A = readdlm("fenics/A2.txt")
A1 = readdlm("fenics/B2.txt")
E = Int64.(readdlm("fenics/edges.txt"))
Edof = get_edge_dof(E, mesh)
A = [A A1]
B = Array(compute_interaction_matrix(mesh))

B[:, mesh.nnode + 1:mesh.ndof] = B[:, mesh.nnode + 1:mesh.ndof][:, Edof]
B[:, mesh.ndof + mesh.nnode + 1:2mesh.ndof] = B[:, mesh.ndof + mesh.nnode + 1:2mesh.ndof][:, Edof]
@show norm(A - B)


@defcomp climatedynamics begin
    # Total radiative forcing
    radforc = Parameter(index=[time])

    # Average global temperature
    temp = Variable(index=[time])

    # lifetempconst
    lifetempconst = Parameter()

    # lifetemplin
    lifetemplin = Parameter()

    # lifetempqd
    lifetempqd = Parameter()

    # Climate sensitivity
    climatesensitivity = Parameter(default = 2.999999803762826)

    function run_timestep(p, v, d, t)

        if is_first(t)
            v.temp[t] = 0.20
        else
            LifeTemp = max(p.lifetempconst + p.lifetemplin * p.climatesensitivity + p.lifetempqd * p.climatesensitivity^2.0, 1.0)

            delaytemp = 1.0 / LifeTemp

            temps = p.climatesensitivity / 5.35 / log(2.0)

            # Calculate temperature
            dtemp = delaytemp * temps * p.radforc[t] - delaytemp * v.temp[t - 1]

            v.temp[t] = v.temp[t - 1] + dtemp
        end
    end
end


using FractionalDiffEq, Plots, SpecialFunctions

phi(x)=0
α=0.9; τ = 0.1
f(t, y, ϕ)=2/gamma(3-α)*t^(2-α)-1/gamma(2-α)*t^(1-α)+2*τ*t-τ^2-τ-y+ϕ
h = 1e-2
realfun(t) = t^2-t

prob = FDDEProblem(f, phi, α, τ, (0, 5))
sol1 = solve(prob, h, DelayPI())
sol2 = solve(prob, h, DelayABM())
sol3 = sove(prob, h, DelayPECE())


######################### Internal/Private functions #######################
function idealGasRho(T, P, R=287.05)
    @fastmath return P/(R*T)
end

function idealGasP(rho, T, R=287.05)
    # PV = mRT
    # P = rho R T
    @fastmath return rho*R*T
end

function calPerfectEnergy(T, fluid::Fluid)
    @fastmath return T * (fluid.Cp - fluid.R)
end

function calPerfectT(e, fluid::Fluid)
    @fastmath return e / (fluid.Cp - fluid.R)
end

######################### Public functions (called by FvCFD.jl) #######################
#=
    Calculate primitives from cellState
=#
function decodePrimitives3D!(primitives, cellState, fluid::Fluid)
    ## Velocity ##
    # Ux = xMom/rho
    primitives[3] = cellState[2]/cellState[1]
    # Uy = yMom/rho
    primitives[4] = cellState[3]/cellState[1]
    # Uz = zMom/rho
    primitives[5] = cellState[4]/cellState[1]

    ## Energy ##
    # e = (eV2/rho) - (mag((Ux, Uy, Uz))^2)/2
    @views e = (cellState[5]/cellState[1]) - (mag(primitives[3:5])^2)/2

    ## Temperature ##
    primitives[2] = calPerfectT(e, fluid)

    ## Pressure ##
    # P = idealGasP(rho, P, R)
    primitives[1] = idealGasP(cellState[1], primitives[2], fluid.R)
end

#=
    Calculate cellState from cellPrimitives

    Arguments:
        cellPrimitives: matrix of primitives: see dataStructuresDefinitions.md
        R: R-value of the fluid (J/(kg*K))
        Cp: Constant pressure specific heat capacity (kJ/(kgK))

    Returns:
        cellState: 2D vector of conserved variable values: see dataStructuresDefinitions.md
=#
function encodePrimitives3D(cellPrimitives::Array{Float64, 2}, fluid::Fluid)
    nCells = size(cellPrimitives, 1)

    cellState = zeros(nCells, 5)
    for c in 1:nCells
        cellState[c,1] = idealGasRho(cellPrimitives[c,2], cellPrimitives[c,1], fluid.R)
        cellState[c,2:4] .= cellPrimitives[c,3:5] .* cellState[c,1]
        e = calPerfectEnergy(cellPrimitives[c,2], fluid)
        cellState[c,5] = cellState[c,1]*(e + (mag(cellPrimitives[c,3:5])^2)/2 )
    end
    return cellState
end

#=
    Calculates fluxes of transported variables at cell center, from
    Arguments:
        fluxes: (output) vector of cell center fluxes (to be calculated/populated)
        prim:   (input) vector of cell center primitives
        state:  (input) vector of cell center state

    Returns:
        None. Calculation results store in fluxes variable

    Notes:
        See dataStructuresDefinitions.md for definitions of state, primitives, etc...

=#
function calculateFluxes3D!(fluxes, prim, state)
    #### Mass Fluxes ####
    fluxes[1] = state[2]
    fluxes[2] = state[3]
    fluxes[3] = state[4]

    #### Momentum Fluxes ####
    ## x-direction momentum fluxes ##
    # xMomxFlux = xMom*Ux + P
    fluxes[4] = state[2]*prim[3] + prim[1]
    # yMomxFlux = xMom*Uy
    fluxes[7] = state[2]*prim[4]
    # zMomxFlux = xMom*Uz
    fluxes[10] = state[2]*prim[5]

    ## y-direction momentum fluxes ##
    # xMomyFlux = yMomxFlux
    fluxes[5] = fluxes[7]
    # yMomyFlux = yMom*Uy + P
    fluxes[8] = state[3]*prim[4] + prim[1]
    # zMomyFlux = yMom*Uz
    fluxes[11] = state[3]*prim[5]

    ## z-direction momentum fluxes ##
    # xMomzFlux = zMomxFlux
    fluxes[6] = fluxes[10]
    # yMomzFlux = zMomyFlux
    fluxes[9] = fluxes[11]
    # zMomzFlux = zMom*Uz + P
    fluxes[12] = state[4]*prim[5] + prim[1]

    #### Energy Fluxes ###
    # eV2xFlux = Ux*eV2 + P*Ux
    fluxes[13] = prim[3]*state[5] + prim[1]*prim[3]
    # eV2yFlux = Uy*eV2 + P*Uy
    fluxes[14] = prim[4]*state[5] + prim[1]*prim[4]
    # eV2zFlux = Uz*eV2 + P*Uz
    fluxes[15] = prim[5]*state[5] + prim[1]*prim[5]
end


module ContComplianceDemo1

using TopOpt, LinearAlgebra, StatsFuns
# using Makie
# using TopOpt.TopOptProblems.Visualization: visualize

E = 1.0 # Young’s modulus
v = 0.3 # Poisson’s ratio
f = 1.0 # downward force
rmin = 4.0 # filter radius
xmin = 0.0001 # minimum density
problem_size = (60, 20)
V = 0.5 # maximum volume fraction
p = 4.0 # penalty
x0 = fill(V, prod(problem_size)) # initial design

problem = HalfMBB(Val{:Linear}, problem_size, (1.0, 1.0), E, v, f)

solver = FEASolver(Direct, problem; xmin=xmin)
cheqfilter = DensityFilter(solver; rmin=rmin)
comp = TopOpt.Compliance(problem, solver)

function obj(x)
    # minimize compliance
    return comp(cheqfilter(x))
end
function constr(x)
    # volume fraction constraint
    return sum(cheqfilter(x)) / length(x) - V
end

m = Model(obj)
addvar!(m, zeros(length(x0)), ones(length(x0)))
Nonconvex.add_ineq_constraint!(m, constr)

options = MMAOptions(; maxiter=1000, tol=Tolerance(; kkt=1e-4, x=1e-4, f=1e-4))
TopOpt.setpenalty!(solver, p)
# Method of Moving Asymptotes
@time r = Nonconvex.optimize(m, MMA87(), x0; options=options)

@show obj(r.minimizer)
@show constr(r.minimizer)
topology = cheqfilter(r.minimizer)
# fig = visualize(problem, solver.u;
#     topology = topology, default_exagg_scale=0.0, scale_range=10.0)
# Makie.display(fig)

end


# Simple Advection problem

# build mesh
include("../src/DiscontinuousGalerkin.jl")
using DiscontinuousGalerkin

const CFL = 0.5
const Tend = 1.0

f(::Type{Val{:jac}},u::Vector) = eye(size(u,1))
f(u::Vector) = u

#define max wave speed
max_w_speed(u, f) = 1

f0(x) = sin(2*π*x)

function get_problem(N)
  mesh = Uniform1DFVMesh(N,-2.0,2.0,:PERIODIC,:PERIODIC)
  ConservationLawsProblem(f0,f,CFL,Tend,mesh)
end
#Compile
prob = get_problem(10)

#Run
prob = get_problem(200)

#Generate basis for Discontinuous Galerkin Scheme
basis=legendre_basis(3)

#Optional dgLimiter
limiter! = DGLimiter(prob, basis, Linear_MUSCL_Limiter())

#Solve problem
@time sol = solve(prob, DiscontinuousGalerkinScheme(basis, advection_num_flux; max_w_speed = max_w_speed); TimeIntegrator = SSPRK22(limiter!))

#Plot
using Plots
plot(sol)


using Distributions, UncertainData

# Test all combinations of different types of uncertain values
M = MixtureModel([Normal(3, 0.2), Normal(2, 1)])

r1 = UncertainValue(Normal, rand(), rand())
r2 = UncertainValue(rand(M, 10000))
r3 = UncertainValue(Normal, rand(Normal(4, 3.2), 10000))

uvals = [r1; r2; r3]

n = 5
for uval in uvals
    # Sine and inverse sine
    @test sin(uval) isa Vector{Float64}
    @test sin(uval, n) isa Vector{Float64}
    @test sind(uval) isa Vector{Float64}
    @test sind(uval, n) isa Vector{Float64}
    @test sinh(uval) isa Vector{Float64}
    @test sinh(uval, n) isa Vector{Float64}

    #@test asin(uval) isa Vector{Float64}
    #@test asin(uval, n) isa Vector{Float64}
    #@test asind(uval) isa Vector{Float64}
    #@test asind(uval, n) isa Vector{Float64}
    #@test asinh(uval) isa Vector{Float64}
    #@test asinh(uval, n) isa Vector{Float64}

    # Cosine and inverse cosine
    @test cos(uval) isa Vector{Float64}
    @test cos(uval, n) isa Vector{Float64}
    @test cosd(uval) isa Vector{Float64}
    @test cosd(uval, n) isa Vector{Float64}
    @test cosh(uval) isa Vector{Float64}
    @test cosh(uval, n) isa Vector{Float64}

    # @test acos(uval) isa Vector{Float64}
    # @test acos(uval, n) isa Vector{Float64}
    # @test acosd(uval) isa Vector{Float64}
    # @test acosd(uval, n) isa Vector{Float64}
    # @test acosh(uval) isa Vector{Float64}
    # @test acosh(uval, n) isa Vector{Float64}

    # Tangent and inverse tangent
    @test tan(uval) isa Vector{Float64}
    @test tan(uval, n) isa Vector{Float64}
    @test tand(uval) isa Vector{Float64}
    @test tand(uval, n) isa Vector{Float64}
    @test tanh(uval) isa Vector{Float64}
    @test tanh(uval, n) isa Vector{Float64}

    # @test atan(uval) isa Vector{Float64}
    # @test atan(uval, n) isa Vector{Float64}
    # @test atand(uval) isa Vector{Float64}
    # @test atand(uval, n) isa Vector{Float64}
    # @test atanh(uval) isa Vector{Float64}
    # @test atanh(uval, n) isa Vector{Float64}

    # Cosecant and inverse cosecant
    @test csc(uval) isa Vector{Float64}
    @test csc(uval, n) isa Vector{Float64}
    @test cscd(uval) isa Vector{Float64}
    @test cscd(uval, n) isa Vector{Float64}
    @test csch(uval) isa Vector{Float64}
    @test csch(uval, n) isa Vector{Float64}

    # @test acsc(uval) isa Vector{Float64}
    # @test acsc(uval, n) isa Vector{Float64}
    # @test acscd(uval) isa Vector{Float64}
    # @test acscd(uval, n) isa Vector{Float64}
    # @test acsch(uval) isa Vector{Float64}
    # @test acsch(uval, n) isa Vector{Float64}

    # Secant and inverse secant
    @test sec(uval) isa Vector{Float64}
    @test sec(uval, n) isa Vector{Float64}
    @test secd(uval) isa Vector{Float64}
    @test secd(uval, n) isa Vector{Float64}
    @test sech(uval) isa Vector{Float64}
    @test sech(uval, n) isa Vector{Float64}

    # @test asec(uval) isa Vector{Float64}
    # @test asec(uval, n) isa Vector{Float64}
    # @test asecd(uval) isa Vector{Float64}
    # @test asecd(uval, n) isa Vector{Float64}
    # @test asech(uval) isa Vector{Float64}
    # @test asech(uval, n) isa Vector{Float64}

    # Cotangent and inverse cotangent
    @test cot(uval) isa Vector{Float64}
    @test cot(uval, n) isa Vector{Float64}
    @test cotd(uval) isa Vector{Float64}
    @test cotd(uval, n) isa Vector{Float64}
    @test coth(uval) isa Vector{Float64}
    @test coth(uval, n) isa Vector{Float64}

    # @test acot(uval) isa Vector{Float64}
    # @test acot(uval, n) isa Vector{Float64}
    # @test acotd(uval) isa Vector{Float64}
    # @test acotd(uval, n) isa Vector{Float64}
    # @test acoth(uval) isa Vector{Float64}
    # @test acoth(uval, n) isa Vector{Float64}

    # Related trig functions
    @test sincos(uval) isa Vector{Tuple{Float64, Float64}}
    @test sincos(uval, n) isa Vector{Tuple{Float64, Float64}}

    @test sinc(uval) isa Vector{Float64}
    @test sinc(uval, n) isa Vector{Float64}

    @test sinpi(uval) isa Vector{Float64}
    @test sinpi(uval, n) isa Vector{Float64}

    @test cosc(uval) isa Vector{Float64}
    @test cosc(uval, n) isa Vector{Float64}

    @test cospi(uval) isa Vector{Float64}
    @test cospi(uval, n) isa Vector{Float64}
end