# This file is a modified from Julia's LinearAlgebra/qr.jl. License is MIT: https://julialang.org/license

# QL and Hessenberg Factorizations
"""
    QL <: Factorization

A QL matrix factorization stored in a packed format, typically obtained from
[`ql`](@ref). If ``A`` is an `m`×`n` matrix, then

```math
A = Q L
```

where ``Q`` is an orthogonal/unitary matrix and ``L`` is lower triangular.
The matrix ``Q`` is stored as a sequence of Householder reflectors ``v_i``
and coefficients ``\\tau_i`` where:

```math
Q = \\prod_{i=1}^{\\min(m,n)} (I - \\tau_i v_i v_i^T).
```

Iterating the decomposition produces the components `Q` and `L`.

The object has two fields:

* `factors` is an `m`×`n` matrix.

  - The lower triangular part contains the elements of ``L``, that is `L =
    tril(F.factors)` for a `QL` object `F`.

  - The subdiagonal part contains the reflectors ``v_i`` stored in a packed format where
    ``v_i`` is the ``i``th column of the matrix `V = I + tril(F.factors, -1)`.

* `τ` is a vector  of length `min(m,n)` containing the coefficients ``\tau_i``.

"""
struct QL{T,S<:AbstractMatrix{T},Tau<:AbstractVector{T}} <: Factorization{T}
    factors::S
    τ::Tau

    function QL{T,S,Tau}(factors, τ) where {T,S<:AbstractMatrix{T},Tau<:AbstractVector{T}}
        require_one_based_indexing(factors)
        new{T,S,Tau}(factors, τ)
    end
end
QL(factors::AbstractMatrix{T}, τ::AbstractVector{T}) where {T} = QL{T,typeof(factors),typeof(τ)}(factors, τ)
function QL{T}(factors::AbstractMatrix, τ::AbstractVector) where {T}
    QL(convert(AbstractMatrix{T}, factors), convert(AbstractVector{T}, τ))
end

# iteration for destructuring into components
Base.iterate(S::QL) = (S.Q, Val(:L))
Base.iterate(S::QL, ::Val{:L}) = (S.L, Val(:done))
Base.iterate(S::QL, ::Val{:done}) = nothing

function generic_qlfactUnblocked!(A::AbstractMatrix{T}) where {T}
    require_one_based_indexing(A)
    m, n = size(A)
    τ = zeros(T, min(m,n))
    for k = n:-1:max((n - m + 1 + (T<:Real)),1)
        μ = m+k-n
        x = view(A, μ:-1:1, k)
        τk = reflector!(x)
        τ[k-n+min(m,n)] = τk
        reflectorApply!(x, τk, view(A, μ:-1:1, 1:k-1))
    end
    QL(A, τ)
end

@inline qlfactUnblocked!(A::AbstractMatrix) = generic_qlfactUnblocked!(A)
@inline qlfactUnblocked!(A::StridedMatrix{T}) where T<:BlasFloat = QL(LAPACK.geqlf!(A)...)

# Generic fallbacks

"""
    ql!(A, pivot=Val(false))

`ql!` is the same as [`ql`](@ref) when `A` is a subtype of
`StridedMatrix`, but saves space by overwriting the input `A`, instead of creating a copy.
An [`InexactError`](@ref) exception is thrown if the factorization produces a number not
representable by the element type of `A`, e.g. for integer types.

# Examples
```jldoctest
julia> a = [1. 2.; 3. 4.]
2×2 Array{Float64,2}:
 1.0  2.0
 3.0  4.0

julia> ql!(a)
LinearAlgebra.QLCompactWY{Float64,Array{Float64,2}}
Q factor:
2×2 LinearAlgebra.QLCompactWYQ{Float64,Array{Float64,2}}:
 -0.316228  -0.948683
 -0.948683   0.316228
 L factor:
2×2 Array{Float64,2}:
 -3.16228  -4.42719
  0.0      -0.632456

julia> a = [1 2; 3 4]
2×2 Array{Int64,2}:
 1  2
 3  4

julia> ql!(a)
ERROR: InexactError: Int64(-3.1622776601683795)
Stacktrace:
[...]
```
"""
@inline _ql!(layout, axes, A, ::Val{false}) = qlfactUnblocked!(A)
@inline _ql!(layout, axes, A) = ql!(A, Val(false))
@inline ql!(A::AbstractMatrix; kwds...) = _ql!(MemoryLayout(typeof(A)), axes(A), A; kwds...)
@inline ql!(A::AbstractMatrix, val; kwds...) = _ql!(MemoryLayout(typeof(A)), axes(A), A, val; kwds...)

@inline _qleltype(::Type{T}) where T = typeof(zero(T)/sqrt(abs2(one(T))))

"""
    ql(A, pivot=Val(false)) -> F

Compute the QL factorization of the matrix `A`: an orthogonal (or unitary if `A` is
complex-valued) matrix `Q`, and a lower triangular matrix `L` such that

```math
A = Q L
```

The returned object `F` stores the factorization in a packed format:

 - `F` is a [`QL`](@ref) object.

The individual components of the decomposition `F` can be retrieved via property accessors:

 - `F.Q`: the orthogonal/unitary matrix `Q`
 - `F.L`: the lower triangular matrix `L`

Iterating the decomposition produces the components `Q`, `L`, and if extant `p`.

The following functions are available for the `QL` objects: [`inv`](@ref), [`size`](@ref),
and [`\\`](@ref). When `A` is rectangular, `\\` will return a least squares
solution and if the solution is not unique, the one with smallest norm is returned. When
`A` is not full rank, factorization with (column) pivoting is required to obtain a minimum
norm solution.

Multiplication with respect to either full/square or non-full/square `Q` is allowed, i.e. both `F.Q*F.L`
and `F.Q*A` are supported. A `Q` matrix can be converted into a regular matrix with
[`Matrix`](@ref).  This operation returns the "thin" Q factor, i.e., if `A` is `m`×`n` with `m>=n`, then
`Matrix(F.Q)` yields an `m`×`n` matrix with orthonormal columns.  To retrieve the "full" Q factor, an
`m`×`m` orthogonal matrix, use `F.Q*Matrix(I,m,m)`.  If `m<=n`, then `Matrix(F.Q)` yields an `m`×`m`
orthogonal matrix.

# Examples
```jldoctest
julia> A = [3.0 -6.0; 4.0 -8.0; 0.0 1.0]
3×2 Array{Float64,2}:
 3.0  -6.0
 4.0  -8.0
 0.0   1.0

julia> F = ql(A)
LinearAlgebra.QLCompactWY{Float64,Array{Float64,2}}
Q factor:
3×3 LinearAlgebra.QLCompactWYQ{Float64,Array{Float64,2}}:
 -0.6   0.0   0.8
 -0.8   0.0  -0.6
  0.0  -1.0   0.0
L factor:
2×2 Array{Float64,2}:
 -5.0  10.0
  0.0  -1.0

julia> F.Q * F.L == A
true
```
"""
@inline ql(A::AbstractMatrix, args...; kwds...) = _ql(MemoryLayout(typeof(A)), axes(A), A, args...; kwds...)

function _ql(layout, axes, A, args...; kwds...)
    require_one_based_indexing(A)
    AA = similar(A, _qleltype(eltype(A)), size(A))
    copyto!(AA, A)
    return ql!(AA, args...; kwds...)
end
@inline ql(x::Number, args...; kwds...) = ql(fill(x,1,1), args...; kwds...)
@inline function ql(v::AbstractVector, args...; kwds...)
    require_one_based_indexing(v)
    ql(reshape(v, (length(v), 1)), args...; kwds...)
end

# Conversions
QL{T}(A::QL) where {T} = QL(convert(AbstractMatrix{T}, A.factors), convert(AbstractVector{T}, A.τ))
Factorization{T}(A::QL{T}) where {T} = A
Factorization{T}(A::QL) where {T} = QL{T}(A)
AbstractMatrix(F::QL) = F.Q * F.L
AbstractArray(F::QL) = AbstractMatrix(F)
Matrix(F::QL) = Array(AbstractArray(F))
Array(F::QL) = Matrix(F)

function show(io::IO, mime::MIME{Symbol("text/plain")}, F::QL)
    summary(io, F); println(io)
    println(io, "Q factor:")
    show(io, mime, F.Q)
    println(io, "\nL factor:")
    show(io, mime, F.L)
end

@inline function getL(F::QL, _)
    m, n = size(F)
    tril!(getfield(F, :factors)[end-min(m,n)+1:end, 1:n], max(n-m,0))
end
@inline getQ(F::QL, _) = QLPackedQ(getfield(F, :factors), F.τ)

getL(F::QL, ::Tuple{AbstractVector,AbstractVector}) = getL(F, size(F))
getQ(F::QL, ::Tuple{AbstractVector,AbstractVector}) = getQ(F, size(F))

getL(F::QL) = getL(F, axes(F.factors))
getQ(F::QL) = getQ(F, axes(F.factors))

function getproperty(F::QL, d::Symbol)
    if d == :L
        return getL(F)
    elseif d == :Q
        return getQ(F)
    else
        getfield(F, d)
    end
end

Base.propertynames(F::QL, private::Bool=false) =
    (:L, :Q, (private ? fieldnames(typeof(F)) : ())...)

"""
    QLPackedQ <: AbstractMatrix

The orthogonal/unitary ``Q`` matrix of a QL factorization stored in [`QL`](@ref).
"""
struct QLPackedQ{T,S<:AbstractMatrix{T},Tau<:AbstractVector{T}} <: LayoutQ{T}
    factors::S
    τ::Tau

    function QLPackedQ{T,S,Tau}(factors, τ) where {T,S<:AbstractMatrix{T},Tau<:AbstractVector{T}}
        require_one_based_indexing(factors)
        new{T,S,Tau}(factors, τ)
    end
end
QLPackedQ(factors::AbstractMatrix{T}, τ::AbstractVector{T}) where {T} = QLPackedQ{T,typeof(factors),typeof(τ)}(factors, τ)
function QLPackedQ{T}(factors::AbstractMatrix, τ::AbstractVector) where {T}
    QLPackedQ(convert(AbstractMatrix{T}, factors), convert(AbstractVector{T}, τ))
end

QLPackedQ{T}(Q::QLPackedQ) where {T} = QLPackedQ(convert(AbstractMatrix{T}, Q.factors), convert(AbstractVector{T}, Q.τ))
AbstractMatrix{T}(Q::QLPackedQ{T}) where {T} = Q
AbstractMatrix{T}(Q::QLPackedQ) where {T} = QLPackedQ{T}(Q)

size(Q::QLPackedQ, dim::Integer) = size(getfield(Q, :factors), dim == 2 ? 1 : dim)

size(F::QL, dim::Integer) = size(getfield(F, :factors), dim)
size(F::QL) = size(getfield(F, :factors))

## Multiplication by Q
function _mul(A::QLPackedQ, B::AbstractMatrix)
    TAB = promote_type(eltype(A), eltype(B))
    Anew = convert(AbstractMatrix{TAB}, A)
    if size(A.factors, 1) == size(B, 1)
        Bnew = copy_oftype(B, TAB)
    elseif size(A.factors, 2) == size(B, 1)
        Bnew = [zeros(TAB, size(A.factors, 1) - size(B,1), size(B, 2)); B]
    else
        throw(DimensionMismatch("first dimension of matrix must have size either $(size(A.factors, 1)) or $(size(A.factors, 2))"))
    end
    lmul!(Anew, Bnew)
end

(*)(A::QLPackedQ, B::StridedMatrix) = _mul(A, B)

### QB
abstract type AbstractQLLayout <: MemoryLayout end

"""
    QLPackedLayout{SLAY,TLAY}()

represents a Packed QL factorization whose
factors are stored with layout SLAY and τ stored with layout TLAY
"""
struct QLPackedLayout{SLAY,TLAY} <: AbstractQLLayout end
struct QLPackedQLayout{SLAY,TLAY} <: AbstractQLayout end
struct AdjQLPackedQLayout{SLAY,TLAY} <: AbstractQLayout end

MemoryLayout(::Type{<:QL{<:Any,Mat,Tau}}) where {Mat,Tau} =
    QLPackedLayout{typeof(MemoryLayout(Mat)),typeof(MemoryLayout(Tau))}()

adjointlayout(::Type, ::QLPackedQLayout{SLAY,TLAY}) where {SLAY,TLAY} = AdjQLPackedQLayout{SLAY,TLAY}()

MemoryLayout(::Type{<:QLPackedQ{<:Any,Mat,Tau}}) where {Mat,Tau} =
    QLPackedQLayout{typeof(MemoryLayout(Mat)),typeof(MemoryLayout(Tau))}()

function materialize!(M::Lmul{<:QLPackedQLayout})
    A,B = M.A, M.B
    require_one_based_indexing(B)
    mA, nA = size(A.factors)
    mB, nB = size(B,1), size(B,2)
    if mA != mB
        throw(DimensionMismatch("matrix A has dimensions ($mA,$nA) but B has dimensions ($mB, $nB)"))
    end
    Afactors = A.factors
    @inbounds begin
        for k = max(nA - mA + 1,1):nA
            μ = mA+k-nA
            for j = 1:nB
                vBj = B[μ,j]
                for i = 1:μ-1
                    vBj += conj(Afactors[i,k])*B[i,j]
                end
                vBj = A.τ[k-nA+min(mA,nA)]*vBj
                B[μ,j] -= vBj
                for i = 1:μ-1
                    B[i,j] -= Afactors[i,k]*vBj
                end
            end
        end
    end
    B
end

function materialize!(M::Lmul{<:AdjQLPackedQLayout})
    adjA,B = M.A, M.B
    require_one_based_indexing(B)
    A = adjA.parent
    mA, nA = size(A.factors)
    mB, nB = size(B,1), size(B,2)
    if mA != mB
        throw(DimensionMismatch("matrix A has dimensions ($mA,$nA) but B has dimensions ($mB, $nB)"))
    end
    Afactors = A.factors
    @inbounds begin
        for k = nA:-1:max(nA - mA + 1,1)
            μ = mA+k-nA
            for j = 1:nB
                vBj = B[μ,j]
                for i = 1:μ-1
                    vBj += conj(Afactors[i,k])*B[i,j]
                end
                vBj = conj(A.τ[k-nA+min(mA,nA)])*vBj
                B[μ,j] -= vBj
                for i = 1:μ-1
                    B[i,j] -= Afactors[i,k]*vBj
                end
            end
        end
    end
    B
end

### QBc/QcBc
function materialize!(M::Rmul{<:Any,<:QLPackedQLayout})
    A,Q = M.A, M.B
    mQ, nQ = size(Q.factors)
    mA, nA = size(A,1), size(A,2)
    if nA != mQ
        throw(DimensionMismatch("matrix A has dimensions ($mA,$nA) but matrix Q has dimensions ($mQ, $nQ)"))
    end
    Qfactors = Q.factors
    begin
        for k = nQ:-1:max(nQ - mQ + 1,1)
            μ = mQ+k-nQ
            for i = 1:mA
                vAi = A[i,μ]
                for j = 1:μ-1
                    vAi += A[i,j]*Qfactors[j,k]
                end
                vAi = vAi*Q.τ[k-nQ+min(mQ,nQ)]
                A[i,μ] -= vAi
                for j = 1:μ-1
                    A[i,j] -= vAi*conj(Qfactors[j,k])
                end
            end
        end
    end
    A
end

### AQc
function materialize!(M::Rmul{<:Any,<:AdjQLPackedQLayout})
    A,adjQ = M.A, M.B
    Q = adjQ.parent
    mQ, nQ = size(Q.factors)
    mA, nA = size(A,1), size(A,2)
    if nA != mQ
        throw(DimensionMismatch("matrix A has dimensions ($mA,$nA) but matrix Q has dimensions ($mQ, $nQ)"))
    end
    Qfactors = Q.factors
    @inbounds begin
        for k = max(nQ - mQ + 1,1):nQ
            μ = mQ+k-nQ
            for i = 1:mA
                vAi = A[i,μ]
                for j = 1:μ-1
                    vAi += A[i,j]*Qfactors[j,k]
                end
                vAi = vAi*conj(Q.τ[k-nQ+min(mQ,nQ)])
                A[i,μ] -= vAi
                for j = 1:μ-1
                    A[i,j] -= vAi*conj(Qfactors[j,k])
                end
            end
        end
    end
    A
end

# Julia implementation similar to xgelsy
ldiv!(F::QL, B::AbstractVecOrMat) = ArrayLayouts.ldiv!(F, B)

function materialize!(Ldv::Ldiv{<:QLPackedLayout,<:Any,<:Any,<:AbstractMatrix{T}}) where T
    A,B = Ldv.A,Ldv.B
    m, n = size(A)
    minmn = min(m,n)
    mB, nB = size(B)
    ArrayLayouts.lmul!(adjoint(A.Q), view(B, 1:m, :))
    L = A.L
    @inbounds begin
        if n > m # minimum norm solution
            τ = zeros(T,m)
            for k = m:-1:1 # Trapezoid to triangular by elementary operation
                x = view(L, k, [k; m + 1:n])
                τk = reflector!(x)
                τ[k] = conj(τk)
                for i = 1:k - 1
                    vLi = L[i,k]
                    for j = m + 1:n
                        vLi += L[i,j]*x[j - m + 1]'
                    end
                    vLi *= τk
                    L[i,k] -= vLi
                    for j = m + 1:n
                        L[i,j] -= vLi*x[j - m + 1]
                    end
                end
            end
        end
        ArrayLayouts.ldiv!(LowerTriangular(view(L, 1:minmn, :)), view(B, 1:minmn, :))
        if n > m # Apply elementary transformation to solution
            B[m + 1:mB,1:nB] .= zero(T)
            for j = 1:nB
                for k = 1:m
                    vBj = B[k,j]
                    for i = m + 1:n
                        vBj += B[i,j]*L[k,i]'
                    end
                    vBj *= τ[k]
                    B[k,j] -= vBj
                    for i = m + 1:n
                        B[i,j] -= L[k,i]*vBj
                    end
                end
            end
        end
    end
    return B
end
materialize!(Ldv::Ldiv{<:QLPackedLayout,<:Any,<:Any,<:AbstractVector{T}}) where T =
    ldiv!(Ldv.A, reshape(Ldv.B, length(Ldv.B), 1))[:]

function (\)(A::QL{TA}, B::AbstractVecOrMat{TB}) where {TA,TB}
    require_one_based_indexing(B)
    S = promote_type(TA,TB)
    m, n = size(A)
    m == size(B,1) || throw(DimensionMismatch("left hand side has $m rows, but right hand side has $(size(B,1)) rows"))

    AA = Factorization{S}(A)

    X = _zeros(S, B, n)
    X[1:size(B, 1), :] = B
    ldiv!(AA, X)
    return _cut_B(X, 1:n)
end

function (\)(A::QL{T}, BIn::VecOrMat{Complex{T}}) where T<:BlasReal
    require_one_based_indexing(BIn)
    m, n = size(A)
    m == size(BIn, 1) || throw(DimensionMismatch("left hand side has $m rows, but right hand side has $(size(BIn,1)) rows"))

# |z1|z3|  reinterpret  |x1|x2|x3|x4|  transpose  |x1|y1|  reshape  |x1|y1|x3|y3|
# |z2|z4|      ->       |y1|y2|y3|y4|     ->      |x2|y2|     ->    |x2|y2|x4|y4|
#                                                 |x3|y3|
#                                                 |x4|y4|
    B = reshape(copy(transpose(reinterpret(T, reshape(BIn, (1, length(BIn)))))), size(BIn, 1), 2*size(BIn, 2))

    X = _zeros(T, B, n)
    X[1:size(B, 1), :] = B

    ldiv!(A, X)

# |z1|z3|  reinterpret  |x1|x2|x3|x4|  transpose  |x1|y1|  reshape  |x1|y1|x3|y3|
# |z2|z4|      <-       |y1|y2|y3|y4|     <-      |x2|y2|     <-    |x2|y2|x4|y4|
#                                                 |x3|y3|
#                                                 |x4|y4|
    XX = reshape(collect(reinterpret(Complex{T}, copy(transpose(reshape(X, div(length(X), 2), 2))))), _ret_size(A, BIn))
    return _cut_B(XX, 1:n)
end


"""
Implements Dirichilet lift

In FEM, Neuman boundary conditions are naturally satisfied
(hey, they are also known as natural BCs!) but Dirichilet BCs
are another problem. The simplest solution is to use basis functions
that satisfy the boundary conditions. Now, by design, your solution
satisfies the BCs. This usually is a difficult thing to do. Except
when the solution is zero at Dirichilet BCs. This homogeneous case
is easy with basis that split into boundary modes and interior modes:

Interior modes satisy the homogeneous requirements! Just drop the relevant
boundary modes!

Now we still have the issue of non-homogeneous BCs. That is easy as well,
just split the solution into two parts: homogeneous and dirichilet:

u(x) = u^h(x) + u^d(x)

The weak form will take the following form:

a(u, v) = a(u^h + u^d, v) = a(u^h, v) + a(u^d, v) = f(x)

Since u^d is known (Dirichiulet BCs),

a(u^h, v) = f(x) - a(u^d, v)

The `DirichiletLift` type and associated methods implement the correction of
the right hand side of the equation at a local element.

This implementation is very simple and probably not the most efficient. Just
compute the matrix as if it were a common internal element. Store this matrix
and the indicies that correspond to the Dirichilet modes.

When you want to lift the solution, just set the correct Dirichilet modes
and the `lift!` function will set the other modes to zero and subtract
the contribution.

 The DirichiletLift constructor accepts these arguments

 * `A0` local elemental matrix
 * `idir` local index of dirichilet modes

"""
type DirichiletLift{T <: Number}
    "Order of local matrix"
    M::Int

    "Number of dof of non-Dirichilet modes"
    nh::Int

    "Number of dof of Dirichilet modes"
    nd::Int

    "Operator matrix (copy)"
    A::Array{T,2}

    "Index of non-Dirichilet modes"
    ih::Vector{Int}

    "Index of Dirichilet modes"
    id::Vector{Int}

end

"""
    Create a new DirichiletLift object.

    * `A0` Full elemental matrix
    * `idir` Index of Dirichilet modes
    """
function DirichiletLift{T<:Number}(A0::AbstractArray{T,2}, idir)
    M = size(A0,1)
    nd = length(idir)
    nh = M - nd
    A = zeros(T, nh, nd)
    ih = zeros(Int, nh)
    id = zeros(Int, nd)
    isx = falses(M)

    for i in 1:nd
        isx[idir[i]] = true
        id[i] = idir[i]
    end
    count = 1
    for i = 1:M
        if !isx[i]
            ih[count] = i
            count = count + 1
        end
    end
    for j = 1:nd
        for k = 1:nh
            A[k,j] = A0[ih[k], id[j]]
        end
    end

    DirichiletLift(M, nh, nd, A, ih, id)
end

"""
Actually lifts the solution

 * `lft` A `DirichiletLift` object that was previously created.
 * `f` A vector of the RHS. Dirichilet BCs are stored in it

"""
function lift!{T<: Number}(lft::DirichiletLift, f::AbstractVector{T})
    nd = lft.nd
    nh = lft.nh
    id = lft.id
    ih = lft.ih
    A = lft.A
    for i = 1:nd
        for k = 1:nh
            f[ih[k]] -= A[k,i] * f[id[i]]
        end
    end
end


using  Utils

const HALF = 16

function parse_file(f)
    size = fill(2 * HALF, 3)
    re = zeros(Bool, size...)

    for (row, line) in enumerate(eachline(f))
        for (col, c) in enumerate(line)
            re[row+HALF,col+HALF,1+HALF] = c == '#'
        end
    end
    re
end

function load()
    open(joinpath(@__DIR__, "17.txt"), "r") do f
        parse_file(f)
    end
end

function neighbors(cubes, r, c, s)
    count(view(cubes, r-1:r+1, c-1:c+1, s-1:s+1)) - Int(cubes[r,c,s])
end

function neighbors(cubes, r, c, s, d)
    count(view(cubes, r-1:r+1, c-1:c+1, s-1:s+1, d-1:d+1)) - Int(cubes[r,c,s,d])
end

function step(cubes::AbstractArray{Bool,3})
    same_dim = axes(cubes, 1)
    range = firstindex(same_dim)+1:lastindex(same_dim)-1

    prev = copy(cubes)
    for slice in range
        for col in range
            for row in range
                ns = neighbors(prev, row, col, slice)
                if prev[row,col,slice] && !(ns == 2 || ns == 3)
                    cubes[row,col,slice] = false
                elseif !prev[row,col,slice] && ns == 3
                    cubes[row,col,slice] = true
                end
            end
        end
    end
end

function step(cubes::AbstractArray{Bool,4})
    same_dim = axes(cubes, 1)
    range = firstindex(same_dim)+1:lastindex(same_dim)-1

    prev = copy(cubes)
    for dimension in range
        for slice in range
            for col in range
                for row in range
                    ns = neighbors(prev, row, col, slice, dimension)
                    if prev[row,col,slice,dimension] && !(ns == 2 || ns == 3)
                        cubes[row,col,slice,dimension] = false
                    elseif !prev[row,col,slice,dimension] && ns == 3
                        cubes[row,col,slice,dimension] = true
                    end
                end
            end
        end
    end
end

function ex1(cubes, to=6)
    cubes = copy(cubes)
    for i in 1:to
        step(cubes)
    end
    count(cubes)
end

function ex2(cubes, to=6)
    more = zeros(Bool, size(cubes)..., 2 * HALF)
    more[:,:,:,1+HALF] = cubes
    ex1(more, to)
end

cubes = load()

println("Alive after 6 rounds: ", ex1(cubes))
println("Alive after 6 hypercube rounds: ", ex2(cubes))

using Test

@testset "Conway Cubes" begin
    input = """
    .#.
    ..#
    ###
    """

    cubes = parse_file(IOBuffer(input))

    @testset "example 1" begin
        @test ex1(cubes) == 112
    end

    @testset "example 2" begin
        @test ex2(cubes, 0) == 5
        @test ex2(cubes, 1) == 29
        @test ex2(cubes) == 848
    end

    @testset "results" begin
    end
end


"""
 WeightSpace represents a space that weights another space.
 Overload weight(S,x).
"""
abstract WeightSpace{S,T,DD,d} <: Space{T,DD,d}

domain(S::WeightSpace)=domain(S.space)

points(sp::WeightSpace,n)=points(sp.space,n)

immutable WeightSpacePlan{S,P,T,V}
    space::S
    plan::P
    points::Vector{T}
    weights::Vector{V}
end

for TYP in (:plan_transform,:plan_itransform)
    @eval function $TYP(S::WeightSpace,vals::Vector)
        pts=points(S,length(vals))
        WeightSpacePlan(S,$TYP(S.space,vals),pts,weight(S,pts))
    end
end

transform(sp::WeightSpace,vals::Vector,plan::WeightSpacePlan) =
    transform(sp.space,vals./(sp==plan.space?plan.weights:weight(sp,plan.points)),plan.plan)
itransform(sp::WeightSpace,cfs::Vector,plan::WeightSpacePlan) =
    itransform(sp.space,cfs,plan.plan).*(sp==plan.space?plan.weights:weight(sp,plan.points))

function evaluate(f::AbstractVector,S::WeightSpace,x...)
    tol=1.0E-14
    fv=Fun(f,S.space)(x...)
    if isa(fv,Number)&&abs(fv)<tol
        #TODO: Why this special case??
        zero(eltype(fv))
    else
        weight(S,x...).*fv
    end
end

include("divide_singularity.jl")
include("JacobiWeight.jl")
include("JacobiWeightOperators.jl")
include("JacobiWeightChebyshev.jl")
include("HeavisideSpace.jl")
include("DiracSpace.jl")
include("LogWeight.jl")
include("ExpWeight.jl")


@testset "test_transferfunction" begin
# Naming convention:
# ------------------
# {type}_{dims}
# type: C: Continuous, D: Discrete
# dims: "npnuny" (np = # poles)

# CONTINUOUS
C_111 = tf([1, 2], [1, 5])
C_211 = tf([1, 2, 3], [1, 8, 15])
C_212 = tf(vecarray(2, 1,[1, 2, 3], [1, 2]), vecarray(2, 1, [1, 8, 15], [1, 8, 15]))
C_221 = tf(vecarray(1, 2,[1, 2, 3], [1, 2]), vecarray(1, 2, [1, 8, 15], [1, 8, 15]))
C_222 = [C_221; C_221]
C_022 = tf(4*[1 0; 0 1])
s = tf("s")

# DISCRETE
D_111 = tf([1, 2], [1, -0.5], 0.005)
D_211 = tf([1, 2, 3], [1, -0.2, -0.15], 0.005)
D_221 = tf(vecarray(1, 2, [1, 2, 3], [1, 2]), vecarray(1, 2, [1, -0.2, -0.15], [1, -0.2, -0.15]), 0.005)
D_222 = [D_221; D_221]
D_022 = tf(4*[1 0; 0 1], 0.005)
z = tf("z", 0.005)

# TESTS
# Constructors
@test s == tf([1, 0], [1])
@test z == tf([1, 0], [1], 0.005)
@test C_022 == tf(vecarray(2, 2, [4], [0], [0], [4]), vecarray(2, 2, [1], [1], [1], [1]))
@test D_022 == tf(vecarray(2, 2, [4], [0], [0], [4]), vecarray(2, 2, [1], [1], [1], [1]), 0.005)
@test C_022 == [tf(4) 0;0 4]
@test C_022 == tf([4 0;0 4])
@test D_022 == tf([4 0;0 4], 0.005)

# Test equality
@test tf([1,2], [2,3,4]) == tf(2*[1,2], 2*[2,3,4])
@test tf([1.0], [2.0,3.0]) == tf(π*[1.0], π*[2.0,3.0])
@test tf([1.0+2.0im], [2.0+im,3.0]) == tf((π+im)*[1+2.0im], (π+im)*[2.0+im,3.0])

# Test approximate equlity
# rtol should just be on the order of ϵ, no particular reason that exactly ϵ
# would work, but apparently it does
ϵ = 1e-14
@test tf([1,2], [2,3,4]) != tf(2*[1,2], (2+ϵ)*[2,3,4])
@test tf([1,2], [2,3,4]) ≈   tf(2*[1,2], (2+ϵ)*[2,3,4]) rtol=ϵ
@test tf([1.0], [2.0,3.0]) != tf((π+ϵ)*[1.0], π*[2.0,3.0])
@test tf([1.0], [2.0,3.0]) ≈ tf((π+ϵ)*[1.0], π*[2.0,3.0]) rtol=ϵ
@test tf([1.0+2.0im], [2.0+im,3.0]) != tf((π+(1+ϵ)im)*[1+2.0im], (π+ϵ+im)*[2.0+(1+ϵ*im),3.0])
@test tf([1.0+2.0im], [2.0+im,3.0]) ≈ tf((π+(1+ϵ)im)*[1+2.0im], (π+ϵ+im)*[2.0+(1+ϵ)*im,3.0]) rtol=ϵ

# Addition
@test C_111 + C_111 == tf([2,14,20], [1,10,25])
@test C_222 + C_222 == tf(vecarray(2, 2, [2,20,68,108,90], [0,2,20,62,60],
              [2,20,68,108,90], [0,2,20,62,60]),
  vecarray(2, 2, [1,16,94,240,225], [1,16,94,240,225],
              [1,16,94,240,225], [1,16,94,240,225]))
@test C_222 + 1 == tf(vecarray(2, 2, [2,10,18], [1,9,17], [2,10,18], [1,9,17]),
  vecarray(2, 2, [1,8,15], [1,8,15], [1,8,15], [1,8,15]))
@test D_111 + D_111 == tf([2,3,-2], [1,-1,0.25], 0.005)

# Subtraction
@test C_111 - C_211 == tf([0,3,18,15], [1,13,55,75])
@test 1 - C_222 == tf(vecarray(2, 2, [0,6,12], [1,7,13], [0,6,12], [1,7,13]), vecarray(2, 2, [1,8,15], [1,8,15], [1,8,15], [1,8,15]))
# We are not doing enough to identify zero numerator here
@test_broken D_111 - D_211 - tf([0,0.3,-2.55,1.2], [1,-0.7,-0.05,0.075], 0.005) == tf([0.0], [1], 0.005)

# Multiplication
@test C_111 * C_221 == tf(vecarray(1, 2, [1,4,7,6], [0,1,4,4]),
  vecarray(1, 2, [1,13,55,75], [1,13,55,75]))
@test C_212 * C_111 == tf(vecarray(2, 1, [1,4,7,6], [0,1,4,4]),
  vecarray(2, 1, [1,13,55,75], [1,13,55,75]))
@test 4*C_222 == tf(vecarray(2, 2, [4,8,12], [0,4,8], [4,8,12], [0,4,8]),
  vecarray(2, 2, [1,8,15], [1,8,15], [1,8,15], [1,8,15]))
 # We are not doing enough to identify zero numerator here
@test_broken D_111 * D_221 - tf(vecarray(1, 2, [1,4,7,6], [0,1,4,4]),
  vecarray(1, 2, [1,-0.7,-0.05,0.075], [1.0,-0.7,-0.05,0.075]), 0.005) ==
tf(vecarray(1, 2, [0], [0]), vecarray(1, 2, [1], [1]), 0.005)

# Division
@test 1/C_111 == tf([1,5], [1,2])
@test C_212/C_111 == tf(vecarray(2, 1, [1,7,13,15], [0,1,7,10]),
  vecarray(2, 1, [1,10,31,30], [1,10,31,30]))
@test 1/D_111 == tf([1.0,-0.5], [1.0,2.0], 0.005)

# Indexing
@test size(C_222) == (2, 2)
@test size(C_212) == (2, 1)
@test C_222[1,1] == tf([1, 2, 3], [1, 8, 15])
@test C_222[1:1,1] == tf([1, 2, 3], [1, 8, 15])
@test C_222[1,1:2] == C_221
@test size(C_222[1,[]]) == (1,0)

# Errors
@test_throws ErrorException tf(vecarray(1, 1, [1,7,13,15]),
   vecarray(2, 1, [1,10,31,30], [1,10,31,30]))

# Printing
res = ("TransferFunction:\nInput 1 to Output 1\ns^2 + 2s + 3\n-------------\ns^2 + 8s + 15\n\nInput 1 to Output 2\ns^2 + 2s + 3\n-------------\ns^2 + 8s + 15\n\nInput 2 to Output 1\n    s + 2\n-------------\ns^2 + 8s + 15\n\nInput 2 to Output 2\n    s + 2\n-------------\ns^2 + 8s + 15\n\nContinuous-time transfer function model")
@test_broken sprint(show, C_222) == res
res = ("TransferFunction:\nInput 1 to Output 1\nz^2 + 2.0z + 3.0\n-----------------\nz^2 - 0.2z - 0.15\n\nInput 1 to Output 2\nz^2 + 2.0z + 3.0\n-----------------\nz^2 - 0.2z - 0.15\n\nInput 2 to Output 1\n     z + 2.0\n-----------------\nz^2 - 0.2z - 0.15\n\nInput 2 to Output 2\n     z + 2.0\n-----------------\nz^2 - 0.2z - 0.15\n\nSample Time: 0.005 (seconds)\nDiscrete-time transfer function model")
@test_broken sprint(show, D_222) == res

@test tf(zpk([1.0 2; 3 4])) == tf([1 2; 3 4])

# Type stability Continuous-time
@test eltype(fill(tf("s"),2)) <: TransferFunction
@test eltype(fill(tf([1],[1,1]),2)) <: TransferFunction
@test eltype(fill(tf(1.0,[1,1]),2)) <: TransferFunction
@test eltype(fill(tf([1 2; 3 4]),2)) <: TransferFunction
@test eltype(fill(tf(1)+tf(2),2)) <: TransferFunction
@test eltype(fill(tf(1)/tf(2),2)) <: TransferFunction
@test eltype(fill(tf(1)+1,2)) <: TransferFunction

# Type stability Discrete-time
@test eltype(fill(tf("z",1.0),2)) <: TransferFunction
@test eltype(fill(tf([1],[1,1],1),2)) <: TransferFunction
@test eltype(fill(tf(1.0,[1,1],1),2)) <: TransferFunction
@test eltype(fill(tf([1 2; 3 4],1),2)) <: TransferFunction
@test eltype(fill(tf(1,1)+tf(2,1),2)) <: TransferFunction
@test eltype(fill(tf(1,1)/tf(2,1),2)) <: TransferFunction
@test eltype(fill(tf(1,1.0)+1,2)) <: TransferFunction

# Errors
@test_throws ErrorException C_111 + C_222             # Dimension mismatch
@test_throws ErrorException C_111 - C_222             # Dimension mismatch
@test_throws ErrorException C_111 * C_222             # Dimension mismatch
@test_throws ErrorException [s 0; 1]                  # Dimension mismatch
@test_throws ErrorException D_111 + C_111             # Sampling time mismatch
@test_throws ErrorException D_111 - C_111             # Sampling time mismatch
@test_throws ErrorException D_111 * C_111             # Sampling time mismatch
D_diffTs = tf([1], [2], 0.1)
@test_throws ErrorException D_111 + D_diffTs          # Sampling time mismatch
@test_throws ErrorException D_111 - D_diffTs          # Sampling time mismatch
@test_throws ErrorException D_111 * D_diffTs          # Sampling time mismatch
@test_throws ErrorException tf([1], [2], -0.1)        # Negative samping time
@test_throws ErrorException tf("s", 0.01)             # s creation can't be discrete
@test_throws ErrorException tf("z", 0)                # z creation can't be continuous
@test_throws ErrorException tf("z")                   # z creation can't be continuous
@test_throws ErrorException [z 0]                     # Sampling time mismatch (inferec could be implemented)

# Test polynomial inputs
Polynomial = ControlSystems.Polynomials.Polynomial
v1, v2, v3, v4 = [1,2,3], [4,5,6], [7,8], [9,10,11]
p1, p2, p3, p4 = Polynomial.(reverse.((v1,v2,v3,v4)))

S1v = tf(v1, v2)
S2v = tf(v3, v4)
Sv = [S1v S2v; 2S1v 3S2v]

# Test polynomial SISO constructor
S1p = tf(p1,p2)
# Test polynomial matrix MIMO constructor
Sp = tf([p1 p3; 2p1 3p3], [p2 p4; p2 p4])

@test S1v == S1p
@test Sv == Sp

# Test constructors
@test ControlSystems.SisoRational{Float64}(v1,v2) isa ControlSystems.SisoRational{Float64}
@test ControlSystems.SisoRational{Float64}(v1,1.0*v2) isa ControlSystems.SisoRational{Float64}
@test ControlSystems.SisoRational{Int64}(v1,1.0*v2) isa ControlSystems.SisoRational{Int64}

@test_throws InexactError ControlSystems.SisoRational{Int64}(v1,1.5*v2)

end


subfunction(x,y) = 3x+2y

derivative_of_my_f(x,y) = ForwardDiff.derivative(x->subfunction(x,y),x)
# ref:http://www.juliadiff.org/ForwardDiff.jl/stable/user/api/


using Plots; pyplot(xtickfontsize=13, ytickfontsize=13, xguidefontsize=16, yguidefontsize=16, legendfontsize=12)
default(palette = palette(:okabe_ito))
using DelimitedFiles
using Printf
#using StatsFuns, SpecialFunctions
using nevanlinna_ac

"""
function spectral_density(J::Real, ω::Real, β::Real; η::Float64=0.05,Γ::Real=1.)
    # 2Imχ(ω)
    if J/Γ == 0.
        return 2π*sinh(Γ*β)/cosh(Γ*β)*(delta(ω-2Γ,η) - delta(ω+2Γ,η))
    elseif J/Γ == 1.
        # Ref: PRX.4.031008(2014) A6
        g0 = 0.858714569; zc = J^(-1/4)
        T = 1/β
        up = zc * g0 * β^(3/4)
        down = 2^(1/4) * √π * gamma(1/8) * gamma(5/8)
        fac = up/down
        res = sinh(ω/(2*T)) * abs(gamma(1/8 - 1im*ω/(2π*T)))^2
        return 2*fac*res
    else
        @error "No exact results. J/Γ should be 1. or 0."
    end
end
"""

function chi_div_w(w::Vector, path::String, n::Int64)
	η = 0.001
	g = make_input(path)
	gs = MasubaraGF(n, g.GF[length(g.GF)-n+1:end])
	return [spectrum_density(i,η,gs) for i in w]
end

#setups
#o = [-i for i in range(-2π,2π,step = 4π/400)]
o = [-i for i in range(-1,1,step = 0.01)]
#brange = [10.0, 20.0, 30.0, 40.0]
#de = [spectral_density(1.0, i, 20) for i in o]

g = 1.0
β = 20

#load Zi-Long Li data
#pathl = @sprintf "./data/TFIsing-Li/Aw_%.1f_b20.txt" g
#dl = readdlm(pathl)

#n = 5 #number of Masubara frequencies used
nrange = [6, 7, 8]
chi = Array{Vector}(undef,3,2)
for  i = 1:3
    path1 = @sprintf "./data/gt/gt8_g_%.1f_b_%i.txt" g β
    path2 = @sprintf "./data/gt/gt28_g_%.1f_b_%i.txt" g β
    chi[i,1] = chi_div_w(o, path1, nrange[i])
    chi[i,2] = chi_div_w(o, path2, nrange[i])
end

#sc = 0.01 .* sum.(chi)
#for i = 1:4
#    s = @sprintf "%.5f" sc[i]
#    println(s)
#end

#v = 2 * abs(g-1)
plot()
#plot!(o, de ./ o, line=(:black,2),label="theory")
#plot!([-v,v], seriestype="vline",line=(:dash, :black), label=false)
#plot!(dl[:,1], dl[:,2] ./ dl[:,1], line=(:black,2), label="Li")
for j=1:3
    plot!(o, chi[j,1], line=(2), label=@sprintf "D=8,n=%i" nrange[j])
end
for j=1:3
    plot!(o, chi[j,2], line=(:dash, 1),
    marker=(:circle, 2, stroke(0)),
    label=@sprintf "D=2×8,n=%i" nrange[j])
end

plot!(xlim=(-1, 1))
plot!(title = @sprintf "g = %.1f, β=20, η = 0.001" g)
plot!(xlabel="ω", ylabel="χ''(ω)/ω")
spath = @sprintf "./notes/chi_g_%.1f_b_20_dchange.pdf" g
savefig(spath)


include("../examples/lorenz63.jl")
include("../src/lss.jl")
using LinearAlgebra
function refine_orbit(n)
	#create epsilon orbit.
	n_runup = 5000
    s = [10., 28., 8/3]
    u0 = rand(3,1)
	u0 = lorenz63(u0, s, n_runup)[end,:,:]
	u_trj = lorenz63(u0, s, n)[:,:,1]
	n, d = size(u_trj)
	eps = 0.1
	noise = eps*randn(n)
	u_obs = copy(u_trj)
	u_obs[:,3] .+= noise
	# run gradient descent to minimize observation error.
	n_gd_steps = 4
	gamma = 0.1
	dJds = zeros(n_gd_steps)
	d_u = 2
	u = zeros(n, d, n_gd_steps)
	# compute_sens assumes many objective functions
	dJ = zeros(1,d,n)
	error = zeros(n_gd_steps)
	for i = 1:n_gd_steps
		# Set up LSS
		# J is now sum of squared observation error.
		dJ[1,3,:] = -2.0*noise
    	du_trj = dlorenz63(u_trj, s)
        X = perturbation(u_trj,s) #ith col in T_{u_{i+1}} M
    	#f = vectorField(u_trj,s)
    	f = zeros(d,n)
    	y, xi = lss(du_trj, X, f, s, d_u)
		dJds[i] = compute_sens(y, xi, dJ, f)[1]
    	println(dJds[i])
		# Make u_trj the computed shadowing trajectory
		ds = gamma*dJds[i]
		s[2] = s[2] - ds
		u_trj = u_trj + ds*y'
		error[i] =  sum((u_trj[:,3] .- u_obs[:,3]).^2)/n
		u[:,:,i] = u_trj
	end
	return u_obs[:,3], u[:,3,:], error
end
function assimilate()
	n_repeat = 1000
	n = 2000
	n_gd_steps = 4
	z_obs = zeros(n, n_repeat)
	z_trj = zeros(n, n_gd_steps, n_repeat)
	errors = zeros(n_gd_steps, n_repeat)
	for i = 1:n_repeat
		res1, res2, res3 = refine_parameter(n-1)
		z_obs[:,i] = res1
		z_trj[:,:,i] = res2
		errors[:,i] = res3
	end
	return z_obs, z_trj, errors
end


### A Pluto.jl notebook ###
# v0.17.1

using Markdown
using InteractiveUtils

# This Pluto notebook uses @bind for interactivity. When running this notebook outside of Pluto, the following 'mock version' of @bind gives bound variables a default value (instead of an error).
macro bind(def, element)
    quote
        local iv = try Base.loaded_modules[Base.PkgId(Base.UUID("6e696c72-6542-2067-7265-42206c756150"), "AbstractPlutoDingetjes")].Bonds.initial_value catch; b -> missing; end
        local el = $(esc(element))
        global $(esc(def)) = Core.applicable(Base.get, el) ? Base.get(el) : iv(el)
        el
    end
end

# ╔═╡ 0b9abd74-d199-4720-803e-eb7861b86572
begin
	using PlutoUI # user-interface do Pluto.jl
	using Plots   # Julia package para gráficos
end

# ╔═╡ c4d275c8-36d5-454f-bf1f-ddbc06beeae0
html"<button onclick='present()'>present</button>"

# ╔═╡ d2a788ef-5292-4229-a1d1-3fd15db67c51
md"""
# Máquina síncrona trifásica

## Motor síncrono de pólos salientes (Ex.8)
Máquinas síncronas trifásicas, exercício 8:
"""

# ╔═╡ 17bc7f80-c13c-11eb-227f-b125d84c8b40
md"""
**Uma máquina síncrona 3~ de pólos salientes, 50MVA, 11kV, 60Hz, enrolamentos do
estator em Y, apresenta as reatâncias: $$X_d=0.8\mathrm pu$$ e $$X_q=0.4\mathrm pu$$.
Como motor síncrono é colocado à plena carga com fator de potência 0,8 indutivo.
As perdas mecânicas representam são 0,15pu.
Despreze as perdas na resistência do induzido.**
"""

# ╔═╡ 20ed504e-08d1-45b9-8494-c42170182898
(Sₙ, Uₙ, f, Xd, Xq, cosφₙ, pᵣₒₜ)=(50e6, 11e3, 60, 0.8, 0.4, 0.8, 0.15)

# ╔═╡ 25f9aa0c-efa0-4c8a-bbfb-c5648bf2f8a2
md"""
## a) Determine $$X_d$$ e $$X_q$$ em Ω;
"""

# ╔═╡ 0545e797-9d40-48c4-ab96-195959331560
md"""
A utilização de *valores por unidade*, $$\mathrm {pu}$$,  apresenta diversas vantagens:
 - permite uma melhor comparação relativa entre máquinas de diferentes potências;
 - torna as grandezas adimensionais, facilitando a observação das mesmas face aos valores nominais de funcionamento;
 - simplifica a utilização do factor $$\sqrt3$$ em sistemas trifásicos.

Definindo a potência de base, $$S_b$$, e a tensão de base, $$U_b$$:

$$S_b=S_n\quad;\quad U_b=U_n$$

como: $$\quad S_b=U_bI_b\quad \Leftrightarrow \quad S_b=\frac{U_b^2}{Z_b}\quad \Rightarrow\quad Z_b=\frac{U_b^2}{S_b}$$

sendo $$Z_b$$ a impedância de base. Assim:

$$\begin{cases}X_d(\Omega) = X_d(\mathrm{pu}).Z_b\\X_q(\Omega) = X_q(\mathrm{pu}).Z_b \end{cases}$$
"""

# ╔═╡ 342aacbe-df8e-42b9-a7a2-8538476f2a80
begin
	Sb=Sₙ
	Ub=Uₙ
	Zb=Ub^2/Sb
	Xd_Ω=Xd*Zb
	Xq_Ω=Xq*Zb
	Xd_Ω, Xq_Ω
end

# ╔═╡ 65dd0a07-ff99-4887-8a86-142248705131
md"""
## b) Determine a fem em pu;
"""

# ╔═╡ 8b677658-96a8-40cb-b4cf-1638703dda22
md"""
A força contra-electromotriz (fcem) de vazio, $$E'_0$$, do motor síncrono de pólos salientes vem dada pela equação vectorial:

$$\overline{E'}_0=\overline{U}-(R+jX_q)\overline{I}-j(X_d-X_q)\overline{I}_d$$

com: $$\quad\overline{E'}=\overline{U}-(R+jX_q)\overline{I}$$

A determinação do vector fcem efectiva, $$\overline{E'}$$, (cálculo intermédio de $$\overline{E'}_0$$) permite definir a posição espacial dos eixos directo (linha dos pólos) e de quadratura (posicão das fcem), uma vez que se obtém o ângulo de carga, $$\delta$$.
"""

# ╔═╡ 3ec74057-fcbb-4d79-8aca-719289c2299e
md"""
O cálculo de $$\overline I_d$$ é obtido a partir da relação trigonométrica com o vector da corrente, $$\overline I$$, observado o diagrama vectorial de tensões:

Assim, no caso concreto:

$$\overline I_d=I\sin(|\varphi|-|\delta|)\angle (\delta-90°)$$
"""

# ╔═╡ 49053499-f759-4043-a9b8-48f42e45589a
md"""
### Diagrama vectorial de tensões do motor síncrono de pólos salientes
"""

# ╔═╡ 19c628e8-60c9-46d0-9ab8-78e70b8bfa6f
md"""
## c) $$P_d^{fcem}(pu)$$; $$P_d^{rel}(pu)$$

**Determine a potências desenvolvidas (em pu) devido à fem de excitação e devido ao
efeito de relutância do rotor;**
"""

# ╔═╡ 2cd26842-6c40-4dc0-9979-da4685630aa1
md"""
As parcelas da potência desenvolvida em *valores por unidade* são determinadas pelas expressões:

$$P_d^{fcem}(pu)=\frac{UE'_0}{X_d}\sin \delta$$
$$P_d^{rel}(pu)=\frac{U^2(X_d-Xq)}{2X_dX_q}\sin(2\delta)$$

O que resulta:
"""

# ╔═╡ 6bc18d01-7285-4b7b-9769-3a7d52baae75
md"""
## d) Limite de estabilidade
**Se a corrente de excitação for reduzida a zero, a máquina continua em sincronismo?
Justifique;**
"""

# ╔═╡ 60f80e79-eeb8-4b60-801f-98caa28b4dfd
md"""
Por conseguinte, como: $$|P_d^{rel}(max)|< |P_d|$$, conclui-se que o motor perderia o sincronismo nesta situação $$(\space I_{exc}=0\space\mathrm {pu})$$, passando a um funcionamento instável.
"""

# ╔═╡ dd7d16e4-7b01-4aa0-a1ba-ecc7881900fb
md"""
## e) Funcionamento em vazio, sem $$I_{exc}$$

**e) Se a carga ao veio for retirada e a corrente de excitação reduzida a zero, determine o valor da corrente do estator (em pu) e o fator de potência. Desenhe o diagrama vectorial da máquina para esta situação.**
"""

# ╔═╡ 61609aa9-9feb-4a63-b4e9-2fd9a88f8106
md"""
Sem carga ao veio, $$\quad P_u=0\mathrm W\quad \Rightarrow \quad P_d=p_{rot}=0.15\mathrm{pu}$$.

Por outro lado, $$\quad I_{exc}=0\mathrm A\quad \Rightarrow \quad E'_0\simeq 0\mathrm V \quad \Rightarrow \quad P_d^{fcem}\simeq 0\mathrm W$$.

Por conseguinte, $$\quad P_d^{rel}=p_{rot}=0.15\mathrm{pu}\quad$$ com $$\quad P_d^{rel}$$:

$$P_d^{rel}(pu)=\frac{U^2(X_d-X_q)}{2X_dX_q}\sin(2\delta_0)$$
"""

# ╔═╡ 28da7f3c-97fd-4a80-ad21-eeb201dcc881
md"""
Do diagrama vectorial de tensões deste motor síncrono, retiram-se as seguintes relações para as componentes da corrente, $$I$$, nos eixos directo e de quadratura, em função do ângulo de carga, δ:

$$\begin{cases}I_q=\frac{U}{X_q}\sin(|\delta|) \\I_d=\frac{U}{X_d}\cos(\delta)-\frac{E'_0}{X_d}\end{cases}$$

Vectorialmente as componentes da corrente nos eixos directo e de quadratura ficam representadas por:

$$\begin{cases}\overline I_q=I_q\angle \delta\\ \overline I_d= I_d\angle (\delta-90°)\end{cases}$$

Assim, o vector de corrente, $$\overline I$$ é obtido por:

$$\overline I=\overline I_d+\overline I_q$$
"""

# ╔═╡ 311eecf2-4bd6-44bc-8c67-a680f4fd5d33
md"""
### Diagrama vectorial de tensões 💻
"""

# ╔═╡ b47b956c-8880-4c22-aeea-d9a55c518b6a
md"""
Para se perceber o efeito das reduções da carga ao veio e da corrente de excitação, criou-se um segundo diagrama vectorial de tensões/correntes, mas dependente da posição de 2 cursores (*sliders*) associados à variação de cada um dos parâmetros $$(\delta$$ e $$I_{exc})$$, permitindo observar os seus efeitos sucessivos, no desenho do diagrama vectorial:
"""

# ╔═╡ 78a2ef93-5ed1-44f8-8aa9-7e7d8d80a2df
md"""
Repare-se para qualquer valor do ângulo de carga, $$\delta$$, quando a corrente de excitação se torna nula, a fcem de vazio, $$\overline {E'}_0$$, torna-se também nula e o diagrama vectorial de tensões pode ser representado apenas pelo triângulo rectângulo formado pelos vectores: $$\quad\overline U$$, $$\space\space-jX_q\overline I_q\quad$$ e $$\quad-jX_d\overline I_d$$.

Ou seja: $$\quad\overline {E'}_0=\overline U -jX_q\overline I_q -jX_d\overline I_d\quad$$ com $$\quad\overline {E'}_0=0 \space\mathrm{pu}$$.
"""

# ╔═╡ c5cc14a5-73c6-4f10-bc3e-bd7b68eddafa
md"""
!!! nota
	Os resultados da **alínea e)** são corretamente apresentados apenas quandos os cursores se encontrem nas posições:

	$$\delta=\delta_0 \quad \mathrm e \quad I_{exc}=0 \space\mathrm {pu}$$
"""

# ╔═╡ d58c2390-6aaa-4e73-b360-9f0e9d20b37e
md"""
# Setup
"""

# ╔═╡ 7f123aae-2f4d-4cf5-825f-7fe3a8f69c0c
md"""
## Notação polar $$\angle$$. $$\quad$$ Operador $$j$$ $$\quad$$ 📌
"""

# ╔═╡ db949a4e-261c-4374-a806-6279370b287e
begin
	∠(x) = cis(deg2rad(x))
	j(x) = (x)*im
end;

# ╔═╡ d5d4554e-428a-457a-8fdb-47dc977f9d01
begin
	φₙ=-acos(cosφₙ)
	φₙ=rad2deg(φₙ)
	I=1 				# 1pu
	U=1 				# 1pu
	I_=(I)∠(φₙ)			# vector da corrente em pu
	U_=(U)∠(0)			# vector da tensão em pu

	# Cálculo de E':
	E_=U_-j(Xq)*I_
	E=abs(E_)
	E=round(E, digits=3)
	δ=angle(E_)
	δ=rad2deg(δ)
	δ=round(δ, digits=2)
	E, δ
end;

# ╔═╡ 4dd894c8-9b9b-474d-9a98-d805328b609e
md"""
Assim, obtém-se $$\overline{E'}=$$ $E ∠ $δ ° $$\space\mathrm {pu}$$.
"""

# ╔═╡ 45b5c9d8-93ce-4b64-8c81-431dcbc6531f
begin
	Pdʳᵉˡmax=-U^2*(Xd-Xq)/(2*Xd*Xq)
	Pdʳᵉˡmax=round(Pdʳᵉˡmax, digits=2)
end;

# ╔═╡ 892b1d19-c88a-4add-ac18-70e799e01092
begin
	δ₀=-0.5*asin(pᵣₒₜ*2*Xd*Xq/(U^2*(Xd-Xq)))
	δ₀=rad2deg(δ₀)
	δ₀=round(δ₀, digits=2)
end;

# ╔═╡ 33d13e0f-80c5-478c-99e5-e9de64a8d39a
md"""
De onde resulta o valor de $$\delta_0=$$ $δ₀ °.
"""

# ╔═╡ 1ea9ab83-10c8-4d5f-8489-e9a4754e7b1b
begin
	H1=("redução de carga, δ -> δ₀", @bind δ₂ PlutoUI.Slider(δ:0.01:δ₀, default=δ,show_value=true))
	H2=("redução de Iₑₓ, pu", @bind K₂ PlutoUI.Slider(0:0.1:1, default=1, show_value=true))
	H1, H2
end

# ╔═╡ 67b54cd9-af1a-4bb7-9a44-c66179adfbcb
begin
	φ₁=abs(φₙ*π/180)
	δ₁=abs(δ*π/180)
	Id_=(I*sin(φ₁-δ₁))∠(δ-90)
	Iq_=(I*cos(φ₁-δ₁))∠(δ)
	Id=abs(Id_)
	Id=round(Id, digits=3)
	Id, δ-90
end;

# ╔═╡ a2aac898-7c8a-42c6-a7ef-a3ff736d1ab6
md"""
O que permite obter: $$\quad \overline I_d=$$ $Id ∠ $(δ-90)° $$\space\mathrm {pu}$$.
"""

# ╔═╡ cde832b5-50ea-44d8-ae4e-f1907d7e31dc
begin
	relut_=j(Xd-Xq)*Id_
	E₀_=E_-relut_
	E₀=abs(E₀_)
	E₀=round(E₀, digits=3)
end;

# ╔═╡ 33a4d731-3f65-460d-a74e-609250fa6292
md"""
O vector da fcem de vazio, $$\overline{E'}_0$$, vem então dado por:

$$\overline{E'}_0=\overline{E'}-j(X_d-X_q)\overline{I}_d$$

Assim, obtém-se: $$\quad\overline{E'}_0=$$ $E₀ ∠ $δ ° $$\space\mathrm {pu}$$.
"""

# ╔═╡ 73b14af8-f204-4070-bab9-180b660a9029
begin
	# Pot. desenvolvida devido à fcem, pu:
	Pdᶠᵉᵐₚᵤ=(U*E₀/Xd)*sin(δ*π/180)
	Pdᶠᵉᵐₚᵤ=round(Pdᶠᵉᵐₚᵤ, digits=2)

	# Pot. desenvolvida devido ao efeito de relutância, pu:
	Pdʳᵉˡₚᵤ=(U^2*(Xd-Xq)/(2*Xd*Xq))*sin(2*δ*π/180)
	Pdʳᵉˡₚᵤ=round(Pdʳᵉˡₚᵤ, digits=2)

	# Resultados
	Pdᶠᵉᵐₚᵤ, Pdʳᵉˡₚᵤ
end

# ╔═╡ a97592ef-28ef-4d83-b979-9e5550045cd0
Pdₚᵤ=Pdᶠᵉᵐₚᵤ + Pdʳᵉˡₚᵤ  ;

# ╔═╡ 2635c31e-8a91-45b0-8e4a-b4b6f6e59ae0
md"""
Com: $$\quad I_{exc}=0\space\mathrm {pu}\quad\Rightarrow\quad E'_0\simeq 0\space\mathrm {pu}\quad \Rightarrow\quad P_d^{fcem}\simeq 0\space\mathrm {pu}$$

Por outro lado, o ponto de funcionamento, $$P_d(\delta)$$, em regime nominal é dado por: $$P_d=P_d^{fcem}+P_d^{rel}=$$ ( $Pdᶠᵉᵐₚᵤ ) + ( $Pdʳᵉˡₚᵤ ) = $Pdₚᵤ pu.
"""

# ╔═╡ f736b324-d6d9-4d8f-93d0-d520b79d4002
md"""
Assim, sem corrente de excitação, a potência desenvolvida devido ao efeito de relutância, $$P_d^{rel}$$,tem de suprir os $Pdₚᵤ pu, do ponto funcionamento.

O valor máximo da potência desenvolvida devido ao efeito de relutância, $$P_d^{rel}(max)$$, verifica-se para $$\delta=-45°$$, que permite obter:
$$P_d^{rel}(max)=\frac{U^2(X_d-Xq)}{2X_dX_q}=$$ $Pdʳᵉˡmax pu
"""

# ╔═╡ c79f1fef-c2f1-45b0-af35-f3e69e6ec77e
begin
	# eixos  d, q:
	plot([0+j(0), (1.3*cos(δ*π/180))+j(1.3*sin(δ*π/180))],
		label="eixo de quadratura", arrow=:head, linecolor=:black,
		linestyle=:dashdot,	linewidth=2)
	plot!([0+j(0), (0.5*cos(δ*π/180+π/2))+j(0.5*sin(δ*π/180+π/2))],
		label="eixo directo", arrow=:head, linecolor=:black, linewidth=2)

	# E':
	K=0.5 # factor de escala da corrente
	_jXqI_=(Xq*I)∠(φₙ-90)
	plot!([0, U_], arrow=:closed, legend=:topright, label="U∠0°", linewidth=2)
	plot!([0, K*I_], arrow=:closed, label="I∠φ", linewidth=2)
	plot!([U_, U_+_jXqI_], arrow=:closed, label="XqI∠-90°", linewidth=2)
	plot!([0,E_], arrow=:closed, minorticks=5, label="E'∠δ",
		linewidth=2, linecolor=:blue, ylims=(-0.75,0.75),
		xlims=(-0.25,1.25), size=(600,600))

	# Id_, Iq_:
	plot!([0, K*Id_],arrow=:closed, label="Id∠(δ-90°)")
	plot!([0, K*Iq_],arrow=:closed, label="Iq∠(δ)")

	#E´₀:
	plot!([U_+_jXqI_,U_+_jXqI_-relut_], arrow=:closed,
		label="(Xd-Xq)Id∠(δ)", linewidth=2)
	plot!([0,E₀_], arrow=:closed, label="E'₀∠δ", linewidth=3)
end

# ╔═╡ 5861683f-db8a-4d43-9822-f2f47431c191
begin
	# vector Iq:
	Iq₂=(U/Xq)*sin(abs(δ₂*π/180))
	Iq₂_=(Iq₂)∠(δ₂)
	# vector Id:
	E₀₂=E₀*K₂
	Id₂=(U/Xd)*cos(δ₂*π/180)-E₀₂/Xd
	Id₂_=(Id₂)∠(δ₂-90)
	# vector I, cosφ:
	I₂_=Id₂_+Iq₂_
	I₂=abs(I₂_)
	I₂=round(I₂, digits=2)
	φ₂=angle(I₂_)
	fdp=cos(φ₂)			# factor de potência (motor subexcitado -> indutivo)
	φ₂=rad2deg(φ₂)
	fdp=round(fdp, digits=3)
	I₂, fdp, δ₂, E₀₂ 	# RESULTADOS
end

# ╔═╡ 9e141f07-ad66-41f4-a53b-b6c25e4afbb2
begin
	# eixos  d, q:
	plot([0+j(0), (1.2*cos(δ₂*π/180))+j(1.2*sin(δ₂*π/180))],
		label="eixo de quadratura", arrow=:head, linecolor=:black, linestyle=:dashdot,			linewidth=2)
	plot!([0+j(0), (0.35*cos(δ₂*π/180+π/2))+j(0.35*sin(δ₂*π/180+π/2))],
		label="eixo directo", arrow=:head, linecolor=:black, linewidth=2,
		ylims=(-0.75,0.75), xlims=(-0.25,1.25), size=(600,600))

	# parte idêntica a polos lisos:
	_jXqI₂_=(Xq*I₂)∠(φ₂-90)
	plot!([0, U_], arrow=:closed, legend=:topright, label="U∠0°", linewidth=2)
	plot!([0, K*I₂_], arrow=:closed, label="I∠φ", linewidth=2)
	plot!([U_, U_+_jXqI₂_], arrow=:closed, label="XqI∠-90°", linewidth=2)
	E₂_=U_-j(Xq)*I₂_
	plot!([0,E₂_], arrow=:closed, minorticks=5, label="E'∠δ", linewidth=2)

	# Id_, Iq_:
	plot!([0, K*Id₂_],arrow=:closed, label="Id∠(δ-90°)", linewidth=1)
	plot!([0, K*Iq₂_],arrow=:closed, label="Iq∠(δ)", linewidth=1)

	#E´₀:
	relut₂_=j(Xd-Xq)*Id₂_
	E₀₂_=E₂_-relut₂_
	plot!([U_+_jXqI₂_,U_+_jXqI₂_-relut₂_], arrow=:closed, label="(Xd-Xq)Id∠(δ)", linewidth=2)
	plot!([0,E₀₂_], arrow=:closed, label="E'₀∠δ", linewidth=3)

	#-jXqIq_ e -jXdId_
	vectorXqIq=-j(Xq)*Iq₂_
	vectorXdId=-j(Xd)*Id₂_
	plot!([U_, U_+vectorXqIq],arrow=:closed, label="XqIq∠(δ-90°)",linewidth=3)
	plot!([U_+vectorXqIq, U_+vectorXqIq+vectorXdId],arrow=:closed, label="XdId∠(δ-180°)", linewidth=3)
end

# ╔═╡ 54f98ff2-02ae-418f-8c87-0e31f70352a0
md"""
## Índice
"""

# ╔═╡ b4aea351-3195-4dc2-8cfa-b423a029b840
TableOfContents(title="Índice")

# ╔═╡ ea2e1c0e-241a-4c37-9420-3f882d78a47f
md"""
!!! info
	No índice deste *notebook*, os tópicos assinalados com "💻" permitem interacção com o utilizador.

	O tópico assinado com "📌" deve ser analisado previamente para melhor compreensão dos códigos em linguagem de programação científica *Julia* apresentados no *notebook*.
"""

# ╔═╡ 520bfd7b-ad1f-44a8-a1ee-503a22923adc
md"""
## _Julia packages_
*Links to package help*: [Plots](http://docs.juliaplots.org/latest/), [PlutoUI](https://juliahub.com/docs/PlutoUI/abXFp/0.7.6/)
"""

# ╔═╡ cd9e2120-93fb-499a-8091-0dd6890d90e1
version=VERSION;

# ╔═╡ cd703dc2-6b7a-49b1-91e1-84ee712a7a9c
md"""
## _Notebook_

*Notebook* realizado em linguagem de programação *Julia* versão $(version) para a unidade curricular de Máquinas Elétricas 2 (ISEL\LEE).

Tempo de cálculo do *notebook* (1ª execução): até cerca de 4 min.

**Ricardo Luís** (Professor Adjunto, ISEL\DEEEA\GDME) \
ISEL, 13/Dez/2021
"""

# ╔═╡ 021642cb-8edd-4349-833f-41ca4144ee58
md"""
Este conteúdo possui licença [![](https://img.shields.io/badge/License-CC%20BY--SA%204.0-lightgrey.svg)](http://creativecommons.org/licenses/by-sa/4.0/).
"""

# ╔═╡ 00000000-0000-0000-0000-000000000001
PLUTO_PROJECT_TOML_CONTENTS = """
[deps]
Plots = "91a5bcdd-55d7-5caf-9e0b-520d859cae80"
PlutoUI = "7f904dfe-b85e-4ff6-b463-dae2292396a8"

[compat]
Plots = "~1.25.2"
PlutoUI = "~0.7.23"
"""

# ╔═╡ 00000000-0000-0000-0000-000000000002
PLUTO_MANIFEST_TOML_CONTENTS = """
# This file is machine-generated - editing it directly is not advised

[[AbstractPlutoDingetjes]]
deps = ["Pkg"]
git-tree-sha1 = "abb72771fd8895a7ebd83d5632dc4b989b022b5b"
uuid = "6e696c72-6542-2067-7265-42206c756150"
version = "1.1.2"

[[Adapt]]
deps = ["LinearAlgebra"]
git-tree-sha1 = "84918055d15b3114ede17ac6a7182f68870c16f7"
uuid = "79e6a3ab-5dfb-504d-930d-738a2a938a0e"
version = "3.3.1"

[[ArgTools]]
uuid = "0dad84c5-d112-42e6-8d28-ef12dabb789f"

[[Artifacts]]
uuid = "56f22d72-fd6d-98f1-02f0-08ddc0907c33"

[[Base64]]
uuid = "2a0f44e3-6c83-55bd-87e4-b1978d98bd5f"

[[Bzip2_jll]]
deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"]
git-tree-sha1 = "19a35467a82e236ff51bc17a3a44b69ef35185a2"
uuid = "6e34b625-4abd-537c-b88f-471c36dfa7a0"
version = "1.0.8+0"

[[Cairo_jll]]
deps = ["Artifacts", "Bzip2_jll", "Fontconfig_jll", "FreeType2_jll", "Glib_jll", "JLLWrappers", "LZO_jll", "Libdl", "Pixman_jll", "Pkg", "Xorg_libXext_jll", "Xorg_libXrender_jll", "Zlib_jll", "libpng_jll"]
git-tree-sha1 = "f2202b55d816427cd385a9a4f3ffb226bee80f99"
uuid = "83423d85-b0ee-5818-9007-b63ccbeb887a"
version = "1.16.1+0"

[[ChainRulesCore]]
deps = ["Compat", "LinearAlgebra", "SparseArrays"]
git-tree-sha1 = "4c26b4e9e91ca528ea212927326ece5918a04b47"
uuid = "d360d2e6-b24c-11e9-a2a3-2a2ae2dbcce4"
version = "1.11.2"

[[ChangesOfVariables]]
deps = ["ChainRulesCore", "LinearAlgebra", "Test"]
git-tree-sha1 = "bf98fa45a0a4cee295de98d4c1462be26345b9a1"
uuid = "9e997f8a-9a97-42d5-a9f1-ce6bfc15e2c0"
version = "0.1.2"

[[ColorSchemes]]
deps = ["ColorTypes", "Colors", "FixedPointNumbers", "Random"]
git-tree-sha1 = "a851fec56cb73cfdf43762999ec72eff5b86882a"
uuid = "35d6a980-a343-548e-a6ea-1d62b119f2f4"
version = "3.15.0"

[[ColorTypes]]
deps = ["FixedPointNumbers", "Random"]
git-tree-sha1 = "024fe24d83e4a5bf5fc80501a314ce0d1aa35597"
uuid = "3da002f7-5984-5a60-b8a6-cbb66c0b333f"
version = "0.11.0"

[[Colors]]
deps = ["ColorTypes", "FixedPointNumbers", "Reexport"]
git-tree-sha1 = "417b0ed7b8b838aa6ca0a87aadf1bb9eb111ce40"
uuid = "5ae59095-9a9b-59fe-a467-6f913c188581"
version = "0.12.8"

[[Compat]]
deps = ["Base64", "Dates", "DelimitedFiles", "Distributed", "InteractiveUtils", "LibGit2", "Libdl", "LinearAlgebra", "Markdown", "Mmap", "Pkg", "Printf", "REPL", "Random", "SHA", "Serialization", "SharedArrays", "Sockets", "SparseArrays", "Statistics", "Test", "UUIDs", "Unicode"]
git-tree-sha1 = "dce3e3fea680869eaa0b774b2e8343e9ff442313"
uuid = "34da2185-b29b-5c13-b0c7-acf172513d20"
version = "3.40.0"

[[CompilerSupportLibraries_jll]]
deps = ["Artifacts", "Libdl"]
uuid = "e66e0078-7015-5450-92f7-15fbd957f2ae"

[[Contour]]
deps = ["StaticArrays"]
git-tree-sha1 = "9f02045d934dc030edad45944ea80dbd1f0ebea7"
uuid = "d38c429a-6771-53c6-b99e-75d170b6e991"
version = "0.5.7"

[[DataAPI]]
git-tree-sha1 = "cc70b17275652eb47bc9e5f81635981f13cea5c8"
uuid = "9a962f9c-6df0-11e9-0e5d-c546b8b5ee8a"
version = "1.9.0"

[[DataStructures]]
deps = ["Compat", "InteractiveUtils", "OrderedCollections"]
git-tree-sha1 = "3daef5523dd2e769dad2365274f760ff5f282c7d"
uuid = "864edb3b-99cc-5e75-8d2d-829cb0a9cfe8"
version = "0.18.11"

[[DataValueInterfaces]]
git-tree-sha1 = "bfc1187b79289637fa0ef6d4436ebdfe6905cbd6"
uuid = "e2d170a0-9d28-54be-80f0-106bbe20a464"
version = "1.0.0"

[[Dates]]
deps = ["Printf"]
uuid = "ade2ca70-3891-5945-98fb-dc099432e06a"

[[DelimitedFiles]]
deps = ["Mmap"]
uuid = "8bb1440f-4735-579b-a4ab-409b98df4dab"

[[Distributed]]
deps = ["Random", "Serialization", "Sockets"]
uuid = "8ba89e20-285c-5b6f-9357-94700520ee1b"

[[DocStringExtensions]]
deps = ["LibGit2"]
git-tree-sha1 = "b19534d1895d702889b219c382a6e18010797f0b"
uuid = "ffbed154-4ef7-542d-bbb7-c09d3a79fcae"
version = "0.8.6"

[[Downloads]]
deps = ["ArgTools", "LibCURL", "NetworkOptions"]
uuid = "f43a241f-c20a-4ad4-852c-f6b1247861c6"

[[EarCut_jll]]
deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"]
git-tree-sha1 = "3f3a2501fa7236e9b911e0f7a588c657e822bb6d"
uuid = "5ae413db-bbd1-5e63-b57d-d24a61df00f5"
version = "2.2.3+0"

[[Expat_jll]]
deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"]
git-tree-sha1 = "b3bfd02e98aedfa5cf885665493c5598c350cd2f"
uuid = "2e619515-83b5-522b-bb60-26c02a35a201"
version = "2.2.10+0"

[[FFMPEG]]
deps = ["FFMPEG_jll"]
git-tree-sha1 = "b57e3acbe22f8484b4b5ff66a7499717fe1a9cc8"
uuid = "c87230d0-a227-11e9-1b43-d7ebe4e7570a"
version = "0.4.1"

[[FFMPEG_jll]]
deps = ["Artifacts", "Bzip2_jll", "FreeType2_jll", "FriBidi_jll", "JLLWrappers", "LAME_jll", "Libdl", "Ogg_jll", "OpenSSL_jll", "Opus_jll", "Pkg", "Zlib_jll", "libass_jll", "libfdk_aac_jll", "libvorbis_jll", "x264_jll", "x265_jll"]
git-tree-sha1 = "d8a578692e3077ac998b50c0217dfd67f21d1e5f"
uuid = "b22a6f82-2f65-5046-a5b2-351ab43fb4e5"
version = "4.4.0+0"

[[FixedPointNumbers]]
deps = ["Statistics"]
git-tree-sha1 = "335bfdceacc84c5cdf16aadc768aa5ddfc5383cc"
uuid = "53c48c17-4a7d-5ca2-90c5-79b7896eea93"
version = "0.8.4"

[[Fontconfig_jll]]
deps = ["Artifacts", "Bzip2_jll", "Expat_jll", "FreeType2_jll", "JLLWrappers", "Libdl", "Libuuid_jll", "Pkg", "Zlib_jll"]
git-tree-sha1 = "21efd19106a55620a188615da6d3d06cd7f6ee03"
uuid = "a3f928ae-7b40-5064-980b-68af3947d34b"
version = "2.13.93+0"

[[Formatting]]
deps = ["Printf"]
git-tree-sha1 = "8339d61043228fdd3eb658d86c926cb282ae72a8"
uuid = "59287772-0a20-5a39-b81b-1366585eb4c0"
version = "0.4.2"

[[FreeType2_jll]]
deps = ["Artifacts", "Bzip2_jll", "JLLWrappers", "Libdl", "Pkg", "Zlib_jll"]
git-tree-sha1 = "87eb71354d8ec1a96d4a7636bd57a7347dde3ef9"
uuid = "d7e528f0-a631-5988-bf34-fe36492bcfd7"
version = "2.10.4+0"

[[FriBidi_jll]]
deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"]
git-tree-sha1 = "aa31987c2ba8704e23c6c8ba8a4f769d5d7e4f91"
uuid = "559328eb-81f9-559d-9380-de523a88c83c"
version = "1.0.10+0"

[[GLFW_jll]]
deps = ["Artifacts", "JLLWrappers", "Libdl", "Libglvnd_jll", "Pkg", "Xorg_libXcursor_jll", "Xorg_libXi_jll", "Xorg_libXinerama_jll", "Xorg_libXrandr_jll"]
git-tree-sha1 = "0c603255764a1fa0b61752d2bec14cfbd18f7fe8"
uuid = "0656b61e-2033-5cc2-a64a-77c0f6c09b89"
version = "3.3.5+1"

[[GR]]
deps = ["Base64", "DelimitedFiles", "GR_jll", "HTTP", "JSON", "Libdl", "LinearAlgebra", "Pkg", "Printf", "Random", "Serialization", "Sockets", "Test", "UUIDs"]
git-tree-sha1 = "30f2b340c2fff8410d89bfcdc9c0a6dd661ac5f7"
uuid = "28b8d3ca-fb5f-59d9-8090-bfdbd6d07a71"
version = "0.62.1"

[[GR_jll]]
deps = ["Artifacts", "Bzip2_jll", "Cairo_jll", "FFMPEG_jll", "Fontconfig_jll", "GLFW_jll", "JLLWrappers", "JpegTurbo_jll", "Libdl", "Libtiff_jll", "Pixman_jll", "Pkg", "Qt5Base_jll", "Zlib_jll", "libpng_jll"]
git-tree-sha1 = "fd75fa3a2080109a2c0ec9864a6e14c60cca3866"
uuid = "d2c73de3-f751-5644-a686-071e5b155ba9"
version = "0.62.0+0"

[[GeometryBasics]]
deps = ["EarCut_jll", "IterTools", "LinearAlgebra", "StaticArrays", "StructArrays", "Tables"]
git-tree-sha1 = "58bcdf5ebc057b085e58d95c138725628dd7453c"
uuid = "5c1252a2-5f33-56bf-86c9-59e7332b4326"
version = "0.4.1"

[[Gettext_jll]]
deps = ["Artifacts", "CompilerSupportLibraries_jll", "JLLWrappers", "Libdl", "Libiconv_jll", "Pkg", "XML2_jll"]
git-tree-sha1 = "9b02998aba7bf074d14de89f9d37ca24a1a0b046"
uuid = "78b55507-aeef-58d4-861c-77aaff3498b1"
version = "0.21.0+0"

[[Glib_jll]]
deps = ["Artifacts", "Gettext_jll", "JLLWrappers", "Libdl", "Libffi_jll", "Libiconv_jll", "Libmount_jll", "PCRE_jll", "Pkg", "Zlib_jll"]
git-tree-sha1 = "74ef6288d071f58033d54fd6708d4bc23a8b8972"
uuid = "7746bdde-850d-59dc-9ae8-88ece973131d"
version = "2.68.3+1"

[[Graphite2_jll]]
deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"]
git-tree-sha1 = "344bf40dcab1073aca04aa0df4fb092f920e4011"
uuid = "3b182d85-2403-5c21-9c21-1e1f0cc25472"
version = "1.3.14+0"

[[Grisu]]
git-tree-sha1 = "53bb909d1151e57e2484c3d1b53e19552b887fb2"
uuid = "42e2da0e-8278-4e71-bc24-59509adca0fe"
version = "1.0.2"

[[HTTP]]
deps = ["Base64", "Dates", "IniFile", "Logging", "MbedTLS", "NetworkOptions", "Sockets", "URIs"]
git-tree-sha1 = "0fa77022fe4b511826b39c894c90daf5fce3334a"
uuid = "cd3eb016-35fb-5094-929b-558a96fad6f3"
version = "0.9.17"

[[HarfBuzz_jll]]
deps = ["Artifacts", "Cairo_jll", "Fontconfig_jll", "FreeType2_jll", "Glib_jll", "Graphite2_jll", "JLLWrappers", "Libdl", "Libffi_jll", "Pkg"]
git-tree-sha1 = "129acf094d168394e80ee1dc4bc06ec835e510a3"
uuid = "2e76f6c2-a576-52d4-95c1-20adfe4de566"
version = "2.8.1+1"

[[Hyperscript]]
deps = ["Test"]
git-tree-sha1 = "8d511d5b81240fc8e6802386302675bdf47737b9"
uuid = "47d2ed2b-36de-50cf-bf87-49c2cf4b8b91"
version = "0.0.4"

[[HypertextLiteral]]
git-tree-sha1 = "2b078b5a615c6c0396c77810d92ee8c6f470d238"
uuid = "ac1192a8-f4b3-4bfe-ba22-af5b92cd3ab2"
version = "0.9.3"

[[IOCapture]]
deps = ["Logging", "Random"]
git-tree-sha1 = "f7be53659ab06ddc986428d3a9dcc95f6fa6705a"
uuid = "b5f81e59-6552-4d32-b1f0-c071b021bf89"
version = "0.2.2"

[[IniFile]]
deps = ["Test"]
git-tree-sha1 = "098e4d2c533924c921f9f9847274f2ad89e018b8"
uuid = "83e8ac13-25f8-5344-8a64-a9f2b223428f"
version = "0.5.0"

[[InteractiveUtils]]
deps = ["Markdown"]
uuid = "b77e0a4c-d291-57a0-90e8-8db25a27a240"

[[InverseFunctions]]
deps = ["Test"]
git-tree-sha1 = "a7254c0acd8e62f1ac75ad24d5db43f5f19f3c65"
uuid = "3587e190-3f89-42d0-90ee-14403ec27112"
version = "0.1.2"

[[IrrationalConstants]]
git-tree-sha1 = "7fd44fd4ff43fc60815f8e764c0f352b83c49151"
uuid = "92d709cd-6900-40b7-9082-c6be49f344b6"
version = "0.1.1"

[[IterTools]]
git-tree-sha1 = "fa6287a4469f5e048d763df38279ee729fbd44e5"
uuid = "c8e1da08-722c-5040-9ed9-7db0dc04731e"
version = "1.4.0"

[[IteratorInterfaceExtensions]]
git-tree-sha1 = "a3f24677c21f5bbe9d2a714f95dcd58337fb2856"
uuid = "82899510-4779-5014-852e-03e436cf321d"
version = "1.0.0"

[[JLLWrappers]]
deps = ["Preferences"]
git-tree-sha1 = "642a199af8b68253517b80bd3bfd17eb4e84df6e"
uuid = "692b3bcd-3c85-4b1f-b108-f13ce0eb3210"
version = "1.3.0"

[[JSON]]
deps = ["Dates", "Mmap", "Parsers", "Unicode"]
git-tree-sha1 = "8076680b162ada2a031f707ac7b4953e30667a37"
uuid = "682c06a0-de6a-54ab-a142-c8b1cf79cde6"
version = "0.21.2"

[[JpegTurbo_jll]]
deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"]
git-tree-sha1 = "d735490ac75c5cb9f1b00d8b5509c11984dc6943"
uuid = "aacddb02-875f-59d6-b918-886e6ef4fbf8"
version = "2.1.0+0"

[[LAME_jll]]
deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"]
git-tree-sha1 = "f6250b16881adf048549549fba48b1161acdac8c"
uuid = "c1c5ebd0-6772-5130-a774-d5fcae4a789d"
version = "3.100.1+0"

[[LZO_jll]]
deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"]
git-tree-sha1 = "e5b909bcf985c5e2605737d2ce278ed791b89be6"
uuid = "dd4b983a-f0e5-5f8d-a1b7-129d4a5fb1ac"
version = "2.10.1+0"

[[LaTeXStrings]]
git-tree-sha1 = "f2355693d6778a178ade15952b7ac47a4ff97996"
uuid = "b964fa9f-0449-5b57-a5c2-d3ea65f4040f"
version = "1.3.0"

[[Latexify]]
deps = ["Formatting", "InteractiveUtils", "LaTeXStrings", "MacroTools", "Markdown", "Printf", "Requires"]
git-tree-sha1 = "a8f4f279b6fa3c3c4f1adadd78a621b13a506bce"
uuid = "23fbe1c1-3f47-55db-b15f-69d7ec21a316"
version = "0.15.9"

[[LibCURL]]
deps = ["LibCURL_jll", "MozillaCACerts_jll"]
uuid = "b27032c2-a3e7-50c8-80cd-2d36dbcbfd21"

[[LibCURL_jll]]
deps = ["Artifacts", "LibSSH2_jll", "Libdl", "MbedTLS_jll", "Zlib_jll", "nghttp2_jll"]
uuid = "deac9b47-8bc7-5906-a0fe-35ac56dc84c0"

[[LibGit2]]
deps = ["Base64", "NetworkOptions", "Printf", "SHA"]
uuid = "76f85450-5226-5b5a-8eaa-529ad045b433"

[[LibSSH2_jll]]
deps = ["Artifacts", "Libdl", "MbedTLS_jll"]
uuid = "29816b5a-b9ab-546f-933c-edad1886dfa8"

[[Libdl]]
uuid = "8f399da3-3557-5675-b5ff-fb832c97cbdb"

[[Libffi_jll]]
deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"]
git-tree-sha1 = "0b4a5d71f3e5200a7dff793393e09dfc2d874290"
uuid = "e9f186c6-92d2-5b65-8a66-fee21dc1b490"
version = "3.2.2+1"

[[Libgcrypt_jll]]
deps = ["Artifacts", "JLLWrappers", "Libdl", "Libgpg_error_jll", "Pkg"]
git-tree-sha1 = "64613c82a59c120435c067c2b809fc61cf5166ae"
uuid = "d4300ac3-e22c-5743-9152-c294e39db1e4"
version = "1.8.7+0"

[[Libglvnd_jll]]
deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg", "Xorg_libX11_jll", "Xorg_libXext_jll"]
git-tree-sha1 = "7739f837d6447403596a75d19ed01fd08d6f56bf"
uuid = "7e76a0d4-f3c7-5321-8279-8d96eeed0f29"
version = "1.3.0+3"

[[Libgpg_error_jll]]
deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"]
git-tree-sha1 = "c333716e46366857753e273ce6a69ee0945a6db9"
uuid = "7add5ba3-2f88-524e-9cd5-f83b8a55f7b8"
version = "1.42.0+0"

[[Libiconv_jll]]
deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"]
git-tree-sha1 = "42b62845d70a619f063a7da093d995ec8e15e778"
uuid = "94ce4f54-9a6c-5748-9c1c-f9c7231a4531"
version = "1.16.1+1"

[[Libmount_jll]]
deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"]
git-tree-sha1 = "9c30530bf0effd46e15e0fdcf2b8636e78cbbd73"
uuid = "4b2f31a3-9ecc-558c-b454-b3730dcb73e9"
version = "2.35.0+0"

[[Libtiff_jll]]
deps = ["Artifacts", "JLLWrappers", "JpegTurbo_jll", "Libdl", "Pkg", "Zlib_jll", "Zstd_jll"]
git-tree-sha1 = "340e257aada13f95f98ee352d316c3bed37c8ab9"
uuid = "89763e89-9b03-5906-acba-b20f662cd828"
version = "4.3.0+0"

[[Libuuid_jll]]
deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"]
git-tree-sha1 = "7f3efec06033682db852f8b3bc3c1d2b0a0ab066"
uuid = "38a345b3-de98-5d2b-a5d3-14cd9215e700"
version = "2.36.0+0"

[[LinearAlgebra]]
deps = ["Libdl"]
uuid = "37e2e46d-f89d-539d-b4ee-838fcccc9c8e"

[[LogExpFunctions]]
deps = ["ChainRulesCore", "ChangesOfVariables", "DocStringExtensions", "InverseFunctions", "IrrationalConstants", "LinearAlgebra"]
git-tree-sha1 = "be9eef9f9d78cecb6f262f3c10da151a6c5ab827"
uuid = "2ab3a3ac-af41-5b50-aa03-7779005ae688"
version = "0.3.5"

[[Logging]]
uuid = "56ddb016-857b-54e1-b83d-db4d58db5568"

[[MacroTools]]
deps = ["Markdown", "Random"]
git-tree-sha1 = "3d3e902b31198a27340d0bf00d6ac452866021cf"
uuid = "1914dd2f-81c6-5fcd-8719-6d5c9610ff09"
version = "0.5.9"

[[Markdown]]
deps = ["Base64"]
uuid = "d6f4376e-aef5-505a-96c1-9c027394607a"

[[MbedTLS]]
deps = ["Dates", "MbedTLS_jll", "Random", "Sockets"]
git-tree-sha1 = "1c38e51c3d08ef2278062ebceade0e46cefc96fe"
uuid = "739be429-bea8-5141-9913-cc70e7f3736d"
version = "1.0.3"

[[MbedTLS_jll]]
deps = ["Artifacts", "Libdl"]
uuid = "c8ffd9c3-330d-5841-b78e-0817d7145fa1"

[[Measures]]
git-tree-sha1 = "e498ddeee6f9fdb4551ce855a46f54dbd900245f"
uuid = "442fdcdd-2543-5da2-b0f3-8c86c306513e"
version = "0.3.1"

[[Missings]]
deps = ["DataAPI"]
git-tree-sha1 = "bf210ce90b6c9eed32d25dbcae1ebc565df2687f"
uuid = "e1d29d7a-bbdc-5cf2-9ac0-f12de2c33e28"
version = "1.0.2"

[[Mmap]]
uuid = "a63ad114-7e13-5084-954f-fe012c677804"

[[MozillaCACerts_jll]]
uuid = "14a3606d-f60d-562e-9121-12d972cd8159"

[[NaNMath]]
git-tree-sha1 = "bfe47e760d60b82b66b61d2d44128b62e3a369fb"
uuid = "77ba4419-2d1f-58cd-9bb1-8ffee604a2e3"
version = "0.3.5"

[[NetworkOptions]]
uuid = "ca575930-c2e3-43a9-ace4-1e988b2c1908"

[[Ogg_jll]]
deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"]
git-tree-sha1 = "7937eda4681660b4d6aeeecc2f7e1c81c8ee4e2f"
uuid = "e7412a2a-1a6e-54c0-be00-318e2571c051"
version = "1.3.5+0"

[[OpenSSL_jll]]
deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"]
git-tree-sha1 = "15003dcb7d8db3c6c857fda14891a539a8f2705a"
uuid = "458c3c95-2e84-50aa-8efc-19380b2a3a95"
version = "1.1.10+0"

[[Opus_jll]]
deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"]
git-tree-sha1 = "51a08fb14ec28da2ec7a927c4337e4332c2a4720"
uuid = "91d4177d-7536-5919-b921-800302f37372"
version = "1.3.2+0"

[[OrderedCollections]]
git-tree-sha1 = "85f8e6578bf1f9ee0d11e7bb1b1456435479d47c"
uuid = "bac558e1-5e72-5ebc-8fee-abe8a469f55d"
version = "1.4.1"

[[PCRE_jll]]
deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"]
git-tree-sha1 = "b2a7af664e098055a7529ad1a900ded962bca488"
uuid = "2f80f16e-611a-54ab-bc61-aa92de5b98fc"
version = "8.44.0+0"

[[Parsers]]
deps = ["Dates"]
git-tree-sha1 = "ae4bbcadb2906ccc085cf52ac286dc1377dceccc"
uuid = "69de0a69-1ddd-5017-9359-2bf0b02dc9f0"
version = "2.1.2"

[[Pixman_jll]]
deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"]
git-tree-sha1 = "b4f5d02549a10e20780a24fce72bea96b6329e29"
uuid = "30392449-352a-5448-841d-b1acce4e97dc"
version = "0.40.1+0"

[[Pkg]]
deps = ["Artifacts", "Dates", "Downloads", "LibGit2", "Libdl", "Logging", "Markdown", "Printf", "REPL", "Random", "SHA", "Serialization", "TOML", "Tar", "UUIDs", "p7zip_jll"]
uuid = "44cfe95a-1eb2-52ea-b672-e2afdf69b78f"

[[PlotThemes]]
deps = ["PlotUtils", "Requires", "Statistics"]
git-tree-sha1 = "a3a964ce9dc7898193536002a6dd892b1b5a6f1d"
uuid = "ccf2f8ad-2431-5c83-bf29-c5338b663b6a"
version = "2.0.1"

[[PlotUtils]]
deps = ["ColorSchemes", "Colors", "Dates", "Printf", "Random", "Reexport", "Statistics"]
git-tree-sha1 = "8fb515c5a2c8941cef957e75afb99a2c24b753f3"
uuid = "995b91a9-d308-5afd-9ec6-746e21dbc043"
version = "1.1.0"

[[Plots]]
deps = ["Base64", "Contour", "Dates", "Downloads", "FFMPEG", "FixedPointNumbers", "GR", "GeometryBasics", "JSON", "Latexify", "LinearAlgebra", "Measures", "NaNMath", "PlotThemes", "PlotUtils", "Printf", "REPL", "Random", "RecipesBase", "RecipesPipeline", "Reexport", "Requires", "Scratch", "Showoff", "SparseArrays", "Statistics", "StatsBase", "UUIDs", "UnicodeFun"]
git-tree-sha1 = "65ebc27d8c00c84276f14aaf4ff63cbe12016c70"
uuid = "91a5bcdd-55d7-5caf-9e0b-520d859cae80"
version = "1.25.2"

[[PlutoUI]]
deps = ["AbstractPlutoDingetjes", "Base64", "Dates", "Hyperscript", "HypertextLiteral", "IOCapture", "InteractiveUtils", "JSON", "Logging", "Markdown", "Random", "Reexport", "UUIDs"]
git-tree-sha1 = "5152abbdab6488d5eec6a01029ca6697dff4ec8f"
uuid = "7f904dfe-b85e-4ff6-b463-dae2292396a8"
version = "0.7.23"

[[Preferences]]
deps = ["TOML"]
git-tree-sha1 = "00cfd92944ca9c760982747e9a1d0d5d86ab1e5a"
uuid = "21216c6a-2e73-6563-6e65-726566657250"
version = "1.2.2"

[[Printf]]
deps = ["Unicode"]
uuid = "de0858da-6303-5e67-8744-51eddeeeb8d7"

[[Qt5Base_jll]]
deps = ["Artifacts", "CompilerSupportLibraries_jll", "Fontconfig_jll", "Glib_jll", "JLLWrappers", "Libdl", "Libglvnd_jll", "OpenSSL_jll", "Pkg", "Xorg_libXext_jll", "Xorg_libxcb_jll", "Xorg_xcb_util_image_jll", "Xorg_xcb_util_keysyms_jll", "Xorg_xcb_util_renderutil_jll", "Xorg_xcb_util_wm_jll", "Zlib_jll", "xkbcommon_jll"]
git-tree-sha1 = "ad368663a5e20dbb8d6dc2fddeefe4dae0781ae8"
uuid = "ea2cea3b-5b76-57ae-a6ef-0a8af62496e1"
version = "5.15.3+0"

[[REPL]]
deps = ["InteractiveUtils", "Markdown", "Sockets", "Unicode"]
uuid = "3fa0cd96-eef1-5676-8a61-b3b8758bbffb"

[[Random]]
deps = ["Serialization"]
uuid = "9a3f8284-a2c9-5f02-9a11-845980a1fd5c"

[[RecipesBase]]
git-tree-sha1 = "6bf3f380ff52ce0832ddd3a2a7b9538ed1bcca7d"
uuid = "3cdcf5f2-1ef4-517c-9805-6587b60abb01"
version = "1.2.1"

[[RecipesPipeline]]
deps = ["Dates", "NaNMath", "PlotUtils", "RecipesBase"]
git-tree-sha1 = "7ad0dfa8d03b7bcf8c597f59f5292801730c55b8"
uuid = "01d81517-befc-4cb6-b9ec-a95719d0359c"
version = "0.4.1"

[[Reexport]]
git-tree-sha1 = "45e428421666073eab6f2da5c9d310d99bb12f9b"
uuid = "189a3867-3050-52da-a836-e630ba90ab69"
version = "1.2.2"

[[Requires]]
deps = ["UUIDs"]
git-tree-sha1 = "8f82019e525f4d5c669692772a6f4b0a58b06a6a"
uuid = "ae029012-a4dd-5104-9daa-d747884805df"
version = "1.2.0"

[[SHA]]
uuid = "ea8e919c-243c-51af-8825-aaa63cd721ce"

[[Scratch]]
deps = ["Dates"]
git-tree-sha1 = "0b4b7f1393cff97c33891da2a0bf69c6ed241fda"
uuid = "6c6a2e73-6563-6170-7368-637461726353"
version = "1.1.0"

[[Serialization]]
uuid = "9e88b42a-f829-5b0c-bbe9-9e923198166b"

[[SharedArrays]]
deps = ["Distributed", "Mmap", "Random", "Serialization"]
uuid = "1a1011a3-84de-559e-8e89-a11a2f7dc383"

[[Showoff]]
deps = ["Dates", "Grisu"]
git-tree-sha1 = "91eddf657aca81df9ae6ceb20b959ae5653ad1de"
uuid = "992d4aef-0814-514b-bc4d-f2e9a6c4116f"
version = "1.0.3"

[[Sockets]]
uuid = "6462fe0b-24de-5631-8697-dd941f90decc"

[[SortingAlgorithms]]
deps = ["DataStructures"]
git-tree-sha1 = "b3363d7460f7d098ca0912c69b082f75625d7508"
uuid = "a2af1166-a08f-5f64-846c-94a0d3cef48c"
version = "1.0.1"

[[SparseArrays]]
deps = ["LinearAlgebra", "Random"]
uuid = "2f01184e-e22b-5df5-ae63-d93ebab69eaf"

[[StaticArrays]]
deps = ["LinearAlgebra", "Random", "Statistics"]
git-tree-sha1 = "3c76dde64d03699e074ac02eb2e8ba8254d428da"
uuid = "90137ffa-7385-5640-81b9-e52037218182"
version = "1.2.13"

[[Statistics]]
deps = ["LinearAlgebra", "SparseArrays"]
uuid = "10745b16-79ce-11e8-11f9-7d13ad32a3b2"

[[StatsAPI]]
git-tree-sha1 = "0f2aa8e32d511f758a2ce49208181f7733a0936a"
uuid = "82ae8749-77ed-4fe6-ae5f-f523153014b0"
version = "1.1.0"

[[StatsBase]]
deps = ["DataAPI", "DataStructures", "LinearAlgebra", "LogExpFunctions", "Missings", "Printf", "Random", "SortingAlgorithms", "SparseArrays", "Statistics", "StatsAPI"]
git-tree-sha1 = "2bb0cb32026a66037360606510fca5984ccc6b75"
uuid = "2913bbd2-ae8a-5f71-8c99-4fb6c76f3a91"
version = "0.33.13"

[[StructArrays]]
deps = ["Adapt", "DataAPI", "StaticArrays", "Tables"]
git-tree-sha1 = "2ce41e0d042c60ecd131e9fb7154a3bfadbf50d3"
uuid = "09ab397b-f2b6-538f-b94a-2f83cf4a842a"
version = "0.6.3"

[[TOML]]
deps = ["Dates"]
uuid = "fa267f1f-6049-4f14-aa54-33bafae1ed76"

[[TableTraits]]
deps = ["IteratorInterfaceExtensions"]
git-tree-sha1 = "c06b2f539df1c6efa794486abfb6ed2022561a39"
uuid = "3783bdb8-4a98-5b6b-af9a-565f29a5fe9c"
version = "1.0.1"

[[Tables]]
deps = ["DataAPI", "DataValueInterfaces", "IteratorInterfaceExtensions", "LinearAlgebra", "TableTraits", "Test"]
git-tree-sha1 = "fed34d0e71b91734bf0a7e10eb1bb05296ddbcd0"
uuid = "bd369af6-aec1-5ad0-b16a-f7cc5008161c"
version = "1.6.0"

[[Tar]]
deps = ["ArgTools", "SHA"]
uuid = "a4e569a6-e804-4fa4-b0f3-eef7a1d5b13e"

[[Test]]
deps = ["InteractiveUtils", "Logging", "Random", "Serialization"]
uuid = "8dfed614-e22c-5e08-85e1-65c5234f0b40"

[[URIs]]
git-tree-sha1 = "97bbe755a53fe859669cd907f2d96aee8d2c1355"
uuid = "5c2747f8-b7ea-4ff2-ba2e-563bfd36b1d4"
version = "1.3.0"

[[UUIDs]]
deps = ["Random", "SHA"]
uuid = "cf7118a7-6976-5b1a-9a39-7adc72f591a4"

[[Unicode]]
uuid = "4ec0a83e-493e-50e2-b9ac-8f72acf5a8f5"

[[UnicodeFun]]
deps = ["REPL"]
git-tree-sha1 = "53915e50200959667e78a92a418594b428dffddf"
uuid = "1cfade01-22cf-5700-b092-accc4b62d6e1"
version = "0.4.1"

[[Wayland_jll]]
deps = ["Artifacts", "Expat_jll", "JLLWrappers", "Libdl", "Libffi_jll", "Pkg", "XML2_jll"]
git-tree-sha1 = "3e61f0b86f90dacb0bc0e73a0c5a83f6a8636e23"
uuid = "a2964d1f-97da-50d4-b82a-358c7fce9d89"
version = "1.19.0+0"

[[Wayland_protocols_jll]]
deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"]
git-tree-sha1 = "66d72dc6fcc86352f01676e8f0f698562e60510f"
uuid = "2381bf8a-dfd0-557d-9999-79630e7b1b91"
version = "1.23.0+0"

[[XML2_jll]]
deps = ["Artifacts", "JLLWrappers", "Libdl", "Libiconv_jll", "Pkg", "Zlib_jll"]
git-tree-sha1 = "1acf5bdf07aa0907e0a37d3718bb88d4b687b74a"
uuid = "02c8fc9c-b97f-50b9-bbe4-9be30ff0a78a"
version = "2.9.12+0"

[[XSLT_jll]]
deps = ["Artifacts", "JLLWrappers", "Libdl", "Libgcrypt_jll", "Libgpg_error_jll", "Libiconv_jll", "Pkg", "XML2_jll", "Zlib_jll"]
git-tree-sha1 = "91844873c4085240b95e795f692c4cec4d805f8a"
uuid = "aed1982a-8fda-507f-9586-7b0439959a61"
version = "1.1.34+0"

[[Xorg_libX11_jll]]
deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg", "Xorg_libxcb_jll", "Xorg_xtrans_jll"]
git-tree-sha1 = "5be649d550f3f4b95308bf0183b82e2582876527"
uuid = "4f6342f7-b3d2-589e-9d20-edeb45f2b2bc"
version = "1.6.9+4"

[[Xorg_libXau_jll]]
deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"]
git-tree-sha1 = "4e490d5c960c314f33885790ed410ff3a94ce67e"
uuid = "0c0b7dd1-d40b-584c-a123-a41640f87eec"
version = "1.0.9+4"

[[Xorg_libXcursor_jll]]
deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg", "Xorg_libXfixes_jll", "Xorg_libXrender_jll"]
git-tree-sha1 = "12e0eb3bc634fa2080c1c37fccf56f7c22989afd"
uuid = "935fb764-8cf2-53bf-bb30-45bb1f8bf724"
version = "1.2.0+4"

[[Xorg_libXdmcp_jll]]
deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"]
git-tree-sha1 = "4fe47bd2247248125c428978740e18a681372dd4"
uuid = "a3789734-cfe1-5b06-b2d0-1dd0d9d62d05"
version = "1.1.3+4"

[[Xorg_libXext_jll]]
deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg", "Xorg_libX11_jll"]
git-tree-sha1 = "b7c0aa8c376b31e4852b360222848637f481f8c3"
uuid = "1082639a-0dae-5f34-9b06-72781eeb8cb3"
version = "1.3.4+4"

[[Xorg_libXfixes_jll]]
deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg", "Xorg_libX11_jll"]
git-tree-sha1 = "0e0dc7431e7a0587559f9294aeec269471c991a4"
uuid = "d091e8ba-531a-589c-9de9-94069b037ed8"
version = "5.0.3+4"

[[Xorg_libXi_jll]]
deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg", "Xorg_libXext_jll", "Xorg_libXfixes_jll"]
git-tree-sha1 = "89b52bc2160aadc84d707093930ef0bffa641246"
uuid = "a51aa0fd-4e3c-5386-b890-e753decda492"
version = "1.7.10+4"

[[Xorg_libXinerama_jll]]
deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg", "Xorg_libXext_jll"]
git-tree-sha1 = "26be8b1c342929259317d8b9f7b53bf2bb73b123"
uuid = "d1454406-59df-5ea1-beac-c340f2130bc3"
version = "1.1.4+4"

[[Xorg_libXrandr_jll]]
deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg", "Xorg_libXext_jll", "Xorg_libXrender_jll"]
git-tree-sha1 = "34cea83cb726fb58f325887bf0612c6b3fb17631"
uuid = "ec84b674-ba8e-5d96-8ba1-2a689ba10484"
version = "1.5.2+4"

[[Xorg_libXrender_jll]]
deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg", "Xorg_libX11_jll"]
git-tree-sha1 = "19560f30fd49f4d4efbe7002a1037f8c43d43b96"
uuid = "ea2f1a96-1ddc-540d-b46f-429655e07cfa"
version = "0.9.10+4"

[[Xorg_libpthread_stubs_jll]]
deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"]
git-tree-sha1 = "6783737e45d3c59a4a4c4091f5f88cdcf0908cbb"
uuid = "14d82f49-176c-5ed1-bb49-ad3f5cbd8c74"
version = "0.1.0+3"

[[Xorg_libxcb_jll]]
deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg", "XSLT_jll", "Xorg_libXau_jll", "Xorg_libXdmcp_jll", "Xorg_libpthread_stubs_jll"]
git-tree-sha1 = "daf17f441228e7a3833846cd048892861cff16d6"
uuid = "c7cfdc94-dc32-55de-ac96-5a1b8d977c5b"
version = "1.13.0+3"

[[Xorg_libxkbfile_jll]]
deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg", "Xorg_libX11_jll"]
git-tree-sha1 = "926af861744212db0eb001d9e40b5d16292080b2"
uuid = "cc61e674-0454-545c-8b26-ed2c68acab7a"
version = "1.1.0+4"

[[Xorg_xcb_util_image_jll]]
deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg", "Xorg_xcb_util_jll"]
git-tree-sha1 = "0fab0a40349ba1cba2c1da699243396ff8e94b97"
uuid = "12413925-8142-5f55-bb0e-6d7ca50bb09b"
version = "0.4.0+1"

[[Xorg_xcb_util_jll]]
deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg", "Xorg_libxcb_jll"]
git-tree-sha1 = "e7fd7b2881fa2eaa72717420894d3938177862d1"
uuid = "2def613f-5ad1-5310-b15b-b15d46f528f5"
version = "0.4.0+1"

[[Xorg_xcb_util_keysyms_jll]]
deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg", "Xorg_xcb_util_jll"]
git-tree-sha1 = "d1151e2c45a544f32441a567d1690e701ec89b00"
uuid = "975044d2-76e6-5fbe-bf08-97ce7c6574c7"
version = "0.4.0+1"

[[Xorg_xcb_util_renderutil_jll]]
deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg", "Xorg_xcb_util_jll"]
git-tree-sha1 = "dfd7a8f38d4613b6a575253b3174dd991ca6183e"
uuid = "0d47668e-0667-5a69-a72c-f761630bfb7e"
version = "0.3.9+1"

[[Xorg_xcb_util_wm_jll]]
deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg", "Xorg_xcb_util_jll"]
git-tree-sha1 = "e78d10aab01a4a154142c5006ed44fd9e8e31b67"
uuid = "c22f9ab0-d5fe-5066-847c-f4bb1cd4e361"
version = "0.4.1+1"

[[Xorg_xkbcomp_jll]]
deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg", "Xorg_libxkbfile_jll"]
git-tree-sha1 = "4bcbf660f6c2e714f87e960a171b119d06ee163b"
uuid = "35661453-b289-5fab-8a00-3d9160c6a3a4"
version = "1.4.2+4"

[[Xorg_xkeyboard_config_jll]]
deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg", "Xorg_xkbcomp_jll"]
git-tree-sha1 = "5c8424f8a67c3f2209646d4425f3d415fee5931d"
uuid = "33bec58e-1273-512f-9401-5d533626f822"
version = "2.27.0+4"

[[Xorg_xtrans_jll]]
deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"]
git-tree-sha1 = "79c31e7844f6ecf779705fbc12146eb190b7d845"
uuid = "c5fb5394-a638-5e4d-96e5-b29de1b5cf10"
version = "1.4.0+3"

[[Zlib_jll]]
deps = ["Libdl"]
uuid = "83775a58-1f1d-513f-b197-d71354ab007a"

[[Zstd_jll]]
deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"]
git-tree-sha1 = "cc4bf3fdde8b7e3e9fa0351bdeedba1cf3b7f6e6"
uuid = "3161d3a3-bdf6-5164-811a-617609db77b4"
version = "1.5.0+0"

[[libass_jll]]
deps = ["Artifacts", "Bzip2_jll", "FreeType2_jll", "FriBidi_jll", "HarfBuzz_jll", "JLLWrappers", "Libdl", "Pkg", "Zlib_jll"]
git-tree-sha1 = "5982a94fcba20f02f42ace44b9894ee2b140fe47"
uuid = "0ac62f75-1d6f-5e53-bd7c-93b484bb37c0"
version = "0.15.1+0"

[[libfdk_aac_jll]]
deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"]
git-tree-sha1 = "daacc84a041563f965be61859a36e17c4e4fcd55"
uuid = "f638f0a6-7fb0-5443-88ba-1cc74229b280"
version = "2.0.2+0"

[[libpng_jll]]
deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg", "Zlib_jll"]
git-tree-sha1 = "94d180a6d2b5e55e447e2d27a29ed04fe79eb30c"
uuid = "b53b4c65-9356-5827-b1ea-8c7a1a84506f"
version = "1.6.38+0"

[[libvorbis_jll]]
deps = ["Artifacts", "JLLWrappers", "Libdl", "Ogg_jll", "Pkg"]
git-tree-sha1 = "c45f4e40e7aafe9d086379e5578947ec8b95a8fb"
uuid = "f27f6e37-5d2b-51aa-960f-b287f2bc3b7a"
version = "1.3.7+0"

[[nghttp2_jll]]
deps = ["Artifacts", "Libdl"]
uuid = "8e850ede-7688-5339-a07c-302acd2aaf8d"

[[p7zip_jll]]
deps = ["Artifacts", "Libdl"]
uuid = "3f19e933-33d8-53b3-aaab-bd5110c3b7a0"

[[x264_jll]]
deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"]
git-tree-sha1 = "4fea590b89e6ec504593146bf8b988b2c00922b2"
uuid = "1270edf5-f2f9-52d2-97e9-ab00b5d0237a"
version = "2021.5.5+0"

[[x265_jll]]
deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"]
git-tree-sha1 = "ee567a171cce03570d77ad3a43e90218e38937a9"
uuid = "dfaa095f-4041-5dcd-9319-2fabd8486b76"
version = "3.5.0+0"

[[xkbcommon_jll]]
deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg", "Wayland_jll", "Wayland_protocols_jll", "Xorg_libxcb_jll", "Xorg_xkeyboard_config_jll"]
git-tree-sha1 = "ece2350174195bb31de1a63bea3a41ae1aa593b6"
uuid = "d8fb68d0-12a3-5cfd-a85a-d49703b185fd"
version = "0.9.1+5"
"""

# ╔═╡ Cell order:
# ╟─c4d275c8-36d5-454f-bf1f-ddbc06beeae0
# ╟─d2a788ef-5292-4229-a1d1-3fd15db67c51
# ╟─17bc7f80-c13c-11eb-227f-b125d84c8b40
# ╠═20ed504e-08d1-45b9-8494-c42170182898
# ╟─25f9aa0c-efa0-4c8a-bbfb-c5648bf2f8a2
# ╟─0545e797-9d40-48c4-ab96-195959331560
# ╠═342aacbe-df8e-42b9-a7a2-8538476f2a80
# ╟─65dd0a07-ff99-4887-8a86-142248705131
# ╟─8b677658-96a8-40cb-b4cf-1638703dda22
# ╟─4dd894c8-9b9b-474d-9a98-d805328b609e
# ╠═d5d4554e-428a-457a-8fdb-47dc977f9d01
# ╟─3ec74057-fcbb-4d79-8aca-719289c2299e
# ╟─a2aac898-7c8a-42c6-a7ef-a3ff736d1ab6
# ╠═67b54cd9-af1a-4bb7-9a44-c66179adfbcb
# ╟─33a4d731-3f65-460d-a74e-609250fa6292
# ╠═cde832b5-50ea-44d8-ae4e-f1907d7e31dc
# ╟─49053499-f759-4043-a9b8-48f42e45589a
# ╠═c79f1fef-c2f1-45b0-af35-f3e69e6ec77e
# ╟─19c628e8-60c9-46d0-9ab8-78e70b8bfa6f
# ╟─2cd26842-6c40-4dc0-9979-da4685630aa1
# ╠═73b14af8-f204-4070-bab9-180b660a9029
# ╟─6bc18d01-7285-4b7b-9769-3a7d52baae75
# ╟─2635c31e-8a91-45b0-8e4a-b4b6f6e59ae0
# ╠═a97592ef-28ef-4d83-b979-9e5550045cd0
# ╟─f736b324-d6d9-4d8f-93d0-d520b79d4002
# ╠═45b5c9d8-93ce-4b64-8c81-431dcbc6531f
# ╟─60f80e79-eeb8-4b60-801f-98caa28b4dfd
# ╟─dd7d16e4-7b01-4aa0-a1ba-ecc7881900fb
# ╟─61609aa9-9feb-4a63-b4e9-2fd9a88f8106
# ╟─33d13e0f-80c5-478c-99e5-e9de64a8d39a
# ╠═892b1d19-c88a-4add-ac18-70e799e01092
# ╟─28da7f3c-97fd-4a80-ad21-eeb201dcc881
# ╠═5861683f-db8a-4d43-9822-f2f47431c191
# ╟─311eecf2-4bd6-44bc-8c67-a680f4fd5d33
# ╟─b47b956c-8880-4c22-aeea-d9a55c518b6a
# ╟─1ea9ab83-10c8-4d5f-8489-e9a4754e7b1b
# ╟─9e141f07-ad66-41f4-a53b-b6c25e4afbb2
# ╟─78a2ef93-5ed1-44f8-8aa9-7e7d8d80a2df
# ╟─c5cc14a5-73c6-4f10-bc3e-bd7b68eddafa
# ╟─d58c2390-6aaa-4e73-b360-9f0e9d20b37e
# ╟─7f123aae-2f4d-4cf5-825f-7fe3a8f69c0c
# ╠═db949a4e-261c-4374-a806-6279370b287e
# ╟─54f98ff2-02ae-418f-8c87-0e31f70352a0
# ╠═b4aea351-3195-4dc2-8cfa-b423a029b840
# ╟─ea2e1c0e-241a-4c37-9420-3f882d78a47f
# ╟─520bfd7b-ad1f-44a8-a1ee-503a22923adc
# ╠═0b9abd74-d199-4720-803e-eb7861b86572
# ╟─cd703dc2-6b7a-49b1-91e1-84ee712a7a9c
# ╟─cd9e2120-93fb-499a-8091-0dd6890d90e1
# ╟─021642cb-8edd-4349-833f-41ca4144ee58
# ╟─00000000-0000-0000-0000-000000000001
# ╟─00000000-0000-0000-0000-000000000002


using Distributions, StatsFuns, StatsBase

include("portfolio_initialization_template.jl")

const numVariables = 1000 + 1
const numAssets = numVariables-1

#*============ MODEL PARAMETERS ================
const mean = Array{Float64}(numAssets)
const stdev = Array{Float64}(numAssets)
for i = 1:numAssets
	mean[i] = 1.05 + 0.3*(numAssets-i)/(numAssets-1)
	stdev[i] = (0.05 + 0.6*(numAssets-i)/(numAssets-1))/3
end

const sigma = zeros(Float64, numAssets, numAssets)
for i = 1:numAssets
	sigma[i,i] = stdev[i]
end
#*===============================================

# compute probability that constraints aren't satisfied
function computeRiskLevel(y_opt::Array{Float64})

	riskLevel::Float64 = 1.0 - normcdf((sum(mean[i]*y_opt[i] for i = 1:numAssets) - y_opt[numAssets+1])/norm(sigma*(y_opt[1:numAssets])))

	return riskLevel
end

# evaluate the constraint values for given decision vector x
# and realization of the random variables xi
function evaluateConstraint(x::Array{Float64},xi::Array{Float64})

	return evaluateConstraint(x,xi,1.0)
end

# evaluate the constraint values for given decision vector x,
# realization of the random variables xi, and constraint scalings
function evaluateConstraint(x::Array{Float64},xi::Array{Float64},scalings::Float64)

	con::Float64 = (x[numVariables] - xi'*x[1:numVariables-1])/scalings

	return con
end

# evaluate constraint gradients
function evaluateConstraintGradients(x::Array{Float64},xi::Array{Float64})

	return evaluateConstraintGradients(x,xi,1.0)
end

# evaluate constraint gradients
function evaluateConstraintGradients(x::Array{Float64},xi::Array{Float64},scalings::Float64)

	grad_con = zeros(Float64,numVariables)
	for j = 1:numVariables-1
		grad_con[j] = -xi[j]
	end
	grad_con[numVariables] = 1
	grad_con /= scalings

	return grad_con
end

# generate random samples of the stock returns
function generateRandomSamples(numSamples::Int64)

	xi = Array{Float64}(numAssets,numSamples)
	for i = 1:numAssets
		xi[i,:] = rand(Normal(mean[i],stdev[i]), numSamples)
	end

	return xi
end

# project on to the set {x[1:n] >= 0, sum(x[1:n]) = 1, x[n+1] >= target}
function projectOntoFeasibleSet(z::Array{Float64},target::Float64,proj_params::Array{Float64})

	const n::Int64 = numVariables-1
	y = deepcopy(z)

	v = Array{Float64}(1)
	v[1] = y[1]
	w = Array{Float64}(0)
	rho::Float64 = y[1] - 1.0

	for j = 2:n
		if(y[j] > rho)
			rho = rho + (y[j] - rho)/(size(v,1) + 1)
			if(rho > y[j] - 1.0)
				push!(v,y[j])
			else
				append!(w,v)
				v = [y[j]]
				rho = y[j] - 1.0
			end
		end
	end

	if(size(w,1) > 0)
		for j = 1:size(w,1)
			if(w[j] > rho)
				push!(v,w[j])
				rho = rho + (w[j] - rho)/size(v,1)
			end
		end
	end

	numDeleted::Int64 = 1

	while(numDeleted > 0)
		initialCard::Int64 = size(v,1)
		numDeleted = 0
		for j = 1:initialCard
			if(v[j-numDeleted] <= rho)
				rho = rho + (rho - v[j-numDeleted])/(size(v,1)-1)
				deleteat!(v,j-numDeleted)
				numDeleted += 1
			end
		end
	end

	x_proj = zeros(numVariables)
	for j = 1:n
		x_proj[j] = max(0,y[j] - rho)
	end
	x_proj[numVariables] = max(target,z[numVariables])

	return x_proj, proj_params
end

# get the stochastic gradient of the approximation
function getStochasticGradient(x::Array{Float64},numSamples::Int64,mu::Float64,tau::Float64,scalings::Float64)

	xi = generateRandomSamples(numSamples)

	stochgrad = zeros(numVariables)

	for samp = 1:numSamples
		con::Float64 = evaluateConstraint(x,xi[:,samp],scalings)

		grad_con = evaluateConstraintGradients(x,xi[:,samp],scalings)

		baseFactor1::Float64 = tau/((sqrt(mu)*exp(0.5*tau*con) + sqrt(1.0/mu)*exp(-0.5*tau*con))^2)

		stochgrad += baseFactor1*grad_con/numSamples
	end

	return stochgrad
end

# get the stochastic gradient of the approximation
function getStochasticGradientForFixedSample(x::Array{Float64},xi::SubArray{Float64,2,Array{Float64,2},Tuple{Base.Slice{Base.OneTo{Int64}},UnitRange{Int64}},true},mu::Float64,tau::Float64,scalings::Float64)

	numSamples::Int64 = size(xi,2)

	stochgrad = zeros(numVariables)

	for samp = 1:numSamples
		con::Float64 = evaluateConstraint(x,xi[:,samp],scalings)

		grad_con = evaluateConstraintGradients(x,xi[:,samp],scalings)

		baseFactor1::Float64 = tau/((sqrt(mu)*exp(0.5*tau*con) + sqrt(1.0/mu)*exp(-0.5*tau*con))^2)

		stochgrad += baseFactor1*grad_con/numSamples
	end

	return stochgrad

end

# estimate constraint scalings
function estimateScalings(x::Array{Float64},numSamples::Int64)

	scaling_tol::Float64 = 1E-06

	xi = generateRandomSamples(numSamples)

	constraint_values = zeros(numSamples)
	for samp = 1:numSamples
		constraint_values[samp] = evaluateConstraint(x,xi[:,samp])
	end

	scalings::Float64 = median(abs.(constraint_values))

	scalings = max(scalings,scaling_tol)

	return scalings
end

# estimate Lipschitz constant of gradient of approximation
function estimateCompositeLipschitzConstant(x_ref::Array{Float64},numSamplesForLipVarEst::Int64,numGradientSamples::Int64,batchSize::Int64,mu::Float64,tau::Float64,scalings::Float64,objectiveBound::Float64)

	proj_params = zeros(Float64,3)

	sample_radius::Float64 = norm(x_ref)/10.0

	xi = generateRandomSamples(batchSize)

	L_avg::Float64 = 0.0
	for batch = 1:batchSize
		xi_start::Int64 = batch
		xi_end::Int64 = batch

		L_max::Float64 = 0.0
		for samp = 1:numSamplesForLipVarEst
			x_1 = pickPointOnSphere(x_ref,sample_radius)
			x_1,~ = projectOntoFeasibleSet(x_1,objectiveBound,proj_params)

			x_2 = pickPointOnSphere(x_ref,sample_radius)
			x_2,~ = projectOntoFeasibleSet(x_2,objectiveBound,proj_params)

			@views grad_1 = getStochasticGradientForFixedSample(x_1,xi[:,xi_start:xi_end],mu,tau,scalings)
			@views grad_2 = getStochasticGradientForFixedSample(x_2,xi[:,xi_start:xi_end],mu,tau,scalings)

			L_est::Float64 = norm(grad_2 - grad_1)/norm(x_2 - x_1)

			if(L_est > L_max)
				L_max = L_est
			end
		end

		L_avg += L_max/batchSize
	end

	return L_avg
end

# estimate step length factor
function estimateStepLength(x_ref::Array{Float64},numGradientSamples::Int64,
							numSamplesForLipVarEst::Int64,batchSize::Int64,maxNumIterations::Int64,
							minNumReplicates::Int64,mu::Float64,tau::Float64,
							scalings::Float64,objectiveBound::Float64)

	proj_params = zeros(Float64,3)

	sample_radius::Float64 = norm(x_ref)/10.0

	rho_est::Float64 = estimateCompositeLipschitzConstant(x_ref,numSamplesForLipVarEst,numGradientSamples,batchSize,mu,tau,scalings,objectiveBound)

	variance_est::Float64 = 0.0
	for samp = 1:numSamplesForLipVarEst
		x_1 = pickPointOnSphere(x_ref,sample_radius)
		x_1,~ = projectOntoFeasibleSet(x_1,objectiveBound,proj_params)

		xi = generateRandomSamples(numGradientSamples*batchSize)

		variance_1::Float64 = 0.0
		for batch = 1:batchSize
			xi_start::Int64 = (batch-1)*numGradientSamples + 1
			xi_end::Int64 = batch*numGradientSamples

			@views grad_1 = getStochasticGradientForFixedSample(x_1,xi[:,xi_start:xi_end],mu,tau,scalings)

			variance_1 += norm(grad_1)^2/batchSize
		end

		if(variance_1 > variance_est)
			variance_est = variance_1
		end
	end

	gamma::Float64 = 1/sqrt(rho_est*variance_est)

	stepLength::Float64 = gamma/sqrt((maxNumIterations+1.0)*minNumReplicates)

	return stepLength
end

# given a reference point, pick a random point within a given radius
function pickPointOnSphere(x_orig::Array{Float64},radius::Float64)

	if(radius < 1E-09)
		error("Sampling radius in pickPointOnSphere is almost zero! Set manually.")
	end

	n::Int64 = size(x_orig,1)
	unifRand = rand(1)
	factor1::Float64 = radius*(unifRand[1])^(1.0/n)
	normRand = rand(Normal(0,1), n)
	factor2::Float64 = norm(normRand)

	x_samp = x_orig + factor1*normRand/factor2

	return x_samp
end


using Test

test_associative(x, y, z, ⊗) = @test (x ⊗ y) ⊗ z ≈ x ⊗ (y ⊗ z)

test_commutative(x, y, ⊗) = @test x ⊗ y ≈ y ⊗ x

function test_inverse(x, eins, ⊗, inv)
    @test x ⊗ inv(x) ≈ eins
    @test inv(x) ⊗ x ≈ eins
end

function test_neutral(x, eins, ⊗)
    @test x ⊗ eins ≈ x
    @test eins ⊗ x ≈ x
end

function test_monoid(x, y, z, ⊗, eins)
    test_associative(x, y, z, ⊗)
    test_neutral(x, eins, ⊗)
    return nothing
end

function test_group(x, y, z, ⊗, eins, inv)
    test_monoid(x, y, z, ⊗, eins)
    test_inverse(x, eins, ⊗, inv)
    return nothing
end

test_multiplicative(x, y, ⊗, f) = @test f(x ⊗ y) ≈ f(x) ⊗ f(y)


function ftau(t, fft_u, fft_v, A1, A2, matr, conjmatr, sigma, llambda)

    u = matr .* ifft(exp.(1im * t * A1) .* fft_u)
    v = matr .* ifft(exp.(1im * t * A1) .* fft_v)

    z = ( u .+ conj.(v)) / 2

    fz1  = abs.(z) .^ (2*sigma) .* z

    u = fft(conjmatr .* fz1)
    u .*= (-1im * llambda * A2 .* exp.(-1im * t * A1))
    v = fft(conjmatr .* conj.(fz1))
    v .*= (-1im * llambda * A2 .* exp.(-1im * t * A1))

    u, v

end


using DelimitedFiles

using LinearAlgebra
using DSP: conv

function derivative(signal, order, halflen, dt)
    @assert 2*halflen ≥ order ≥ 0
    @assert iseven(halflen)
    S = (-halflen:halflen)' .^ (0:order);
    F = qr(S')
    filter = Matrix(F.Q)/Matrix(F.R')
    der = conv(signal, 1/-dt*filter[:, 2])
    return der[halflen+1:end-halflen]
end

function bayeslinreg(dΩ, Ω, U, σ, μβ, invΣβ)
    p = Ω[:,1]; q = Ω[:,2]; r = Ω[:,3]
    ψ = hcat(p.^2, q.^2, r.^2, p.*q, p.*r, q.*r, U)'
    invA = inv(1/σ^2*ψ*ψ' + invΣβ)
    μ(y) = 1/σ^2*invA*(ψ*y + invΣβ*μβ)
    μ.(eachcol(dΩ)), invA
end

σ = 1.0
μβ = zeros(9)
invΣβ = inv(diagm(0=>ones(9)))
Ω = readdlm("/tmp/omega.txt")
U = readdlm("/tmp/controls.txt")
dΩ = hcat(derivative.(eachcol(Ω), 4, 30, 0.01)...)

using MAT
file = matopen("/home/arun/Documents/shared-scripts/learning.mat", "w")
write(file, "cflie", hcat(dΩ, Ω, U))
close(file)

# slice some inbetween data to train
#  Ω = Ω[251:500,:]
#  U = U[251:500,:]
#  dΩ = dΩ[251:500,:]

postμ, postσ = bayeslinreg(dΩ, Ω, U, σ, μβ, invΣβ)

## for testing only
function predictivedist(Ωx, Ux, postμ, postσ, σ)
    p, q, r = Ωx
    ψx = [p^2, q^2, r^2, p*q, p*r, q*r, Ux...]
    μ = Ref(ψx').*postμ
    σ = ψx'*postσ*ψx .+ σ^2
    μ, σ
end
pred = predictivedist.(eachrow(Ω), eachrow(U), Ref(postμ), Ref(postσ), Ref(σ))
predx = [p[2][2] for p in pred]
plot(dΩ[:,2]); plot!(predx)


module _MSobject

export MSobject
    """
        Mobj = MSobject()

    Returns a multiscale entropy object (`Mobj`) based on that originally
    proposed by Costa et. al. (2002) using the following default parameters:
    EnType = SampEn(), embedding dimension = 2, time delay = 1,
    radius = 0.2*SD(`Sig`), logarithm = natural

        Mobj = MSobject(EnType::Function)

    Returns a multiscale entropy object by passing the entropy function
    (`EnType`) and the specifying default parameters for that entropy function.
    To see the default parameters for a particular entropy method,
    type:  `? EntropyHub.EnType`  \n
    (e.g.  `? EntropyHub.SampEn`)

        Mobj = MSobject(EnType::Function; kwargs...)

    Returns a multiscale entropy object using the specified entropy method
    (`EnType`) and the 'keyword' parameters for that particular method.
    To see the default parameters for a particular entropy method,
    type:   ? EntropyHub.EnType   (e.g.  ? EntropyHub.SampEn)

    EnType can be any of the following entropy functions:

    # Base Entropies:
        -----------------
        `ApEn`      - Approximate Entropy  \n
        `SampEn`    - Sample Entropy   \n
        `FuzzEn`    - Fuzzy Entropy    \n
        `K2En`      - Kolmogorov Entropy   \n
        `PermEn`    - Permutation Entropy	 \n
        `CondEn`    - Conditional Entropy	   \n
        `DistEn`    - Distribution Entropy	    \n
        `DispEn`    - Dispersion Entropy	    \n
        `SpecEn`    - Spectral Entropy   \n
        `SyDyEn`    - Symbolic Dynamic Entropy	  \n
        `IncrEn`    - Increment Entropy	    \n
        `CoSiEn`    - Cosine Similarity Entropy	\n
        `PhasEn`    - Phase Entropy	    \n
        `SlopEn`    - Slope Entropy       \n
        `BubbEn`    - Bubble Entropy   \n
        `GridEn`    - Gridded Distribution Entropy  \n
        `EnofEn`    - Entropy of Entropy	\n
        `AttnEn`    - Attention Entropy    \n

    # Cross Entropies:
        ------------------
        `XApEn`     - Cross-Approximate Entropy    \n
        `XSampEn`   - Cross-Sample Entropy  \n
        `XFuzzEn`   - Cross-Fuzzy Entropy   \n
        `XK2En`     - Cross-Kolmogorov Entropy  \n
        `XPermEn`   - Cross-Permutation Entropy   \n
        `XCondEn`   - Cross-Conditional Entropy    \n
        `XDistEn`   - Cross-Distribution Entropy    \n
        `XSpecEn`   - Cross-Spectral Entropy   \n

    # See also `MSEn`, `XMSEn`, `rMSEn`, `cMSEn`, `hMSEn`, `rXMSEn`, `cXMSEn`, `hXMSEn`

    """
    function MSobject(EnType::Function=SampEn; kwargs...)
    #function MSobject(EnType::Function= EntropyHub.SampEn; kwargs...)

    Chk = ["ApEn";"SampEn";"FuzzEn";"K2En";"PermEn";"CondEn";"DistEn";
        "DispEn";"SyDyEn";"IncrEn";"CoSiEn";"PhasEn";"SpecEn";"SlopEn";
        "GridEn";"BubbEn";"EnofEn";"AttnEn";"XApEn";"XSampEn";"XFuzzEn";
        "XPermEn";"XCondEn";"XDistEn";"XSpecEn";"XK2En"]
    (String(Symbol(EnType)) in Chk) ? nothing :
        error("EnType:      must be a valid entropy function name.
        For more info, type:   julia>  ? EntropyHub.MSobject")

    #Mobj = (Func=getfield(Main.EntropyHub, Symbol(EnType)), kwargs...)

    Mobj = (Func= EnType, kwargs...)

    return Mobj
    end

end

"""
Copyright 2021 Matthew W. Flood, EntropyHub

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

For Terms of Use see https://github.com/MattWillFlood/EntropyHub
"""


using Test, Random
using NiLang, NiLang.AD

function _sce(x::AbstractArray{T,N}, p) where {T,N}
    x = x .- maximum(x; dims=N)  # avoid data overflow
    rho = exp.(x)
    Z = sum(rho; dims=N)
    return dropdims(sum((log.(Z) .- x) .* p; dims=N), dims=N)
end

@testset "softmax_crossentropy" begin
    Random.seed!(2)
    x = randn(10)
    x0 = copy(x)
    p = randn(10); p=p./maximum(p)
    res = _sce(x, p)
    imax = 0
    Z = 0.0
    out = 0.0
    xmax = 0.0
    x_ = x
    p_ = p
    @instr i_softmax_crossentropy(x_, p_, imax, xmax, Z, out)
    @show Z
    @test isapprox(imax, argmax(x0), atol=1e-8)
    @test isapprox(out, res[], atol=1e-8)
    @instr (~i_softmax_crossentropy)(x_, p_, imax, xmax, Z, out)
    args = x_, p_, imax, xmax, Z, out
    @test check_inv(i_softmax_crossentropy, args)
    args = x_, p_, imax, xmax, Z, out
    @test check_grad(i_softmax_crossentropy, args; iloss=6, verbose=true)
end

@testset "logsumexp" begin
	function logsumexp2(x)
	  	mx = maximum(x)
	  	log.(sum(exp.(x .- mx))) .+ mx
	end

	x = randn(100)
	@test i_ascending!(Float64[], Int[], x)[1][end] == maximum(x)
	@test i_logsumexp(0.0, 0.0, Float64[], Int[], x)[1] ≈ logsumexp2(x)
	@test check_inv(i_logsumexp, (0.0, 0.0, Float64[], Int[], x))
end