"""
Implements the reaction network detailed in https://www.pnas.org/content/pnas/suppl/2003/05/27/1132112100.DC1/2112SuppText.pdf

Accompanying article is here https://www.pnas.org/content/100/12/7051
"""

function CircadianOscillator(input=nothing)
println("default input is t -> 1.5. If CircadianOscillator() is called with no arguments, the default is used")
@parameters t
D = Differential(t)
@variables  M_P(t) M_C(t) M_B(t) P_C(t) C_C(t) P_CP(t) C_CP(t) PC_C(t) PC_N(t) PC_CP(t) PC_NP(t) B_C(t) B_CP(t) B_N(t) B_NP(t) I_N(t)

@parameters k_1 k_2 k_3 k_4 k_5 k_6 k_7 k_8 K_AP K_AC K_IB k_dmb k_dmc  k_dmp k_dn  k_dnc K_d K_dp K_p  K_mB K_mC K_mP k_sB k_sC k_sP m n V_1B V_1C V_1P V_1PC V_2B V_2C V_2P V_2PC V_3B V_3PC V_4B V_4PC V_phos v_dBC v_dBN v_dCC v_dIN v_dPC v_dPCC v_dPCN v_mB v_mC v_mP v_sB v_sC #v_sP

#also have v_phos = 0.4 in the paper

#v_sP is the transcription rate of PER mRNA. In the paper, the effect of light is modelled through changes in this parameter.

v_sP = input
if v_sP === nothing
    v_sP = 1.5
end

eqs =
[ D(M_P) ~ v_sP*(B_N^n/(K_AP^n + B_N^n)) - v_mP*(M_P/(K_mP + M_P)) - k_dmp*M_P,
  D(M_C) ~ v_sC*B_N^n/(K_AC^n + B_N^n) - v_mC*(M_C/(K_mC + M_C)) - k_dmc*M_C,
  D(M_B) ~ v_sB*K_IB^m/(K_IB^m + B_N^m ) - v_mB*(M_B/(K_mB + M_B)) - k_dmb*M_B,
  D(P_C) ~ k_sP*M_P - V_1P*P_C/(K_p + P_C) + V_2P*P_CP/(K_dp + P_CP) + k_4*PC_C - k_3*P_C*C_C - k_dn*P_C,
  D(C_C) ~ k_sC*M_C - V_1C*C_C/(K_p + C_C) + V_2C*C_CP/(K_dp + C_CP) + k_4*PC_C - k_3*P_C*C_C - k_dnc*C_C,
  D(P_CP) ~ V_1P*P_C/(K_p+P_C) - V_2P*P_CP/(K_dp + P_CP) - v_dPC*P_CP/(K_d + P_CP) - k_dn*P_CP,
  D(C_CP) ~ V_1C*C_C/(K_p +C_C) - V_2C*C_CP/(K_dp+C_CP) - v_dCC*C_CP/(K_d + C_CP) - k_dn*C_CP,
  D(PC_C) ~ -V_1PC*PC_C/(K_p + PC_C) + V_2PC*PC_CP/(K_dp + PC_CP) - k_4*PC_C + k_3*P_C*C_C + k_2*PC_N - k_1*PC_C - k_dn*PC_C,
  D(PC_N) ~ -V_3PC*PC_N/(K_p + PC_N) + V_4PC*PC_NP/(K_dp + PC_NP) - k_2*PC_N + k_1*PC_C - k_7*B_N*PC_N + k_8*I_N - k_dn*PC_N,
  D(PC_CP) ~ V_1PC*PC_C/(K_p + PC_C) - V_2PC*PC_CP/(K_dp + PC_CP) - v_dPCC*PC_CP/(K_d + PC_CP) - k_dn*PC_CP,
  D(PC_NP) ~ V_3PC*PC_N/(K_p + PC_N) - V_4PC*PC_NP/(K_dp + PC_NP) - v_dPCN*PC_NP/(K_d + PC_NP) - k_dn*PC_NP,
  D(B_C) ~ k_sB*M_B - V_1B*B_C/(K_p + B_C) + V_2B*B_CP/(K_dp + B_CP) - k_5*B_C + k_6*B_N - k_dn*B_C,
  D(B_CP) ~ V_1B*B_C/(K_p + B_C) - V_2B*B_CP/(K_dp + B_CP) - v_dBC*B_CP/(K_d + B_CP) - k_dn*B_CP,
  D(B_N) ~ -V_3B*B_N/(K_p + B_N) + V_4B*B_NP/(K_dp + B_NP) + k_5*B_C - k_6*B_N - k_7*B_N*PC_N + k_8*I_N - k_dn*B_N,
  D(B_NP) ~ V_3B*B_N/(K_p + B_N) - V_4B*B_NP/(K_dp + B_NP) - v_dBN*B_NP/(K_d + B_NP) - k_dn*B_NP,
  D(I_N) ~ -k_8*I_N + k_7*B_N*PC_N - v_dIN*I_N/(K_d + I_N) - k_dn*I_N
  ]

tspan = (0.,100.)

ic = [M_P=> 2.391615385357426 ,M_C=> 1.7547090952244027 ,M_B=> 8.561088205594936 ,P_C=> 0.9030978254420003 ,C_C=> 3.5790996574043907 ,P_CP=> 0.11556396765994004 ,C_CP=> 0.6301918743084862 ,PC_C=> 0.8525514643541497 ,PC_N=> 0.09761528221027084 ,PC_CP=> 0.1755706805316031 ,PC_NP=> 0.06289128872787488 ,B_C=> 2.2764826266300875 ,B_CP=> 0.9063725609755553 ,B_N=> 1.763837030511116 ,B_NP=> 0.32028080421972577 ,I_N=> 0.024197413185517866 ]

ps = [k_1=>0.4, k_2=>0.2, k_3=>0.4, k_4=>0.2, k_5=>0.4, k_6=>0.2, k_7=>0.5, k_8=>0.1, K_AP=>0.7, K_AC=>0.6, K_IB=>2.2, k_dmb=>0.01, k_dmc => 0.01, k_dmp=>0.01, k_dn => 0.01, k_dnc=>0.12, K_d=>0.3, K_dp=>0.1, K_p => 0.1, K_mB=>0.4, K_mC=>0.4, K_mP=>0.31, k_sB=>0.12, k_sC=>1.6, k_sP=>0.6, m=>2, n=>4, V_1B=>0.5, V_1C=>0.6, V_1P=>0.4, V_1PC=>0.4, V_2B=>0.1, V_2C=>0.1, V_2P=>0.3, V_2PC=>0.1, V_3B=>0.5, V_3PC=>0.4, V_4B=>0.2, V_4PC=>0.1, v_dBC=>0.5, v_dBN=>0.6, v_dCC=>0.7, v_dIN=>0.8, v_dPC=>0.7, v_dPCC=>0.7, v_dPCN=>0.7, v_mB=>0.8, v_mC=>1.0, v_mP=>1.1, v_sB=>1.0, v_sC=>1.1] #, v_sP=>1.5]

od = ODESystem(eqs, t, first.(ic), first.(ps))
# of = ODEFunction(od)
tspan = (0.,100.)
return od, ic, tspan, ps
end


using CompScienceMeshes
using BEAST
using LinearAlgebra
using FastBEAST

CM = CompScienceMeshes
function test_beast_laplace_singlelayer(h)
    Γ = CM.meshsphere(1, h)

    X = lagrangecxd0(Γ)

    𝒱 = Helmholtz3D.singlelayer(wavenumber=0.0)

    hmat = hassemble(𝒱,X,X, treeoptions = BoxTreeOptions(nmin=100))

    return  hmat
end

hmat = test_beast_laplace_singlelayer(0.1)


# License for this file: MIT (expat)
# Copyright 2017-2018, DLR Institute of System Dynamics and Control
#
# This file is part of module
#   ModiaMath.Frames (ModiaMath/Frames/_module.jl)
#

@eval using LinearAlgebra

"""
    const ModiaMath.RotationMatrix = SMatrix{3,3,Float64,9}

Describes the rotation from a frame 1 into a frame 2. An instance `R` of `RotationMatrix`
has the following interpretation:

```julia
R::RotationMatrix = [ex ey ez]
```

where `ex`, `ey`, `ez` are unit vectors in the direction of the x-axis, y-axis, and z-axis
of frame 1, resolved in frame 2, respectively (for example ex=[1.0, 0.0, 0.0])
Therefore, if `v1` is vector `v` resolved in frame 1 and `v2` is vector `v`
resolved in frame 2, the following relationship holds:

```julia
v2 = R*v1
v1 = R'*v2
```
"""
const RotationMatrix = SMatrix{3,3,Float64,9}

"""
Constant RotationMatrix that defines no rotation from frame 1 to frame 2.
"""
const NullRotation = SMatrix{3,3,Float64,9}(Matrix(1.0I, 3, 3))

"""
    ModiaMath.assertRotationMatrix(R::AbstractMatrix)

Assert that matrix R has the properties of a rotation matrix
(is 3x3 and R'*R - eye(3) = zeros(3,3))
"""
function assertRotationMatrix(R::AbstractMatrix)
   @assert(size(R,1)==3, size(R,2)==3)
   @assert(norm(R'*R - NullRotation) <= 1e-10)
end

"""
    R = ModiaMath.rot1(angle)

Return RotationMatrix R that rotates with angle `angle` along the x-axis of frame 1.
"""
@inline function rot1(angle::Number)::RotationMatrix
   s = sin(angle)
   c = cos(angle)
   R = @SMatrix [1.0  0.0  0.0;
                 0.0   c    s ;
                 0.0  -s    c ]
end

"""
    R = ModiaMath.rot2(angle)

Return RotationMatrix R that rotates with angle `angle` in [radian] along the y-axis of frame 1.
"""
@inline function rot2(angle::Number)::RotationMatrix
   s = sin(angle)
   c = cos(angle)
   R = @SMatrix [ c  0.0 -s ;
                 0.0 1.0 0.0;
                  s  0.0  c ]
end

"""
    R = ModiaMath.rot3(angle)

Return RotationMatrix R that rotates with angle `angle` in [radian] along the z-axis of frame 1.
"""
@inline function rot3(angle::Number)::RotationMatrix
   s = sin(angle)
   c = cos(angle)
   R = @SMatrix [ c   s  0.0;
                 -s   c  0.0;
                 0.0 0.0 1.0]
end

"""
    R = ModiaMath.rot123(angle1, angle2, angle3)

Return RotationMatrix R by rotating with angle1 along the x-axis of frame 1,
then with angle2 along the y-axis of this frame and then with angle3 along
the z-axis of this frame.
"""
rot123(angle1::Number, angle2::Number, angle3::Number)::RotationMatrix = rot3(angle3)*rot2(angle2)*rot1(angle1)

"""
    R = ModiaMath.rot_e(e, angle)

Return RotationMatrix that rotates around angle `angle` along unit axis `e`.
This function assumes that `norm(e) == 1`.
"""
rot_e(e::Vector3D,angle::Number)::RotationMatrix = e*e' + (NullRotation - e*e')*cos(angle) - skew(e)*sin(angle)
rot_e(e::AbstractVector, angle::Number)::RotationMatrix    = rot_e( Vector3D(e), convert(Float64,angle) )

"""
    R = ModiaMath.rot_nxy(nx, ny)

It is assumed that the two input vectors `nx` and `ny` are resolved in frame 1 and
are directed along the x and y axis of frame 2.
The function returns the RotationMatrix R to rotate from frame 1 to frame 2.

The function is robust in the sense that it returns always a RotationMatrix R,
even if `ny` is not orthogonal to `nx` or if one or both vectors have zero length.
This is performed in the following way:
If `nx` and `ny` are not orthogonal to each other, first a unit vector `ey` is
determined that is orthogonal to `nx` and is lying in the plane spanned by
`nx` and `ny`. If `nx` and `ny` are parallel or nearly parallel to each other
or `ny` is a vector with zero or nearly zero length, a vector `ey` is selected
arbitrarily such that `ex` and `ey` are orthogonal to each other.
If both `nx` and `ny` are vectors with zero or nearly zero length, an
arbitrary rotation matrix is returned.

# Example

```julia
using Unitful
import ModiaMath

R1 = ModiaMath.rot1(90u"°")
R2 = ModiaMath.rot_nxy([1  , 0, 0], [0  , 0, 1  ])
R3 = ModiaMath.rot_nxy([0.9, 0, 0], [1.1, 0, 1.1])
isapprox(R1,R2)   # returns true
isapprox(R1,R3)   # returns true
```
"""
function rot_nxy(nx::Vector3D, ny::Vector3D)::RotationMatrix
  abs_nx  = norm(nx)
  e1      = abs_nx < 1e-10 ?  Vector3D(1.0, 0.0, 0.0) : nx/abs_nx
  n3_aux  = cross(e1, ny)
  e2_aux  = dot(n3_aux,n3_aux) > 1e-6 ? ny : ( abs(e1[1]) > 1e-6 ? Vector3D(0.0,1.0,0.0)
                                                                 : Vector3D(1.0,0.0,0.0))
  n3_aux2 = cross(e1, e2_aux)
  e3      = normalize(n3_aux2)
  R       = vcat(e1', cross(e3,e1)', e3')
end
rot_nxy(nx::AbstractVector, ny::AbstractVector) = rot_nxy(Vector3D(nx), Vector3D(ny))

"""
    v1 = ModiaMath.resolve1([R|q], v2)

Transform vector v2 (v resolved in frame 2) to vector v1 (v resolved in frame 1)
given either [`ModiaMath.RotationMatrix`](@ref) ` R` or
[`ModiaMath.Quaternion`](@ref) ` q` (to rotate a frame 1 into a frame 2).
"""
resolve1(R::RotationMatrix, v2::Vector3D)::Vector3D = R'*v2
resolve1(R::RotationMatrix, v2::AbstractVector)::Vector3D     = R'*Vector3D(v2)

"""
    v2 = ModiaMath.resolve2([R|q], v1)

Transform vector v1 (v resolved in frame 1) to vector v2 (v resolved in frame 2)
given either [`ModiaMath.RotationMatrix`](@ref) ` R` or
[`ModiaMath.Quaternion`](@ref) ` q` (to rotate a frame 1 into a frame 2).
"""
resolve2(R::RotationMatrix, v1::Vector3D)::Vector3D = R*v1
resolve2(R::RotationMatrix, v1::AbstractVector)::Vector3D     = R*Vector3D(v1)

"""
     R2 = ModiaMath.absoluteRotation(R1, R_rel)
     q2 = ModiaMath.absoluteRotation(q1, q_rel)

Return [`ModiaMath.RotationMatrix`](@ref)` R2` or [`ModiaMath.Quaternion`](@ref)` q2`
defining the rotation from frame 0 to frame 2 from RotationMatrix `R1` or Quaternion `q1`that define the
rotation from frame 0 to frame 1 and the relative RotationMatrix `R_rel` or the
relative Quaternion `q_rel` that define the rotation from frame 1 to frame 2.
"""
absoluteRotation(R1::RotationMatrix, R_rel::RotationMatrix)::RotationMatrix = R_rel*R1

"""
     R_rel = ModiaMath.relativeRotation(R1, R2)
     q_rel = ModiaMath.relativeRotation(q1, q2)

Return relative [`ModiaMath.RotationMatrix`](@ref)` R_rel` or relative
[`ModiaMath.Quaternion`](@ref)` q_rel` defining the rotation from frame 1 to frame 2
from absolute RotationMatrix `R1` or absolute Quaternion `q1`that define the
rotation from frame 0 to frame 1 and the absolute RotationMatrix `R2` or the
absolute Quaternion `q2` that define the rotation from frame 0 to frame 2.
"""
relativeRotation(R1::RotationMatrix, R2::RotationMatrix)::RotationMatrix = R2*R1'

"""
     R_inv = ModiaMath.inverseRotation(R)
     q_inv = ModiaMath.inverseRotation(q)

Return inverse [`ModiaMath.RotationMatrix`](@ref)` R_inv` or inverse
[`ModiaMath.Quaternion`](@ref)` q_inv` defining the rotation from frame 1 to frame 0
from RotationMatrix `R` or Quaternion `q`that define the
rotation from frame 0 to frame 1.
"""
inverseRotation(R::RotationMatrix)::RotationMatrix = R'

"""
    angle = planarRotationAngle(e, v1, v2; angle_guess = 0.0)

Return `angle` of a planar rotation, given the normalized axis of
rotation to rotate frame 1 around `e` into frame 2 (norm(e) == 1 required),
and the representations of a vector in frame 1 (`v1`) and frame 2 (`v2`).
Hereby, it is required that `v1` is not parallel to `e`.
The returned angle is in the range `-pi <= angle - angle_guess <= pi`
(from the infinite many solutions, the one is returned that is closest to `angle_guess`).

# Example

```julia
import ModiaMath
using Unitful

angle1 = 45u"°"
e      = normalize([1.0, 1.0, 1.0])
R      = ModiaMath.rot_e(e, angle1)

v1 = [1.0, 2.0, 3.0]
v2 = ModiaMath.resolve2(R, v1)

angle2 = planarRotationAngle(e, v1, v2)
isapprox(angle1, angle2)
```
"""
@inline function planarRotationAngle(e::AbstractVector, v1::AbstractVector, v2::AbstractVector; angle_guess::Number=0.0)::Number
   angle1 = atan( dot(-cross(e,v1), v2), dot(v1,v2) - dot(e,v1)*dot(e,v2) )
   pi2    = 2*pi
   return angle1 + pi2*round(Int, (pi+angle_guess-angle1)/(pi2), RoundDown)
end

#=
Derivation of algorithm for planarRotaionAngle:

Vector v is resolved in frame 1 and frame 2 according to:
   (1)  v2 = (e*e' + (NullRotation - e*e')*cos(angle) - skew(e)*sin(angle))*v1
           = e*(e'*v1) + (v1 - e*(e'*v1))*cos(angle) - cross(e,v1)*sin(angle)

Equation (1) is multiplied with "v1'" resulting in (note: e'*e = 1)

   (2)  v1'*v2 = (v1'*e)*(e'*v2) + (v1'*v1 - (v1'*e)*(e'*v1))*cos(angle)

and therefore

   (3)  cos(angle) = ( v1'*v2 - (v1'*e)*(e'*v2)) / (v1'*v1 - (v1'*e)*(e'*v1))

Similarly, equation (1) is multiplied with cross(e,v1), that is a
a vector that is orthogonal to e and to v1:

   (4)  cross(e,v1)'*v2 = -cross(e,v1)'*cross(e,v1)*sin(angle)

and therefore:

   (5) sin(angle) = -cross(e,v1)'*v2/(cross(e,v1)'*cross(e,v1))

We have e'*e=1. Therefore

   (6) v1'*v1 - (e*v1)'*(e*v1) = |v1|^2 - (|v1|*cos(e,v1))^2

and

   (7) cross(e,v1)'*cross(e,v1) = (|v1|*sin(e,v1))^2
                                 = |v1|^2*(1 - cos(e,v1)^2)
                                 = |v1|^2 - (|v1|*cos(e,v1))^2

The denominators of (3) and (5) are identical, according to (6) and (7).
Furthermore, the denominators are always positive according to (7).
Therefore, in the equation "angle = atan2(sin(angle), cos(angle))" the
denominators of sin(angle) and cos(angle) can be removed, resulting in:

   (8) angle1 = atan2(-cross(e,v1)'*v2, v1'*v2 - (e'*v1)*(e'*v2));

This angle is in the range -pi <= angle1 <= pi. The returned angle should be
as close to angle_guess. If angle_guess = 0, angle1 is just returned. Otherwise:

       -pi < angle - angle_guess <= pi
       -pi < angle1 + 2*pi*N - angle_guess <= pi
       (-pi+angle_guess-angle1)/(2*pi) < N <= (pi+angle_guess-angle1)/(2*pi)
       -> N := round(Int, (pi+angle_guess-angle1)/(2*pi), RoundDown )

resulting in

    (9) angle = angle1 + 2*pi*round(Int, (pi+angle_guess-angle1)/(2*pi))
=#

"""
    e = eAxis(axis::Int)

Return unit vector `e` in direction of axis `axis` (`axis` = 1,2,3 or -1,-2-,3).

# Example

```julia
import ModiaMath

e1 = ModiMath.eAxis(1)    # e1 = Vector3D(1.0,  0.0, 0.0)
e2 = ModiMath.eAxis(-2)   # d2 = Vector3D(0.0, -1.0, 0.0)
```
"""
eAxis(axis::Int) = axis ==  1 ? Vector3D(  1.0,  0.0,  0.0) :
                   axis ==  2 ? Vector3D(  0.0,  1.0,  0.0) :
                   axis ==  3 ? Vector3D(  0.0,  0.0,  1.0) :
                   axis == -1 ? Vector3D( -1.0,  0.0,  0.0) :
                   axis == -2 ? Vector3D(  0.0, -1.0,  0.0) :
                   axis == -3 ? Vector3D(  0.0,  0.0, -1.0) :
                   error("ModiaMath.eAxis(axis): axis = ", axis, " but must be 1, 2, 3, -1, -2, or -3.")


function unpackdemo(D::Int = 2, M::Int = 3)

    W_array = collect( Utilities.generaterandomposdefmat(D) for m = 1:M )
    L_array = collect( cholesky(W_array[m]) for m = 1:M )

    b_array = collect( randn(D) for m = 1:M )
    c_array = rand(M)

    v = packbcW(b_array, c_array, W_array)

    b, c, W = unpackbcW(v,M)

    return v, b, c, W, b_array, c_array, W_array
end


# Hicks, G. A., & Ray, W. H. (1971).
# Approximation methods for optimal control synthesis.
# The Canadian Journal of Chemical Engineering, 49(4), 522-528.

function reference_tracking(; store_results=false::Bool)
    datadir = nothing
    if store_results
        datadir = generate_data_subdir(@__FILE__)
    end

    # irreversible reaction
    reaction = "irreversible"
    time = 20.0f0  # final time (θ in original paper)
    cf = 1.0f0
    Tf = 300.0f0
    Tc = 290.0f0
    J = 100.0f0
    α = 1.95f-4
    k10 = 300.0f0
    N = 25.2f0
    u_upper = 1500.0f0
    u_lower = 0.0f0
    y1s = 0.408126f0
    y2s = 3.29763f0
    us = 370.0f0

    # # reversible reaction
    # reaction = "reversible"
    # time = 1f0  # final time (θ in original paper)
    # cf = 1f0
    # Tf = 323f0
    # Tc = 326f0
    # J = 100f0
    # α = 1f0
    # k10 = 1.5f7
    # k20 = 1.5f10
    # N = 10f0
    # γ = 1.5f0
    # u_upper = 9.5f0
    # u_lower = 0f0
    # y1s = 0.433848f0
    # y2s = 0.659684f0
    # us = 3.234f0

    # adimensional constants
    yf = Tf / (J * cf)
    yc = Tc / (J * cf)

    # case 1
    # α1 = 1f6
    # α2 = 2f3
    # α3 = 1f-3

    # case 2
    # α1 = 1f6
    # α2 = 2f3
    # α3 = 0f0

    # case 3
    # α1 = 10f0
    # α2 = 1f0
    # α3 = 0.1f0

    # case 4
    # α1 = 10f0
    # α2 = 1f0
    # α3 = 0f0

    # custom case
    α1 = 1.0f0
    α2 = 1.0f1
    α3 = 1.0f-1

    # initial conditions and timepoints
    @show u0 = [1.0f0, yf]
    @show tspan = (0.0f0, time)
    Δt = 1f-2

    function system!(du, u, p, t, controller)

        # neural network outputs controls taken by the system
        y1, y2 = u
        c = controller(u, p)[1]  # controller output is a 1-element container

        # reaction rate
        r = k10 * y1 * exp(-N / y2)  # irreversible (case 1 & case 2)
        # r = k10 * y1 * exp(-N/y2) - k20 * exp(-γ*N/y2) * (1-y1)  # reversible (case 3 & case 4)

        # dynamics of the controlled system
        y1_prime = (1 - y1) / time - r
        y2_prime = (yf - y2) / time + r - α * c * (y2 - yc)

        # update in-place
        @inbounds begin
            du[1] = y1_prime
            du[2] = y2_prime
        end
    end

    # set arquitecture of neural network controller
    controller = FastChain(
        FastDense(2, 16, tanh_fast),
        FastDense(16, 16, tanh_fast),
        FastDense(16, 2),
        (x, p) -> [u_lower + (u_upper - u_lower) * sigmoid_fast(x[1])],  # controllers ∈ [u_lower, u_upper]
    )

    controlODE = ControlODE(controller, system!, u0, tspan; Δt)

    # model weights are destructured into a vector of parameters
    θ = initial_params(controller)

    # define objective function to optimize
    function loss(controlODE, params)

        # curious error with ROS3P()
        sol = solve(controlODE, params) |> Array # integrate ODE system

        sum_squares = 0.0f0
        for state in eachcol(sol)
            control = controller(state, params)
            sum_squares +=
                α1 * (state[1] - y1s)^2 + α2 * (state[2] - y2s)^2 + α3 * (control[1] - us)^2
        end
        return sum_squares * 0.01f0
    end

    # closures to comply with required interface
    loss(params) = loss(controlODE, params)
    function plotting_callback(params, loss)
        return plot_simulation(controlODE, params; only=:controls, show=loss)
    end

    plot_simulation(controlODE, θ; only=:controls, show=loss(θ))

    result = sciml_train(
        loss,
        θ,
        LBFGS(; linesearch=BackTracking());
        allow_f_increases=true,
        cb=plotting_callback,
    )

    plot_simulation(
        controlODE,
        result.minimizer;
        only=:states,
        vars=[1],
        show=loss(result.minimizer),
        yrefs=[y1s],
    )
    plot_simulation(
        controlODE,
        result.minimizer;
        only=:states,
        vars=[2],
        show=loss(result.minimizer),
        yrefs=[y2s],
    )
    plot_simulation(
        controlODE,
        result.minimizer;
        only=:controls,
        show=loss(result.minimizer),
        yrefs=[us],
    )

    metadata = Dict(
        :loss => loss(result.minimizer),
        :reaction => reaction,
        :α1 => α1,
        :α2 => α2,
        :α3 => α3,
    )
    return store_simulation(
        reaction, controlODE, result.minimizer; metadata, datadir
    )
end  # script wrapper


abstract type IsingMeasurement <: AbstractMeasurement end

"""
    IsingEnergyMeasurement(mc::MC, model::IsingModel, rate=1)

Measures observables related to the energy of the IsingModel. This includes
- `E`: the total energy
- `E2`: the squared energy
- `e`: the energy per spin
- `C`: the specific heat
"""
struct IsingEnergyMeasurement <: IsingMeasurement
    invN::Float64
    E::Observable
    E2::Observable
    e::Observable
    C::Observable

    IsingEnergyMeasurement(mc::MC, model::IsingModel) = new(
        1.0 / model.l.sites,
        Observable(Float64, "Total energy"),
        Observable(Float64, "Total energy squared"),
        Observable(Float64, "Energy per site"),
        Observable(Float64, "Specific heat")
    )
end

function measure!(m::IsingEnergyMeasurement, mc::MC, model::IsingModel, i::Int64)
    push!(m.E, model.energy[])
    push!(m.E2, model.energy[]^2)
    push!(m.e, model.energy[] * m.invN)
    nothing
end

function finish!(m::IsingEnergyMeasurement, mc::MC, model::IsingModel)
    E = mean(m.E)
    E2 = mean(m.E2)
    push!(m.C, mc.p.beta^2 * m.invN * (E2 - E^2))
    nothing
end

"""
    IsingMagnetizationMeasurement(mc::MC, model::IsingModel, rate=1)

Measures observables related to the magnetization of the IsingModel. This
includes
- `M`: the total magnetization
- `M2`: the squared magnetization
- `m`: the magnetization per spin
- `chi`: the magentic susceptibility
"""
struct IsingMagnetizationMeasurement <: IsingMeasurement
    invN::Float64
    M::Observable
    M2::Observable
    m::Observable
    chi::Observable

    IsingMagnetizationMeasurement(mc::MC, model::IsingModel) = new(
        1.0 / model.l.sites,
        Observable(Float64, "Total magnetization"),
        Observable(Float64, "Total magnetization squared"),
        Observable(Float64, "Magnetization per site"),
        Observable(Float64, "Magnetic susceptibility")
    )
end

function measure!(m::IsingMagnetizationMeasurement, mc::MC, model::IsingModel, i::Int64)
    M = abs(sum(mc.conf))
    push!(m.M, M)
    push!(m.M2, M^2)
    push!(m.m, M * m.invN)
    nothing
end

function finish!(m::IsingMagnetizationMeasurement, mc::MC, model::IsingModel)
    M = mean(m.M)
    M2 = mean(m.M2)
    push!(m.chi, mc.p.beta * m.invN * (M2 - M^2))
    nothing
end

function default_measurements(mc::MC, model::IsingModel)
    Dict(
        :conf => ConfigurationMeasurement(mc, model),
        :Magn => IsingMagnetizationMeasurement(mc, model),
        :Energy => IsingEnergyMeasurement(mc, model)
    )
end


# Breadth First Search Example

using ThreeJS
using Escher
using LightGraphs
using NetworkViz

main(window) = begin
    push!(window.assets, "widgets")
    push!(window.assets,("ThreeJS","threejs"))
    nodes = Signal(10)
    btn = Signal(Any, leftbutton)
    start = Signal(false)
    map(btn) do _
      push!(start,!start.value)
    end
    map(start,nodes,sampleon(btn,nodes)) do t,n1,n2
    vbox(
        vbox(
          h1("Breadth-First Traversal"),
          vskip(2em),
          "Number of Nodes",
          slider(10:100,disabled=!t) >>> nodes,
          vskip(2em),
          hbox(
            hskip(4em),
            button("Draw",disabled=!t) >>> btn
          )
        ) |> pad(2em),
        hbox(
          hskip(2em),
          vbox(
            if t
              vbox(
                md"""## Original Graph""",
                drawGraph(CompleteGraph(n1),z=1)
              )
            else
              vbox(
                md"""## BFS Traversal of the Graph""",
                drawGraph(bfs_tree(CompleteGraph(n2),1),z=1)
              )
            end
          )
        )
      )
  end
end


## For computing a binned local polynomial
## regression estimator of a univariate regression
## function or its derivative.
## The data are discretised on an equally
## spaced grid. The bandwidths are discretised on a
## logarithmically spaced grid.
function locpoly(x::Vector{Float64}, y::Vector{Float64}, bandwidth::Union(Float64, Vector{Float64});
    drv::Int = 0,
    degree::Int=drv+1,
    kernel::Symbol = :normal,
    gridsize::Int = 401,
    bwdisc::Int = 25,
    range_x::Vector{Float64}=Float64[],
    binned::Bool = false,
    truncate::Bool = true)

    if any(bandwidth .<  0.0)
        error("'bandwidth' must be strictly positive")
    end

    if range_x == Float64[] && !binned
        maxx = maximum(x)
        minx = minimum(x)
        if y == Float64[]
            extra = 0.05 * (maxx - minx)
            range_x = [minx - extra, maxx + extra]
        else
            range_x = [minx, maxx]
        end
    end

    ## Rename common variables
    M = gridsize
    Q = bwdisc
    a = range_x[1]
    b = range_x[2]
    pp = degree + 1
    ppp = 2*degree + 1
    tau = 4

    ## Decide whether a density estimate or regression estimate is required.

    if y == Float64[]    # obtain density estimate
        n = length(x)
        gpoints = linspace(a, b, M)
        xcounts = linbin(x, gpoints, truncate)
        ycounts = (M-1) .* xcounts ./ (n*(b-a))
        xcounts = ones(M) #rep(1, M)
    else           # obtain regression estimate
        ## Bin the data if not already binned
        if !binned
            gpoints = linspace(a, b, M)
            xcounts, ycounts = rlbin(x, y, gpoints, truncate)
        else
            xcounts = x
            ycounts = y
            M = length(xcounts)
            gpoints = linspace(a, b, M)
        end
    end

    ## Set the bin width
    delta = (b-a)/(M-1)

    # Lvec = Int[]
    # hdisc = Float64[]
    # indic = Int[]
    # ## Discretise the bandwidths
    # if length(bandwidth) == M
    #     hlow = minimum(bandwidth)
    #     hupp = maximum(bandwidth)
    #     hdisc = [exp(h) for h in linspace(log(hlow),log(hupp),Q)]

    #     ## Determine value of L for each member of "hdisc"
    #     Lvec = [ifloor(tau*h/delta) for h in hdisc]

    #     ## Determine index of closest entry of "hdisc"
    #     ## to each member of "bandwidth"
    #     indic = if Q > 1
    #                 gap = (log(hdisc[Q])-log(hdisc[1]))/(Q-1)
    #                 if gap == 0
    #                     ones(Int, M)
    #                 else
    #                     lhlow = log(hlow)
    #                     [iround((log(b) - lhlow)/gap + 1.0) for b in bandwidth]
    #                 end
    #             else
    #                 ones(Int, M)
    #             end
    # elseif length(bandwidth) == 1
    #     indic = ones(Int, M)
    #     Q = 1
    #     Lvec = fill(ifloor(tau*bandwidth/delta), Q)
    #     hdisc = fill(bandwidth, Q)
    # else
    #     error("'bandwidth' must be a vector of length one or of length 'gridsize'")
    # end
    (indic, Q, Lvec, hdisc) = discretise_the_bandwidths(bandwidth, M, Q, tau, delta)

    if minimum(Lvec) == 0
        error("Binning grid too coarse for current (small) bandwidth: consider increasing 'gridsize'")
    end

    ## Allocate space for the kernel vector and final estimate

    dimfkap = 2 * sum(Lvec) + Q
    fkap = zeros(dimfkap)
    curvest = zeros(M)
    midpts = zeros(Int, Q)
    ss = zeros(M, ppp)
    tt = zeros(M, pp)
    Smat = zeros(pp, pp)
    Tvec = zeros(pp)
    ipvt = zeros(Int, pp)

    ccall((:locpol_, libkernsmooth), Ptr{Void},
      (Ptr{Float64}, Ptr{Float64}, Ptr{Int}, Ptr{Float64}, Ptr{Float64}, Ptr{Int}, Ptr{Int}, Ptr{Int},
       Ptr{Int}, Ptr{Int}, Ptr{Float64}, Ptr{Int}, Ptr{Int}, Ptr{Float64}, Ptr{Float64}, Ptr{Float64},
       Ptr{Float64}, Ptr{Int}, Ptr{Float64}),
      xcounts, ycounts,  &drv, &delta, hdisc, Lvec, indic, midpts, &M, &Q, fkap, &pp, &ppp,
      ss, tt, Smat, Tvec, ipvt, curvest)

    curvest = gamma(drv+1) .* curvest

    (gpoints, curvest)
end

locpoly(x::Vector{Float64}, bandwidth::Union(Float64, Vector{Float64}); args...) = locpoly(x, Float64[], bandwidth, args...)


### A Pluto.jl notebook ###
# v0.16.1

using Markdown
using InteractiveUtils

# ╔═╡ 06a7ec74-0684-4974-aa00-1c12dc3acc7b
begin
	using Plots
	using DelimitedFiles
	using Random; Random.seed!()
	using Printf
	"using packages"
end

# ╔═╡ ce825258-41b1-4c40-91f7-05d851b248e2
function Masubara_freq(n::Int64, β::Real; type::Symbol= :f)
    if type == :b  N = 2n
    elseif type == :f  N = 2n + 1
    else @error "type should be :b for bosons and :f for fermions"
    end
    return N*π/β
end

# ╔═╡ 9cbcd233-c37f-49f6-a6bf-5d92cf744972
function masubara(n::Int64, ϵ::Real, β::Real)
	ωn = Masubara_freq(n,β)
	return 1/(1.0im * ωn - ϵ)
end

# ╔═╡ 74ae31c5-545b-4b82-b9f0-1d2b7cccaa04
function Masubara_GF(n::Int64, A::Function, β::Real, type::Symbol;
    Λ::Float64=10., L::Int64=1000000)
    # G(iωn) = 1/2π ∫dΩ A(Ω)/(iωn - Ω)
    ωn = Masubara_freq(n, β, type=type)
    dL = 2*Λ/L
    Gn = 0.
    for n = 0:L
        ω = -Λ + dL*n
        Gn += A(ω)/(1.0im*ωn-ω) * dL
    end
    return Gn/(2π)
end

# ╔═╡ 1474d231-7d61-4a6d-80f8-2dd89f039168
function delta(x::Real; c::Real = 0., η::Real = 0.05)
    num = η
    den = ((x-c)^2 + η^2) * π
    return num/den
end

# ╔═╡ 7cda46b1-0a59-47f0-b612-6b877de3ecb6
function gaussian(x::Real, fac::AbstractVector)
    # f(x) = 1/(σ√(2π)) exp(-((x-μ)/σ)^2 /2 )
    μ = fac[1]; σ2 = fac[2]
    σ2 <= 0. ? (@error "σ2 should be positive.") :
    norm = √(2π * σ2)
    m = (x-μ)^2 / σ2
    return 1/norm*exp(-m/2)
end

# ╔═╡ c1758648-ae67-4ce6-9f5f-7d1b8512445e
function multi_gaussian(N::Int64; ms::Vector{Float64}=[-3.,3.], smax::Real=3.)
    fac = rand(N,2)
    fac[:,1] = rand(N) .* (ms[2] - ms[1]) .+ ms[1]
    fac[:,2] = rand(N) .* smax
    function f(x::Real)
        res = 0.
        for i = 1: N
            res += gaussian(x, fac[i,:])
        end
        res/N
    end
    return f, fac
end

# ╔═╡ e25846d0-fe93-4f2e-b500-9f218998af08
β = 100

# ╔═╡ e720ee46-b4c8-4539-b598-5655959f51f5
ϵ = 1.0

# ╔═╡ 2d0673e6-1cbf-4435-9d29-dd4303f36358
N = [i for i=1:36]

# ╔═╡ 5fec2475-8abf-4572-8d4c-58838e54a170
freq = [Masubara_freq(i,β,type=:f) for i in N]

# ╔═╡ e3ae6751-dcad-49d4-9ff9-8dbc7e42fcb8
md"""
Generate data:

$$H = \epsilon c^\dagger c$$
$$G(i\omega_n) = \frac{1}{i\omega_n - \epsilon}$$
$$A(\omega) = 2\pi \delta(\omega - \epsilon)$$
"""

# ╔═╡ f1228fa1-a310-496d-8a86-6ba8de8b4575
g1 = [1/(1.0im*w - ϵ) for w in freq]

# ╔═╡ 37ae6364-6465-4368-9bbd-b05e3b30497b
begin
	open("../References/run/input_delta.txt", "w") do file
		istring = @sprintf "idelta.txt %i odelta.txt" length(N)
		write(file,istring)
	end
	"input file"
end

# ╔═╡ c333b147-9776-4a79-a50e-564590115a17
begin
	open("../References/run/idelta.txt", "w") do file
		for i =1:length(N)
			writedlm(file,[freq[i] real(g1[i]) imag(g1[i])])
		end
	end
	"ifile.txt"
end

# ╔═╡ ebd94ab6-bdc8-49fd-bde0-ac573c5b3bae
md"""
Gaussian
"""

# ╔═╡ 3428ee85-399c-441e-a3e9-e3acac392734
omega=[i for i in range(-5,5,length=100)]

# ╔═╡ 8403d091-a0b4-4540-9fea-4c8d9e7e9fbe
A1, fac1 = multi_gaussian(1)

# ╔═╡ 541dc0b0-2fac-49b3-bfdd-8575aa2c056b
begin
	y1 = [A1(x) for x in omega]
	plot(omega, y1, lw=2, label=false)
	plot!(xlabel="ω", ylabel = "A(ω)")
end

# ╔═╡ 65938662-b78e-4713-bb21-4715105aeed4
begin
	open("../References/run/Ag1.txt", "w") do file
		for i =1:length(omega)
			writedlm(file,[omega[i] y1[i]])
		end
	end
	"Afile.txt"
end

# ╔═╡ dc06a8da-da64-4468-8e56-c544ef71d408
begin
	open("../References/run/input_g1.txt", "w") do file
		istring = @sprintf "ig1.txt %i og1.txt" length(N)
		write(file,istring)
	end
	"input file"
end

# ╔═╡ 8245d16b-81d7-41a6-9197-0c7aaabbeaad
g2 = [Masubara_GF(i,A1,β,:f) for i in N]

# ╔═╡ 944d8b32-73bb-44b9-af06-c8aa6202bf5c
begin
	open("../References/run/ig1.txt", "w") do file
		for i =1:length(N)
			writedlm(file,[freq[i] real(g2[i]) imag(g2[i])])
		end
	end
	"ifile.txt"
end

# ╔═╡ 5a46bfa5-f0b0-45de-bcaf-2e1619cd9c49
md"""
N=3 Gaussian
"""

# ╔═╡ 74686cc2-9214-42d6-9d35-2ed4bf63b43a
A3, fac3 = multi_gaussian(3)

# ╔═╡ c13a426a-2805-41ae-b95f-259baacf6d33
begin
	y3 = [A3(x) for x in omega]
	plot(omega, y3, lw=2, label=false)
	plot!(xlabel="ω", ylabel = "A(ω)")
end

# ╔═╡ bd203a88-79d3-42ff-ac53-9b264d53f1f7
begin
	open("../References/run/Ag3.txt", "w") do file
		for i =1:length(omega)
			writedlm(file,[omega[i] y3[i]])
		end
	end
	"Afile.txt"
end

# ╔═╡ 21b0b5ae-b058-4239-813b-bd7f6967d7c1
begin
	open("../References/run/input_g3.txt", "w") do file
		istring = @sprintf "ig3.txt %i og3.txt" length(N)
		write(file,istring)
	end
	"input file"
end

# ╔═╡ 824527d8-e9f4-4363-8db4-d1de26b2d256
g3 = [Masubara_GF(i,A3,β,:f) for i in N]

# ╔═╡ fc8f5f1d-0541-49f2-93d0-6badc50c6a19
begin
	open("../References/run/ig3.txt", "w") do file
		for i =1:length(N)
			writedlm(file,[freq[i] real(g3[i]) imag(g3[i])])
		end
	end
	"ifile.txt"
end

# ╔═╡ 959b5fd8-84d3-46cd-baed-c3d25d8c187f
md"""
$G(iω_n) = \frac{1}{2π} ∫dΩ \frac{A(Ω)}{(iω_n - Ω)}$

如何写积分？
"""

# ╔═╡ 00000000-0000-0000-0000-000000000001
PLUTO_PROJECT_TOML_CONTENTS = """
[deps]
DelimitedFiles = "8bb1440f-4735-579b-a4ab-409b98df4dab"
Plots = "91a5bcdd-55d7-5caf-9e0b-520d859cae80"
Printf = "de0858da-6303-5e67-8744-51eddeeeb8d7"
Random = "9a3f8284-a2c9-5f02-9a11-845980a1fd5c"

[compat]
Plots = "~1.22.0"
"""

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

[[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"

[[ColorSchemes]]
deps = ["ColorTypes", "Colors", "FixedPointNumbers", "Random"]
git-tree-sha1 = "9995eb3977fbf67b86d0a0a0508e83017ded03f2"
uuid = "35d6a980-a343-548e-a6ea-1d62b119f2f4"
version = "3.14.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 = "4866e381721b30fac8dda4c8cb1d9db45c8d2994"
uuid = "34da2185-b29b-5c13-b0c7-acf172513d20"
version = "3.37.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 = "bec2532f8adb82005476c141ec23e921fc20971b"
uuid = "9a962f9c-6df0-11e9-0e5d-c546b8b5ee8a"
version = "1.8.0"

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

[[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"

[[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 = "dba1e8614e98949abfa60480b13653813d8f0157"
uuid = "0656b61e-2033-5cc2-a64a-77c0f6c09b89"
version = "3.3.5+0"

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

[[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 = "ef49a187604f865f4708c90e3f431890724e9012"
uuid = "d2c73de3-f751-5644-a686-071e5b155ba9"
version = "0.59.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 = "7bf67e9a481712b3dbe9cb3dac852dc4b1162e02"
uuid = "7746bdde-850d-59dc-9ae8-88ece973131d"
version = "2.68.3+0"

[[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 = "60ed5f1643927479f845b0135bb369b031b541fa"
uuid = "cd3eb016-35fb-5094-929b-558a96fad6f3"
version = "0.9.14"

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

[[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"

[[IterTools]]
git-tree-sha1 = "05110a2ab1fc5f932622ffea2a003221f4782c18"
uuid = "c8e1da08-722c-5040-9ed9-7db0dc04731e"
version = "1.3.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 = "c7f1c695e06c01b95a67f0cd1d34994f3e7db104"
uuid = "b964fa9f-0449-5b57-a5c2-d3ea65f4040f"
version = "1.2.1"

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

[[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 = "761a393aeccd6aa92ec3515e428c26bf99575b3b"
uuid = "e9f186c6-92d2-5b65-8a66-fee21dc1b490"
version = "3.2.2+0"

[[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"

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

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

[[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 = "438d35d2d95ae2c5e8780b330592b6de8494e779"
uuid = "69de0a69-1ddd-5017-9359-2bf0b02dc9f0"
version = "2.0.3"

[[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 = "2537ed3c0ed5e03896927187f5f2ee6a4ab342db"
uuid = "995b91a9-d308-5afd-9ec6-746e21dbc043"
version = "1.0.14"

[[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"]
git-tree-sha1 = "b1a708d607125196ea1acf7264ee1118ce66931b"
uuid = "91a5bcdd-55d7-5caf-9e0b-520d859cae80"
version = "1.22.0"

[[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 = "44a75aa7a527910ee3d1751d1f0e4148698add9e"
uuid = "3cdcf5f2-1ef4-517c-9805-6587b60abb01"
version = "1.1.2"

[[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 = "4036a3bd08ac7e968e27c203d45f5fff15020621"
uuid = "ae029012-a4dd-5104-9daa-d747884805df"
version = "1.1.3"

[[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 = "3240808c6d463ac46f1c1cd7638375cd22abbccb"
uuid = "90137ffa-7385-5640-81b9-e52037218182"
version = "1.2.12"

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

[[StatsAPI]]
git-tree-sha1 = "1958272568dc176a1d881acb797beb909c785510"
uuid = "82ae8749-77ed-4fe6-ae5f-f523153014b0"
version = "1.0.0"

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

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

[[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 = "1162ce4a6c4b7e31e0e6b14486a6986951c73be9"
uuid = "bd369af6-aec1-5ad0-b16a-f7cc5008161c"
version = "1.5.2"

[[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"

[[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", "Wayland_jll"]
git-tree-sha1 = "2839f1c1296940218e35df0bbb220f2a79686670"
uuid = "2381bf8a-dfd0-557d-9999-79630e7b1b91"
version = "1.18.0+4"

[[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:
# ╟─9cbcd233-c37f-49f6-a6bf-5d92cf744972
# ╟─74ae31c5-545b-4b82-b9f0-1d2b7cccaa04
# ╟─ce825258-41b1-4c40-91f7-05d851b248e2
# ╟─1474d231-7d61-4a6d-80f8-2dd89f039168
# ╟─7cda46b1-0a59-47f0-b612-6b877de3ecb6
# ╟─c1758648-ae67-4ce6-9f5f-7d1b8512445e
# ╟─e25846d0-fe93-4f2e-b500-9f218998af08
# ╟─e720ee46-b4c8-4539-b598-5655959f51f5
# ╟─2d0673e6-1cbf-4435-9d29-dd4303f36358
# ╟─5fec2475-8abf-4572-8d4c-58838e54a170
# ╟─e3ae6751-dcad-49d4-9ff9-8dbc7e42fcb8
# ╟─f1228fa1-a310-496d-8a86-6ba8de8b4575
# ╟─37ae6364-6465-4368-9bbd-b05e3b30497b
# ╟─c333b147-9776-4a79-a50e-564590115a17
# ╟─ebd94ab6-bdc8-49fd-bde0-ac573c5b3bae
# ╟─3428ee85-399c-441e-a3e9-e3acac392734
# ╟─8403d091-a0b4-4540-9fea-4c8d9e7e9fbe
# ╟─541dc0b0-2fac-49b3-bfdd-8575aa2c056b
# ╟─65938662-b78e-4713-bb21-4715105aeed4
# ╟─dc06a8da-da64-4468-8e56-c544ef71d408
# ╟─8245d16b-81d7-41a6-9197-0c7aaabbeaad
# ╟─944d8b32-73bb-44b9-af06-c8aa6202bf5c
# ╟─5a46bfa5-f0b0-45de-bcaf-2e1619cd9c49
# ╟─74686cc2-9214-42d6-9d35-2ed4bf63b43a
# ╟─c13a426a-2805-41ae-b95f-259baacf6d33
# ╟─bd203a88-79d3-42ff-ac53-9b264d53f1f7
# ╠═21b0b5ae-b058-4239-813b-bd7f6967d7c1
# ╠═824527d8-e9f4-4363-8db4-d1de26b2d256
# ╟─fc8f5f1d-0541-49f2-93d0-6badc50c6a19
# ╟─06a7ec74-0684-4974-aa00-1c12dc3acc7b
# ╟─959b5fd8-84d3-46cd-baed-c3d25d8c187f
# ╟─00000000-0000-0000-0000-000000000001
# ╟─00000000-0000-0000-0000-000000000002


include("8.1 Error functions.jl")
include("8.10 Bessel functions.jl")
include("8.2 Fresnel integral functions.jl")
include("8.3 Exponential integral functions.jl")
include("8.4 Trig integral functions.jl")
include("8.5 Hyperbolic integral functions.jl")
include("8.6 Gamma functions.jl")
include("8.7 Zeta function.jl")
include("8.8 Polylogarithm function.jl")
include("8.9 Product logarithm function.jl")


"""Abstract index supertype"""
abstract type Index end

"""An index whose value is not known. Identified by its name. There should be
no need to set the id by the user.
"""
struct FreeIndex{T<:AbstractVectorSpace} <: Index
    V::T
    name::String
    id::Int
end

FreeIndex(V::T, name::String) where {T<:AbstractVectorSpace} = FreeIndex(V, name, 0)

"""Creates an index with the same name in the dual space."""
Base.adjoint(i::FreeIndex) = FreeIndex(dual(i.V), i.name, i.id)

"""An index whose value is known."""
struct FixedIndex{T<:AbstractVectorSpace} <: Index
    V::T
    value::Int
    function FixedIndex(V::T, value::Int) where {T<:AbstractVectorSpace}
        if dim(V) === nothing || value < 1 || value > dim(V)
            throw(DomainError(value, string("Value not in range for the vectorspace ", V)))
        end
        new{T}(V, value)
    end
end

"""Creates an index with the same value in the dual space."""
Base.adjoint(i::FixedIndex) = FixedIndex(dual(i.V), i.value)

"""A node for holding indices."""
struct Indices <: Node
    indices
    children
end

Indices(indices::Vararg{Index}) = Indices(indices, ())

"""Creates an index from an integer and VectorSpace."""
function toindex(V::AbstractVectorSpace, i::Int)
    return FixedIndex(V, i)
end

"""Creates an index from a string and VectorSpace."""
function toindex(V::AbstractVectorSpace, s::String)
    return FreeIndex(V, s)
end

"""Added for convenience."""
function toindex(V::AbstractVectorSpace, i::Index)
    return i
end

Base.show(io::IO, indices::Indices) = print(io, indices.indices)

function Base.show(io::IO, i::FreeIndex)
    if i.name != ""
        print(io, i.name)
    else
        print(io, "FreeIndex")
    end
    if i.V isa DualVectorSpace
        print(io, "*")
    end
end

Base.show(io::IO, i::FixedIndex) = print(io, i.value)


using ODEOpt
using ODE
using Base.Test

function testgrav()
    # create time samples
    tmin = 0.0
    tmax = 10
    nsamples = 500
    trange = linspace(tmin, tmax, nsamples)
    #initial position
    ystart = [0.2; 1.0]::Vector{Float64}
    time, y = ode45(grav, ystart, trange)
    x,v = extract(y)
    tol = 1e-2
    @test_approx_eq_eps  time[0.0 .<= abs(x).<=tol][end] 2.9458917835671343 1e-8
end
testgrav()

function noise(σ, n::Integer)
    r = randn(n)
    return σ*r
end

using Optim
using PyPlot
let
# create time samples
srand(0)
tmin = 0.0
tmax = 10
ntimes = 400
trange = linspace(tmin, tmax, ntimes)
#initial position
ystart = [0.0; 1.0]::Vector{Float64}
β = 1.0
βmin = 0.0
βmax = 4.0

σ = 0.01
PLOT = true
samplefactor = 2

function simulate(fgen::Function, β::Float64, sol::Solution, samples, PLOT::Bool)
    t, x,v = extract(sol)
    r = noise(σ, length(samples))
    data = x[samples] .+  r
    tdata = t[samples]
    #= @show tdata[end] t[end] length(t) length(tdata) =#
    # TODO: why are the samples missing the end of the domain ?
    #       the samples IntRange is constructed from a different solution from sol.
    # TODO: sample uniformly rather than at Runge Kutta points.
    if PLOT
        solutionplot(t, x,v)
        plot(tdata, data, "o", label="data")
        legend()
    end

    @show norm(σ*r)
    z = soserr(t, x, tdata, data,)
    #= @test_approx_eq_eps norm(σ*r) z 1e-8 =#

    @show sqrt(soserr(t, x, tdata, data))
    tsolve, xsolve, vsolve, d² = obj(f, ystart, tdata, data)
    @show sqrt(d²)

    γ(x) = obj(fgen(x), ystart, tdata, data)[end]
    @show res = optimize(γ, βmin, βmax)

    β⁺ = res.minimum
    t⁺, y⁺ = ode45(fgen(β⁺), ystart, trange)
    x⁺, v⁺ = extract(y⁺)
    if PLOT
        solutionplot(t⁺, x⁺, v⁺)
    end

    rec =  Record(β, ystart, length(samples),  res)
    return rec
end

recs = Record[]
f = fgen(β)
sol = Solution(ode45(f, ystart, trange)...)
function test_smallerr(recs)
    @test_approx_eq_eps recs[end].β recs[end].res.minimum 1e-1
end

for samplefactor in [1,2,3,4,5,6,7,8,8,9,10]
    samples = 1:samplefactor:length(trange)
    #= @show samples length(trange) length(samples) length(trange[samples]) =#
    r = simulate(fgen, β, sol, samples, false)
    push!(recs, r)
    test_smallerr(recs)
end

println(STDOUT, "n\tβ\tβ⁺\terr\tfval")
for (i,r) in enumerate(recs)
    tprint(STDOUT, r)
end

samplefactor=1
for β in float([1,2,3,4,5,6,7,8,8,9,10])
    βmax = 3β
    f = fgen(β)
    sol = Solution(ode45(f, ystart, trange)...)
    samples = 1:samplefactor:length(trange)
    r = simulate(fgen, β, sol, samples, β==10?true:false)
    push!(recs, r)
    test_smallerr(recs)
end
println(STDOUT, "n\tβ\tβ⁺\terr\tfval")
for (i,r) in enumerate(recs)
    tprint(STDOUT, r)
end
end
include("secondorder.jl")


module _cMSEn
export cMSEn
using Statistics: std, mean, median, var
using Plots
    """
        MSx, CI = cMSEn(Sig, Mobj)

    Returns a vector of composite multiscale entropy values (`MSx`) for the data
    sequence (`Sig`) using the parameters specified by the multiscale object
    (`Mobj`) using the composite multiscale entropy method over 3 temporal scales.

        MSx, CI = cMSEn(Sig::AbstractArray{T,1} where T<:Real, Mobj::NamedTuple;
                            Scales::Int=3, RadNew::Int=0, Refined::Bool=false, Plotx::Bool=false)

    Returns a vector of composite multiscale entropy values (`MSx`) of the
    data sequence (`Sig`) using the parameters specified by the multiscale
    object (`Mobj`) and the following 'keyword' arguments:

    # Arguments:
    `Scales`   - Number of temporal scales, an integer > 1   (default: 3) \n
    `RadNew`   - Radius rescaling method, an integer in the range [1 4].
                 When the entropy specified by `Mobj` is `SampEn` or `ApEn`,
                 RadNew allows the radius threshold to be updated at each
                 time scale (Xt). If a radius value is specified by `Mobj` (`r`),
                 this becomes the rescaling coefficient, otherwise it is set
                 to 0.2 (default). The value of RadNew specifies one of the
                 following methods:\n
                 [1]    Standard Deviation          - r*std(Xt)\n
                 [2]    Variance                    - r*var(Xt) \n
                 [3]    Mean Absolute Deviation     - r*mean_ad(Xt) \n
                 [4]    Median Absolute Deviation   - r*med_ad(Xt)\n
    `Refined`  - Refined-composite MSEn method. When `Refined == true` and the
                 entropy function specified by Mobj is `SampEn`, cMSEn returns
                 the refined-composite multiscale entropy (rcMSEn) [default: false]\n
    `Plotx`    - When `Plotx` == true, returns a plot of the entropy value at each
                time scale (i.e. the multiscale entropy curve) [default: false]\n

    # See also `MSobject`, `rMSEn`, `MSEn`, `hMSEn`, `SampEn`, `ApEn`, `XMSEn`

    # References:
        [1] Madalena Costa, Ary Goldberger, and C-K. Peng,
            "Multiscale entropy analysis of complex physiologic time series."
            Physical review letters
            89.6 (2002): 068102.

        [2] Vadim V. Nikulin, and Tom Brismar,
            "Comment on “Multiscale entropy analysis of complex physiologic
            time series”."
            Physical review letters
            92.8 (2004): 089803.

        [3] Madalena Costa, Ary L. Goldberger, and C-K. Peng.
            "Costa, Goldberger, and Peng reply."
            Physical Review Letters
            92.8 (2004): 089804.

         [4] Shuen-De Wu, et al.,
            "Time series analysis using composite multiscale entropy."
            Entropy
            15.3 (2013): 1069-1084.

        [5] Shuen-De Wu, et al.,
            "Analysis of complex time series using refined composite
            multiscale entropy."
            Physics Letters A
            378.20 (2014): 1369-1374.

    """
    function cMSEn(Sig::AbstractArray{T,1} where T<:Real, Mobj::NamedTuple;
            Scales::Int=3, RadNew::Int=0, Refined::Bool=false, Plotx::Bool=false)

    (size(Sig,1)>10) ? nothing : error("Sig:   must be a numeric vector" )
    (length(Mobj) >= 1) ? nothing :  error("Mobj:    must be a multiscale entropy object created
                with the function EntropyHub.MSobject")
    (Scales>1) ? nothing : error("Scales:     must be an integer > 1")
    (RadNew==0 || (RadNew in 1:4 && String(Symbol(Mobj.Func)) in ("SampEn","ApEn"))) ? nothing :
        error("RadNew:     must be 0, or an integer in range [1 4] with
                entropy function 'SampEn' or 'ApEn'")

    MSx = zeros(Scales)
    Args = NamedTuple{keys(Mobj)[2:end]}(Mobj)
    if RadNew > 0
        if RadNew == 1
            Rnew = x -> std(x, corrected=false)
        elseif RadNew == 2
            Rnew = x -> var(x, corrected=false)
        elseif RadNew == 3
            Rnew = x -> mean(abs.(x .- mean(x)))
        elseif RadNew == 4
            Rnew = x -> median(abs.(x .- median(x)))
        end

        if haskey(Mobj,:r)
            Cx = Mobj.r
        else
            Cy = ("Standard Deviation","Variance","Mean Abs Deviation",
                    "Median Abs Deviation")
            @warn("No radius value provided in Mobj.
                Default set to 0.2*$(Cy[RadNew]) of each new time-series.")
            Cx = .2
        end
    end

    for T = 1:Scales
        Temp = modified(Sig,T)
        N = Int(T*floor(length(Temp)/T))
        Temp2 = zeros(T)
        Temp3 = zeros(T)

        for k = 1:T
            print(". ")
            RadNew > 0 ? Args = (Args..., r=Cx*Rnew(Temp[k:T:N])) : nothing

            if Refined
                _, Ma, Mb = Mobj.Func(Temp[k:T:N]; Args...)
                Temp2[k] = Ma[end]
                Temp3[k] = Mb[end]
            else
                Temp2 = Mobj.Func(Temp[k:T:N]; Args...)
                typeof(Temp2)<:Tuple ? Temp3[k] = Temp2[1][end] : Temp3[k] = Temp2[end]
                #Temp3[k] = Temp2[1][end]
            end
        end

        Refined ? MSx[T] = -log(sum(Temp2)/sum(Temp3)) : MSx[T] = mean(Temp3)

    end
    CI = sum(MSx)
    print("\n")
    if any(isnan.(MSx))
        println("Some entropy values may be undefined.")
    end

    if Plotx
        Refined ? strx = "Refined-Composite" : strx = "Composite"

        p1 = plot(1:Scales, MSx, c=RGB(8/255, 63/255, 77/255), lw=3)
        scatter!(1:Scales, MSx, markersize=6, c=RGB(1, 0, 1),
        xlabel = "Scale Factor", ylabel = "Entropy Value",
        guidefont = font(12, "arial", RGB(7/255, 54/255, 66/255)),
        tickfontsize = 10, tickfontfamily="arial", legend=false,
        title = strx*" Multiscale $(Mobj.Func)",
        plot_titlefontsize=16, plot_titlefontcolor=RGB(7/255, 54/255, 66/255)) #ylim=(0,maximum(MSx)+.2),
        display(p1)
    end

    return MSx, CI
    end

    function modified(Z,sx)
        Ns = length(Z) - sx + 1
        Y = zeros(Ns)
        for k = 1:Ns
            Y[k] = mean(Z[k:k+sx-1])
        end
        return Y
    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 StokesDiffEq, Parameters
Δx = 1//2^3 # Make this much smaller (1^2-5) for your own tests
mesh = FDMMesh(Δx,mins=[-1;-1],maxs=[1;1])
prob = prob_stokes_dirichletzero
prob = prob_stokes_homogenous

@unpack Δxs,grids,dims,gridSize,square,mins,maxs = mesh
u = zeros(gridSize[1]-1,gridSize[2])
v = zeros(gridSize[1],gridSize[2]-1)
p = zeros(gridSize[1]-1,gridSize[2]-1)
rp = zeros(p)
δq = zeros(p)

#Calculate grids for u,v, and p
ux = grids[1][1:end-1,:]
uy = (grids[2]+Δxs[2]/2)[1:end-1,:]
vx = (grids[1]+Δxs[1]/2)[:,1:end-1]
vy = grids[2][:,1:end-1]
px = grids[1][1:end-1,1:end-1]+Δxs[1]/2
py = grids[2][1:end-1,1:end-1]+Δxs[2]/2
#prob = dirichletzeroStokesExample()
@unpack f₁,f₂,ugD,vgD,uanalytic,vanalytic,panalytic,g,trueknown = prob

u_analytic = float(uanalytic(ux,uy))
vTrue = float(vanalytic(vx,vy))
pTrue = float(panalytic(px,py))

# Impose boundary conditions, cut out ghost points
u[:,1]   = ugD(ux[:,1]  ,uy[:,1])
u[:,end] = ugD(ux[:,end],uy[:,end])
v[1,:]   = vgD(vx[1,:]  ,vy[1,:])
v[end,:] = vgD(vx[end,:],vy[end,:])

#Gauss-Seidel of Velocity Test
err1 = Vector{Float64}(0)
err2 = Vector{Float64}(0)
StokesDiffEq.GSu!(u,f₁,Δxs,pTrue,ugD,grids,ux,uy) # Inplace u => uhalf
StokesDiffEq.GSv!(v,f₂,Δxs,pTrue,vgD,grids,vx,vy) # Inplace v => vhalf
for j = 1:100
  StokesDiffEq.GSu!(u,f₁,Δxs,pTrue,ugD,grids,ux,uy) # Inplace u => uhalf
  StokesDiffEq.GSv!(v,f₂,Δxs,pTrue,vgD,grids,vx,vy) # Inplace v => vhalf
  StokesDiffEq.push!(err1,maximum(u-u_analytic))
  StokesDiffEq.push!(err2,maximum(v-vTrue))
end

#Should Converge
#TEST_PLOT && plot(1:100,[err1 err2],yscale=:log10)

#Now test rp
err1 = Vector{Float64}(0)
val  = Vector{Float64}(0)

StokesDiffEq.calc_rp!(rp,u_analytic,vTrue,Δxs,g,px,py)

#Should be close to zero with true values.
maximum(rp) < 1e-2


module DualNumbers

"""
Dual numbers with a scalar dual part
"""
struct Dual{T} <: Real
    primal::T
    dual::T
    Dual{T}(x, y) where {T} = new{T}(T(x), T(y))
end
Dual(x::T1, y::T2) where {T1,T2} = Dual{promote_type(T1, T2)}(x, y)
Dual{T}(x) where {T} = Dual{T}(x, 0)
Dual(x) = Dual(x, 0)
export Dual

# Only compare primal parts
Base.:(==)(x::Dual, y::Dual) = x.primal == y.primal
Base.:(<)(x::Dual, y::Dual) = x.primal < y.primal

Base.:(==)(x::Dual, y::Real) = x == Dual(y)
Base.:(<)(x::Dual, y::Real) = x < Dual(y)
Base.:(==)(x::Real, y::Dual) = Dual(x) == y
Base.:(<)(x::Real, y::Dual) = Dual(x) < y

Base.promote_rule(::Type{Dual{T1}}, ::Type{Dual{T2}}) where {T1,T2} = Dual{promote_type(T1, T2)}
Base.promote_rule(::Type{T1}, ::Type{Dual{T2}}) where {T1<:Real,T2} = Dual{promote_type(T1, T2)}
Base.promote_rule(::Type{Bool}, ::Type{Dual{T2}}) where {T2} = Dual{promote_type(Bool, T2)}

Base.zero(x::Dual) = Dual(zero(x.primal))
Base.one(x::Dual) = Dual(one(x.primal))

Base.:+(x::Dual) = Dual(+x.primal, +x.dual)
Base.:-(x::Dual) = Dual(-x.primal, -x.dual)

Base.:+(x::Dual, y::Dual) = Dual(x.primal + y.primal, x.dual + y.dual)
Base.:-(x::Dual, y::Dual) = Dual(x.primal - y.primal, x.dual - y.dual)

Base.inv(x::Dual) = Dual(inv(x.primal), -x.dual / x.primal^2)

Base.:*(x::Dual, y::Dual) = Dual(x.primal * y.primal, x.dual * y.primal + x.primal * y.dual)
Base.:/(x::Dual, y::Dual) = Dual(x.primal / y.primal, x.dual / y.primal - x.primal / y.primal^2 * y.dual)
Base.:\(x::Dual, y::Dual) = Dual(x.primal \ y.primal, x.primal \ y.dual - (x.primal^2 \ y.primal) * x.dual)

Base.literal_pow(::typeof(^), x::Dual, ::Val{0}) = one(x)
function Base.literal_pow(::typeof(^), x::Dual, ::Val{n}) where {n}
    return Dual(Base.literal_pow(^, x.primal, Val(n)), n * Base.literal_pow(^, x.primal, Val(n - 1)) * x.dual)
end
Base.:^(x::Dual, n::Integer) = n == 0 ? one(x) : Dual(x.primal^n, n * x.primal^(n - 1) * x.dual)
Base.:^(x::Dual, y::Real) = (@assert !(y isa Dual); Dual(x.primal^y, y * x.primal^(y - 1) * x.dual))
Base.:^(x::Dual, y::Dual) = error("not implemented")

Base.cos(x::Dual) = Dual(cos(x.primal), -x.dual * sin(x.primal))
Base.log(x::Dual) = Dual(log(x.primal), x.dual / x.primal)
Base.sin(x::Dual) = Dual(sin(x.primal), x.dual * cos(x.primal))
Base.sqrt(x::Dual) = Dual(sqrt(x.primal), x.dual / (2 * sqrt(x.primal)))

################################################################################

"""
    derivative(f, x)
    derivative(f, xs, i)
    derivative(f, x, c)
    derivative(f, xs, i, c)

Evaluate the partial derivative of the scalar-argument function `f(x)`
at `x`, or of the array-argument function `f(xs)` at `xs[i]`.

When the argument `x` (or `xs[i]`) is complex, then an additional
argument `c` chooses whether the partial derivative is with respect to
the real (`c==1`) or imaginary (`c==2`) part of the argument.
"""
function derivative end
export derivative

derivative(f, x::T) where {T<:Real} = f(Dual(x, 1)).dual

function derivative(f, xs::AbstractArray{T}, n::Integer) where {T<:Real}
    xs′ = similar(xs, Dual{T})
    for i in 1:length(xs′)
        xs′[i] = Dual(xs[i], i == n)
    end
    return f(xs′).dual
end

function derivative(f, x::Complex{T}, c::Integer) where {T<:Real}
    fx′ = f(Complex(Dual(real(x), c == 1), Dual(imag(x), c == 2)))
    fx′::Complex
    return Complex(real(fx′).dual, imag(fx′).dual)
end

function derivative(f, xs::AbstractArray{Complex{T}}, n::Integer, c::Integer) where {T<:Real}
    xs′ = similar(xs, Complex{Dual{T}})
    for i in 1:length(xs′)
        xs′[i] = Complex(Dual(real(xs[i]), i == n && c == 1), Dual(imag(xs[i]), i == n && c == 2))
    end
    fx′ = f(xs′)
    fx′::Complex
    return Complex(real(fx′).dual, imag(fx′).dual)
end

end


struct Scaling{Dim,T} <: Transform
  vec::Point{Dim,T}
end
Scaling(vals::T...) where {T} = Scaling(Point{length(vals),T}(vals))
Scaling(vec::AbstractVector) where {Dim,T} = Scaling(Point{length(vec),eltype(vec)}(vec))
(s::Scaling)(p::AbstractVector) = s.vec .* p

Base.:∘(t1::Scaling, t2::Scaling) = Scaling(t1.vec .* t2.vec)

(≈)(x::Scaling, y::Scaling) = x.vec ≈ y.vec

inv(s::Scaling) = Scaling(inv.(s.vec))

const Scaled{O,Dim,T} = Transformed{O,Scaling{Dim,T}}
Scaled(obj::O, transf::Scaling{Dim,T}) where {O,Dim,T} = Scaled{O,Dim,T}(obj, transf)

Base.show(io::IO, s::Scaled{O,Dim,T}) where {O,Dim,T} = print(io, "Scaled{$O, $Dim, $T}($(s.obj), $(s.transf))")

struct UniformScaling{T} <: Transform
  factor::T
end
(s::UniformScaling)(p::AbstractVector) = s.factor .* p

Base.:∘(t1::UniformScaling, t2::UniformScaling) = UniformScaling(t1.factor * t2.factor)

inv(s::UniformScaling) = UniformScaling(inv(s.factor))


struct ScaledArray{T<:AbstractFloat, N} <: AbstractArray{T, N}
    A::AbstractArray{T,N}
    s::T
end

Base.size(SA::ScaledArray{T,N}) where {T,N} = size(SA.A)
Base.getindex(SA::ScaledArray,i...) = getindex(SA.A,i...)
Base.setindex!(SA::ScaledArray,x::Real,i...) = setindex!(SA.A,x,i...)

ScaledArray(A::AbstractArray) = ScaledArray(A,one(eltype(A)))
ScaledArray(A::AbstractArray{T},s::Real) where T = ScaledArray(A,T(s))

unscale(SA::ScaledArray{T,N}) where {T,N} = ScaledArray(SA.A*SA.s,one(T))
rescale(SA::ScaledArray{T,N},s::Real) where {T,N} = ScaledArray(SA.A*(SA.s/T(s)),T(s))

Base.:(*)(X::ScaledArray{T,N},Y::ScaledArray{T,N}) where {T,N} = ScaledArray(X.A*Y.A,X.s*Y.s)


# Adapted from https://github.com/tpapp/TransformVariables.jl/blob/master/src/special_arrays.jl

using LinearAlgebra
const CorrCholeskyUpper = CorrCholeskyFactor

export CorrCholeskyLower

"""
    CorrCholeskyLower(n)
Lower Cholesky factor of a correlation matrix of size `n`.
Transforms ``n(n-1)/2`` real numbers to an ``n×n`` lower-triangular matrix `L`, such that
`L*L'` is a correlation matrix (positive definite, with unit diagonal).

# Notes
If
- `z` is a vector of `n` IID standard normal variates,
- `σ` is an `n`-element vector of standard deviations,
- `L` is obtained from `CorrCholeskyLower(n)`,
then `Diagonal(σ) * L * z` is a zero-centered multivariate normal variate with the standard deviations `σ` and
correlation matrix `L * L'`.
"""
struct CorrCholeskyLower <: TV.VectorTransform
    n::Int
end

TV.dimension(t::CorrCholeskyLower) = TV.dimension(CorrCholeskyUpper(t.n))

# TODO: For now we just transpose the CorrCholeskyUpper result. We should
# consider whether it can help performance to implement this directly for the
# lower triangular case
function TV.transform_with(flag::TV.LogJacFlag, t::CorrCholeskyLower, x::AbstractVector, index)
    U, ℓ, index = TV.transform_with(flag, CorrCholeskyUpper(t.n), x, index)
    return U', ℓ, index
end

TV.inverse_eltype(t::CorrCholeskyLower, L::LowerTriangular) = TV.extended_eltype(L)

function TV.inverse_at!(x::AbstractVector, index, t::CorrCholeskyLower, L::LowerTriangular)
    return TV.inverse_at!(x, index, CorrCholeskyUpper(t.n), L')
end


## readme script
using Pkg;Pkg.activate("./")
using VortexDistributions, Plots
gr(xlabel="x",ylabel="y",transpose=true,legend=false)

## make a simple 2D test field
Nx = 400; Ny = Nx
Lx = 200; Ly = Lx
x = LinRange(-Lx / 2, Ly / 2, Nx); y = x
psi0 = one.(x*y') |> complex

## doubly periodic boundary conditions
psi = Torus(psi0,x,y)

# make a point vortex
pv = PointVortex(30.0,70.3,-1)

# make a scalar GPE vortex with exact core
spv = ScalarVortex(pv)
vortex!(psi,spv)

## make some more random vortices
vort = rand_vortex(10,psi)
vortex!(psi,vort)

using BenchmarkTools
@btime findvortices(psi)


include("../src/TrueSkill.jl")
using .TrueSkill
global const ttt = TrueSkill
using Test
using CSV
using JLD2
using Dates
using DataFrames

@testset "Test OGS" begin
    println("Opening dataset")
    data = CSV.read("input/summary_filtered.csv")
    prior_dict = Dict{String,ttt.Player}()
    for h_key in Set([(row.handicap, row.width) for row in eachrow(data) ])
        prior_dict[string(h_key)] = ttt.Player(ttt.Gaussian(0.,25.0/3.),0.,1.0/100)
    end
    prior_copy = copy(prior_dict)
    results = [row.black_win == 1 ? [0.,1.] : [1., 0.] for row in eachrow(data) ]
    events = [ r.handicap<2 ? [[string(r.white)],[string(r.black)]] : [[string(r.white)],[string(r.black),string((r.handicap,r.width))]] for r in eachrow(data) ]
    times = Vector{Int64}()

    println(now())
    println("Creating History")
    h = ttt.History(events, results, times , prior_dict, mu=0.0,sigma=10.,beta=1.,gamma=0.2125,iter=16)
    ts_log_evidence = ttt.log_evidence(h)
    println(now())
    println("Converging History")
    ttt.convergence(h)
    ttt_log_evidence = ttt.log_evidence(h)
    println(now())

    print("TS: ", ts_log_evidence, ", TTT:", ttt_log_evidence)
    @test ts_log_evidence < ttt_log_evidence
    @test isapprox(ttt_log_evidence, -195388.7125823722)

    w_mean = [ ttt.posterior(h.batches[r], string(data[r,"white"])).mu for r in 1:size(data)[1]]
    b_mean = [ ttt.posterior(h.batches[r], string(data[r,"black"])).mu  for r in 1:size(data)[1]]
    w_std = [ ttt.posterior(h.batches[r], string(data[r,"white"])).sigma for r in 1:size(data)[1]]
    b_std = [ ttt.posterior(h.batches[r], string(data[r,"black"])).sigma for r in 1:size(data)[1]]

    h_mean = [ data[r,"handicap"] > 1 ? ttt.posterior(h.batches[r] ,string((data[r,"handicap"],data[r,"width"]))).mu : 0.0 for r in 1:size(data)[1]]
    h_std = [ data[r,"handicap"] > 1 ? ttt.posterior(h.batches[r] ,string((data[r,"handicap"],data[r,"width"]))).sigma : 0.0 for r in 1:size(data)[1]]
    evidence = [ h.batches[r].events[1].evidence for r in 1:size(data)[1]]

    df = DataFrame(id = data[:"id"]
                  ,white = data[:"white"]
                  ,black = data[:"black"]
                  ,handicap = data[:"handicap"]
                  ,width = data[:"width"]
                  ,w_mean = w_mean
                  ,b_mean = b_mean
                  ,w_std = w_std
                  ,b_std = b_std
                  ,h_mean = h_mean
                  ,h_std = h_std
                  ,evidence = evidence)

    CSV.write("output/longtest_output.csv", df; header=true)

end


# This file is a part of BAT.jl, licensed under the MIT License (MIT).

using BAT
using Test
using Statistics, StatsBase, Distributions
using HypothesisTests

@testset "Multimodal Tests" begin
    cm = MixtureModel([Distributions.TDist(1), Distributions.TDist(1)])
    prod = Distributions.Product([Distributions.TDist(1), Distributions.TDist(1)])

    #Instantiates multimodal Student T with Mixture Model and Product
    @test @inferred(BAT.MultimodalStudentT(cm, 0.1, 2, prod)) isa BAT.MultimodalStudentT
    #Instantiates multimodal Student T with μ, σ and n
    @test @inferred(BAT.MultimodalStudentT(μ=60, σ=1., ν = 3, n = 2)) isa BAT.MultimodalStudentT
    #Error if n = 1
    @test_throws ArgumentError  BAT.MultimodalStudentT(μ=60, σ=1., ν = 3, n = 1)

    #If n = 1, student t = cauchy
    mt = BAT.MultimodalStudentT(μ = 1., σ = 0.1, ν = 1, n=4)

    @test @inferred(Base.size(mt)) == (4,)
    @test @inferred(Base.length(mt)) == 4
    @test @inferred(Base.eltype(mt)) == Float64
    @test all(isnan, Statistics.var(mt))
    @test all(isnan, Statistics.mean(mt))

    params = @inferred(StatsBase.params(mt))

    #Parameters
    @test params[1] == [-1., 1., 0., 0.]
    @test params[2] == [0.1, 0.1, 0.1, 0.1]
    @test params[3] == 4

    #logpdf
    @test @inferred(Distributions._logpdf(mt, [-1., 0., 1., 2.])) == -11.283438151658

    #If μ = 0, d = 1 the Distribution should be Cauchy-like in every dimension
    mmc = BAT.MultimodalStudentT(μ = 0., σ = 0.1, ν = 1, n=2)
    ks_test = HypothesisTests.ExactOneSampleKSTest(rand(mmc, 10^6)[1,:], Cauchy(0., 0.1))#First dimension
    @test pvalue(ks_test) > 0.05
    ks_test = HypothesisTests.ExactOneSampleKSTest(rand(mmc, 10^6)[2,:], Cauchy(0., 0.1))#Second dimension
    @test pvalue(ks_test) > 0.05

    #If μ = 0, ν > 1 the Distribution should be Student t-like in every dimension
    mmc = BAT.MultimodalStudentT(μ = 0., σ = 0.1, ν = 3, n=4)
    tdist = LocationScale(0, 0.1, TDist(3))
    ks_test = HypothesisTests.ExactOneSampleKSTest(rand(mmc, 10^6)[1,:], tdist)
    @test pvalue(ks_test) > 0.05
    ks_test = HypothesisTests.ExactOneSampleKSTest(rand(mmc, 10^6)[2,:], tdist)
    @test pvalue(ks_test) > 0.05

    @test ~any(isnan, Statistics.var(mmc))
    @test ~any(isnan, Statistics.mean(mmc))

end


module RecursiveClusterAlgorithm

# not currently in project but can compile locally!
using VortexDistributions

using VortexDistributions:VortexGroup, Dipole, xpos, ypos

using LinearAlgebra, SparseArrays, LightGraphs, SimpleWeightedGraphs

mutable struct Cluster <: VortexGroup
    vortices::Array{PointVortex,1}
    tree::Array{LightGraphs.SimpleGraphs.SimpleEdge{Int64},1}
end

include("get_dipoles.jl")
include("seed_clusters.jl")
include("utils.jl")
Cluster(vort::Array{PointVortex,1}) = Cluster(vort,spanning_tree(xpos(vort),ypos(vort)))

export Cluster, get_dipoles, seed_clusters, distances, periodic_distances,
sparse_distances, spanning_tree, vortex_marker, plot_vortices!,
plot_cluster!, vortex_marker

end # module


# Section 4.4 of
# A. A. Ahmadi, and A. Majumdar
# DSOS and SDSOS Optimization: More Tractable Alternatives to Sum of Squares and Semidefinite Optimization
# 2017

using MultivariateMoments

@testset "Options Pricing with $(factory.constructor)" for factory in sdp_factories
    isscs(factory) && continue
    continue # see https://github.com/JuliaOpt/SumOfSquares.jl/issues/48
    @polyvar x y z
    σ = [184.04, 164.88, 164.88, 184.04, 164.88, 184.04]
    X = [x^2, x*y, x*z, y^2, y*z, z^2, x, y, z, 1]
    μ = measure([σ .+ 44.21^2; 44.21 * ones(3); 1],
                X)
    function optionspricing(K, cone)
        m = SOSModel(factory)
        @variable m p Poly(X)
        @constraint m p in cone
        @constraint m p - (x - K) in cone
        @constraint m p - (y - K) in cone
        @constraint m p - (z - K) in cone
        @objective m Min dot(μ, p)
        JuMP.optimize!(m)
        @test JuMP.primal_status(m) == MOI.FeasiblePoint
        JuMP.objective_value(m)
    end

    K = [30, 35, 40, 45, 50]
    dsos_codsos_exp   = [132.63, 132.63, 132.63, 132.63, 132.63]
    sdsos_cosdsos_exp = [ 21.51,  17.17,  13.20,   9.85,   7.30]
    sos_cosos_exp = sdsos_cosdsos_exp
    for i in eachindex(K)
        @test optionspricing(K[i], CoDSOSCone())  ≈ dsos_codsos_exp[i]   atol=1e-2
        @test optionspricing(K[i], CoSDSOSCone()) ≈ sdsos_cosdsos_exp[i] atol=1e-2
        @test optionspricing(K[i], CoSOSCone())   ≈ sos_cosos_exp[i]     atol=1e-2
    end
end


function createSampleLTISystem(; uses::Bool = false, usek::Bool = false, usee::Bool = false, usef::Bool =false )
    A = [1.0 1.0;
         0.0 1.0]
    B = [0.0;
         1.0]
    C = [1.0 0.0;
         0.0 1.0]
    D = [0.0;
         0.0]
    Ts = 0.1
    sys = StateSpace( A, B, C, D, Ts )

    Q = [1.0 1.0;
         1.0 1.0]
    R = [1.0]

    E = Array{Float64}(undef, 0, 2)
    F = Array{Float64}(undef, 0, 1)
    g = Array{Float64}(undef, 0)
    K = Array{Float64}(undef, 0, 0)

    if( uses )
      S = [1.0;
           0.0]
    else
      S = [ 0.0;
            0.0]
    end

    if( usek )
        # Define a simple controller that has both poles at 0.5
        K = [ 0.25 1.0 ]
    else
        K = [ 0.0 0.0 ]
    end

    if( usee )
        # Define a set of bound constraints between -1 <= u <= 1
        E = [ 0.0 0.0;
              0.0 0.0 ]
        F = [ 1.0;
             -1.0 ]
        g = [ g;
              1.0;
              1.0]
    end

    if( usef )
        # Define a set of bounds and an affine constraint
        E = [ E;
              1.0  0.0;
             -1.0  0.0;
              0.0  1.0;
              0.0 -1.0;
              1.0  1.0 ]
        F = [ F;
              0.0;
              0.0;
              0.0;
              0.0;
              0.0 ]
        g = [ g;
              1.0;
              1.0;
              1.0;
              1.0;
              4.0 ]
    end

    if( usef && usee )
        # Create a coupling constraint between the states and inputs
        F = [ F;
              1.0 ]
        E = [ E;
              1.0 0.0 ]
        g = [ g;
              1.0 ]
    end

    return ConstrainedTimeInvariantLQR( sys, 10, Q, R, :Q, S=S, K=K, E=E, F=F, g=g )

end


"""
    plsrosa(X, Y, weights = ones(size(X, 1)); nlv)
Partial Least Squares Regression (PLSR) with the ROSA algorithm (Liland et al. 2016).
* `X` : X-data.
* `Y` : Y-data.
* `weights` : Weights of the observations.
* `nlv` : Nb. latent variables (LVs) to consider.

The function has the following differences with the original
algorithm of Liland et al. (2016):
* Scores T are not normed to 1.
* Multivariate Y is allowed. In such a case, the entered
    Y-columns should have the same scale (for finding the winning blocks,
    the squared residuals are summed over the columns)

Vector `weights` (row-weighting) is internally normalized to sum to 1.
See the help of `plskern` for details.

`X` and `Y` are internally centered.

## References

Liland, K.H., Næs, T., Indahl, U.G., 2016. ROSA—a fast extension of partial least
squares regression for multiblock data analysis. Journal of Chemometrics 30,
651–662. https://doi.org/10.1002/cem.2824

"""
function plsrosa(X, Y, weights = ones(size(X, 1)); nlv)
    plsrosa!(copy(X), copy(Y), weights; nlv = nlv)
end

function plsrosa!(X, Y, weights = ones(size(X, 1)); nlv)
    X = ensure_mat(X)
    Y = ensure_mat(Y)
    n = size(X, 1)
    p = size(X, 2)
    q = size(Y, 2)
    nlv = min(nlv, n, p)
    weights = mweights(weights)
    D = Diagonal(weights)
    xmeans = colmeans(X, weights)
    ymeans = colmeans(Y, weights)
    center!(X, xmeans)
    center!(Y, ymeans)
    # Pre-allocation
    XtY = similar(X, p, q)
    T = similar(X, n, nlv)
    P = similar(X, p, nlv)
    W = copy(P)
    C = similar(X, q, nlv)
    TT = similar(X, nlv)
    t = similar(X, n)
    dt = similar(X, n)
    zp = similar(X, p)
    w = similar(X, p)
    c = similar(X, q)
    # End
    @inbounds for a = 1:nlv
        XtY .= X' * (D * Y)
        if q == 1
            w .= vec(XtY)
            w ./= sqrt(dot(w, w))
        else
            w .= svd!(XtY).U[:, 1]
        end
        mul!(t, X, w)
        if a > 1
            z = vcol(T, 1:(a - 1))
            t .= t .- z * inv(z' * D * z) * z' * (D * t)
            z = vcol(W, 1:(a - 1))
            w = w .- z * (z' * w)
            w ./= sqrt(dot(w, w))
        end
        dt .= weights .* t
        tt = dot(t, dt)
        mul!(c, Y', dt)
        c ./= tt
        mul!(zp, X', dt)
        zp ./= tt
        Y .-= t * c'
        P[:, a] .= zp
        T[:, a] .= t
        W[:, a] .= w
        C[:, a] .= c
        TT[a] = tt
     end
     R = W * inv(P' * W)
     Plsr(T, P, R, W, C, TT, xmeans, ymeans, weights, nothing)
end


module SCF

#Julia Libraries/Modules
using Printf
using LinearAlgebra

#LearnHatreeFock.jl Modules
using TypesBasis,TypesParticles
using CoulombExchange

export runscf

@doc raw"""
function scf()

description: Self-consistent field soultion approach.

The Hatree-Fock operator for non-interacting electrons in an orthonormal basis set is give as:

$H_o C = \epsilon S C$ where $C,epsilon$ are the eigenvalues,eigenvectors and $S$ is the overlap matrix.

The first step is to find the eigenvalues and vectors of the overlap matrix. This will be used to transform from
atomic centered basis-set to a orthonormal molecular basis-set, for example, $H_{MO}=
"""
function runscf(numelec::Int,overlap::Array{Float64,2},
             ho::Array{Float64,2},elecelecrepul::Array{Float64,4};
             maxcycle=30::Int,tolerance=1.0e-6::Float64)

    convergflag = false;

    #Orthogonal overlap matrix, i.e., molecular orbitals
    oevals,oevecs = eigen(overlap);
    oevalsmat = Diagonal(oevals);

    #Try to invert eigenvalues matrix assuming non-singular, if singular
    # promote datatype ComplexF64
    invoevalsmat = returnevalsinvhalf(oevalsmat);
    orthoverlap = oevecs*invoevalsmat*adjoint(oevecs);
    orthoverlapstar = adjoint(orthoverlap);

    #Fock matrix construction
    fock = copy(ho);
    fockprime = orthoverlapstar*fock*orthoverlap;

    #HF Roothaan eq.
    #Find molecular orbital single electron solutions
    evals,evecsprime = eigen(fockprime);

    #Go back to atomic orbitals
    evecs = orthoverlap*evecsprime;

    #Sort lowest eigen values of for atomic orbitals
    sorteigen!(evals,evecs);

    #Build electron density for Hatree potential?
    density = builddensity(evecs,numelec)

    totalenergy = zeros(Float64,maxcycle+1);
    #SCF cycle
    for cycle=2:maxcycle+1

        #Get exchange and fock potential
        exchangepot = getcoulexchange(density,elecelecrepul)
        fock = ho + exchangepot;

        #find solutions
        fockprime = orthoverlapstar*fock*orthoverlap;
        evals,evecsprime = eigen(fockprime);
        evecs = orthoverlap*evecsprime;

        sorteigen!(evals,evecs);

        density = builddensity(evecs,numelec);

        totalenergy[cycle] = getfockenergy(ho,fock,density);

        #Check conditions
        delta = abs(totalenergy[cycle]-totalenergy[cycle-1]);
        printscf(cycle-1,delta,totalenergy[cycle])
        if delta < tolerance
            return totalenergy[cycle]
        end

    end #cycle=2:maxcycle+1

    return totalenergy[end];

end #runscf

@doc raw"""
function returnevalsinvhalf(evals)

Try to invert eigenvalues matrix assuming non-singular, if singular promote
datatype ComplexF64

"""
function returnevalsinvhalf(evals)
    try
        invevalsmat = evals^-0.5e0;
    catch
        invevalsmat = (convert(Array{ComplexF64},evals))^(-0.5e0);
    end
end #returnevalsinvhalf

@doc raw"""
function builddensity(evecs::Array{Number,2},n::)

Restricted HF density

"""
function builddensity(evecs::Matrix,numelec::Int)
    #Restricted HF density
    nhalf = Int(numelec/2);
    esize = size(evecs)[1];

    if typeof(evecs) == Array{Complex,2}
        density = zeros(Complex,esize,esize)
    else
        density = zeros(esize,esize)
    end
    for n=1:esize
        for m=1:esize
            for i=1:nhalf
                density[n,m] += evecs[n,i]*evecs[m,i];
            end #i=1:nhalf
        end
    end #n=1:esize
    return density

end #builddensity

@doc raw"""
function sortevals!(evals::Array,numelec::Int)

description: sort the eignevalues and vectors.

NOTES: I would like to rework this so that its more
    succinct.
"""
function sorteigen!(evals::Vector{T},evecs::Matrix{T}) where {T<:Real}
    ncol = size(evecs)[2];

    #Make a shallow copy and force local scope
    #CHECK: I'm not sure what the most Julianic way is to do this.
    local sortedevals = copy(evals);
    local sortedevecs = copy(evecs);

    #Get sorted indexes for eigenvals
    sortedindex = sortperm(evals);
    evals[:] = sortedevals[sortedindex];

    #Get sorted eigenvectors
    for i=1:ncol
        sortedevecs[:,i] = evecs[:,sortedindex[i]];
    end

    evecs[:,:] = sortedevecs[:,:];

end #sortevals

function sorteigen!(evals::Vector{T},evecs::Matrix{T}) where {T<:Complex}
    ncol = size(evecs)[2];

    #Make a shallow copy and force local scope
    #CHECK: I'm not sure what the most Julianic way is to do this.
    local sortedevals = copy(evals);
    local sortedevecs = copy(evecs);

    magevals = adjoint(evals)*evals;
    println(magevals);
    #Get sorted indexes for eigenvals
    sortedindex = sortperm(real(magevals));
    evals[:] = sortedevals[sortedindex];

    #Get sorted eigenvectors
    for i=1:ncol
        sortedevecs[:,i] = evecs[:,sortedindex[i]];
    end

    evecs[:,:] = sortedevecs[:,:];

end #sortevals

@doc raw"""
sorteigen()

TODO
"""
function sorteigen(evals::Vector{T},evecs::Matrix{T}) where {T<:Real}
    p = sortperm(evals);
    return evals[p], evecs[p,:]
end

@doc raw"""
function getfockenergy()

"""
function getfockenergy(ho::Array,fock::Array,density::Array)
    numbasis = size(density)[1];
    energy = 0.00e0;
    for n=1:numbasis
        for m=1:numbasis
            energy += density[n,m] * (ho[n,m]+fock[n,m]);
        end
    end#n=1:numbasis
    return energy
end #getfockenergy

@doc raw"""
function printscf()

"""
function printscf(iteration::Int,delta::Float64,energy::Float64)
    if iteration < 2
        @printf("                   HF SCF                         \n");
        @printf("--------------------------------------------------\n");
        @printf("Iteration         ΔE [Ha]          HF-Energy [Ha] \n");
        @printf("--------------------------------------------------\n");
        @printf("%i                %0.6f           %0.6e   \n",
            iteration,delta,energy);
    else
        @printf("%i                %0.6f           %0.6e   \n",
            iteration,delta,energy);
    end
end #printscf

end #SCF