abstract type AbstractFeatureScaling end

#####
##### Zero-mean unit-variance feature scaling
#####

struct ZeroMeanUnitVarianceScaling{T} <: AbstractFeatureScaling
    μ :: T
    σ :: T
end

"""
    ZeroMeanUnitVarianceScaling(data)

Returns a feature scaler for `data` with zero mean and unit variance.
"""
function ZeroMeanUnitVarianceScaling(data)
    μ, σ = mean(data), std(data)
    return ZeroMeanUnitVarianceScaling(μ, σ)
end

scale(x, s::ZeroMeanUnitVarianceScaling) = (x .- s.μ) / s.σ
unscale(y, s::ZeroMeanUnitVarianceScaling) = s.σ * y .+ s.μ

#####
##### Min-max feature scaling
#####

struct MinMaxScaling{T} <: AbstractFeatureScaling
           a :: T
           b :: T
    data_min :: T
    data_max :: T
end

"""
    MinMaxScaling(data; a=0, b=1)

Returns a feature scaler for `data` with minimum `a` and `maximum `b`.
"""
function MinMaxScaling(data; a=0, b=1)
    data_min, data_max = extrema(data)
    return MinMaxScaling{typeof(data_min)}(a, b, data_min, data_max)
end

scale(x, s::MinMaxScaling) = s.a + (x - s.data_min) * (s.b - s.a) / (s.data_max - s.data_min)
unscale(y, s::MinMaxScaling) = s.data_min .+ (y .- s.a) * (s.data_max - s.data_min) / (s.b - s.a)

#####
##### Convenience functions
#####

(s::AbstractFeatureScaling)(x) = scale(x, s)
Base.inv(s::AbstractFeatureScaling) = y -> unscale(y, s)


function generate_random_unit_quaternion()

	q0::Float64 = randn()
	q1::Float64 = randn()
	q2::Float64 = randn()
	q3::Float64 = randn()

	q_norm::Float64 = sqrt( q0^2 + q1^2 + q2^2 + q3^2 )

	q0 /= q_norm
	q1 /= q_norm
	q2 /= q_norm
	q3 /= q_norm

	return (q0, q1, q2, q3)
end


mutable struct KSTestResult <: TestResult
    n_obs::Int
    statistic::Float64
    pval::Float64
end

function show(io::IO, kstr::KSTestResult)
    println(io, "\n        One Sample Kolmogorov-Smirnov test")
    println(io, "\n Result:\n", coeftable(kstr))
end

function coeftable(kstr::KSTestResult)
    CoefTable(
        [kstr.n_obs, kstr.statistic, kstr.pval],
        ["N", "D", "P-value"],
        [""],
        3, 2
    )
end

function pkstwo(x::Float64, tol = 1e-06)
    k_max = floor(Int, sqrt(2.0 - log(tol)))
    if x < 1
        z = - (π^2 / 8) / (x^2)
        w = log(x)
        s = 0.0
        k = 1
        while k < k_max
            s += exp(k^2 * z - w)
            k += 2
        end
        return s * sqrt(2 * π)
    else
        z = -2 * x²
        s = -1.0
        old = 0.0
        new = 1.0
        while abs(old - new) > tol
            old = new
            new += 2 * s * exp(z * k^2)
            s *= -1.0
            k += 1
        end
        return new
    end
end

function ccdfNonExact(d::KSDist, x::Float64)
    return 1 - pkstwo(sqrt(d.n) * x)
end

"""
    ks_test(x, distr)

One sample Kolmogorov-Smirnov test

The argument `x` can be a numeric `Vector`.

The argument `distr` can be a `Distributions.UnivariateDistribution`.
"""
function ks_test(
    x::NumVector,
    distr::UnivariateDistribution;
    exact::Union{Bool, Nothing}=nothing
)::KSTestResult
    n = length(x)
    ties = false
    if length(unique(x)) < n
        @warn "ties should not be present for the Kolmogorov-Smirnov test"
        ties = true
    end

    if isnothing(exact)
        exact = (n < 100) & !ties
    end

    x_density_sorted = D.cdf.(distr, sort(x)) - collect(0:(n-1))/n
    statistic = max(
        Base.maximum(x_density_sorted),
        Base.maximum(1/n .- x_density_sorted)
    )

    if exact
        pval = D.ccdf(KSDist(n), statistic)
    else
        pval = ccdfNonExact(KSDist(n), statistic)
    end

    res = KSTestResult(n, statistic, pval)
    return res
end


# this script does a Monte Carlo that shows efficiency of GLS compared to OLS
# when there are AR1 errors
using Econometrics, Plots

# the function to be Monte Carlo'ed
function wrapper(args)
	n, ρ = args    # sample size and AR parameter
	x = randn(n)     # an exogenous regressor
	# make the AR1 errors
    ϵ = zeros(n)
    ϵ[1] = randn()/sqrt(1-ρ^2)
	for t = 2:n
        ϵ[t] = ρ*ϵ[t-1] + randn()
    end
	# the dep var
	y = 1. .+ x .+ ϵ
	# OLS
	x = [ones(n) x]
	β_ols = x \ y
	# estimate ρ
	e = y - x*β_ols
    # estimating ρ using correlation, to keep in stationary region
    ρhat = cor(e[1:end-1], e[2:end])
	ystar = y - ρhat.*lag(y,1)
	xstar = x - ρhat*lag(x,1)
    ystar[1] = sqrt(1-ρhat^2) .*y[1]
    xstar[1,:] = sqrt(1-ρhat^2) .* x[1,:]
	β_gls = xstar\ystar
	β_ols, β_gls
end

# do the Monte Carlo
n = 30
ρ = 0.9
args = (n, ρ)
reps = 1000
β_ols = zeros(reps,2)
β_gls = zeros(reps,2)
for i = 1:reps
    β_ols[i,:], β_gls[i,:] = wrapper(args)
end

# analyze results
p1 = histogram(β_ols[:,2],bins=30, legend=false, xlabel="OLS")
p2 = histogram(β_gls[:,2],bins=30, legend=false, xlabel="GLS")
plot(p1,p2)
#savefig("AR1errors_OLSvsGLS.png")
gui()


function juliaset(z, z0, nmax::Int64)
    for n = 1:nmax
        if abs(z) > 2 (return n-1) end
        z = z^2 + z0
    end
    return nmax
end


@doc raw"""
    Triangle3

An object `t` of the type [`Triangle3`](@ref) is a triangle in the
three-dimensional Euclidean space ``Ε^2``.

As the triangle is not a full-dimensional object, there is only a test whether
a point lies on the triangle or not.
"""
Triangle3

"""
    Triangle3(p::Point3, q::Point3, r::Point3)

Introduces a triangle `t` with vertices `p`, `q` and `r`.
"""
Triangle3(p::Point3, q::Point3, r::Point3)

"""
    ==(t₁::Triangle3, t₂::Triangle3)

Test for equality: two triangles `t₁` and `t₂` are equal, iff there exists a
cyclic permutation of the vertices of `t₂`, such that  they are equal to the
vertices of `t₁`.
"""
==(t₁::Triangle3, t₂::Triangle3)

"""
    vertex(t::Triangle3, i::Integer)

Returns the `i`ᵗʰ vertex modulo 3 of `t`.
"""
vertex(t::Triangle3, i::Integer)

"""
    supporting_plane(t::Triangle3)

Returns the supporting plane of `t`, with same orientation.
"""
supporting_plane(t::Triangle3)

"""
    is_degenerate(t::Triangle3)

Triangle `t` is degenerate, if the vertices are collinear.
"""
is_degenerate(t::Triangle3)

"""
    squared_area(t::Triangle3)

Returns a square of the area of `t`.
"""
squared_area(t::Triangle3)

"""
    bbox(t::Triangle3)

Returns a bounding box containing `t`.
"""
bbox(t::Triangle3)

"""
    transform(t::Triangle3, at::AffTransformation3)

Returns the triangle obtained by applying `at` on the three vertices of `t`.
"""
transform(t::Triangle3, at::AffTransformation3)


export CompleteHypergraph

import Combinatorics.combinations

"""
`CompleteHypergraph(n,k)` creates a complete hypergraph with vertex set
`{1,2,...,n}`. The edges are all `k`-element subsets of the vertices.
"""
function CompleteHypergraph(n::Int, k::Int)::SimpleHypergraph{Int}
    @assert n >= 0 && k >= 0 "both arguments to CompleteHypergraph must be nonnegative"

    H = IntHypergraph(n)

    if k > n
        return H
    end

    for e in combinations(1:n, k)
        add!(H, e)
    end

    return H
end


function autocovariance(z::AbstractVector{T}, k::Integer) where {T}
    N = length(z)
    m = mean(z)
    γ = zero(T)
    @inbounds @simd for i in 1:N - k
        γ += (z[i + k] - m) * (z[i] - m)
    end
    return γ / N
end

function autocorrelation(z::AbstractVector, k::Integer)
    return autocovariance(z, k) / autocovariance(z, 0)
end

function autocovariance_matrix(z::AbstractVector, k::Integer)
    N = length(z)
    T = typeof(zero(eltype(z)) / 1)
    γ = Vector{T}(undef, k)
    for i in 1:k
        γ[i] = autocovariance(z, i - 1)
    end
    Γ = Matrix{T}(undef, k, k)
    for j in 1:k
        for i in 1:k
            Γ[i,j] = γ[abs(i - j) + 1]
        end
    end
    return Γ
end

function autocorrelation_matrix(z::AbstractVector, k::Integer)
    return autocovariance_matrix(z, k) ./ autocovariance(z, 0)
end

function partial_autocorrelation(z::AbstractVector, k::Integer)
    Ρ = autocorrelation_matrix(z, k)
    Ρstar = copy(Ρ)
    Ρstar[1:k - 1,k] = Ρstar[2:k,1]
    Ρstar[k,k] = autocorrelation(z, k)
    return det(Ρstar) / det(cholesky(Symmetric(Ρ)))
end


module NonDimensional

using LinearAlgebra: norm
using PrettyTables

export dynamic_pressure, force_coefficient, moment_coefficient, moment_coefficients, rate_coefficient, pressure_coefficient, aerodynamic_coefficients, print_coefficients, reynolds_number, print_derivatives

"""
    dynamic_pressure(ρ, V)

Compute the dynamic pressure given density ``\\rho`` and speed ``V``.
"""
dynamic_pressure(ρ, V) = 1/2 * ρ * V^2

"""
    force_coefficient(force, q, S)

Compute the non-dimensional force coefficient given force, dynamic pressure ``q``, and area ``S``.
"""
force_coefficient(force, q, S) = force / (q * S)

"""
    moment_coefficient(moment, q, S, L)

Compute the non-dimensional moment coefficient given force, dynamic pressure ``q``, area ``S``, and reference length ``L``.
"""
moment_coefficient(moment, q, S, L) = force_coefficient(moment, q, S) / L

"""
    rate_coefficient(Ω, V, L)

Compute the non-dimensional angular velocity coefficient given angular speed ``\\Omega``, reference speed ``V``, and reference length ``L``.
"""
rate_coefficient(Ω, V, L) = Ω * L / 2V

"""
    pressure_coefficient(force, ρ, V, S)

Compute the pressure coefficient given force, reference density ``\\rho``, speed ``V`` and area ``S``.
"""
pressure_coefficient(force, ρ, V, S) = force_coefficient(force, dynamic_pressure(ρ, V), S)

pressure_coefficient(mag, vels) = 1 - (norm(vels) / mag)^2

moment_coefficient(moment, q, S, b, c) = moment_coefficient.(moment, q, S, [b, c, b])

rate_coefficient(Ω, V, b, c) = rate_coefficient.(Ω, V, [b, c, b])

"""
    aerodynamic_coefficients(force, moment, Ω, V, S, b, c, ρ)

Compute the relevant aerodynamic coefficients given a net force, moment and angular rates ``\\Omega`` with reference speed ``V``, area ``S``, span ``b``, chord ``c``, and density ``\\rho``.
"""
function aerodynamic_coefficients(force, moment, Ω, V, S, b, c, ρ)
    q = dynamic_pressure(ρ, V)

    [ force_coefficient(force, q, S) 		 ;
      moment_coefficient(moment, q, S, b, c) ;
      rate_coefficient(Ω, V, b, c) 			 ]
end

function print_coefficients(nf_coeffs, ff_coeffs, name = ""; browser = false)
    coeffs = [ ifelse(length(nf_coeffs) == 11, ["CD", "CDp"], []); [ "CDi", "CY", "CL", "Cl", "Cm", "Cn", "p̄", "q̄", "r̄" ] ]
    data = [ coeffs nf_coeffs [ ff_coeffs; fill("—", 6) ] ]
    head = [ name, "Nearfield", "Farfield" ]
    h1 = Highlighter( (data,i,j) -> (j == 1), foreground = :blue, bold = true)
    if browser
        pretty_table(String, data, head, alignment = [:c, :c, :c], tf = tf_html_minimalist, backend = :html, highlighters = HTMLHighlighter( (data,i,j) -> (j == 1), HTMLDecoration(font_weight = "bold")), formatters = ft_round(8))
    else
        pretty_table(data, head, alignment = [:c, :c, :c], tf = tf_compact, highlighters = h1, vlines = :none, formatters = ft_round(8))
    end
end

function print_derivatives(derivs, name = ""; browser = false)
    coeffs = ["∂CD", "∂CY", "∂CL", "∂Cl", "∂Cm", "∂Cn"]
    nf_vars = [ "$name" "" "Nearfield" "Stability" "Derivatives" "" ; "" "∂α, 1/rad" "∂β, 1/rad" "∂p̄" "∂q̄" "∂r̄" ]
    nf_rows = [ coeffs derivs ]

    if browser
        pretty_table(String, nf_rows, nf_vars, alignment = :c, tf = tf_html_minimalist, backend = :html, highlighters = HTMLHighlighter( (data,i,j) -> (j == 1), HTMLDecoration(color = "blue", font_weight = "bold")), formatters = ft_round(8))
    else
        pretty_table(nf_rows, nf_vars, alignment = :c, tf = tf_compact, header_crayon = Crayon(bold = true), subheader_crayon = Crayon(foreground = :yellow, bold = true), highlighters = Highlighter( (data,i,j) -> (j == 1), foreground = :blue, bold = true), vlines = :none, formatters = ft_round(8))
    end
end

reynolds_number(ρ, V, c, μ) = ρ * V * c / μ
reynolds_number(V, c, ν) = V * c / ν

force(CF, q, S) = CF * q * S
moment(CM, q, S, c) = CM * q * S * c
moment(CM, q, S, b, c) = moment.(CM, q, S, [b, c, b])

end


using Symbolics

function lotka_volterra!(du, u, p, t)
  x, y = u
  α, β, δ, γ = p
  du[1] = dx = α*x - β*x*y
  du[2] = dy = -δ*y + γ*x*y
end

@variables t du[1:2] u[1:2] p[1:4]
lotka_volterra!(du, u, p, t)
D = Differential(t)
f = build_function((@. D(u) ~ du), u, p, t, target=Symbolics.JuliaTarget())

Base.remove_linenums!(f[1])


@doc raw"""
    sumBassUB(p,q,m,c0,alpha,F,T,Pi)

This function implements Udell an Boyd's algorithm to maximize a sum of Bass functions
and returns the maximizer and maximum.
"""
function sumBassUB(p,q,m,c0,alpha,F,T,Pi::Real)
    n = length(p)
    # Generate model for solution
    f = Vector{Function}(undef,n)
    df = Vector{Function}(undef,n)
    z = Vector{Float64}(undef,n)
    for i in 1:n
        f[i] = t -> alpha[i]*Bass(t,p[i],q[i],m[i],c0[i])
        df[i] = t -> alpha[i]*Bass_derivative(t,p[i],q[i],m[i],c0[i])
        z[i] = Bass_inflection(p[i],q[i],m[i],c0[i])
    end

    C = ones(1,n)
    d = [Pi]

    A = zeros(0,n)
    b = zeros(0)

    problem = LinearSP(f, df, z, A, b, C, d)

    pq, bestnodes, lbs, ubs = solve_sp(F, T, problem,TOL=1e-6)

    node = dequeue!(pq)

    maximizer = Vector{Float64}(undef,n)
    maximizer .= node.x
    maximum = sum(alpha[i]*Bass(maximizer[i],p[i],q[i],m[i],c0[i]) for i in 1:n)
    return maximizer, maximum
end

    # Maximizer
    # node.x
    # Lower bound of maximum
    # node.lb
    # Upper bound of maximum
    # node.ub


#=

The sum of the primes below 10 is 2 + 3 + 5 + 7 = 17.

Find the sum of all the primes below two million.

=#
using Distributed
addprocs(3)

max_num = Int(2e6)

@everywhere is_prime(n) = !any(n%i == 0 for i in 2:BigInt(floor(sqrt(n))))
partial_sum(start, stop) = sum(i for i in start:stop if is_prime(i))

res = @distributed (+) for i = 2:max_num
    if is_prime(i)
        i
    else
        0
    end
end

# sieve of erathostenes
# soooo much faster
max_num =  Int(2e6)
prime_candidates = trues(max_num)
for i in 2:max_num
    if prime_candidates[i]
        prime_candidates[i^2:i:max_num] .= false
    end
end

sum(findall(prime_candidates))-1


function sp_value_from_primitive_values(x...)
    #=
    assume the values provided are x1,x2...
    generates the list of values for e1,e2...
    For example, x=1,y=2,z=3
    Then e1=x+y+z = 6,e2=xy+yz+xz = 11, e3=xyz = 6
    =#
    length(x) < 1 && return []
    p = [1,x[1]]
    for i = 2:length(x)
        tmp = [0,x[i]*p...]
        tmp[1:length(p)] += p
        p=tmp
    end
    return p[2:end]
end

function evaluate_monomial(sp_values,m::semi_elementary_monomial)
    if !is_elementary(m)
        throw(ArgumentError("currently only support evaluating elementary monomials"))
    elseif length(sp_values) != dim(m)
        throw(DimensionMismatch("first argument must have same length"))
    end
    return prod([sp_values[i]^m.powers[i] for i=1:length(sp_values)])
end

function evaluate_polynomial(sp_values,m::semi_elementary_polynomial)
    return sum([evaluate_monomial(sp_values,k)*v for (k,v) in m.terms])
end


function factorial(n)
	fact = 1
	for i in 1:n
		fact *= i
	end
	return fact
end

println(@time factorial(49))


module GaussianBeamLens

# Set optimization level 1 for faster compilation, at potential cost of later performance
if isdefined(Base, :Experimental) && isdefined(Base.Experimental, Symbol("@optlevel"))
    @eval Base.Experimental.@optlevel 1
end

#include("GtkGUI.jl")

using FFTW: ifft, fft, ifftshift, fftshift

export GaussianBeamLensPropagate

const L = 1                      # wavelength of light
const n = 1                      # refractive index
const k = 2*pi*n/L               # wave number in material
const Nx = 1024                  # number of x cells
const dx = L/2                   # length of x cells
const Lx = Nx*dx                 # length of grid in x
const dkx = (2*pi)/Lx            # length of kx cell
const Nkx = Nx                   # same number of cells in real and fourier space

const x = dx.*(-(Nx/2):Nx/2-1)
const kx = dkx.*(-(Nkx/2):Nkx/2-1)
const kz = sqrt.(k^2 .- kx.^2)

const dz = 10                    # steps in z
const zmax = 6000                # end of grid in z
const z = 0:dz:zmax              # z grid

const FE = zeros(Complex{Float64}, length(z), length(x))

const cache = Dict{Tuple{Int64,Int64,Int64,Int64},Matrix{Float64}}()

"""
    GaussianBeamLensPropagate(x0,w0,f,Lens_z)
        x0:     Lateral offset of the input beam
        w0:     Width of the Gaussian beam in object space
        f:      Focal length of the lens
        Lens_z: Axial Position of the lens

Computes the intensity of the optical field at every point in x (lateral) and z (axial).
This method caches previously generated images in a dictionary at `GaussianBeamLens.cache`.
See GaussianBeamLensPropagate.GaussianBeamLensPropagateUncached for the actual implementation.
"""
function GaussianBeamLensPropagate(x0,w0,f,Lens_z)
    if haskey(cache,(x0,w0,f,Lens_z))
        return cache[(x0,w0,f,Lens_z)]
    else
        return GaussianBeamLensPropagateUncached(x0, w0, f, Lens_z)
    end
end

"""
    GaussianBeamLensPropagateUncached(x0,w0,f,Lens_z)
        x0:     Lateral offset of the input beam
        w0:     Width of the Gaussian beam in object space
        f:      Focal length of the lens
        Lens_z: Axial Position of the lens
Computes the intensity of the optical field at every point in x (lateral) and z (axial)
"""
function GaussianBeamLensPropagateUncached(x0, w0, f, Lens_z)
    E = exp.(-((x .- x0)./w0).^2)             # initial transverse electric field
    Tf = exp.(1im.*k.*(x.^2)/(2*f))           # transmission function for a thin lens

    # Propagate beam
    #FE = zeros(Complex{Float64},length(z),length(E))
    FE[1,:] = E

    for d = 1:length(z)-1
        # We could save some time if we saved the prior iteration in Fourier space
        FE[d+1,:] = ifft(ifftshift( fftshift(fft(FE[d,:])).*exp.(-1im.*kz.*dz) ))
        if abs(z[d]- Lens_z) < dz/2
            FE[d+1,:] .*= Tf
        end
    end
    I = abs2.(FE)                # intensity of the field at every point in x and z
    cache[(x0,w0,f,Lens_z)] = I
    return I
end

precompile(GaussianBeamLensPropagateUncached, (Int64, Int64, Int64, Int64))
precompile(GaussianBeamLensPropagate, (Int64, Int64, Int64, Int64))

end


"""
    cl6(x::Float64)::Float64

Returns the value of the Clausen function ``\\operatorname{Cl}_6(x)``
for a real angle ``x`` of type `Float64`.  This function is defined as

```math
\\operatorname{Cl}_6(x) = \\Im[\\operatorname{Li}_6(e^{ix})] = \\sum_{k=1}^\\infty \\frac{\\sin(kx)}{k^6}
```

Author: Alexander Voigt

License: MIT

# Example
```jldoctest; setup = :(using ClausenFunctions), output = false
julia> cl6(1.0)
0.855629273183937
```
"""
function cl6(x::Float64)::Float64
    zeta3 = 1.2020569031595943
    pi28 = pi*pi/8.0

    (x, sgn) = range_reduce_even(x)

    if x == zero(x) || x == pi
        return zero(x)
    end

    if x < 0.5*pi
        P = (1.0369277551433699e+00, -2.087195444107175e-01,
             2.0652251045312954e-02, -1.383438138256840e-04)
        Q = (1.0000000000000000e+00, -8.0784096827362542e-03,
             5.8074568862993102e-06, -5.1960620033050114e-10)
        y = x*x
        y2 = y*y
        p = P[1] + y * P[2] + y2 * (P[3] + y * P[4])
        q = Q[1] + y * Q[2] + y2 * (Q[3] + y * Q[4])
        sgn*x*(p/q - 1/120*y2*log(x))
    else
        P = (7.9544504578027050e-01, -1.9255025309738589e-01,
             1.5805208288846591e-02, -5.4175380521534706e-04,
             6.7577493541009068e-06)
        Q = (1.0000000000000000e+00, -7.0798422394109274e-02,
             7.1744189715634762e-04,  3.9098747334347093e-06,
             3.5669441618295266e-08,  2.5315391843409925e-10)
        y = pi - x
        z = y*y - pi28
        z2 = z*z
        z4 = z2*z2
        p = P[1] + z * P[2] + z2 * (P[3] + z * P[4]) + z4 * P[5]
        q = Q[1] + z * Q[2] + z2 * (Q[3] + z * Q[4]) + z4 * (Q[5] + z * Q[6])
        sgn*y*p/q
    end
end


using MultipleViewGeometry, Base.Test
using MultipleViewGeometry.ModuleCostFunction
using MultipleViewGeometry.ModuleTypes
using MultipleViewGeometry.ModuleConstraints
using MultipleViewGeometry.ModuleConstruct
using MultipleViewGeometry.ModuleDraw
using MultipleViewGeometry.ModuleTriangulation
using BenchmarkTools, Compat
using StaticArrays
using  MAT, Plots

# Load MATLAB matrices that represent a pair of images and that contain
# a set of manually matched corresponding points.
file = matopen("./data/teapot.mat")
X = read(file,"pts3D")
# X is a 4 x N matrix, where N is the number of points expressed in homogeneous
# coordinates.
X = X[:,1:10:end]
close(file)

# Fix random seed.
srand(1234)
plotlyjs()

# Transform the point cloud so that it will be oriented upright and
# in front of two cameras which we will define next.
T1 = eye(4)
T1[:,4] = -mean(X,2)
T2 = [1 0 0 0;
      0 1 0 0;
      0 0 1 -2500;
      0 0 0 1]
R = eye(4)
R[1:3,1:3] = rotx(deg2rad(90))
X = T2*R*T1*X;

# Convert the 4 x N matrix into a list of 3D points expressed in homogeneous coordinates.
𝒳 = [Point3DH(X[1,i],X[2,i],X[3,i],1.0) for i = 1:size(X,2)]

# Intrinsic and extrinsic parameters of camera one.
𝐊₁ = eye(3)
𝐑₁ =  eye(3)
𝐭₁ = [0.0, 0.0, 0.0]
𝐜₁ = [0.0, 0.0, 0.0]
𝐄₁ = [𝐑₁ 𝐭₁]

# Intrinsic and extrinsic parameters of camera two.
𝐊₂ = eye(3)
𝐑₂ = eye(3)
𝐭₂ = [450.0,   0.0, 0.0]
𝐜₂ = [0.0, 0.0, 0.0]
𝐄₂ = [𝐑₂ 𝐭₂]

# Camera projection matrices.
𝐏₁ = construct(ProjectionMatrix(),𝐊₁,𝐑₁,𝐭₁)
𝐏₂ = construct(ProjectionMatrix(),𝐊₂,𝐑₂,𝐭₂)

# Set of corresponding points.
ℳ = project(Pinhole(),𝐏₁,𝒳)
ℳʹ = project(Pinhole(),𝐏₂,𝒳)

# Visualise the data points
p1 = Plots.plot(X[1,:],X[2,:],X[3,:],seriestype = :scatter, ms=1,grid = false, box = :none, legend = false)
draw!(WorldCoordinateSystem3D(), 1, p1)
draw!(Camera3D(), 𝐊₁, 𝐑₁, 𝐭₁, 1, p1)
draw!(Camera3D(), 𝐊₂, 𝐑₂, 𝐭₂, 1, p1)

# Plot the projections of the point cloud in the image pair.
p2 =  Plots.plot();
for n = 1:length(ℳ)
    m = ℳ[n]
    Plots.plot!([m[1]],[m[2]], grid = false, box = :none, legend = false,
                    seriestype = :scatter, ms = 2, markercolor=:Black,
                    aspect_ratio = :equal, yflip = true)
end

p3 =  Plots.plot();
for n = 1:length(ℳʹ)
    mʹ = ℳʹ[n]
    Plots.plot!([mʹ[1]],[mʹ[2]], grid = false, box = :none, legend = false,
                    seriestype = :scatter, ms = 2, markercolor=:Black,
                    aspect_ratio = :equal, yflip = true)
end

# Visualise the 3D point cloud, as well as the projected images.
l = @layout [ a; [b c] ]
p4 = Plots.plot(p1,p2, p3, layout = l)

# Estimate of the fundamental matrix and the true fundamental matrix.
𝐅 = estimate(FundamentalMatrix(), DirectLinearTransform(), (ℳ, ℳʹ))
𝐄 = construct(EssentialMatrix(), 𝐅,  𝐊₁, 𝐊₂)

𝐏₁, 𝐏₂ = construct(ProjectionMatrix(), 𝐄, (ℳ, ℳʹ))

𝒴 = triangulate(DirectLinearTransform(),𝐏₁,𝐏₂,(ℳ,ℳʹ))
Y = reinterpret(Float64,map(SVector{4,Float64},𝒴),(4,length(𝒴)))

# The point cloud can be reconsructed only up to an unknown scale factor.
p5 = Plots.plot(Y[1,:],Y[2,:],Y[3,:],seriestype = :scatter, ms=1,grid = false, box = :none, legend = false)
draw!(WorldCoordinateSystem3D(), 0.002, p5)
draw!(Camera3D(), 𝐊₁, 𝐏₁[1:3,1:3], 𝐏₁[:,4], 0.002, p5)
draw!(Camera3D(), 𝐊₂, 𝐏₂[1:3,1:3], 𝐏₂[:,4], 0.002, p5)

display(p4)
#display(p5)


#Complex numbers
1+2im
(1 + 2im)*(2 - 3im)
(1 + 2im)/(1 - 2im)
(1 + 2im)+(1 - 2im)
(-3 + 2im)-(5 - 1im)
(-1 + 2im)^2.5
3(2 - 5im)^2

(2 + 3im) - 1
(2 + 3im) - 0.5im

1 + 3/4im
# 3/(4im) == -(3/4im)

z = 1 + 2im
real(1 + 2im)
imag(1 + 2im)
conj(z)
abs(z) #sua distancia do 0
abs(z)^2
abs2(z)
angle(z) #rad
sqrt(1im)
sqrt(1 + 2im)
cos(z)
exp(z)
sinh(z)
√(-4+0im)
a=1;b=2;
a + bim
a + b*im
#not recomended, use this instead:
complex(a,b)
1 + Inf*im
1 + NaN*im

#Rational Numbers
2//3
6//9
-4//8
5//-15
-4//-12
numerator(2//3)
denominator(2//3)
2//3 == 6//9
2//3 == 9//27
2//4 + 1//6
float(3//4)
isequal(float(a//b), a/b)

5//0 #inf
-3//0
typeof(ans) #Rational{Int64}
0//0
#trying to construct a NaN,but is invalid

3//5 + 1
2//7 * (1 + 2im)
2//7 * (1.5 + 2im)
1//2 + 2im
0.5 == 1//2
0.5 == 1/2


function calc_width_ngon(a,b)
if length(a) == length(b) && length(a) >= 2
    # SIDES
    v = zeros(length(a)+1);
    v[1] = abs(a[1]+im*b[1]);
    for i = 2:length(a)
        v[i] = abs((a[i]-a[i-1])+im*(b[i]-b[i-1]));
    end
    v[length(a)+1] = abs(a[length(a)]+im*b[length(a)]);
    # HEIGHTS
    h = zeros(length(a)+1);
    # h1
    j = 1;
    while j <= length(a)
        h1 = abs(a[j]*(b[1]-b[j])-b[j]*(a[1]-a[j]))/v[1];
        if h1 > h[1]
            h[1] = h1;
        end
        j = j+1;
    end
    # hi
    for i = 2:length(a)
        h[i] = abs(a[i-1]*b[i]-b[i-1]*a[i])/v[i];
        j = 1;
        while j <= length(a)
            hi = abs((a[i-1]-a[j])*(b[i]-b[j])-(b[i-1]-b[j])*(a[i]-a[j]))/v[i];
            if hi > h[i]
                h[i] = hi;
            end
            j = j+1;
        end
    end
    # hn
    j = 1;
    while j <= length(a)
        hn = abs((a[length(a)]-a[j])*b[j]-(b[length(a)]-b[j])*a[j])/v[length(a)+1];
        if hn > h[length(a)+1]
            h[length(a)+1] = hn;
        end
        j = j+1;
    end
    # WIDTH
    W = minimum(h);
    return W
end
end


using LinearAlgebra
include("../src/ExactDiagonalization.jl")
using .ExactDiagonalization
using Test
#-------------------------------------------------------------------------------------------------------------------------
# XY Model
#-------------------------------------------------------------------------------------------------------------------------
@testset "XY" begin
    L = 4
    println("------------------------------")
    println("XY Model with L = $L")
    println("------------------------------")
    basis = projectedbasis(x->sum(x)==2, L)
    println("Basis Vector:")
    for i=1:length(basis.I)
        change!(basis, i)
        println("$i : |$(basis.dgt...)⟩")
    end
    X = [0 1; 1 0]
    Y = [0 -1; 1 0] * 1im
    mat = div.(real(kron(X, X) + kron(Y, Y)), 2)
    H = trans_inv_operator(mat, 2, basis)
    Hmat = Array(H)
    println("\nMatrix:")
    display(Hmat)
    print("\n")
    HXY = [0  1  0  0  1  0;
           1  0  1  1  0  1;
           0  1  0  0  1  0;
           0  1  0  0  1  0;
           1  0  1  1  0  1;
           0  1  0  0  1  0]
    @test Hmat == HXY
end

#-------------------------------------------------------------------------------------------------------------------------
# PXP Model
#-------------------------------------------------------------------------------------------------------------------------
@testset "PXP" begin
    L = 4
    println("\n------------------------------")
    println("PXP Model with L = $L")
    println("------------------------------")
    pxpf(v::Vector{Int}) = all(v[i]==0 || v[mod(i, length(v))+1]==0 for i=1:length(v))
    basis = projectedbasis(pxpf, L)
    println("Basis Vector:")
    for i=1:length(basis.I)
        change!(basis, i)
        println("$i : |$(basis.dgt...)⟩")
    end
    P = Diagonal([1, 1, 1, 0, 1, 1, 0, 0])
    X = [0 1; 1 0]
    mat = P * kron(I(2), X, I(2)) * P
    H = trans_inv_operator(mat, 3, basis)
    Hmat = Array(H)
    println("\nMatrix:")
    display(Hmat)
    print("\n")
    HPXP = [0  1  1  1  0  1  0;
            1  0  0  0  1  0  0;
            1  0  0  0  0  0  1;
            1  0  0  0  1  0  0;
            0  1  0  1  0  0  0;
            1  0  0  0  0  0  1;
            0  0  1  0  0  1  0]
    @test Hmat == HPXP
end


# Diffusion maps
# --------------
# Diffusion maps,
# Coifman, R. & Lafon, S., Applied and Computational Harmonic Analysis, Elsevier, 2006, 21, 5-30

"""

    DiffMap{T <: Real} <: AbstractDimensionalityReduction

The `DiffMap` type represents diffusion maps model constructed for `T` type data.
"""
struct DiffMap{T <: Real} <: AbstractDimensionalityReduction
    t::Int
    α::Real
    ɛ::Real
    λ::AbstractVector{T}
    K::AbstractMatrix{T}
    proj::Projection{T}
end

## properties
outdim(R::DiffMap) = size(R.proj, 1)
eigvals(R::DiffMap) = R.λ

## custom
"""Returns the kernel matrix of the diffusion maps model `R`"""
kernel(R::DiffMap) = R.K

## show
summary(io::IO, R::DiffMap) = print(io, "Diffusion Maps(outdim = $(outdim(R)), t = $(R.t), α = $(R.α), ɛ = $(R.ɛ))")
function show(io::IO, R::DiffMap)
    summary(io, R)
    io = IOContext(io, :limit=>true)
    println(io)
    println(io, "Kernel: ")
    Base.print_matrix(io, R.K, "[", ",","]")
    println(io)
    println(io, "Embedding:")
    Base.print_matrix(io, transform(R), "[", ",","]")
end

## interface functions
"""
    fit(DiffMap, data; maxoutdim=2, t=1, α=1.0, ɛ=1.0)

Fit a isometric mapping model to `data`.

# Arguments
* `data::Matrix`: a (n_features, n_observations) matrix of observations. Each column of `data` is an observation.
  if `isnothing(kernel)`, `data` is instead the (n_observations, n_observations) precomputed Gram matrix.

# Keyword arguments
* `kernel::Union{Nothing, Function}=(x, y) -> exp(-sum((x .- y) .^ 2) / ɛ)`: the kernel function.
 maps two input vectors (observations) to a scalar (a metric of their similarity).
 by default, a Gaussian kernel. if `isnothing(kernel)`, we assume `data` is instead
 the (n_observations, n_observations) precomputed Gram matrix.
* `ɛ::Real=1.0`: the Gaussian kernel variance (the scale parameter). ignored if custom `kernel` passed.
* `maxoutdim::Int=2`: the dimension of the reduced space.
* `t::Int=1`: the number of transitions
* `α::Real=0.0`: a normalization parameter

# Examples
```julia
X = rand(3, 100)     # toy data matrix, 100 observations

# default kernel
M = fit(DiffMap, X)  # construct diffusion map model
R = transform(M)     # perform dimensionality reduction

# custom kernel
kernel = (x, y) -> x' * y # linear kernel
M = fit(DiffMap, X, kernel=kernel)

# precomputed Gram matrix
kernel = (x, y) -> x' * y # linear kernel
K = StatsBase.pairwise(kernel, eachcol(X), symmetric=true) # custom Gram matrix
M = fit(DiffMap, K, kernel=nothing)
```
"""
function fit(::Type{DiffMap}, X::AbstractMatrix{T};
             ɛ::Real=1.0,
             kernel::Union{Nothing, Function}=(x, y) -> exp(-sum((x .- y) .^ 2) / convert(T, ɛ)),
             maxoutdim::Int=2,
             t::Int=1,
             α::Real=0.0
            ) where {T<:Real}
    if isa(kernel, Function)
        # compute Gram matrix
        L = pairwise(kernel, eachcol(X), symmetric=true)
    else
        # X is the pre-computed Gram matrix
        L = deepcopy(X) # deep copy needed b/c of procedure for α > 0
        @assert issymmetric(L)
    end

    # Calculate Laplacian & normalize it
    if α > 0
        D = transpose(sum(L, dims=1))
        L ./= (D * transpose(D)) .^ convert(T, α)
    end
    D = Diagonal(vec(sum(L, dims=1)))
    M = inv(D) * L # normalize rows to interpret as transition probabilities

    # D = Diagonal(vec(sum(L, dims=1)))
    # D⁻ᵅ = inv(D^α)
    # Lᵅ = D⁻ᵅ*L*D⁻ᵅ
    # Dᵅ = Diagonal(vec(sum(Lᵅ, dims=1)))
    # M = inv(Dᵅ)*Lᵅ

    # Eigendecomposition & reduction
    F = eigen(M, permute=false, scale=false)
    # for symmetric matrix, eigenvalues should be real but owing to numerical imprecision, could have nonzero-imaginary parts.
    λ = real.(F.values)
    idx = sortperm(λ, rev=true)[2:maxoutdim+1]
    λ = λ[idx]
    V = real.(F.vectors[:, idx])
    Y = (λ .^ t) .* V'

    return DiffMap{T}(t, α, ɛ, λ, L, Y)
end

"""
    transform(R::DiffMap)

Transforms the data fitted to the diffusion map model `R` into a reduced space representation.
"""
transform(R::DiffMap) = R.proj


### A Pluto.jl notebook ###
# v0.12.18

using Markdown
using InteractiveUtils

# ╔═╡ 4aebe8f8-502d-11eb-1925-694f35421317
md"""
# Recursion

Recursive function calls itself with a smaller instance of the same problem and ends with a base case. Three required ingredients:
1. Calls itself
2. with a smaller input parameter
3. end with a base case

Be careful with Infinite Recursion, which can occur when
- parameter don't decrease/increase towards the base case criteria
- base case cannot be reached

"""

# ╔═╡ 8c30ca4a-502d-11eb-2c0b-398047ab685f
function addrecursive(x)
	x >= 1 && error("x < 1")  # Defense against infinite recursion
    if x == 1  # if x is less than 1 this becomes an infinite recursion
        return 1
    else
        return x + addrecursive(x - 1)
    end
end

# ╔═╡ 0d1581d2-502e-11eb-2b2b-e3cc836fc2b0
# The Call Stack of Recursion

# ╔═╡ 130ca5e8-502e-11eb-3f82-5ff3930f7421
# Recurrence Relations

# ╔═╡ 1b0fe3ae-502e-11eb-2d34-c5a885ec3791
md"""
Applications
- Factorial
- Reverse a string
- Palindrome Checking
- List permutation
- String Permutations
- keypad Combinations
- deep reverse ?
- Tower of Hanoi
- Staircase ?

"""

# ╔═╡ Cell order:
# ╟─4aebe8f8-502d-11eb-1925-694f35421317
# ╠═8c30ca4a-502d-11eb-2c0b-398047ab685f
# ╠═0d1581d2-502e-11eb-2b2b-e3cc836fc2b0
# ╠═130ca5e8-502e-11eb-3f82-5ff3930f7421
# ╠═1b0fe3ae-502e-11eb-2d34-c5a885ec3791


### A Pluto.jl notebook ###
# v0.15.1

using Markdown
using InteractiveUtils

# This Pluto notebook uses @bind for interactivity. When running this notebook outside of Pluto, the following 'mock version' of @bind gives bound variables a default value (instead of an error).
macro bind(def, element)
    quote
        local el = $(esc(element))
        global $(esc(def)) = Core.applicable(Base.get, el) ? Base.get(el) : missing
        el
    end
end

# ╔═╡ 750ded6c-f15d-11eb-02ef-5156d6bf3200
using BenchmarkTools, CUDA, Images, PlutoUI

# ╔═╡ 8548f5a7-de5b-44a1-a0f2-55314a6152b3
md"""
# Parallelizing the Julia Set

##### This notebook combines the Mandelbrot set example from the Julia Computing parallel programming class with the [Julia set example by Simon Danisch](https://nextjournal.com/sdanisch/julia-gpu-programming).
##### Also see [this Discourse thread](https://discourse.julialang.org/t/julia-sets-in-julia-using-interact-jl-id-like-to-get-it-faster/35533) on the Julia set.
##### Interesting values for the parameter `c` were chosen from the [Julia set wikipedia page](https://en.wikipedia.org/wiki/Julia_set).
"""

# ╔═╡ 062c93ab-df7e-485e-a4bc-f7e39c013c3f
md"#### A macro to view stdout"

# ╔═╡ 6d12faf1-d8bc-400a-b934-e2c36a0fcc5c
macro terminal(stuff)
	quote
		with_terminal() do
			$(esc(stuff))
		end
	end
end

# ╔═╡ 9da77179-daff-4eb7-b95b-1c5b9da5e524
md"#### The escape/iteration function"

# ╔═╡ 81fa04e2-147d-496d-bea4-cfe14a67868a
function escape(z0, c, maxiter)
    z = z0
    for i ∈ 1:maxiter
        abs2(z) > 4 && return (i-1) % UInt8
         z = z*z + c
    end
    return maxiter % UInt8
end

# ╔═╡ 62c83a9e-d5ba-4479-b3b4-4584bbf2b70f
@terminal @code_warntype escape(1.0+1im, 0+0.5im, 5)

# ╔═╡ c05f8497-0ab5-4dae-8dac-3df2bfd36355
md"#### Loop through each pixel of the image and count the number of escape iterations."

# ╔═╡ 1001c583-b7ee-47e0-87ac-2d4be6e8b23b
function juliaset(;width=800, height=600, maxiter=255, c = ComplexF64(-0.512511498387847167, 0.521295573094847167))
	real = range(-1.5, 1.5, length=width)
	imag = range(-1.0, 1.0, length=height)*im
    result =  zeros(Int, height, width)
	for x = 1:width
		for y = 1:height
			z₀ = real[x] + imag[y]
			result[y, x] = escape(z₀, c, maxiter)
		end
	end
    return result
end

# ╔═╡ 55bfc407-6b22-413e-aff5-d17bfd343f87
@terminal @code_warntype juliaset()

# ╔═╡ 58eda8b1-5205-4527-8e0c-69f6a3cb7607
md"#### Plot a nice Julia set image"

# ╔═╡ 4aa12ee7-dce5-4e30-a1b8-569296760cb9
js = juliaset();

# ╔═╡ 6cd3120e-8164-4177-98cf-6b96953f9567
Gray.((js)/maximum(js))

# ╔═╡ 0ee22f75-e720-4fd2-b081-80eed5e57928
md"""
### Choose your own `c` value
##### Use arrows to shift by 0.05, or type a value in the field
"""

# ╔═╡ 7db79200-d9ba-4199-a2ea-4d0862b36015
md"""`real(c)` = $(@bind cr NumberField(-2:0.05:2, default=-0.4))
`imag(c)` = $(@bind ci NumberField(-2:0.05:2, default=0.6))
"""

# ╔═╡ a0cdf4a6-06ce-4379-b683-5c4d67192b21
md"#### Run the single-threaded benchmark on the default image"

# ╔═╡ 49f344df-bc93-4cee-9179-a75214f0f905
jss = @benchmark juliaset()

# ╔═╡ 6a9382e4-7e8f-44e8-9ca2-a50d19eb35c1
md"#### Parallelize with threads and benchmark"

# ╔═╡ add716ef-d45b-4aab-8e00-666adaf38c77
function juliaset_threads(;width=800, height=600, maxiter=256, c = ComplexF64(-0.512511498387847167, 0.521295573094847167))
	real = range(-1.5, 1.5, length=width)
	imag = range(-1, 1, length=height)*im
    result =  zeros(Int, height, width)
	Threads.@threads for x = 1:width
		for y = 1:height
			z₀ = real[x] + imag[y]
			result[y, x] = escape(z₀, c, maxiter)
		end
	end
    return result
end

# ╔═╡ 2ee833fe-31e2-4784-be9a-1a4331ee10f5
jst = @benchmark juliaset_threads()

# ╔═╡ 6ffd8442-f7fc-44bc-b3e4-a7d6ddb0fb1f
md"#### Parallelize with tasks"

# ╔═╡ b39c8c16-9162-46e7-9fbf-4400eb69dd17
function juliaset_spawn(;width=800, height=600, maxiter=256, c = ComplexF64(-0.512511498387847167, 0.521295573094847167))
	real = range(-1.5, 1.5, length=width)
	imag = range(-1, 1, length=height)*im
    result =  zeros(Int, height, width)
	@sync for x = 1:width
		Threads.@spawn for y = 1:height
			z₀ = real[x] + imag[y]
			result[y, x] = escape(z₀,c, maxiter)
		end
	end
    return result
end

# ╔═╡ c1f94c04-6796-4945-9adf-b97a4ef06d56
js_image = juliaset_spawn(c=cr+ci*im);

# ╔═╡ 0d5b251f-e0bb-46fd-a3ad-6ea3618eed5b
Gray.(js_image/maximum(js_image))

# ╔═╡ d7ffd22f-d634-46d2-92ed-2d8bbacb0ad5
jsp = @benchmark juliaset_spawn()

# ╔═╡ f1620901-49ca-4bb5-90b7-c258c25a3e7a
has_cuda() ||	md"#### WARNING: CUDA not available!"

# ╔═╡ 6ddaf0d0-ee62-4f36-94c6-05f3bfb18e49
function run!(in, out, c; maxiter=16)
    out .= escape.(in, c, maxiter)
end

# ╔═╡ 6341b004-4426-4e30-9015-b1ee1a75e0da
function juliaset_gpu(;width=800, height=600, maxiter=256, c = ComplexF64(-0.512511498387847167, 0.521295573094847167))
    q =  CuArray([ComplexF64(real, imag) for imag in range(-2.0, 0.5, length=width), real in range(-1.0, 1.0, length=height)])
    result = CuArray(zeros(UInt8, size(q)))
    run!(q, result, maxiter=maxiter)
    return result
end

# ╔═╡ 509fa4b9-c9a6-45da-a7a5-574f5a57b8cf
if has_cuda()
	jsg = @benchmark juliaset_gpu(width=width, height=height)
	jsgt = round(jsg.times[1]/1e6, digits=2)
else
	jsgt = 1.1 # milliseconds, approx time on a K80 (via JuliaHub)
end

# ╔═╡ 3185c612-de1e-4acb-a232-dd5dfd3949d2
md"
##### 1-thread: $(round(jss.times[1]/1e6, digits=2)) ms
##### $(Threads.nthreads())-threads: $(round(jst.times[1]/1e6, digits=2)) ms
##### $(800*600)-tasks: $(round(jsp.times[1]/1e6, digits=2)) ms
##### GPU version: $jsgt ms
"

# ╔═╡ 00000000-0000-0000-0000-000000000001
PLUTO_PROJECT_TOML_CONTENTS = """
[deps]
BenchmarkTools = "6e4b80f9-dd63-53aa-95a3-0cdb28fa8baf"
CUDA = "052768ef-5323-5732-b1bb-66c8b64840ba"
Images = "916415d5-f1e6-5110-898d-aaa5f9f070e0"
PlutoUI = "7f904dfe-b85e-4ff6-b463-dae2292396a8"

[compat]
BenchmarkTools = "~1.1.1"
CUDA = "~3.3.4"
Images = "~0.24.1"
PlutoUI = "~0.7.9"
"""

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

[[AbstractFFTs]]
deps = ["LinearAlgebra"]
git-tree-sha1 = "485ee0867925449198280d4af84bdb46a2a404d0"
uuid = "621f4979-c628-5d54-868e-fcf4e3e8185c"
version = "1.0.1"

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

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

[[ArrayInterface]]
deps = ["IfElse", "LinearAlgebra", "Requires", "SparseArrays", "Static"]
git-tree-sha1 = "655d9e28a75f88eea3fb81a12f62da9bade89fb5"
uuid = "4fba245c-0d91-5ea0-9b3e-6abc04ee57a9"
version = "3.1.19"

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

[[AxisAlgorithms]]
deps = ["LinearAlgebra", "Random", "SparseArrays", "WoodburyMatrices"]
git-tree-sha1 = "a4d07a1c313392a77042855df46c5f534076fab9"
uuid = "13072b0f-2c55-5437-9ae7-d433b7a33950"
version = "1.0.0"

[[AxisArrays]]
deps = ["Dates", "IntervalSets", "IterTools", "RangeArrays"]
git-tree-sha1 = "d127d5e4d86c7680b20c35d40b503c74b9a39b5e"
uuid = "39de3d68-74b9-583c-8d2d-e117c070f3a9"
version = "0.4.4"

[[BFloat16s]]
deps = ["LinearAlgebra", "Test"]
git-tree-sha1 = "4af69e205efc343068dc8722b8dfec1ade89254a"
uuid = "ab4f0b2a-ad5b-11e8-123f-65d77653426b"
version = "0.1.0"

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

[[BenchmarkTools]]
deps = ["JSON", "Logging", "Printf", "Statistics", "UUIDs"]
git-tree-sha1 = "c31ebabde28d102b602bada60ce8922c266d205b"
uuid = "6e4b80f9-dd63-53aa-95a3-0cdb28fa8baf"
version = "1.1.1"

[[CEnum]]
git-tree-sha1 = "215a9aa4a1f23fbd05b92769fdd62559488d70e9"
uuid = "fa961155-64e5-5f13-b03f-caf6b980ea82"
version = "0.4.1"

[[CUDA]]
deps = ["AbstractFFTs", "Adapt", "BFloat16s", "CEnum", "CompilerSupportLibraries_jll", "DataStructures", "ExprTools", "GPUArrays", "GPUCompiler", "LLVM", "LazyArtifacts", "Libdl", "LinearAlgebra", "Logging", "Printf", "Random", "Random123", "RandomNumbers", "Reexport", "Requires", "SparseArrays", "SpecialFunctions", "TimerOutputs"]
git-tree-sha1 = "5e696e37e51b01ae07bd9f700afe6cbd55250bce"
uuid = "052768ef-5323-5732-b1bb-66c8b64840ba"
version = "3.3.4"

[[CatIndices]]
deps = ["CustomUnitRanges", "OffsetArrays"]
git-tree-sha1 = "a0f80a09780eed9b1d106a1bf62041c2efc995bc"
uuid = "aafaddc9-749c-510e-ac4f-586e18779b91"
version = "0.2.2"

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

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

[[ColorVectorSpace]]
deps = ["ColorTypes", "FixedPointNumbers", "LinearAlgebra", "SpecialFunctions", "Statistics", "TensorCore"]
git-tree-sha1 = "42a9b08d3f2f951c9b283ea427d96ed9f1f30343"
uuid = "c3611d14-8923-5661-9e6a-0046d554d3a4"
version = "0.9.5"

[[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 = "344f143fa0ec67e47917848795ab19c6a455f32c"
uuid = "34da2185-b29b-5c13-b0c7-acf172513d20"
version = "3.32.0"

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

[[ComputationalResources]]
git-tree-sha1 = "52cb3ec90e8a8bea0e62e275ba577ad0f74821f7"
uuid = "ed09eef8-17a6-5b46-8889-db040fac31e3"
version = "0.3.2"

[[CoordinateTransformations]]
deps = ["LinearAlgebra", "StaticArrays"]
git-tree-sha1 = "6d1c23e740a586955645500bbec662476204a52c"
uuid = "150eb455-5306-5404-9cee-2592286d6298"
version = "0.6.1"

[[CustomUnitRanges]]
git-tree-sha1 = "537c988076d001469093945f3bd0b300b8d3a7f3"
uuid = "dc8bdbbb-1ca9-579f-8c36-e416f6a65cce"
version = "1.0.1"

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

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

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

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

[[Distances]]
deps = ["LinearAlgebra", "Statistics", "StatsAPI"]
git-tree-sha1 = "abe4ad222b26af3337262b8afb28fab8d215e9f8"
uuid = "b4f34e82-e78d-54a5-968a-f98e89d6e8f7"
version = "0.10.3"

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

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

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

[[EllipsisNotation]]
deps = ["ArrayInterface"]
git-tree-sha1 = "8041575f021cba5a099a456b4163c9a08b566a02"
uuid = "da5c29d0-fa7d-589e-88eb-ea29b0a81949"
version = "1.1.0"

[[ExprTools]]
git-tree-sha1 = "b7e3d17636b348f005f11040025ae8c6f645fe92"
uuid = "e2ba6199-217a-4e67-a87a-7c52f15ade04"
version = "0.1.6"

[[FFTViews]]
deps = ["CustomUnitRanges", "FFTW"]
git-tree-sha1 = "70a0cfd9b1c86b0209e38fbfe6d8231fd606eeaf"
uuid = "4f61f5a4-77b1-5117-aa51-3ab5ef4ef0cd"
version = "0.3.1"

[[FFTW]]
deps = ["AbstractFFTs", "FFTW_jll", "LinearAlgebra", "MKL_jll", "Preferences", "Reexport"]
git-tree-sha1 = "f985af3b9f4e278b1d24434cbb546d6092fca661"
uuid = "7a1cc6ca-52ef-59f5-83cd-3a7055c09341"
version = "1.4.3"

[[FFTW_jll]]
deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"]
git-tree-sha1 = "3676abafff7e4ff07bbd2c42b3d8201f31653dcc"
uuid = "f5851436-0d7a-5f13-b9de-f02708fd171a"
version = "3.3.9+8"

[[FileIO]]
deps = ["Pkg", "Requires", "UUIDs"]
git-tree-sha1 = "256d8e6188f3f1ebfa1a5d17e072a0efafa8c5bf"
uuid = "5789e2e9-d7fb-5bc7-8068-2c6fae9b9549"
version = "1.10.1"

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

[[GPUArrays]]
deps = ["AbstractFFTs", "Adapt", "LinearAlgebra", "Printf", "Random", "Serialization", "Statistics"]
git-tree-sha1 = "ececbf05f8904c92814bdbd0aafd5540b0bf2e9a"
uuid = "0c68f7d7-f131-5f86-a1c3-88cf8149b2d7"
version = "7.0.1"

[[GPUCompiler]]
deps = ["DataStructures", "ExprTools", "InteractiveUtils", "LLVM", "Libdl", "Logging", "TimerOutputs", "UUIDs"]
git-tree-sha1 = "0da0f52fc521ff23b8291e7fda54c61907609f12"
uuid = "61eb1bfa-7361-4325-ad38-22787b887f55"
version = "0.12.6"

[[Graphics]]
deps = ["Colors", "LinearAlgebra", "NaNMath"]
git-tree-sha1 = "2c1cf4df419938ece72de17f368a021ee162762e"
uuid = "a2bd30eb-e257-5431-a919-1863eab51364"
version = "1.1.0"

[[IdentityRanges]]
deps = ["OffsetArrays"]
git-tree-sha1 = "be8fcd695c4da16a1d6d0cd213cb88090a150e3b"
uuid = "bbac6d45-d8f3-5730-bfe4-7a449cd117ca"
version = "0.3.1"

[[IfElse]]
git-tree-sha1 = "28e837ff3e7a6c3cdb252ce49fb412c8eb3caeef"
uuid = "615f187c-cbe4-4ef1-ba3b-2fcf58d6d173"
version = "0.1.0"

[[ImageAxes]]
deps = ["AxisArrays", "ImageCore", "Reexport", "SimpleTraits"]
git-tree-sha1 = "794ad1d922c432082bc1aaa9fa8ffbd1fe74e621"
uuid = "2803e5a7-5153-5ecf-9a86-9b4c37f5f5ac"
version = "0.6.9"

[[ImageContrastAdjustment]]
deps = ["ColorVectorSpace", "ImageCore", "ImageTransformations", "Parameters"]
git-tree-sha1 = "2e6084db6cccab11fe0bc3e4130bd3d117092ed9"
uuid = "f332f351-ec65-5f6a-b3d1-319c6670881a"
version = "0.3.7"

[[ImageCore]]
deps = ["AbstractFFTs", "Colors", "FixedPointNumbers", "Graphics", "MappedArrays", "MosaicViews", "OffsetArrays", "PaddedViews", "Reexport"]
git-tree-sha1 = "db645f20b59f060d8cfae696bc9538d13fd86416"
uuid = "a09fc81d-aa75-5fe9-8630-4744c3626534"
version = "0.8.22"

[[ImageDistances]]
deps = ["ColorVectorSpace", "Distances", "ImageCore", "ImageMorphology", "LinearAlgebra", "Statistics"]
git-tree-sha1 = "6378c34a3c3a216235210d19b9f495ecfff2f85f"
uuid = "51556ac3-7006-55f5-8cb3-34580c88182d"
version = "0.2.13"

[[ImageFiltering]]
deps = ["CatIndices", "ColorVectorSpace", "ComputationalResources", "DataStructures", "FFTViews", "FFTW", "ImageCore", "LinearAlgebra", "OffsetArrays", "Requires", "SparseArrays", "StaticArrays", "Statistics", "TiledIteration"]
git-tree-sha1 = "bf96839133212d3eff4a1c3a80c57abc7cfbf0ce"
uuid = "6a3955dd-da59-5b1f-98d4-e7296123deb5"
version = "0.6.21"

[[ImageIO]]
deps = ["FileIO", "Netpbm", "PNGFiles", "TiffImages", "UUIDs"]
git-tree-sha1 = "d067570b4d4870a942b19d9ceacaea4fb39b69a1"
uuid = "82e4d734-157c-48bb-816b-45c225c6df19"
version = "0.5.6"

[[ImageMagick]]
deps = ["FileIO", "ImageCore", "ImageMagick_jll", "InteractiveUtils", "Libdl", "Pkg", "Random"]
git-tree-sha1 = "5bc1cb62e0c5f1005868358db0692c994c3a13c6"
uuid = "6218d12a-5da1-5696-b52f-db25d2ecc6d1"
version = "1.2.1"

[[ImageMagick_jll]]
deps = ["JpegTurbo_jll", "Libdl", "Libtiff_jll", "Pkg", "Zlib_jll", "libpng_jll"]
git-tree-sha1 = "1c0a2295cca535fabaf2029062912591e9b61987"
uuid = "c73af94c-d91f-53ed-93a7-00f77d67a9d7"
version = "6.9.10-12+3"

[[ImageMetadata]]
deps = ["AxisArrays", "ColorVectorSpace", "ImageAxes", "ImageCore", "IndirectArrays"]
git-tree-sha1 = "ae76038347dc4edcdb06b541595268fca65b6a42"
uuid = "bc367c6b-8a6b-528e-b4bd-a4b897500b49"
version = "0.9.5"

[[ImageMorphology]]
deps = ["ColorVectorSpace", "ImageCore", "LinearAlgebra", "TiledIteration"]
git-tree-sha1 = "68e7cbcd7dfaa3c2f74b0a8ab3066f5de8f2b71d"
uuid = "787d08f9-d448-5407-9aad-5290dd7ab264"
version = "0.2.11"

[[ImageQualityIndexes]]
deps = ["ColorVectorSpace", "ImageCore", "ImageDistances", "ImageFiltering", "OffsetArrays", "Statistics"]
git-tree-sha1 = "1198f85fa2481a3bb94bf937495ba1916f12b533"
uuid = "2996bd0c-7a13-11e9-2da2-2f5ce47296a9"
version = "0.2.2"

[[ImageShow]]
deps = ["Base64", "FileIO", "ImageCore", "OffsetArrays", "Requires", "StackViews"]
git-tree-sha1 = "832abfd709fa436a562db47fd8e81377f72b01f9"
uuid = "4e3cecfd-b093-5904-9786-8bbb286a6a31"
version = "0.3.1"

[[ImageTransformations]]
deps = ["AxisAlgorithms", "ColorVectorSpace", "CoordinateTransformations", "IdentityRanges", "ImageCore", "Interpolations", "OffsetArrays", "Rotations", "StaticArrays"]
git-tree-sha1 = "d966631de06f36c8cd4bec4bb2e8fa731db16ed9"
uuid = "02fcd773-0e25-5acc-982a-7f6622650795"
version = "0.8.12"

[[Images]]
deps = ["AxisArrays", "Base64", "ColorVectorSpace", "FileIO", "Graphics", "ImageAxes", "ImageContrastAdjustment", "ImageCore", "ImageDistances", "ImageFiltering", "ImageIO", "ImageMagick", "ImageMetadata", "ImageMorphology", "ImageQualityIndexes", "ImageShow", "ImageTransformations", "IndirectArrays", "OffsetArrays", "Random", "Reexport", "SparseArrays", "StaticArrays", "Statistics", "StatsBase", "TiledIteration"]
git-tree-sha1 = "8b714d5e11c91a0d945717430ec20f9251af4bd2"
uuid = "916415d5-f1e6-5110-898d-aaa5f9f070e0"
version = "0.24.1"

[[IndirectArrays]]
git-tree-sha1 = "c2a145a145dc03a7620af1444e0264ef907bd44f"
uuid = "9b13fd28-a010-5f03-acff-a1bbcff69959"
version = "0.5.1"

[[Inflate]]
git-tree-sha1 = "f5fc07d4e706b84f72d54eedcc1c13d92fb0871c"
uuid = "d25df0c9-e2be-5dd7-82c8-3ad0b3e990b9"
version = "0.1.2"

[[IntelOpenMP_jll]]
deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"]
git-tree-sha1 = "d979e54b71da82f3a65b62553da4fc3d18c9004c"
uuid = "1d5cc7b8-4909-519e-a0f8-d0f5ad9712d0"
version = "2018.0.3+2"

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

[[Interpolations]]
deps = ["AxisAlgorithms", "ChainRulesCore", "LinearAlgebra", "OffsetArrays", "Random", "Ratios", "Requires", "SharedArrays", "SparseArrays", "StaticArrays", "WoodburyMatrices"]
git-tree-sha1 = "1470c80592cf1f0a35566ee5e93c5f8221ebc33a"
uuid = "a98d9a8b-a2ab-59e6-89dd-64a1c18fca59"
version = "0.13.3"

[[IntervalSets]]
deps = ["Dates", "EllipsisNotation", "Statistics"]
git-tree-sha1 = "3cc368af3f110a767ac786560045dceddfc16758"
uuid = "8197267c-284f-5f27-9208-e0e47529a953"
version = "0.5.3"

[[IterTools]]
git-tree-sha1 = "05110a2ab1fc5f932622ffea2a003221f4782c18"
uuid = "c8e1da08-722c-5040-9ed9-7db0dc04731e"
version = "1.3.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 = "81690084b6198a2e1da36fcfda16eeca9f9f24e4"
uuid = "682c06a0-de6a-54ab-a142-c8b1cf79cde6"
version = "0.21.1"

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

[[LLVM]]
deps = ["CEnum", "LLVMExtra_jll", "Libdl", "Printf", "Unicode"]
git-tree-sha1 = "733abcbdc67337bb6aaf873c6bebbe1e6440a5df"
uuid = "929cbde3-209d-540e-8aea-75f648917ca0"
version = "4.1.1"

[[LLVMExtra_jll]]
deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"]
git-tree-sha1 = "b36c0677a0549c7d1dc8719899a4133abbfacf7d"
uuid = "dad2f222-ce93-54a1-a47d-0025e8a3acab"
version = "0.0.6+0"

[[LazyArtifacts]]
deps = ["Artifacts", "Pkg"]
uuid = "4af54fe1-eca0-43a8-85a7-787d91b784e3"

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

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

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

[[LogExpFunctions]]
deps = ["DocStringExtensions", "LinearAlgebra"]
git-tree-sha1 = "7bd5f6565d80b6bf753738d2bc40a5dfea072070"
uuid = "2ab3a3ac-af41-5b50-aa03-7779005ae688"
version = "0.2.5"

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

[[MKL_jll]]
deps = ["Artifacts", "IntelOpenMP_jll", "JLLWrappers", "LazyArtifacts", "Libdl", "Pkg"]
git-tree-sha1 = "c253236b0ed414624b083e6b72bfe891fbd2c7af"
uuid = "856f044c-d86e-5d09-b602-aeab76dc8ba7"
version = "2021.1.1+1"

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

[[MappedArrays]]
git-tree-sha1 = "18d3584eebc861e311a552cbb67723af8edff5de"
uuid = "dbb5928d-eab1-5f90-85c2-b9b0edb7c900"
version = "0.4.0"

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

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

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

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

[[MosaicViews]]
deps = ["MappedArrays", "OffsetArrays", "PaddedViews", "StackViews"]
git-tree-sha1 = "b34e3bc3ca7c94914418637cb10cc4d1d80d877d"
uuid = "e94cdb99-869f-56ef-bcf0-1ae2bcbe0389"
version = "0.3.3"

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

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

[[Netpbm]]
deps = ["ColorVectorSpace", "FileIO", "ImageCore"]
git-tree-sha1 = "09589171688f0039f13ebe0fdcc7288f50228b52"
uuid = "f09324ee-3d7c-5217-9330-fc30815ba969"
version = "1.0.1"

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

[[OffsetArrays]]
deps = ["Adapt"]
git-tree-sha1 = "4f825c6da64aebaa22cc058ecfceed1ab9af1c7e"
uuid = "6fe1bfb0-de20-5000-8ca7-80f57d26f881"
version = "1.10.3"

[[OpenSpecFun_jll]]
deps = ["Artifacts", "CompilerSupportLibraries_jll", "JLLWrappers", "Libdl", "Pkg"]
git-tree-sha1 = "13652491f6856acfd2db29360e1bbcd4565d04f1"
uuid = "efe28fd5-8261-553b-a9e1-b2916fc3738e"
version = "0.5.5+0"

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

[[PNGFiles]]
deps = ["Base64", "CEnum", "ImageCore", "IndirectArrays", "OffsetArrays", "libpng_jll"]
git-tree-sha1 = "520e28d4026d16dcf7b8c8140a3041f0e20a9ca8"
uuid = "f57f5aa1-a3ce-4bc8-8ab9-96f992907883"
version = "0.3.7"

[[PaddedViews]]
deps = ["OffsetArrays"]
git-tree-sha1 = "0fa5e78929aebc3f6b56e1a88cf505bb00a354c4"
uuid = "5432bcbf-9aad-5242-b902-cca2824c8663"
version = "0.5.8"

[[Parameters]]
deps = ["OrderedCollections", "UnPack"]
git-tree-sha1 = "2276ac65f1e236e0a6ea70baff3f62ad4c625345"
uuid = "d96e819e-fc66-5662-9728-84c9c7592b0a"
version = "0.12.2"

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

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

[[PkgVersion]]
deps = ["Pkg"]
git-tree-sha1 = "a7a7e1a88853564e551e4eba8650f8c38df79b37"
uuid = "eebad327-c553-4316-9ea0-9fa01ccd7688"
version = "0.1.1"

[[PlutoUI]]
deps = ["Base64", "Dates", "InteractiveUtils", "JSON", "Logging", "Markdown", "Random", "Reexport", "Suppressor"]
git-tree-sha1 = "44e225d5837e2a2345e69a1d1e01ac2443ff9fcb"
uuid = "7f904dfe-b85e-4ff6-b463-dae2292396a8"
version = "0.7.9"

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

[[ProgressMeter]]
deps = ["Distributed", "Printf"]
git-tree-sha1 = "afadeba63d90ff223a6a48d2009434ecee2ec9e8"
uuid = "92933f4c-e287-5a05-a399-4b506db050ca"
version = "1.7.1"

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

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

[[Random123]]
deps = ["Libdl", "Random", "RandomNumbers"]
git-tree-sha1 = "0e8b146557ad1c6deb1367655e052276690e71a3"
uuid = "74087812-796a-5b5d-8853-05524746bad3"
version = "1.4.2"

[[RandomNumbers]]
deps = ["Random", "Requires"]
git-tree-sha1 = "56ead4aaafc41d83694e17b0dd89d3e929d01a14"
uuid = "e6cf234a-135c-5ec9-84dd-332b85af5143"
version = "1.5.0"

[[RangeArrays]]
git-tree-sha1 = "b9039e93773ddcfc828f12aadf7115b4b4d225f5"
uuid = "b3c3ace0-ae52-54e7-9d0b-2c1406fd6b9d"
version = "0.3.2"

[[Ratios]]
git-tree-sha1 = "37d210f612d70f3f7d57d488cb3b6eff56ad4e41"
uuid = "c84ed2f1-dad5-54f0-aa8e-dbefe2724439"
version = "0.4.0"

[[Reexport]]
git-tree-sha1 = "5f6c21241f0f655da3952fd60aa18477cf96c220"
uuid = "189a3867-3050-52da-a836-e630ba90ab69"
version = "1.1.0"

[[Requires]]
deps = ["UUIDs"]
git-tree-sha1 = "4036a3bd08ac7e968e27c203d45f5fff15020621"
uuid = "ae029012-a4dd-5104-9daa-d747884805df"
version = "1.1.3"

[[Rotations]]
deps = ["LinearAlgebra", "StaticArrays", "Statistics"]
git-tree-sha1 = "2ed8d8a16d703f900168822d83699b8c3c1a5cd8"
uuid = "6038ab10-8711-5258-84ad-4b1120ba62dc"
version = "1.0.2"

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

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

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

[[SimpleTraits]]
deps = ["InteractiveUtils", "MacroTools"]
git-tree-sha1 = "5d7e3f4e11935503d3ecaf7186eac40602e7d231"
uuid = "699a6c99-e7fa-54fc-8d76-47d257e15c1d"
version = "0.9.4"

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

[[SpecialFunctions]]
deps = ["ChainRulesCore", "LogExpFunctions", "OpenSpecFun_jll"]
git-tree-sha1 = "508822dca004bf62e210609148511ad03ce8f1d8"
uuid = "276daf66-3868-5448-9aa4-cd146d93841b"
version = "1.6.0"

[[StackViews]]
deps = ["OffsetArrays"]
git-tree-sha1 = "46e589465204cd0c08b4bd97385e4fa79a0c770c"
uuid = "cae243ae-269e-4f55-b966-ac2d0dc13c15"
version = "0.1.1"

[[Static]]
deps = ["IfElse"]
git-tree-sha1 = "62701892d172a2fa41a1f829f66d2b0db94a9a63"
uuid = "aedffcd0-7271-4cad-89d0-dc628f76c6d3"
version = "0.3.0"

[[StaticArrays]]
deps = ["LinearAlgebra", "Random", "Statistics"]
git-tree-sha1 = "885838778bb6f0136f8317757d7803e0d81201e4"
uuid = "90137ffa-7385-5640-81b9-e52037218182"
version = "1.2.9"

[[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 = "fed1ec1e65749c4d96fc20dd13bea72b55457e62"
uuid = "2913bbd2-ae8a-5f71-8c99-4fb6c76f3a91"
version = "0.33.9"

[[Suppressor]]
git-tree-sha1 = "a819d77f31f83e5792a76081eee1ea6342ab8787"
uuid = "fd094767-a336-5f1f-9728-57cf17d0bbfb"
version = "0.2.0"

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

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

[[TensorCore]]
deps = ["LinearAlgebra"]
git-tree-sha1 = "1feb45f88d133a655e001435632f019a9a1bcdb6"
uuid = "62fd8b95-f654-4bbd-a8a5-9c27f68ccd50"
version = "0.1.1"

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

[[TiffImages]]
deps = ["ColorTypes", "DocStringExtensions", "FileIO", "FixedPointNumbers", "IndirectArrays", "Inflate", "OffsetArrays", "OrderedCollections", "PkgVersion", "ProgressMeter"]
git-tree-sha1 = "03fb246ac6e6b7cb7abac3b3302447d55b43270e"
uuid = "731e570b-9d59-4bfa-96dc-6df516fadf69"
version = "0.4.1"

[[TiledIteration]]
deps = ["OffsetArrays"]
git-tree-sha1 = "52c5f816857bfb3291c7d25420b1f4aca0a74d18"
uuid = "06e1c1a7-607b-532d-9fad-de7d9aa2abac"
version = "0.3.0"

[[TimerOutputs]]
deps = ["ExprTools", "Printf"]
git-tree-sha1 = "209a8326c4f955e2442c07b56029e88bb48299c7"
uuid = "a759f4b9-e2f1-59dc-863e-4aeb61b1ea8f"
version = "0.5.12"

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

[[UnPack]]
git-tree-sha1 = "387c1f73762231e86e0c9c5443ce3b4a0a9a0c2b"
uuid = "3a884ed6-31ef-47d7-9d2a-63182c4928ed"
version = "1.0.2"

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

[[WoodburyMatrices]]
deps = ["LinearAlgebra", "SparseArrays"]
git-tree-sha1 = "59e2ad8fd1591ea019a5259bd012d7aee15f995c"
uuid = "efce3f68-66dc-5838-9240-27a6d6f5f9b6"
version = "0.5.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"

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

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

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

# ╔═╡ Cell order:
# ╟─8548f5a7-de5b-44a1-a0f2-55314a6152b3
# ╠═750ded6c-f15d-11eb-02ef-5156d6bf3200
# ╟─062c93ab-df7e-485e-a4bc-f7e39c013c3f
# ╠═6d12faf1-d8bc-400a-b934-e2c36a0fcc5c
# ╟─9da77179-daff-4eb7-b95b-1c5b9da5e524
# ╠═81fa04e2-147d-496d-bea4-cfe14a67868a
# ╠═62c83a9e-d5ba-4479-b3b4-4584bbf2b70f
# ╟─c05f8497-0ab5-4dae-8dac-3df2bfd36355
# ╠═1001c583-b7ee-47e0-87ac-2d4be6e8b23b
# ╠═55bfc407-6b22-413e-aff5-d17bfd343f87
# ╟─58eda8b1-5205-4527-8e0c-69f6a3cb7607
# ╠═4aa12ee7-dce5-4e30-a1b8-569296760cb9
# ╠═6cd3120e-8164-4177-98cf-6b96953f9567
# ╟─0ee22f75-e720-4fd2-b081-80eed5e57928
# ╟─7db79200-d9ba-4199-a2ea-4d0862b36015
# ╟─0d5b251f-e0bb-46fd-a3ad-6ea3618eed5b
# ╠═c1f94c04-6796-4945-9adf-b97a4ef06d56
# ╟─a0cdf4a6-06ce-4379-b683-5c4d67192b21
# ╠═49f344df-bc93-4cee-9179-a75214f0f905
# ╟─6a9382e4-7e8f-44e8-9ca2-a50d19eb35c1
# ╟─add716ef-d45b-4aab-8e00-666adaf38c77
# ╠═2ee833fe-31e2-4784-be9a-1a4331ee10f5
# ╟─6ffd8442-f7fc-44bc-b3e4-a7d6ddb0fb1f
# ╠═b39c8c16-9162-46e7-9fbf-4400eb69dd17
# ╠═d7ffd22f-d634-46d2-92ed-2d8bbacb0ad5
# ╠═f1620901-49ca-4bb5-90b7-c258c25a3e7a
# ╠═6ddaf0d0-ee62-4f36-94c6-05f3bfb18e49
# ╠═6341b004-4426-4e30-9015-b1ee1a75e0da
# ╠═509fa4b9-c9a6-45da-a7a5-574f5a57b8cf
# ╟─3185c612-de1e-4acb-a232-dd5dfd3949d2
# ╟─00000000-0000-0000-0000-000000000001
# ╟─00000000-0000-0000-0000-000000000002


using TaylorSeries
#using Plots

#plotlyjs()

tₚ = Float64[]
xe₁ = Float64[]
xe₂ = Float64[]
x₁ = Float64[]
x₂ = Float64[]
q₁ = Float64[]
q₂ = Float64[]

function linear(coeff::Vector{Float64})
  if coeff[2] == 0.0
    res = Complex{Float64}[Inf]
  else
    res = Complex{Float64}[-coeff[1] / coeff[2]]
  end
  return res
end

function quadratic(coeff::Vector{Float64})
  if coeff[3] == 0.0
    res = linear(coeff[1:2])
  else
    res = Array{Complex{Float64}}(2)
    if coeff[1] == 0.0
      res[1] = 0.0
      res[2] = linear(coeff[2:3])
    else
      if coeff[2] == 0.0
        r = -coeff[1] / coeff[3]
        if r < 0.0
          res[1] = sqrt(-r)*im
          res[2] = -imag(res[1])*im
        else
          res[1] = sqrt(r)
          res[2] = -real(res[1])
        end
      else
        Δ = 1.0 - 4coeff[1]*coeff[3] / (coeff[2]*coeff[2])
        if Δ < 0.0
          res[1] = -0.5coeff[2]/coeff[3]+0.5coeff[2]*sqrt(-Δ)/coeff[3]*im
          res[2] = real(res[1]) - imag(res[1])*im
        else
          q = -0.5*(1.0+sign(coeff[2])*sqrt(Δ))*coeff[2]
          res[1] = q / coeff[3]
          res[2] = coeff[1] / q
        end
      end
    end
  end
  return res
end

function cubic(coeff::Vector{Float64})
  if coeff[4] == 0.0
    res = quadratic(coeff[1:3])
  else
    res = Array{Complex{Float64}}(3)
    if coeff[1] == 0.0
      res[1] = 0.0
      res[2:3] = quadratic(coeff[2:4])
    else
      A = coeff[3]/coeff[4]
      B = coeff[2]/coeff[4]
      C = coeff[1]/coeff[4]
      Q = (A^2-3B)/9
      R = (2*A^3-9A*B+27C)/54
      S = -A/3
      if R^2 < Q^3
        P = -2*sqrt(Q)
        ϕ = acos(R/sqrt(Q^3))
        res[1] = P*cos(ϕ/3)+S
        res[2] = P*cos((ϕ+2π)/3)+S
        res[3] = P*cos((ϕ-2π)/3)+S
      else
        T = -sign(R)*cbrt(abs(R)+sqrt(R^2-Q^3))
        U = 0.0
        if T != 0.0
          U = Q/T
        end
        V = 0.5*(T+U)
        W = 0.5*sqrt(3)*(T-U)
        res[1] = S+2V
        res[2] = S-V+W*im
        res[3] = conj(res[2])
      end
    end
  end
  return res
end

function quartic(coeff::Vector{Float64})
  if coeff[5] == 0.0
    res = cubic(coeff[1:4])
  else
    res = Array{Complex{Float64}}(4)
    if coeff[1] == 0.0
      res[1] = 0.0
      res[2:4] = cubic(coeff[2:5])
    else
      a₀ = coeff[1]/coeff[5]
      a₁ = coeff[2]/coeff[5]
      a₂ = coeff[3]/coeff[5]
      a₃ = coeff[4]/coeff[5]
      y₁ = cubic([4a₂*a₀-a₁^2-a₃^2*a₀, a₁*a₃-4a₀, -a₂, 1.0])[1]
      R = sqrt(0.25a₃^2-a₂+y₁)
      if R == 0.0
        A = 0.75a₃^2-2a₂
        B = 2*sqrt(y₁^2-4a₀)
      else
        A = 0.75a₃^2-R^2-2a₂
        B = (a₃*a₂-2a₁-0.25a₃^3)/R
      end
      D = sqrt(A+B)
      E = sqrt(A-B)
      res[1] = -0.25a₃+0.5R+0.5D
      res[2] = res[1]-D
      res[3] = -0.25a₃-0.5R+0.5E
      res[4] = res[3]-E
    end
  end
  return res
end

function roots(coeff::Vector{Float64}) :: Vector{Complex{Float64}}
  n = length(coeff)
  if n == 1
    res = Complex{Float64}[]
  elseif n == 2
    res = linear(coeff)
  elseif n == 3
    res = quadratic(coeff)
  elseif n == 4
    res = cubic(coeff)
  elseif n == 5
    res = quartic(coeff)
  else
    if coeff[n] == 0.0
      res = roots(coeff[1:n-1])
    else
      res = Array{Complex{Float64}}(n-1)
      if coeff[1] == 0.0
        res[1] = 0.0
        res[2:n-1] = roots(coeff[2:n])
      else
        mat = zeros(Float64, n-1, n-1)
        mat[2:n-1, 1:n-2] = eye(Float64, n-2)
        mat[:, n-1] = - coeff[1:n-1] / coeff[n]
        res[1:n-1] = eigvals(mat)
        #res[1:n-1] = roots(Poly(coeff))
      end
    end
  end
  return res
end

function brent(f::Function, x0::Number, x1::Number, args...;
               xtol::AbstractFloat=1e-7, ytol=2eps(Float64),
               maxiter::Integer=50)
    EPS = eps(Float64)
    y0 = f(x0,args...)
    y1 = f(x1,args...)
    if abs(y0) < abs(y1)
        # Swap lower and upper bounds.
        x0, x1 = x1, x0
        y0, y1 = y1, y0
    end
    x2 = x0
    y2 = y0
    x3 = x2
    bisection = true
    for _ in 1:maxiter
        # x-tolerance.
        if abs(x1-x0) < xtol
            return x1
        end

        # Use inverse quadratic interpolation if f(x0)!=f(x1)!=f(x2)
        # and linear interpolation (secant method) otherwise.
        if abs(y0-y2) > ytol && abs(y1-y2) > ytol
            x = x0*y1*y2/((y0-y1)*(y0-y2)) +
                x1*y0*y2/((y1-y0)*(y1-y2)) +
                x2*y0*y1/((y2-y0)*(y2-y1))
        else
            x = x1 - y1 * (x1-x0)/(y1-y0)
        end

        # Use bisection method if satisfies the conditions.
        delta = abs(2EPS*abs(x1))
        min1 = abs(x-x1)
        min2 = abs(x1-x2)
        min3 = abs(x2-x3)
        if (x < (3x0+x1)/4 && x > x1) ||
           (bisection && min1 >= min2/2) ||
           (!bisection && min1 >= min3/2) ||
           (bisection && min2 < delta) ||
           (!bisection && min3 < delta)
            x = (x0+x1)/2
            bisection = true
        else
            bisection = false
        end

        y = f(x,args...)
        # y-tolerance.
        if abs(y) < ytol
            return x
        end
        x3 = x2
        x2 = x1
        if sign(y0) != sign(y)
            x1 = x
            y1 = y
        else
            x0 = x
            y0 = y
        end
        if abs(y0) < abs(y1)
            # Swap lower and upper bounds.
            x0, x1 = x1, x0
            y0, y1 = y1, y0
        end
    end
    error("Max iteration exceeded")
end

function integrate_ode(f::Function, q₀::Float64, q::Vector{Taylor1{Float64}}, order::Int, i::Int)
  q[i].coeffs[2:end] = 0.0
  q[i].coeffs[1] = q₀
  for n in 1:order
    q[i].coeffs[n+1] = f(q).coeffs[n]/n
  end
  q[i]
end

function nth_derivative(q₀::Float64, f::Function, q::Vector{Taylor1{Float64}}, order::Int, i::Int)
  q[i].coeffs[2:end] = 0.0
  q[i].coeffs[1] = q₀
  q[i] = integrate(f(q), q₀)
  for k in 1:order-1
    q[i] = integrate(f(q), q₀)
  end
  q[i].coeffs[end]
  #integrate_ode(f, q₀, q, order, i).coeffs[end]
end

function f₁(q::Vector)
  0.01*q[2]
end

function f₂(q::Vector)
  2020.0-100.0*q[1]-100.0*q[2]
end

function test(order::Int, Δq::Float64, duration::Float64)
  λ, P = eig([0.0 0.01; -100.0 -100.0])
  C = inv(P)*[0.0; 2020.0]
  E = -C./λ
  D = -E + inv(P)*[0.0; 20.0]
  f = [f₁, f₂]
  q = [0*Taylor1(zeros(order+1))+0.0, 0*Taylor1(zeros(order+1))+20.0]
  x = [integrate(f[1](q), 0.0), integrate(f[2](q), 20.0)]
  for i in 1:order-1
    q = deepcopy(x)
    x = [integrate(f[1](q), 0.0), integrate(f[2](q), 20.0)]
  end
  #q = deepcopy(x)
  #q[1].coeffs[end] = 0.0
  #q[2].coeffs[end] = 0.0
  tₙ = [0.0, 0.0]
  t = 0.0
  it = [0, 0]
  start = true
  while t < duration && sum(it) < 100000
    tₒ = t
    t, i = findmin(tₙ)
    ##println("-------- $tₙ, $i")
    j = 3 - i
    it[i] += 1
    xe = P*diagm(exp.(λ*t))*inv(P)*[0.0; 20.0]+P*diagm((exp.(λ*t)-1)./λ)*inv(P)*[0.0;2020.0]
    if t-tₒ > 0.0
      for tt in 0.0:(t-tₒ)/20:t-tₒ
        xe = P*diagm(exp.(λ*(tₒ+tt)))*inv(P)*[0.0; 20.0]+P*diagm((exp.(λ*(tₒ+tt))-1)./λ)*inv(P)*[0.0;2020.0]
        push!(tₚ, tₒ+tt)
        push!(x₁, evaluate(x[1], tt))#x[1].coeffs[1])
        push!(x₂, evaluate(x[2], tt))#x[2].coeffs[1])
        push!(q₁, evaluate(q[1], tt))#q[1].coeffs[1])
        push!(q₂, evaluate(q[2], tt))#q[2].coeffs[1])
        push!(xe₁, xe[1])
        push!(xe₂, xe[2])
      end
    end
    x₀ = evaluate(x[i], t-tₒ)
    x[i] = evaluate(x[i], Taylor1([t-tₒ, 1.0]))
    q[i] = evaluate(q[i], Taylor1([t-tₒ, 1.0]))
    ##println("$t, $i, q = $(q[i])")
    q[j] = evaluate(q[j], Taylor1([t-tₒ, 1.0]))
    ##println("$t, $j, q = $(q[j])")
    q[i] = deepcopy(x[i])
    q[i][end] = 0.0
    tₙ[i] = t + (abs(Δq/x[i].coeffs[end]))^(1.0/order)
    #println("$t, $i, q = $(q[i])")
    #println("$t, $i, x = $(x[i])")
    x₀ = evaluate(x[j], t-tₒ)
    x[j] = integrate(f[j](q), x₀)
    #println("$t, $j, x = $(x[j])")
    p = (x[j]-q[j]).coeffs
    p[1] -= Δq
    a = roots(p)
    p[1] += 2Δq
    b = roots(p)
    r = filter(v->abs(imag(v)) < 1.0e-15 && real(v)>=0.0, [a..., b...])
    min_r = Inf
    for val_r in r
      if real(val_r) < min_r
        min_r = real(val_r)
      end
    end
    tₙ[j] = t + min_r
    # t₂ = brent(nth_derivative_time, 0.0, tₙ[j]-t, f[j], deepcopy(q), order, i)
    # println("$(tₙ[j]), $(t₂+t)")
    # if t₂ > 0.0 && t + t₂ < tₙ[j]
    #   tₙ[j] = t + t₂
    # end
    # if start
    #   tₙ[j] = 0.0
    #   start = false
    # end
  end
  println(it)
end
δ = 1e-6
@time test(4, δ, 500.0)
#plot(tₚ, [abs(x₁-xe₁), abs(x₂-xe₂)])
println("$(mean(abs.(x₁-xe₁))), $(maximum(abs.(x₁-xe₁))), $(mean(abs.(x₂-xe₂))), $(maximum(abs.(x₂-xe₂))), $(2δ)")
#plot(tₚ, [x₁, q₁, xe₁, x₂, q₂, xe₂])


## PART 3

println("\nPart 3");

# Estimate the covariance matrix for these quantities
println("\n\tCalculating the covariance matrices, Cmoldy_1069 and Cmoldy_1304");

# Calculate the mean values of each quantity
mdavg_1069 = zeros(3);
mdavg_1304 = zeros(3);
for n in 1:3
    mdavg_1069[n] = mean(md_1069[:,n]);
    mdavg_1304[n] = mean(md_1304[:,n]);
end

# Calculate the covariance for T = 1.069...
println("\n\tCovariance matrix for T = 1.069:\n")
D = md_1069 - ones(lengthmd) * transpose(mdavg_1069);
Cmoldy_1069 = transpose(D) * D ./ lengthmd
show(Cmoldy_1069)

# ...and for T = 1.304.
println("\n\n\tCovariance matrix for T = 1.304\n")
D = md_1304 - ones(lengthmd) * transpose(mdavg_1304);
Cmoldy_1304 = transpose(D) * D ./ lengthmd;
show(Cmoldy_1304)


export make_continuous!

"""
`make_continuous!(vals,mod)` assumes that the list of numbers in `vals`
should represent a continuous stream of numbers, but with ambiguity modulo `mod`.
For example, they are the angles of a continuous list of complex values, but there
is an ambiguity modulo 2π. This function adjusts the values so they
appear continuous.

For finer control, use `make_continuous!(vals,mod,thresh)` where `thresh` is
the maximum allowable difference between consecutive entries in `val`.
"""
function make_continuous!(vals::Array{T,1}, mod::Real, thresh::Real) where T
    @assert mod>0 "mod must be positive, not $mod"
    @assert thresh>0 "threshold must be positive, not $thresh"
    @assert thresh<mod/2 "threshold must be less than half the modulus, not $thresh"

    nvals = length(vals)
    for j=2:nvals
        while abs(vals[j-1]-vals[j]) > thresh
            if vals[j] < vals[j-1]
                vals[j] += mod
            else
                vals[j] -= mod
            end
        end
    end
    nothing
end
make_continuous!(vals::Array{T,1}, mod::Real) where T = make_continuous!(vals, mod, mod/5)


using DiffEqBayes

using Distributions
using OrdinaryDiffEq, RecursiveArrayTools, ParameterizedFunctions, DiffEqUncertainity
using Plots
using DiffEqMonteCarlo

gr(fmt=:png)

f = @ode_def LotkaVolterraTest begin
    dx = a*x - b*x*y
    dy = -c*y + d*x*y
end a b c d

u0 = [1.0,1.0]
tspan = (0.0,10.0)
p = [1.5,1.0,3.0,1,0]

prob = ODEProblem(f,u0,tspan,p)
sol = solve(prob,Tsit5())

t = collect(range(1,stop=10,length=10))
sig = 0.49
data = convert(Array, VectorOfArray([(sol(t[i]) + sig*randn(2)) for i in 1:length(t)]))

scatter(t, data[1,:], lab="#prey (data)")
scatter!(t, data[2,:], lab="#predator (data)")
plot!(sol)

priors = [Truncated(Normal(1.5,0.5),0.5,2.5),Truncated(Normal(1.2,0.5),0,2),Truncated(Normal(3.0,0.5),1,4),Truncated(Normal(1.0,0.5),0,2)]

cb = AdaptiveProbIntsUncertainty(5)
monte_prob = MonteCarloProblem(prob)
sim = solve(monte_prob,Tsit5(),num_monte=100,callback=cb,reltol=1e-5,abstol=1e-5)
plot(sim,vars=(0,1),linealpha=0.4)

@time bayesian_result_stan = stan_inference(prob,t,data,priors;reltol=1e-5,abstol=1e-5,vars =(StanODEData(),InverseGamma(3,2)))

plot_chain(bayesian_result_stan)

@time bayesian_result_turing = turing_inference(prob,Tsit5(),t,data,priors)

plot_chain(bayesian_result_turing)


n=rand(1:10)
H = Hankel([-n:n])
@test diag(full(H)) == [-n:2:n]


using IntervalLinearAlgebra, StaticArrays, LazySets
using Test

const IA = IntervalArithmetic

include("test_classify.jl")
include("test_multiplication.jl")
include("test_utils.jl")

include("test_eigenvalues/test_interval_eigenvalues.jl")
include("test_eigenvalues/test_verify_eigs.jl")
include("test_solvers/test_enclosures.jl")
include("test_solvers/test_epsilon_inflation.jl")
include("test_solvers/test_precondition.jl")
include("test_solvers/test_oettli_prager.jl")


# This is basically maple/lda_c_vwn.mpl and maple/vwn.mpl from libxc
#
# LDA correlation according to Vosko Wilk,and Nusair, (DOI 10.1139/p80-159)
#
# This version generates the "standard" parametrization, where a Pade approximation of a
# numerical diffusion Monte-Carlo simulation is used.

as_rational(x) = nsimplify(Sym(x))

A_vwn  = as_rational.([ 0.0310907, 0.01554535, -1/(6*PI^2)])
b_vwn  = as_rational.([ 3.72744,   7.06042,    1.13107  ])
c_vwn  = as_rational.([12.9352,   18.0578,    13.0045   ])
x0_vwn = as_rational.([-0.10498,  -0.32500,   -0.0047584])

A_rpa  = as_rational.([ 0.0310907,  0.01554535,  -1/(6*PI^2)])
b_rpa  = as_rational.([13.0720,    20.1231,      1.06835  ])
c_rpa  = as_rational.([42.7198,   101.578,      11.4813   ])
x0_rpa = as_rational.([-0.409286,  -0.743294,   -0.228344 ])

Q(b, c)      = sqrt(4*c - b^2)
f1(b, c)     = 2*b/Q(b, c)
f2(b, c, x0) = b*x0/(x0^2 + b*x0 + c)
f3(b, c, x0) = 2*(2x0 + b)/Q(b, c)

fpp = 4/(9*(2^(1//3) - 1))

fx(b, c, rs) = rs + b*sqrt(rs) + c

f_aux(A, b, c, x0, rs) = A*(
  + log(rs/fx(b, c, rs))
  + (f1(b, c) - f2(b, c, x0)*f3(b, c, x0))*atan(Q(b, c)/(2*sqrt(rs) + b))
  - f2(b, c, x0)*log((sqrt(rs) - x0)^2/fx(b, c, rs))
)

DMC(rs, z) = (
    + f_aux(A_vwn[2], b_vwn[2], c_vwn[2], x0_vwn[2], rs)
    - f_aux(A_vwn[1], b_vwn[1], c_vwn[1], x0_vwn[1], rs)
)

DRPA(rs, z) = (
    + f_aux(A_rpa[2], b_rpa[2], c_rpa[2], x0_rpa[2], rs)
    - f_aux(A_rpa[1], b_rpa[1], c_rpa[1], x0_rpa[1], rs)
)

f_vwn(rs, z) = (
  + f_aux(A_vwn[1], b_vwn[1], c_vwn[1], x0_vwn[1], rs)
  + f_aux(A_vwn[3], b_vwn[3], c_vwn[3], x0_vwn[3], rs)*f_zeta(z)*(1 - z^4)/fpp
  +  DMC(rs, z)*f_zeta(z)*z^4
)

f(rs, z, junk...) = f_vwn(rs, z)


function retro_substituicao(A, b)
    n = size(A)[2]
    x = zeros(n)
    x[n] = b[n]/A[n,n]
    for i = n-1:-1:1
        temp = 0
        for k = i+1:n
            temp += A[i,k]*x[k]
        end
        x[i] = (b[i]-temp)/A[i,i]
    end
    return x
end

function substituicao(A, b)
    n = size(A)[2]
    x = zeros(n)
    x[1] = b[1]/A[1,1]
    for i = 2:n
        temp = 0
        for k = 1:i-1
            temp += A[i,k]*x[k]
        end
        x[i] = (b[i]-temp)/A[i,i]
    end
    return x
end

function eliminacao(A)
    row, col = size(A)
    for j in 1:row-1
        for i in j+1:row
            m = A[i,j]/A[j,j]
            A[i,:] = A[i,:]-m*A[j,:]
        end
    end
    return A
end

function fatoracao_lu(A)
    row, col = size(A)
    L = eye(row)

    for j in 1:row-1
        for i in j+1:row
            L[i,j] = A[i,j]/A[j,j]
            A[i,:] = A[i,:]-L[i,j]*A[j,:]
        end
    end

    U = A[:, 1:end-1]
    return L, A
end

function resolve_fatlu(A)
    for i in 6:8
        L, U = fatoracao_lu(A[:, [1:5; i]])
        println("A fatoração LU estará coreta se L*U for igual a A ")
        println(L*U, A)

        y = substituicao(L, b)
        x = retro_substituicao(U, y)
        println("Valor do y é $y o valor do x é $x")
    end
end


@doc raw"""
# Lorenz Attractor

"""
module LorenzAttractor

    using GeometricEquations

    export lorenz_attractor_ode, plot_lorenz_attractor

    Δt = 0.01
    nt = 1000

    const q₀ = [1., 1., 1.]
    const default_params = (σ = 10., ρ = 28., β = 8/3)

    function lorenz_attractor_v(t, x, v, params=default_params)
        σ, ρ, β = params
        v[1] = σ * (x[2] - x[1])
        v[2] = x[1] * (ρ - x[3]) - x[2]
        v[3] = x[1] * x[2] - β * x[3]
        nothing
    end

    function lorenz_attractor_ode(q₀=q₀)
        ODE(lorenz_attractor_v, q₀)
    end

end


# # Auto-tuning Hyperparameters

#md # [![](https://img.shields.io/badge/show-github-579ACA.svg)](@__REPO_ROOT_URL__/docs/src/examples/autotuning-ridge.jl)

# This example shows how to learn a hyperparameter in Ridge Regression using a gradient descent routine.
# Let the regularized regression problem be formulated as:

# ```math
# \begin{equation}
# \min_{w} \quad \frac{1}{2nd} \sum_{i=1}^{n} (w^T x_{i} - y_i)^2 + \frac{\alpha}{2d} \| w \|_2^2
# \end{equation}
# ```

# where
# - `x`, `y` are the data points
# - `w` are the learned weights
# - `α` is the hyperparameter acting on regularization.

# The main optimization model will be formulated with JuMP.
# Using the gradient of the optimal weights with respect to the regularization parameters
# computed with DiffOpt, we can perform a gradient descent on top of the inner model
# to minimize the test loss.

# This tutorial uses the following packages

using JuMP     # The mathematical programming modelling language
import DiffOpt # JuMP extension for differentiable optimization
import Ipopt    # Optimization solver that handles quadratic programs
import Plots   # Graphing tool
import LinearAlgebra: norm, dot
import Random

# ## Generating a noisy regression dataset

Random.seed!(42)

N = 100
D = 20
noise = 5

w_real = 10 * randn(D)
X = 10 * randn(N, D)
y = X * w_real + noise * randn(N)
l = N ÷ 2  # test train split

X_train = X[1:l, :]
X_test  = X[l+1:N, :]
y_train = y[1:l]
y_test  = y[l+1:N];

# ## Defining the regression problem

# We implement the regularized regression problem as a function taking the problem data,
# building a JuMP model and solving it.

function fit_ridge(model, X, y, α)
    JuMP.empty!(model)
    set_silent(model)
    N, D = size(X)
    @variable(model, w[1:D])
    @expression(model, err_term, X * w - y)
    @objective(
        model,
        Min,
        dot(err_term, err_term) / (2 * N * D) + α * dot(w, w) / (2 * D),
    )
    optimize!(model)
    @assert termination_status(model) in [MOI.OPTIMAL, MOI.LOCALLY_SOLVED, MOI.ALMOST_LOCALLY_SOLVED]
    return w
end

# We can solve the problem for several values of α
# to visualize the effect of regularization on the testing and training loss.

αs = 0.00:0.01:0.50
mse_test = Float64[]
mse_train = Float64[]
model = Model(() -> DiffOpt.diff_optimizer(Ipopt.Optimizer))
(Ntest, D) = size(X_test)
(Ntrain, D) = size(X_train)
for α in αs
    w = fit_ridge(model, X_train, y_train, α)
    ŵ = value.(w)
    ŷ_test = X_test * ŵ
    ŷ_train = X_train * ŵ
    push!(mse_test, norm(ŷ_test - y_test)^2 / (2 * Ntest * D))
    push!(mse_train, norm(ŷ_train - y_train)^2 / (2 * Ntrain * D))
end

# Visualize the Mean Score Error metric

Plots.plot(
    αs, mse_test ./ sum(mse_test),
    label="MSE test", xaxis = "α", yaxis="MSE", legend=(0.8, 0.2),
    width=3,
)
Plots.plot!(
    αs, mse_train ./ sum(mse_train),
    label="MSE train",
    linestyle=:dash,
    width=3,
)
Plots.title!("Normalized MSE on training and testing sets")

# ## Leveraging differentiable optimization: computing the derivative of the solution

# Using DiffOpt, we can compute `∂w_i/∂α`, the derivative of the learned solution `̂w`
# w.r.t. the regularization parameter.

function compute_dw_dα(model, w)
    D = length(w)
    dw_dα = zeros(D)
    MOI.set(
        model,
        DiffOpt.ForwardObjectiveFunction(),
        dot(w, w)  / (2 * D),
    )
    DiffOpt.forward_differentiate!(model)
    for i in 1:D
        dw_dα[i] = MOI.get(
            model,
            DiffOpt.ForwardVariablePrimal(),
            w[i],
        )
    end
    return dw_dα
end

# Using `∂w_i/∂α` computed with `compute_dw_dα`,
# we can compute the derivative of the test loss w.r.t. the parameter α
# by composing derivatives.

function d_testloss_dα(model, X_test, y_test, w, ŵ)
    N, D = size(X_test)
    dw_dα = compute_dw_dα(model, w)
    err_term = X_test * ŵ - y_test
    return sum(eachindex(err_term)) do i
        dot(X_test[i,:], dw_dα) * err_term[i]
    end / (N * D)
end

# We can define a meta-optimizer function performing gradient descent
# on the test loss w.r.t. the regularization parameter.

function descent(α0, max_iters=100; fixed_step = 0.01, grad_tol=1e-3)
    α_s = Float64[]
    ∂α_s = Float64[]
    test_loss = Float64[]
    α = α0
    N, D = size(X_test)
    model = Model(() -> DiffOpt.diff_optimizer(Ipopt.Optimizer))
    for iter in 1:max_iters
        w = fit_ridge(model, X_train, y_train, α)
        ŵ = value.(w)
        err_term = X_test * ŵ - y_test
        ∂α = d_testloss_dα(model, X_test, y_test, w, ŵ)
        push!(α_s, α)
        push!(∂α_s, ∂α)
        push!(test_loss, norm(err_term)^2 / (2 * N * D))
        α -= fixed_step * ∂α
        if abs(∂α) ≤ grad_tol
            break
        end
    end
    return α_s, ∂α_s, test_loss
end

ᾱ, ∂ᾱ, msē = descent(0.10, 500)
iters = 1:length(ᾱ);

# Visualize gradient descent and convergence

Plots.plot(
    αs, mse_test,
    label="MSE test", xaxis = ("α"),
    legend=:topleft,
    width=2,
)
Plots.plot!(
    ᾱ, msē,
    label="learned α", width = 5,
    style=:dot,
)
Plots.title!("Regularizer learning")

# Visualize the convergence of α to its optimal value

Plots.plot(
    iters, ᾱ, label = nothing, color = :blue,
    xaxis = ("Iterations"), legend=:bottom,
    title = "Convergence of α"
)

# Visualize the convergence of the objective function

Plots.plot(
    iters, msē, label = nothing, color = :red,
    xaxis = ("Iterations"), legend=:bottom,
    title = "Convergence of MSE"
)

# Visualize the convergence of the derivative to zero

Plots.plot(
    iters, ∂ᾱ, label = nothing, color = :green,
    xaxis = ("Iterations"), legend=:bottom,
    title = "Convergence of ∂α"
)


# Code used to generate closest correlation matrix problems. Save in Testproblems/ClosestCorr with an accurante
# solution from MOSEK

# Original problem has the following format:
# min_X   1/2 ||X-C||^2
# s.t.    Xii = 1
#         X ⪴ 0

# workspace()
# using JLD, JuMP, Mosek, Distributions

# specify number of problems that start with matrix C as a perturbed correlation matrix and problems where C is a random matrix
nnCorr = 25
nnRand = 25
nn = nnCorr+nnRand
# this function creates a matrix A that slices out the diagonal entries Xii of a vectorized square matrix x=vec(X)
function createDiagonalExtractor(n)
  A = spzeros(n,n^2)
  A[1,1] = 1
  for iii=2:n-1
    col = (iii-1)*(n+1)
    A[iii,col+1] = 1
  end
  A[n,n^2] = 1
  return A
end

# https://stats.stackexchange.com/questions/2746/how-to-efficiently-generate-random-positive-semidefinite-correlation-matrices
# VINE METHOD to generate random correlation matrices
#distributed ~ det(S)^eta [or maybe det(S)^(eta-1), not sure]
function randomCorrMatrix(d, eta)
    β = eta + (d-1)/2;
    P = zeros(d,d);           #storing partial correlations
    S = eye(d);

    for k = 1:d-1
        β = β - 1/2;
        for i = k+1:d
            P[k,i] = rand(Beta(β,β)) # sampling from β
            P[k,i] = (P[k,i]-0.5)*2;     #linearly shifting to [-1, 1]
            p = P[k,i];
            for l = (k-1):-1:1 #converting partial correlation to raw correlation
                p = p * sqrt((1-P[l,i]^2)*(1-P[l,k]^2)) + P[l,i]*P[l,k];
            end
            S[k,i] = p;
            S[i,k] = p;
        end
    end
    return S
end

rng = MersenneTwister(12345)
dirPath = "../DataFiles/Julia/ClosestCorr/"
!ispath(dirPath) && mkdir(dirPath)

 for iii =1:1:nn
  # choose size of problem
  n = rand(rng,6:40)
  eta = abs(randn(rng))
  # create random correlation matrix and perturb it
  if iii<=nnCorr
    C = randomCorrMatrix(n,eta)
    E  = randn(n,n)*1e-1
    C = C + (E + E')/2
  else
    C = randn(rng,n,n)
  end

  c = vec(C)

  isposdef(C) && warn("The perturbed correlation matrix is still pos def.")

  # put problem into solver format
  # min   x'Ix - 1*t
  # s.t.  Ax + s1 = 0
  #       x = s4
  #       -t <= c'*x <=t
  # s1 in 0, s2,s3 in R+, s4 in SDPcone

  n2 = n^2
  m = n+n2

  P = speye(n2)
  q = -vec(C)
  r = 0.5*vec(C)'*vec(C)
  b = [ones(n);zeros(n2)]
  A = createDiagonalExtractor(n)
  Aa = [A; -speye(n2)]
  # specify cone
  Kf = n
  Kl = 0
  Kq = []
  Ks = [n^2]
  # # solve accurately once with mosek
  model = Model(solver=MosekSolver())
  @variable(model, X[1:n,1:n], SDP)
  @variable(model, t)
  x = vec(X)
  @objective(model, Min, t)
  @constraint(model, norm(x-c) <= t)
  @constraint(model, A*x.== b[1:n])
  status = JuMP.solve(model)

  # correct optimal objective value since slightly different problem is solved
  objTrue = getobjectivevalue(model)
  objCorrected = 0.5*objTrue^2
  solTrue = getvalue(x)
  # println("Objective value: ",objTrue)
  # println("Corrected objective value: ", objCorrected)
  # println("x = ", getvalue(x))

  nr = "$(iii)"
  if iii < 10
    nr = "0$(iii)"
  end

  fn = "ClosestCorr"*nr*".jld"
  problemType = "Closest Correlation Matrix"
  problemName = "ClosestCorr"*nr

  JLD.save(dirPath*fn,"n",n,"m",m,"A",Aa,"b",b,"P",P,"q",q,"r",r,"C",C,"objTrue",objCorrected,"solTrue",solTrue,"problemType",problemType,"problemName",problemName,"Kf",Kf,"Kl",Kl,"Kq",Kq,"Ks",Ks)
  println("$(iii)/$(nn) completed!")
end


"""
So, ReX = oscillatory integral + hyperbolic integral. I expanded the first integral ages ago, but it was not an arbitrary precision algorithm. I tested solving ReX with this old expansion for the oscillatory integral added to the hyperbolic integral solved by arbitrary precision quadgk, and it still diverged when digits beyond Float64 became important. Thus, the oscillatory integral needs to be rewritten in the arbitrary precision summation algorithm (the algorithm I used for BesselI-StruveL). I will do this here and also test it against brute forcing the integral with arb. prec. quadgk provided quadgk manages to solve this!
"""

include("bessel_minus_struve.jl")
import .bessel_minus_struve

using QuadGK
using Plots
using ArbNumerics
plotly()
Plots.PlotlyBackend()

function oscillatory_integral(Ω, β, α, v, w)

    R = (v^2 - w^2) / (w^2 * v)
    a = sqrt(β^2 / 4 + R * β * coth(β * v / 2))
    b = R * β / sinh(β * v / 2)

    coefficient = 2 * α * β^(3 / 2) * v^3 * sinh(Ω * β / 2) / (3 * √π * w^3 * sinh(β / 2))

    integrand(x) = (sin(Ω * x) * cos(x) / (x^2 + a^2 - b * cos(v * x))^(3 / 2))

    integral = QuadGK.quadgk(x -> integrand(x), 0, Inf)[1]
    @show(coefficient * integral)
    BigFloat(coefficient * integral)
end

function arb_binomial(x, y)
    x = ArbReal("$x")
    y = ArbReal("$y")
    one = ArbReal("1")
    ArbNumerics.gamma(x + one) / (ArbNumerics.gamma(y + one) * ArbNumerics.gamma(x - y + one))
end

function oscillatory_integral_expansion(Ω, β, α, v, w; prec = 64)

    # Initialise precision of ArbReal to prec.
    setextrabits(0)
    setprecision(ArbReal, prec + 8)

    Ω = ArbReal("$Ω")
    β = ArbReal("$β")
    α = ArbReal("$α")
    v = ArbReal("$v")
    w = ArbReal("$w")

    R = ArbReal((v^2 - w^2) / (w^2 * v))
    a = ArbReal(sqrt(β^2 / 4 + R * β * coth(β * v / 2)))
    b = ArbReal(R * β / sinh(β * v / 2))

    coefficient = ArbReal(α * β^(3/2) * v^3 / (3 * a * √π * w^3 * sinh(β / 2)))

    θ(z, n) = ArbReal(√π * ArbNumerics.gamma(-n - 1/2) * abs(z)^n * z * bessel_minus_struve.BesselI_minus_StruveL(n + 1, a * abs(z); prec = prec) / 2)

    s = ArbReal(sinh(Ω * β / 2))

    n = ArbReal("0")
    result = ArbReal("0.0")
    err = eps(ArbReal(0, bits = prec + 8))  # Machine accuracy of specified precision prec.

    while true

        previous_result = ArbReal("$result")

        if mod(n, 2) == 0
            even_term = ArbReal("0.0")
            @fastmath @inbounds @simd for z in [ArbReal(Ω + 1), ArbReal(Ω - 1)]
                even_term += ArbReal(arb_binomial(n, n/2) * s * θ(z, n) / 2)
            end
        else
            even_term = ArbReal("0.0")
        end

        k_term = ArbReal("0.0")
        @fastmath @inbounds @simd for k in ArbReal("0"):ArbReal(floor(n / 2 - 1 / 2))
            k_coeff = ArbReal(arb_binomial(n, k))
            @fastmath @inbounds @simd for z in [ArbReal(Ω + 1 + v * (n - 2 * k)), ArbReal(Ω - 1 + v * (n - 2 * k)), ArbReal(Ω + 1 - v * (n - 2 * k)), ArbReal(Ω - 1 - v * (n - 2 * k))]
                k_term += ArbReal(k_coeff * s * θ(z, n) / 2)
            end
        end

        term = ArbReal(arb_binomial(-3/2, n) * (-b / (4 * a))^n * (even_term
         + k_term))

        # Break loop if term smaller than accuracy of result.
        if abs(term) < err
            break
        end

        result += ArbReal(term)
        println("term: n = ", n, "\nterm value: ", coefficient * term, "\ncurrent result: ", coefficient * result, "\n")

        n += ArbReal("1")

        # Double precision if rounding error in result exceeds accuracy specified by prec.
        if ball(result)[2] > err
            setprecision(ArbReal, precision(result) + 8)

            println("Not precise enough. Required error < ", err, ". Increasing precision to ", precision(result) + 8, " bits.\n")

            Ω = ArbReal("$Ω")
            β = ArbReal("$β")
            α = ArbReal("$α")
            v = ArbReal("$v")
            w = ArbReal("$w")

            R = ArbReal((v^2 - w^2) / (w^2 * v))
            a = ArbReal(sqrt(β^2 / 4 + R * β * coth(β * v / 2)))
            b = ArbReal(R * β / sinh(β * v / 2))

            coefficient = ArbReal(α * β^(3/2) * v^3 / (3 * a * √π * w^3 * sinh(β / 2)))

            θ(z, n) = ArbReal(√π * ArbNumerics.gamma(-n - 1/2) * abs(z)^n * z * bessel_minus_struve.BesselI_minus_StruveL(n + 1, a * abs(z); prec = precision(result)) / 2)

            s = ArbReal(sinh(Ω * β / 2))

            n = ArbReal("$(n - 1)")
            result = ArbReal("$previous_result")
        end
    end

    result *= ArbReal(coefficient)
    println("Frequency: ", ArbReal(Ω, bits = prec + 8), ". Final result: ", ArbReal(result, bits = prec + 8))
    ArbReal(result, bits = prec + 8)
end

Ω_range = 0.01:0.5:20.01
β = 2.0
α = 7.0
v = 5.8
w = 1.6
# For Ω_range = 3.1:
# 9.828299 seconds (46.17 M allocations: 1.859 GiB, 12.89% gc time)
@time oscill_expansion = [oscillatory_integral_expansion(Ω, β, α, v, w; prec = 64) for Ω in Ω_range]
# oscill_expansion = fill(36.009791933858445279331705890513148915488)
# @show(oscill_expansion)
# 273.411207 seconds (931.57 M allocations: 32.002 GiB, 5.19% gc time)
@time oscill_integral = [oscillatory_integral(Ω, β, α, v, w) for Ω in Ω_range]

diff = oscill_expansion .- oscill_integral
@show(diff)
# oscill_integral = fill(-5.81034244862553995092)
# @show(oscill_integral)
# p = plot(Ω_range, abs.(oscill_expansion), yaxis = :log, label = "Oscillatory", xlabel = "Ω", ylabel = "ReX")
# plot!(Ω_range, abs.(oscill_integral), label = "Integral")
# display(p)


include("../src/AlphaShapes.jl")
using LinearAlgebraicRepresentation
Lar = LinearAlgebraicRepresentation

if VERSION < VersionNumber("1.0.0")
	using Base.Test
else
	using Test
end

include("deWall.jl")
include("delaunay.jl")
include("alpha_complex.jl")
include("geometry.jl")


module Model
export make_model_data

#using Plots

function calculate_data(scale::Float64, r::Float64)

	q = 10 .^ (-1:0.01:1) # q-values [nm^-1]

	#scale = 0.02 # Number of spheres per nm^3 [nm^-3]
	#r = 0.5 # nm
	rho = 1 # nm^-2
	background = 0 # nm^-1

	qr = q .* r
	intensity = scale .* ((3*(4*pi*r^3/3)*rho).*(sin.(qr) - qr .* cos.(qr)) ./ qr.^3).^2 .+ background # nm^-1

	phi = scale * (4*pi*r^3/3)

	psi = 3*phi / (1 - phi)
	b = psi .* ((cos.(qr) + qr .* sin.(qr)) .* (sin.(qr) - qr .* cos.(qr))) ./ qr.^3
	c = psi .* (sin.(qr) - qr .* cos.(qr)).^2 ./ qr.^3
	d = 1 .+ psi .* (qr.^2 .* sin.(qr) .* cos.(qr)) ./ qr.^3
	e = psi .* (qr.^2 .* (sin.(qr).^2)) ./ qr.^3
	f = psi .* (qr .* sin.(qr) .* (sin.(qr) - qr .* cos.(qr))) ./ qr.^3
	g = - psi .* (qr .* cos.(qr) .* (sin.(qr) - qr .* cos.(qr))) ./ qr.^3

	X = 1 .+ b + (2 .* e .* f .* g + d .* (f.^2 - g.^2)) ./ (d.^2 + e.^2)
	Y = c + (2 .* d .* f .* g - e .* (f.^2 - g.^2)) ./ (d.^2 + e.^2)

	S = (Y ./ c) ./ (X.^2 + Y.^2)

	#gr()
	#global plt = plot(q, intensity; xaxis = :log, yaxis = :log)

	intensity = S .* intensity

	#plot!(q, intensity)

	return q::Array{Float64, 1}, intensity::Array{Float64, 1}

end

function save_data_to_file(data::Tuple{Array{Float64, 1}, Array{Float64, 1}}, outputFileName::String)

	io = open(outputFileName, "w")
	if isempty(read(outputFileName, String))
		println(io, "q-Value [nm^-1], Intensity [nm^-1]")
	end

	for i in 1:length(data[1])

		local qValue = data[1][i]
		local intensity = data[2][i]

		println(io, qValue, ", ", intensity)

	end

	close(io)

	return nothing

end

function make_model_data(scale::Float64, r::Float64)

	save_data_to_file(calculate_data(scale::Float64, r::Float64), "calculated.data")

	return nothing

end

end # Model


######### Stan optimize example  ###########

using StanOptimize, Test

bernoulli_model = "
data {
  int<lower=1> N;
  int<lower=0,upper=1> y[N];
}
parameters {
  real<lower=0,upper=1> theta;
}
model {
  theta ~ beta(1,1);
  y ~ bernoulli(theta);
}
";

bernoulli_data = Dict("N" => 10, "y" => [0, 1, 0, 1, 0, 0, 0, 0, 0, 1])

stanmodel = OptimizeModel("optimize_1",  bernoulli_model);

(sample_path, log_path) = stan_sample(stanmodel, data=bernoulli_data);

if sample_path !== Nothing
  optim1, cnames = read_optimize(stanmodel)

  @test optim1["theta"][end] ≈ 0.3 atol=0.001
end

# Same with saved iterations
sm = OptimizeModel("optimize_2", bernoulli_model;
  method = StanOptimize.Optimize(save_iterations = true));

(sample_path, log_path)  = stan_optimize(sm, data=bernoulli_data);

if sample_path !== Nothing
  optim2, cnames = read_optimize(sm)
  @test optim2["theta"][end] ≈ 0.3 atol=0.001
end


using DataDrivenDiffEq
using ModelingToolkit
using LinearAlgebra
using OrdinaryDiffEq

using Symbolics: scalarize

function cart_pole(u, p, t)
    du = similar(u)
    F = -0.2 + 0.5*sin(6*t) # the input
    du[1] = u[3]
    du[2] = u[4]
    du[3] = -(19.62*sin(u[1])+sin(u[1])*cos(u[1])*u[3]^2+F*cos(u[1]))/(2-cos(u[1])^2)
    du[4] = -(sin(u[1])*u[3]^2 + 9.81*sin(u[1])*cos(u[1])+F)/(2-cos(u[1])^2)
    return du
end

u0 = [0.3; 0; 1.0; 0]
tspan = (0.0, 5.0)
dt = 0.1
cart_pole_prob = ODEProblem(cart_pole, u0, tspan)
solution = solve(cart_pole_prob, Tsit5(), saveat = dt)

# Create the differential data
X = solution[:,:]
DX = similar(X)
for (i, xi) in enumerate(eachcol(X))
    DX[:, i] = cart_pole(xi, [], solution.t[i])
end
t = solution.t

ddprob = ContinuousDataDrivenProblem(
    X , t, DX = DX[3:4, :], U = (u,p,t) -> [-0.2 + 0.5*sin(6*t)]
)

@variables u[1:4] du[1:2] x[1:1] t
u = scalarize(u)
du = scalarize(du)
x = scalarize(x)

polys = polynomial_basis(u, 2)
push!(polys, sin.(u[1]))
push!(polys, cos.(u[1]))
push!(polys, sin.(u[1])^2)
push!(polys, cos.(u[1])^2)
push!(polys, sin.(u[1]).*u[3:4]...)
push!(polys, sin.(u[1]).*u[3:4].^2...)
push!(polys, sin.(u[1]).*cos.(u[1])...)
push!(polys, sin.(u[1]).*cos.(u[1]).*u[3:4]...)
push!(polys, sin.(u[1]).*cos.(u[1]).*u[3:4].^2...)
implicits = [du;  du[1] .* u; du[2] .* u; du .* cos(u[1]);   du .* cos(u[1])^2; polys]
push!(implicits, x...)
push!(implicits, x[1]*cos(u[1]))
push!(implicits, x[1]*sin(u[1]))

basis= Basis(implicits, [u; du], controls = x,  iv = t)

# Simply use any optimizer you would use for sindy
λ = [1e-4;5e-4;1e-3;2e-3;3e-3;4e-3;5e-3;6e-3;7e-3;8e-3;9e-3;1e-2;2e-2;3e-2;4e-2;5e-2;
6e-2;7e-2;8e-2;9e-2;1e-1;2e-1;3e-1;4e-1;5e-1;6e-1;7e-1;8e-1;9e-1;1;1.5;2;2.5;3;3.5;4;4.5;5;
6;7;8;9;10;20;30;40;50;100;200];

opt = ImplicitOptimizer(λ)
# AICC
ĝ(x) = x[1] <= 1 ? Inf : 2*x[1]-2*log(x[2])
res = solve(ddprob, basis, opt, du, maxiter = 1000, g = ĝ, scale_coefficients = true)
m = metrics(res)

@test m.Sparsity == 10
@test m.AICC < 180.0
@test m.Error < 1.0