text:
struct TRT{Force, T <: Real} <: CollisionModel
    τ_symmetric::T
    τ_asymmetric::T
    force::Force
end
TRT(τ_a, τ_s) = TRT(τ_s, τ_a, nothing)

function CollisionModel(
    cm::Type{<:TRT},
    q::Quadrature,
    problem::FluidFlowProblem;
    Λ = 1 / 4,
)
    τ = q.speed_of_sound_squared * lattice_viscosity(problem) + 0.5
    τ_ = 0.5 + Λ / (τ - 0.5)

    force_field = has_external_force(problem) ?
        (x_idx, y_idx, t) -> lattice_force(problem, x_idx, y_idx, t) : nothing

    return TRT(τ, τ_, force_field)
end

"""
Acts like a factory for the TRT operator where the magic parameter Λ can be set

According to [TODO cite] Λ has the following effect:
- Λ = 1 / 12 # Cancels third-order spatial error, leading to optimal results for
  pure advection problems
- Λ = 1 / 6 # Cancels fourth-order spatial error, providing the most accurate
  results for the pure diffusion equation.
- Λ = 3 / 16 # Boundary wall location implemented via bounce-back for the
  Poiseuille flow exactly in the middle between horizontal walls and fluid nodes.
- Λ = 1 / 4 # Provides the most stable simulations
"""
struct TRT_Λ{T <: Real}
    Λ::T
end
function CollisionModel(cm::TRT_Λ, q::Quadrature, problem::FluidFlowProblem)
    CollisionModel(TRT, q, problem, Λ = cm.Λ)
end

function collide!(
    collision_model::TRT{Force},
    q::Quadrature,
    f_in::Populations,
    f_out::Populations;
    time = 0.0,
) where {Force, T <: Real, Populations <: AbstractArray{T, 3}}
    τ_s = collision_model.τ_symmetric
    τ_a = collision_model.τ_asymmetric

    feq = Array{T}(undef, size(f_in, 3))
    f = Array{T}(undef, size(f_in, 3))
    u = zeros(dimension(q))
    if !(Force <: Nothing)
        F = zeros(dimension(q))
    end

    nx, ny, nf = size(f_in)
    @inbounds for x in 1:nx, y in 1:ny
        @inbounds for f_idx in 1:nf
            f[f_idx] = f_in[x, y, f_idx]
        end

        ρ = density(q, f)

        # Momentum
        velocity!(q, f, ρ, u)

        # Temperature
        # temperature = temperature(q, f, ρ, u)
        temperature = 1.0

        if Force <: Nothing
            equilibrium!(q, ρ, u, temperature, feq)
        else
            F .= collision_model.force(x, y, time)

            equilibrium!(q, ρ, u + τ_a * F, temperature, feq)
        end

        @inbounds for f_idx in 1:nf
            opposite_idx = opposite(q, f_idx)
            feq_symmetric = 0.5 * (feq[f_idx] + feq[opposite_idx])
            feq_asymmetric = 0.5 * (feq[f_idx] - feq[opposite_idx])
            f_symmetric = 0.5 * (f[f_idx] + f[opposite_idx])
            f_asymmetric = 0.5 * (f[f_idx] - f[opposite_idx])

            f_out[x, y, f_idx] =
                f[f_idx] + (
                    -(1 / τ_s) * (f_symmetric - feq_symmetric) -
                    (1 / τ_a) * (f_asymmetric - feq_asymmetric)
                )
        end
    end
    return
end


text:
using Test, JuLIP
using IPFitting
using IPFitting: Dat
using IPFitting.Data: configtype
using JuLIP: write_dict, read_dict

##

@info("Testing Dat:")
println("  * Dat constructor")
println("  * vec, devec")
println("  * energy(dat), forces(dat), virial(dat)")
println("  * (de-)dictionisation, ==")

at = rattle!(bulk(:Fe) * 3, 0.1)
lj = LennardJones(r0 = rnn(:Fe)) * C2Shift(2.5*rnn(:Fe))
dat = Dat(at, "test"; E = energy(lj, at),
                      F = forces(lj, at),
                      V = virial(lj, at) )

println(@test(configtype(dat) == "test"))
println(@test(energy(dat) == energy(lj, at)))
println(@test(forces(dat) == forces(lj, at)))
println(@test(virial(dat) ≈ virial(lj, at)))

##

@info("Test (de-)serialisation of det")
println(@test all(JuLIP.Testing.test_fio(dat)))

ext: jl


text:
InputFolder = './Images/NucleiCytoo/';
OutputFolder = './Results/Images/NucleiCytoo2/';

@iA = '*C00*.tif';
@iB = '*C01*.tif';

@fxg_mMultiPassThr [iA] > [M];
params.NeighRad = 20;
params.GaussRad = 1;
params.Rescale = 1;
params.Th1 = 4;
params.Th2 = 20;
params.Rel = 0.75;
params.MinArea = 100;
/endf

/show iA > M;
/keep M > tif;

max_stars_repo_name: SebastienTs/LOBSTER

max_issues_repo_name: SebastienTs/LOBSTER

max_forks_repo_name: SebastienTs/LOBSTER


text:
## --- Binning.jl

    # Prepare test arrays
    M3 = zeros(3)
    N3 = fill(0,3)
    M33 = zeros(3,3)
    N33 = fill(0,3,3)
    r = [17.0 117.0 217.0; 50.0 150.0 250.0; 83.0 183.0 283.0]

    # Means
    @test nanbinmean([1:100..., 1], [1:100..., NaN], 0, 100, 3) == [17, 50, 83]
    @test nanbinmean([1:100..., 1], [1:100..., NaN], range(0,100,length=4)) == [17, 50, 83]
    nanbinmean!(M3, N3, [1:100..., 1],[1:100..., NaN],0,100,3)
    @test M3 == [17, 50, 83]
    @test N3 == fill(33,3)
    nanbinmean!(M3,[1:100..., 1],[1:100..., NaN],0,100,3)
    @test M3 == [17, 50, 83]
    @test nanbinmean(1:100, reshape(1:300,100,3), 0, 100, 3) == r
    @test nanbinmean(1:100, reshape(1:300,100,3), range(0,100,length=4)) == r

    # Weighted means
    @test nanbinmean([1:100..., 1], [1:100..., NaN], ones(101), 0,100,3) == [17, 50, 83]
    @test nanbinmean([1:100..., 1], [1:100..., NaN], ones(101), range(0,100,length=4)) == [17, 50, 83]
    @test nanbinmean(1:100, reshape(1:300,100,3), ones(101), 0, 100, 3) == r
    @test nanbinmean(1:100, reshape(1:300,100,3), ones(101), range(0,100,length=4)) == r
    nanbinmean!(M33, N33, 1:100, reshape(1:300,100,3), ones(101), 0, 100, 3)
    @test M33 == r
    @test N33 == fill(33,3,3)

    # Medians
    @test nanbinmedian([1:100..., 1],[1:100..., NaN],0,100,3) == [17, 50, 83]
    @test nanbinmedian([1:100..., 1],[1:100..., NaN],range(0,100,length=4)) == [17, 50, 83]
    nanbinmedian!(M3, N3, [1:100..., 1],[1:100..., NaN],0,100,3)
    @test M3 == [17, 50, 83]
    @test N3 == fill(33,3)
    @test nanbinmedian(1:100, reshape(1:300,100,3), 0, 100, 3) == r
    @test nanbinmedian(1:100, reshape(1:300,100,3), range(0,100,length=4)) == r
    nanbinmedian!(M33, N33, 1:100, reshape(1:300,100,3), 0, 100, 3)
    @test M33 == r
    @test N33 == fill(33,3,3)

    # 2D Means
    x = y = z = 0.5:9.5
    xedges = yedges = 0:10
    mu = nanbinmean(x,y,z,xedges,yedges)
    @test isequal(mu, [((i==j) ? i-0.5 : NaN) for i in 1:10, j in 1:10])

## --- Test results and warnings when N is too small to hold results

    # Means
    M2 = zeros(2)
    N2 = fill(0,2)
    w = "length(MU) < nbins; any bins beyond length(MU) will not be filled"
    @test_logs (:warn, w) nanbinmean!(M2, N2, [1:100..., 1],[1:100..., NaN],0,100,3)
    @test M2 == [17, 50]
    @test_logs (:warn, w) nanbinmean!(M2, N2, 1:100,1:100,0,100,3)
    @test M2 == [17, 50]

    M23 = zeros(2,3)
    N23 = fill(0,2,3)
    w = "size(MU,1) < nbins; any bins beyond size(MU,1) will not be filled"
    @test_logs (:warn, w) nanbinmean!(M23, N23, 1:100, reshape(1:300,100,3), 0, 100, 3)
    @test M23 == r[1:2,1:3]
    w = "size(MU,2) < size(y,2); any columns beyond size(MU,2) will not be filled"
    @test_logs (:warn, w) nanbinmean!(M23', N23', 1:100, reshape(1:300,100,3), 0, 100, 3)
    @test M23' == r[1:3,1:2]

    # Weighted Means
    M2 = zeros(2)
    N2 = fill(0,2)
    w = "length(MU) < nbins; any bins beyond length(MU) will not be filled"
    @test_logs (:warn, w) nanbinmean!(M2, N2, [1:100..., 1],[1:100..., NaN],ones(101), 0,100,3)
    @test M2 == [17, 50]
    @test_logs (:warn, w) nanbinmean!(M2, N2, 1:100,1:100,ones(101), 0,100,3)
    @test M2 == [17, 50]

    M23 = zeros(2,3)
    N23 = fill(0,2,3)
    w = "size(MU,1) < nbins; any bins beyond size(MU,1) will not be filled"
    @test_logs (:warn, w) nanbinmean!(M23, N23, 1:100, reshape(1:300,100,3), ones(101), 0, 100, 3)
    @test M23 == r[1:2,1:3]
    w = "size(MU,2) < size(y,2); any columns beyond size(MU,2) will not be filled"
    @test_logs (:warn, w) nanbinmean!(M23', N23', 1:100, reshape(1:300,100,3), ones(101), 0, 100, 3)
    @test M23' == r[1:3,1:2]

    # 2D case
    x = y = z = 0.5:9.5
    xedges = yedges = 0:10
    w1 = "size(MU,1) < nybins; any y bins beyond size(MU,1) will not be filled"
    w2 = "size(MU,2) < nxbins; any x bins beyond size(MU,2) will not be filled"
    @test_logs (:warn, w1) (:warn, w2) nanbinmean!(M33, N33, x,y,z,xedges,yedges)
    @test isequal(M33, [((i==j) ? i-0.5 : NaN) for i in 1:3, j in 1:3])

## -- End of File


text:
### A Pluto.jl notebook ###
# v0.12.21

using Markdown
using InteractiveUtils

# ╔═╡ 33bd93ec-77d9-11eb-2da8-87271f8b30c7
begin
	using Pkg
	Pkg.activate("MLJ_env", shared=true)

	# using MLJ
	using CSV
	using DataFrames
	using PlutoUI
	using Test
	using Printf
	# using Plots
	using LinearAlgebra
end

# ╔═╡ 6d69b148-77d9-11eb-04f5-034603897c31
begin
	using JSON

	important_words = open("../../ML_UW_Spec/C03/data/important_words.json", "r") do f
    	JSON.parse(f)
	end;

	length(important_words)
end

# ╔═╡ 0f6bb770-77df-11eb-0f1f-4dddbd7e2498
include("./C03w01_text_proc.jl");

# ╔═╡ baa8ca1c-77d8-11eb-2e26-3b2445acbfa1
md"""
## Implementing logistic regression from scratch

The goal of this notebook is to implement your own logistic regression classifier. We will:

  - Extract features from Amazon product reviews.
  - Convert an DataFrame into a Julia array.
  - Implement the link function for logistic regression.
  - Write a function to compute the derivative of the log likelihood function with respect to a single coefficient.
  - Implement gradient ascent.
  - Given a set of coefficients, predict sentiments.
  - Compute classification accuracy for the logistic regression model.

"""

# ╔═╡ 3ad4770e-77d9-11eb-3a93-addf3e427025
md"""
### Load review dataset

For this assignment, we will use a subset of the Amazon product review dataset. The subset was chosen to contain similar numbers of positive and negative reviews, as the original dataset consisted primarily of positive reviews.
"""

# ╔═╡ 6e264178-77d9-11eb-33c1-19462dde66e2
begin
	products = train = CSV.File("../../ML_UW_Spec/C03/data/amazon_baby_subset.csv";
		header=true) |> DataFrame;

	first(products, 3)
end

# ╔═╡ 75f2e820-77de-11eb-19eb-a5a5977b5257
size(products)

# ╔═╡ 36f2c616-77de-11eb-03b4-3b92a532949a
## how many missing values for the column :review?
sum(ismissing.(products.review))

# ╔═╡ 6d4ef592-77d9-11eb-3844-1bea7dc71094
## in-place replace(ment) of missing values for review  with empty string
replace!(products.review, missing =>"");

# ╔═╡ d7b9c812-77de-11eb-0ebb-6bb75de9725b
## no more missing values for column :review
@test sum(ismissing.(products.review)) == 0

# ╔═╡ 6e09ca34-77d9-11eb-1a99-6578f9141004
md"""
One column of this dataset is 'sentiment', corresponding to the class label with +1 indicating a review with positive sentiment and -1 indicating one with negative sentiment.
"""

# ╔═╡ 6ded8978-77d9-11eb-3d94-e7507a85197e
products.sentiment[1:20]

# ╔═╡ 6dd12c56-77d9-11eb-0350-1dfc08d17dd6
md"""
Let us quickly explore more of this dataset. The 'name' column indicates the name of the product. Here we list the first 10 products in the dataset. We then count the number of positive and negative reviews.
"""

# ╔═╡ 6db87918-77d9-11eb-2082-edecdf8b7576
first(products, 10).name

# ╔═╡ 6d9e0568-77d9-11eb-1369-bb6990e1e4a7
# num. of positive reviews, num. of negative reviews
(num_pos=sum(products.sentiment .== 1), num_neg=sum(products.sentiment .== -1))

# ╔═╡ 6d316f86-77d9-11eb-107b-1bf5708655d5
md"""
Now, we will perform 2 simple data transformations:

  1. Remove punctuation using Python's built-in string functionality.
  2. Compute word counts (only for important_words)

"""

# ╔═╡ 734ed4d4-77df-11eb-3dab-e5c10b4cda62
md"""
###### Step 1.
"""

# ╔═╡ 0f528d0e-77df-11eb-1415-2fb0e9d6792d
# products[!, :review_clean] = remove_punctuation.(products[:, :review]);
products[!, :review] = remove_punctuation.(products[:, :review]);

# ╔═╡ 0f362fec-77df-11eb-02a6-210a58c2839b
names(products)

# ╔═╡ 0f1c6b16-77df-11eb-2197-93c4a82cc135
md"""
###### Step 2.

For each word in `important_words`, we compute a count for the number of times the word occurs in the review. We will store this count in a separate column (one for each word). The result of this feature processing is a single column for each word in `important_words` which keeps a count of the number of times the respective word occurs in the review text.

"""

# ╔═╡ 0057a50e-77e0-11eb-29d6-85c2dac67743
function wc(text::String, word::String)
 	# length(filter(w -> w == word, split(text)))
	length(findall(word, text))
end

# ╔═╡ 51a94e8e-77e3-11eb-04c8-d1389b1f5856
function addcols!(df, words)
	for word ∈ words
		df[!, word] = wc.(df.review_clean, word)
	end

	#for word ∈ words
	#	transform!(df, :review_clean => r -> wc.(r, word) => word)
	#end
end

# ╔═╡ 0f03e474-77df-11eb-2791-f165fd011737
@time addcols!(products, important_words)
# 135.474563 seconds (2.43 G allocations: 181.104 GiB, 9.77% gc time)
# transform!  5.6s
# df[!, wrod] 5.5s

# ╔═╡ 0ee68550-77df-11eb-0a6e-c5bf5e3fd9ac
length(names(products))

# ╔═╡ 0ecc0054-77df-11eb-21a4-3f677548ecf9
products[!, :perfect], length(products[!, :perfect])

# ╔═╡ 0eb50d9a-77df-11eb-0b7d-557ab3ea2c81
md"""
Now, write some code to compute the number of product reviews that contain the word perfect.
"""

# ╔═╡ 0e9884fe-77df-11eb-3199-13ab2fbdcbc8
sum(products.perfect .>= 1)

# ╔═╡ 0e815eaa-77df-11eb-3422-515926dab50d
md"""
**Quiz Question**. How many reviews contain the word perfect?
  - cf. above cell.
"""

# ╔═╡ 0e4f6222-77df-11eb-3c39-239382844c12
md"""
#### Using Julia array
"""

# ╔═╡ 0e383a04-77df-11eb-2ec9-c5aeffc4b131
function get_data(df::DF, features, output) where {DF <: Union{DataFrame, SubDataFrame}}
	s_features = [Symbol(f) for f ∈ features]
	df[:, :intercept] .= 1.0
	s_features = [:intercept, s_features...]
	X_matrix = convert(Matrix, select(df, s_features))
	y = df[!, output]

	(X_matrix, y)
end

# ╔═╡ 0e1d5f86-77df-11eb-12c9-75b0b884ad25
@time feature_matrix, sentiment = get_data(products, important_words, :sentiment);

# ╔═╡ 0e03942c-77df-11eb-1bde-4f7b6739a3ee
size(feature_matrix)

# ╔═╡ 0deb769c-77df-11eb-2b53-7b73905353cd
md"""
**Quiz Question**: How many features are there in the feature_matrix?
"""

# ╔═╡ 0dd0bc6c-77df-11eb-2df1-09ca3d5aaafc
size(feature_matrix)[2]

# ╔═╡ fd5a8f7c-77e6-11eb-34dd-11ba6b2d7409
md"""
**Quiz Question**: Assuming that the intercept is present, how does the number of features in feature_matrix relate to the number of features in the logistic regression model?
Let x = [number of features in feature_matrix] and y = [number of features in logistic regression model].

  - [ ] y = x - 1
  - [x] y = x
  - [ ] y = x + 1
  - [ ] None of the above

"""

# ╔═╡ fd419c9c-77e6-11eb-2925-0d832a3fe2ca
sentiment

# ╔═╡ fd23d180-77e6-11eb-2091-af1e84d8fbcf
md"""
####  Estimating conditional probability with link function

Recall from lecture that the link function is given by:

$$P(y_i = +1 | \mathbf{x}_i,\mathbf{w}) = \frac{1}{1 + \exp(-\mathbf{w}^T h(\mathbf{x}_i))},$$

where the feature vector $h(\mathbf{x}_i)$ represents the word counts of `important_words` in the review  $\mathbf{x}_i$.

Implement the `predict_probability` function that implements the link function:
"""

# ╔═╡ fd120ad4-77e6-11eb-15ea-bb7d7ea35587
function predict_probability(feature_matrix::Matrix, coeffs)
	1. ./ (1. .+ exp.(-(feature_matrix * coeffs)))
end

# ╔═╡ fcf1dd2e-77e6-11eb-0619-e7fd2a622877
md"""
**Aside**. How the link function works with matrix algebra

Since the word counts are stored as columns in **feature_matrix**, each $i$-th row of the matrix corresponds to the feature vector $h(\mathbf{x}_i)$:

```math
[feature\_matrix] =
\left[
	\begin{array}{c}
		h(\mathbf{x}_1)^T \\
		h(\mathbf{x}_2)^T \\
		\vdots \\
		h(\mathbf{x}_N)^T
	\end{array}
\right] = \left[
	\begin{array}{cccc}
	h_0(\mathbf{x}_1) & h_1(\mathbf{x}_1) & \cdots & h_D(\mathbf{x}_1) \\
	h_0(\mathbf{x}_2) & h_1(\mathbf{x}_2) & \cdots & h_D(\mathbf{x}_2) \\
	\vdots & \vdots & \ddots & \vdots \\
	h_0(\mathbf{x}_N) & h_1(\mathbf{x}_N) & \cdots & h_D(\mathbf{x}_N)
\end{array}
\right]
```

By the rules of matrix multiplication, the score vector containing elements $\mathbf{w}^T h(\mathbf{x}_i)$ is obtained by multiplying **feature_matrix** and the coefficient vector $\mathbf{w}$.

```math
[score] =
[feature\_matrix]\mathbf{w} =
\left[
\begin{array}{c}
h(\mathbf{x}_1)^T \\
h(\mathbf{x}_2)^T \\
\vdots \\
h(\mathbf{x}_N)^T
\end{array}
\right]
\mathbf{w}
= \left[
\begin{array}{c}
h(\mathbf{x}_1)^T\mathbf{w} \\
h(\mathbf{x}_2)^T\mathbf{w} \\
\vdots \\
h(\mathbf{x}_N)^T\mathbf{w}
\end{array}
\right]
= \left[
\begin{array}{c}
\mathbf{w}^T h(\mathbf{x}_1) \\
\mathbf{w}^T h(\mathbf{x}_2) \\
\vdots \\
\mathbf{w}^T h(\mathbf{x}_N)
\end{array}
\right]
```
"""

# ╔═╡ bb677796-77e7-11eb-2baf-ffb0ee41fff6
md"""
**Checkpoint**

Just to make sure we are on the right track, we have provided a few examples. If our predict_probability function is implemented correctly, then the following should pass

"""

# ╔═╡ bb4f23d0-77e7-11eb-1f2d-efd11a1d5f66
begin
	d_feature_matrix = [1. 2. 3.; 1. -1. -1]
	d_coeffs = [1.; 3.; -1]

	# c_scores = d_feature_matrix .* d_coeffs
	c_scores = sum(d_feature_matrix .* d_coeffs', dims=2)
	c_preds  = [1. ./ (1. .+ exp.(-c_scores[1])); 1. / (1. .+ exp.(-c_scores[2]))]

	ϵ = 1e-7
	@test all(t -> abs(t[1] - t[2]) ≤ ϵ,
		zip(predict_probability(d_feature_matrix, d_coeffs), c_preds))
end

# ╔═╡ 9744c78c-77ed-11eb-3bf6-fdc3d8cf5281
md"""
#### Compute derivative of log likelihood with respect to a single coefficient

Recall from lecture:

```math
\frac{\partial\ell}{\partial w_j} = \sum_{i=1}^N h_j(\mathbf{x}_i)\left(\mathbf{1}[y_i = +1] - P(y_i = +1 | \mathbf{x}_i, \mathbf{w})\right)
```

We will now write a function that computes the derivative of log likelihood with respect to a single coefficient $w_j$. The function accepts two arguments:
  - `errors` vector containing $\mathbf{1}[y_i = +1] - P(y_i = +1 | \mathbf{x}_i, \mathbf{w})$ for all $i$.
  - `feature` vector containing $h_j(\mathbf{x}_i)$  for all $i$.

"""

# ╔═╡ 2cddfe30-77ee-11eb-389c-130f0af51f70
feature_derivative(errors::Matrix, feature::Vector) = dot(errors, feature)

# ╔═╡ 2cbeab98-77ee-11eb-2108-7596888619c2
md"""
In the main lecture, our focus was on the likelihood.  In the advanced optional video, however, we introduced a transformation of this likelihood *called the log likelihood* that simplifies the derivation of the gradient and is more numerically stable.  Due to its numerical stability, we will use the log likelihood instead of the likelihood to assess the algorithm.

The log likelihood is computed using the following formula (see the advanced optional video if you are curious about the derivation of this equation):

$$\ell\ell(\mathbf{w}) = \sum_{i=1}^N \Big( (\mathbf{1}[y_i = +1] - 1)\mathbf{w}^T h(\mathbf{x}_i) - \ln\left(1 + \exp(-\mathbf{w}^T h(\mathbf{x}_i))\right) \Big)$$

"""

# ╔═╡ 2ca74a52-77ee-11eb-0f50-fd3c15702be1
function log_likelihood(feature_matrix::Matrix, sentiment, coeffs)
	indic = sentiment .== 1
	scores = feature_matrix * coeffs
	log_exp = log.(1. .+ exp.(-scores))

	mask = isinf.(log_exp)
	log_exp[mask] = -scores[mask]

	sum((indic .- 1) .* scores .- log_exp)
end

# ╔═╡ 2c8d9e7c-77ee-11eb-3897-c5bd68dbea53
md"""
**Checkpoint**

Just to make sure we are on the same page, the following code block should pass.
"""

# ╔═╡ 889c2692-77ef-11eb-057c-47b467b71f21
begin
	# re-using d_feature_matrix, d_coeffs above
	d_sentiment = [-1; 1]

	c_indic  = [-1 == +1; 1 == +1] # [Int(-1 == +1); Int(1 == +1)]
	# c_scores (as above)

	c_1st_term  = [(c_indic[1] - 1) * c_scores[1]; (c_indic[2] - 1) * c_scores[2]]
	c_2nd_term = [log(1. + exp(-c_scores[1])); log(1. + exp(-c_scores[2]))]

	c_ll = sum([c_1st_term[1] .- c_2nd_term[1]; c_1st_term[2] .- c_2nd_term[2]])

	@test abs(log_likelihood(d_feature_matrix, d_sentiment, d_coeffs) - c_ll) ≤ ϵ
end

# ╔═╡ 972e0c72-77ed-11eb-0ae7-c127694faf89
md"""
#### Taking gradient steps

Now we are ready to implement our own logistic regression. All we have to do is to write a gradient ascent function that takes gradient steps towards the optimum.
"""

# ╔═╡ 970fba24-77ed-11eb-1a3e-e769d6049078
function logistic_regression(feature_matrix::Matrix, sentiment, init_coeffs;
		η=1e-7, max_iter=200)
	coeffs = copy(init_coeffs)
	# println("coeffs: $(size(coeffs))")

	for ix ∈ 1:max_iter
		preds = predict_probability(feature_matrix, coeffs)
		indic = sentiment .== 1
		errors = indic .- preds
		# println(size(preds), size(indic), size(errors))

		for jx ∈ 1:length(coeffs)
			# println("==> $(size(feature_matrix[:, jx]))")
			∂c = feature_derivative(errors, feature_matrix[:, jx])
			# println("coeffs: $(size(coeffs[jx])), ∂c: $(size(∂c))")
			coeffs[jx] += η * ∂c
		end

		if ix ≤ 15 || (ix ≤ 100 && ix % 10 == 0) || (ix ≤ 1000 && ix % 100 == 0) ||
			(ix ≤ 10000 && ix % 1000 == 0) || ix % 10000 == 0
			lp = log_likelihood(feature_matrix, sentiment, coeffs)
			@printf("iter: %d / %d: log likelihood of observed labels: %.8f\n",
				ceil(Int, log10(max_iter)), ix, lp)
		end
 	end

	coeffs
end

# ╔═╡ 96f4d234-77ed-11eb-0e9d-8b4483b9d020
@time coeffs = logistic_regression(feature_matrix, sentiment,
	zeros(Float64, (194, 1)); max_iter=301);

# ╔═╡ 656b8c04-780c-11eb-16ef-2d0ab940e7b7
size(feature_matrix)

# ╔═╡ a01911d6-7808-11eb-34e1-9dde4224fca8
md"""
**Quiz Question:** As each iteration of gradient ascent passes, does the log likelihood increase or decrease?
  - increase
"""

# ╔═╡ a000fdf0-7808-11eb-32af-fba57700380e
md"""
### Predicting sentiments

Recall from lecture that class predictions for a data point $\mathbf{x}$ can be computed from the coefficients $\mathbf{w}$ using the following formula:

```math
\hat{y}_i =
\left\{
\begin{array}{ll}
      +1 & \mathbf{x}_i^T\mathbf{w} > 0 \\
      -1 & \mathbf{x}_i^T\mathbf{w} \leq 0 \\
\end{array}
\right.
```

Now, we will write some code to compute class predictions. We will do this in two steps:
  1. First compute the **scores** using **feature_matrix** and **coefficients** using a dot product.
  2.  Using the formula above, compute the class predictions from the scores.

Step 1 can be implemented as follows:
"""

# ╔═╡ 9fe93efc-7808-11eb-3755-11a93bd3374a
function calc_preds(feature_matrix::Matrix, coeffs)
	scores = feature_matrix * coeffs
	ifelse.(scores .> 0., 1, -1)
end

# ╔═╡ 9fd0d0b0-7808-11eb-2f66-c5815f2f4929
preds = calc_preds(feature_matrix, coeffs);

# ╔═╡ 9fb85b86-7808-11eb-1af6-0f648740b822
md"""
**Quiz Question:** How many reviews were predicted to have positive sentiment?
"""

# ╔═╡ 380fef64-780c-11eb-3676-7d2fa5d2f726
begin
	n_pos, n_neg = length(preds[preds .== 1]), length(preds[preds .== -1])
	@assert n_pos + n_neg == length(preds)

	(pred_pos=n_pos, pred_neg=n_neg)
end

# ╔═╡ 6bd80650-780f-11eb-20f9-8f99728a72f3
md"""
### Measuring accuracy

We will now measure the classification accuracy of the model. Recall from the lecture that the classification accuracy can be computed as follows:

```math
\mbox{accuracy} = \frac{{\mbox{\# correctly classified data points}}}{\mbox{\# total data points}}
```
"""

# ╔═╡ c7ff6cc0-780f-11eb-3ac5-ffd1fd251698
t = (num_mistakes=sum(sentiment .- preds .!= 0), total_data_points=size(products)[1])

# ╔═╡ 7e0f62a4-7810-11eb-34d9-85a2f2de4296
md"""
**Quiz Question**: What is the accuracy of the model on predictions made above? (round to 2 digits of accuracy)
"""

# ╔═╡ c7e77f86-780f-11eb-0511-5394adb6b0dd
begin
	accuracy = (t.total_data_points - t.num_mistakes) / t.total_data_points;
	round(accuracy, digits=2)
end

# ╔═╡ 8b92d55a-7810-11eb-2495-0112a869bcc9
md"""
### Which words contribute most to positive & negative sentiments?

Recall that in Module 2 assignment, we were able to compute the "**most positive words**". These are words that correspond most strongly with positive reviews. In order to do this, we will first do the following:

  - Treat each coefficient as a tuple, i.e. (**word**, **coefficient_value**).

  - Sort all the (**word**, **coefficient_value**) tuples by **coefficient_value** in descending order.
"""

# ╔═╡ 8c6e485a-7811-11eb-3fe7-19876cc85ee4
md"""
##### Ten "most positive" words

Now, we compute the 10 words that have the most positive coefficient values. These words are associated with positive sentiment.

"""

# ╔═╡ c7cacf90-780f-11eb-21ac-37de331bfd0d
function top_n(;words=important_words, coeffs=coeffs[2:end], key=:positive)
  	sort(
    	[(w, c) for (w, c) ∈ zip(words, coeffs)],
    	by=t -> t[2],
    	rev= key == :positive
	)
end

# ╔═╡ 28b5ed9a-7811-11eb-2e7a-e9ee7b3c7db2
md"""
**Quiz Question**: Which word is **NOT** present in the top 10 "most positive" words?
"""

# ╔═╡ 42107bfc-7811-11eb-35f7-f52a103f7be6
map(t -> t[1], top_n()[1:10])

# ╔═╡ 7321412c-7811-11eb-14eb-f529581256f1
md"""
  - **cheap** is NOT in previous list.
"""

# ╔═╡ 86669250-7811-11eb-1938-5dedabd25ad7
md"""
##### Ten "most negative" words

Next, we repeat this exercise on the 10 most negative words.  That is, we compute the 10 words that have the most negative coefficient values. These words are associated with negative sentiment.
"""

# ╔═╡ 864d674e-7811-11eb-0071-85e6c429d503
map(t -> t[1], top_n(;key=:negative)[1:10])

# ╔═╡ 862e1b1e-7811-11eb-1212-25bce5527b8b
md"""
  - **need** is NOT in previous list.
"""

# ╔═╡ Cell order:
# ╟─baa8ca1c-77d8-11eb-2e26-3b2445acbfa1
# ╠═33bd93ec-77d9-11eb-2da8-87271f8b30c7
# ╟─3ad4770e-77d9-11eb-3a93-addf3e427025
# ╠═6e264178-77d9-11eb-33c1-19462dde66e2
# ╠═75f2e820-77de-11eb-19eb-a5a5977b5257
# ╠═36f2c616-77de-11eb-03b4-3b92a532949a
# ╠═6d4ef592-77d9-11eb-3844-1bea7dc71094
# ╠═d7b9c812-77de-11eb-0ebb-6bb75de9725b
# ╟─6e09ca34-77d9-11eb-1a99-6578f9141004
# ╠═6ded8978-77d9-11eb-3d94-e7507a85197e
# ╟─6dd12c56-77d9-11eb-0350-1dfc08d17dd6
# ╠═6db87918-77d9-11eb-2082-edecdf8b7576
# ╠═6d9e0568-77d9-11eb-1369-bb6990e1e4a7
# ╠═6d69b148-77d9-11eb-04f5-034603897c31
# ╟─6d316f86-77d9-11eb-107b-1bf5708655d5
# ╠═0f6bb770-77df-11eb-0f1f-4dddbd7e2498
# ╟─734ed4d4-77df-11eb-3dab-e5c10b4cda62
# ╠═0f528d0e-77df-11eb-1415-2fb0e9d6792d
# ╠═0f362fec-77df-11eb-02a6-210a58c2839b
# ╟─0f1c6b16-77df-11eb-2197-93c4a82cc135
# ╠═0057a50e-77e0-11eb-29d6-85c2dac67743
# ╠═51a94e8e-77e3-11eb-04c8-d1389b1f5856
# ╠═0f03e474-77df-11eb-2791-f165fd011737
# ╠═0ee68550-77df-11eb-0a6e-c5bf5e3fd9ac
# ╠═0ecc0054-77df-11eb-21a4-3f677548ecf9
# ╟─0eb50d9a-77df-11eb-0b7d-557ab3ea2c81
# ╠═0e9884fe-77df-11eb-3199-13ab2fbdcbc8
# ╟─0e815eaa-77df-11eb-3422-515926dab50d
# ╟─0e4f6222-77df-11eb-3c39-239382844c12
# ╠═0e383a04-77df-11eb-2ec9-c5aeffc4b131
# ╠═0e1d5f86-77df-11eb-12c9-75b0b884ad25
# ╠═0e03942c-77df-11eb-1bde-4f7b6739a3ee
# ╟─0deb769c-77df-11eb-2b53-7b73905353cd
# ╠═0dd0bc6c-77df-11eb-2df1-09ca3d5aaafc
# ╟─fd5a8f7c-77e6-11eb-34dd-11ba6b2d7409
# ╠═fd419c9c-77e6-11eb-2925-0d832a3fe2ca
# ╟─fd23d180-77e6-11eb-2091-af1e84d8fbcf
# ╠═fd120ad4-77e6-11eb-15ea-bb7d7ea35587
# ╟─fcf1dd2e-77e6-11eb-0619-e7fd2a622877
# ╟─bb677796-77e7-11eb-2baf-ffb0ee41fff6
# ╠═bb4f23d0-77e7-11eb-1f2d-efd11a1d5f66
# ╟─9744c78c-77ed-11eb-3bf6-fdc3d8cf5281
# ╠═2cddfe30-77ee-11eb-389c-130f0af51f70
# ╟─2cbeab98-77ee-11eb-2108-7596888619c2
# ╠═2ca74a52-77ee-11eb-0f50-fd3c15702be1
# ╟─2c8d9e7c-77ee-11eb-3897-c5bd68dbea53
# ╠═889c2692-77ef-11eb-057c-47b467b71f21
# ╟─972e0c72-77ed-11eb-0ae7-c127694faf89
# ╠═970fba24-77ed-11eb-1a3e-e769d6049078
# ╠═96f4d234-77ed-11eb-0e9d-8b4483b9d020
# ╠═656b8c04-780c-11eb-16ef-2d0ab940e7b7
# ╟─a01911d6-7808-11eb-34e1-9dde4224fca8
# ╟─a000fdf0-7808-11eb-32af-fba57700380e
# ╠═9fe93efc-7808-11eb-3755-11a93bd3374a
# ╠═9fd0d0b0-7808-11eb-2f66-c5815f2f4929
# ╟─9fb85b86-7808-11eb-1af6-0f648740b822
# ╠═380fef64-780c-11eb-3676-7d2fa5d2f726
# ╟─6bd80650-780f-11eb-20f9-8f99728a72f3
# ╠═c7ff6cc0-780f-11eb-3ac5-ffd1fd251698
# ╟─7e0f62a4-7810-11eb-34d9-85a2f2de4296
# ╠═c7e77f86-780f-11eb-0511-5394adb6b0dd
# ╟─8b92d55a-7810-11eb-2495-0112a869bcc9
# ╟─8c6e485a-7811-11eb-3fe7-19876cc85ee4
# ╠═c7cacf90-780f-11eb-21ac-37de331bfd0d
# ╟─28b5ed9a-7811-11eb-2e7a-e9ee7b3c7db2
# ╠═42107bfc-7811-11eb-35f7-f52a103f7be6
# ╟─7321412c-7811-11eb-14eb-f529581256f1
# ╟─86669250-7811-11eb-1938-5dedabd25ad7
# ╠═864d674e-7811-11eb-0071-85e6c429d503
# ╟─862e1b1e-7811-11eb-1212-25bce5527b8b


text:
using Test
using Dorothy

@DorothyAll()

@testset "TRR" begin

	@testset "Single" begin
		f1 = infer("$(Dorothy.datapath)/eMHC.trr")
		@test getformat(f1) == "trajectory/x-trr"
		f2 = infer(open("$(Dorothy.datapath)/eMHC.trr"))
		@test getformat(f2) == "trajectory/x-trr"
		m1 = read(f2)
		@test m1.header.step == 0
		@test m1.header.time == 0.0
		@test length(m1) == 6002
		@test volume(m1.header.cell) ≈ 665416.28
		m2 = read!(f2, MolecularModel())
		@test m2.header.step == 500e3
		@test m2.header.time == 2e3
		@test isapprox(m2.R[1], [88.65, 82.36, 47.93], atol=0.01)
		@test isapprox(m2.R[end], [72.15, 56.0, 41.75], atol=0.01)
	end

	@testset "Trajectory" begin
		streamf("$(Dorothy.datapath)/eMHC.trr") do s
			@test length(s) == 501
			frame = MolecularModel()
			i = 0
			while !eof(s)
				read!(s, frame)
				i += 1
				if frame.header.step == 500e3
					@test frame.header.time == 2e3
					@test isapprox(frame.R[1], [88.65, 82.36, 47.93],
							atol=0.01)
					@test isapprox(frame.R[end], [72.15, 56.0, 41.75],
							atol=0.01)
				end
			end
			@test i == length(s)
		end
	end

	@testset "Round-trip" begin
		buffer1 = IOBuffer(read("$(Dorothy.datapath)/eMHC.trr"))
		buffer2 = IOBuffer(write=true)
		traj1 = streamf(buffer1)
		traj2 = streamf(specify(buffer2, "trajectory/x-trr"),
				nparticles=traj1.meta.nparticles)
		frame = MolecularModel()
		while !eof(traj1)
			read!(traj1, frame)
			write(traj2, frame)
		end
		@test take!(buffer1) == take!(buffer2)
	end

end # @testset


text:
using Word2Vec
using Base.Test
using Blocks
using Base.FS

data_dir = joinpath(Pkg.dir("Word2Vec"), "test", "data")
model_dir = joinpath(Pkg.dir("Word2Vec"), "test", "models")

test_filename = isempty(ARGS) ? "text8_tiny" : ARGS[1]
# test_filename = "text8"
test_file = joinpath(data_dir, test_filename)
model_file = joinpath(model_dir, test_filename * ".model")

subsampling = 0
#subsampling = 1e-4

function test_word_embedding(inputfile, subsampling)
    println("=======================================")
    println("Testing word embedding with $inputfile")
    println("=======================================")

    embed = WordEmbedding(30, Word2Vec.random_inited, Word2Vec.huffman_tree, subsampling = subsampling)
    @time train(embed, inputfile)

    save(embed, model_file)
    embed = restore(model_file)

    inp = ["king", "queen", "prince"]
    for w in inp
        println("nearest words to $w")
        println(find_nearest_words(embed, w))
    end
    println("nearest words to $inp")
    println(find_nearest_words(embed, ["king", "queen", "prince"], []))
end

test_word_embedding(test_file, subsampling)


text:
export PoolingFunction, Pooling
abstract PoolingFunction
abstract StdPoolingFunction <: PoolingFunction # built-in poolings

module Pooling
using ..Mocha.StdPoolingFunction

type Max <: StdPoolingFunction end
type Mean <: StdPoolingFunction end

end # module Pooling


text:
using NonUniformRandomVariateGeneration
using Distributions
using BenchmarkTools

n = 100000
p = 0.25

function benchNative(n::Int64, p::Float64, N::Int64)
  m::Float64 = 0.0
  for i in 1:N
    m += sampleBinomial(n, p)
  end
  return m / N
end

function benchDistributions(n::Int64, p::Float64, N::Int64)
  binom = Distributions.Binomial(n, p)
  m::Float64 = 0.0
  for i in 1:N
    m += rand(binom)
  end
  return m / N
end

@btime benchNative(n, p, 1024*1024*8)
@btime benchDistributions(n, p, 1024*1024*8)


text:
# This file is a part of Julia. License is MIT: https://julialang.org/license

export LAPACKException,
       ARPACKException,
       SingularException,
       PosDefException,
       RankDeficientException

struct LAPACKException <: Exception
    info::BlasInt
end

struct ARPACKException <: Exception
    info::BlasInt
end

function Base.showerror(io::IO, ex::ARPACKException)
    print(io, "ARPACKException: ")
    if ex.info == -8
        print(io, "error return from calculation of a real Schur form.")
    elseif ex.info == -9
        print(io, "error return from calculation of eigenvectors.")
    elseif ex.info == -14
        print(io, string("did not find any eigenvalues to sufficient accuracy. ",
            "Try with a different starting vector or more Lanczos vectors ",
            "by increasing the value of ncv."))
    else
        print(io, "unspecified ARPACK error: $(ex.info)")
    end
end

struct SingularException <: Exception
    info::BlasInt
end

struct PosDefException <: Exception
    info::BlasInt
end
function Base.showerror(io::IO, ex::PosDefException)
    print(io, "PosDefException: matrix is not ")
    if ex.info == -1
        print(io, "Hermitian")
    else
        print(io, "positive definite")
    end
    print(io, "; Cholesky factorization failed.")
end

struct RankDeficientException <: Exception
    info::BlasInt
end

max_stars_repo_name: greimel/julia

max_issues_repo_name: greimel/julia

max_forks_repo_name: greimel/julia


text:
include("StaticAnalyzer.jl")
include("PredefinedSubroutines.jl")

binary_op_and_type_to_opcode = Dict{Tuple{TokenClass,MPType},String}(
  (plus, MInt) => "add",
  (plus, MReal) => "fadd",
  (minus, MInt) => "sub",
  (minus, MReal) => "fsub",
  (kw_or, MBool) => "or",
  (times, MInt) => "mul",
  (times, MReal) => "fmul",
  (divide, MInt) => "sdiv",
  (divide, MReal) => "fdiv",
  (modulo, MInt) => "srem",
  (kw_and, MBool) => "and",

  (equals, MInt) => "icmp eq",
  (equals, MBool) => "icmp eq",
  (equals, MReal) => "fcmp oeq",
  (not_equal, MInt) => "icmp ne",
  (not_equal, MBool) => "icmp ne",
  (not_equal, MReal) => "fcmp one",
  (less_than, MInt) => "icmp slt",
  (less_than, MBool) => "icmp ult",
  (less_than, MReal) => "fcmp olt",
  (less_than_or_equal, MInt) => "icmp sle",
  (less_than_or_equal, MBool) => "icmp ule",
  (less_than_or_equal, MReal) => "fcmp ole",
  (greater_than, MInt) => "icmp sgt",
  (greater_than, MBool) => "icmp ugt",
  (greater_than, MReal) => "fcmp ogt",
  (greater_than_or_equal, MInt) => "icmp sge",
  (greater_than_or_equal, MBool) => "icmp uge",
  (greater_than_or_equal, MReal) => "fcmp oge",
)

unary_op_and_type_to_opcode_and_first_arg = Dict{Tuple{TokenClass,MPType},Tuple{String,String}}(
  (kw_not, MBool) => ("add", "1"),
  (minus, MInt) => ("mul", "-1"),
  (minus, MReal) => ("fmul", "-1.0"),
)

mptype_to_llvm_type = Dict{MPType,String}(
  MInt => "i32",
  MIntRef => "i32*",
  MReal => "double",
  MRealRef => "double*",
  MBool => "i1",
  MString => STRING_TYPE_ID * "*",
  MStringRef => STRING_TYPE_ID * "**",
  MBoolRef => "i1*",
  MNothing => "void",
  MIntArray => INT_ARRAY_TYPE_ID,
  MRealArray => REAL_ARRAY_TYPE_ID,
  MBoolArray => BOOL_ARRAY_TYPE_ID,
  MStringArray => STRING_ARRAY_TYPE_ID,
)

op_and_type_to_cond = Dict{Tuple{TokenClass,MPType},String}(
  (equals, MInt) => "eq",
  (equals, MBool) => "eq",
  (equals, MReal) => "oeq",
  (not_equal, MInt) => "ne",
  (not_equal, MBool) => "ne",
  (not_equal, MReal) => "one",
  (less_than, MInt) => "slt",
  (less_than, MBool) => "ult",
  (less_than, MReal) => "olt",
  (less_than_or_equal, MInt) => "sle",
  (less_than_or_equal, MBool) => "ule",
  (less_than_or_equal, MReal) => "ole",
  (greater_than, MInt) => "sgt",
  (greater_than, MBool) => "ugt",
  (greater_than, MReal) => "ogt",
  (greater_than_or_equal, MInt) => "sge",
  (greater_than_or_equal, MBool) => "uge",
  (greater_than_or_equal, MReal) => "oge",
)

mutable struct GenerationContext
  counter::Int
  subroutines::Vector{SubroutineEntry}
  generated_subrs::Dict{String,String}
  calls::Vector{Call}
  string_literals::Vector{LiteralFactor}
  all_var_names_and_types::Vector{Tuple{String,MPType}}
  io
end

function GenerationContext(
    subroutines::Vector{SubroutineEntry},
    calls::Vector{Call},
    string_literals::Vector{LiteralFactor},
    io)
    GenerationContext(0, subroutines, Dict{String,String}(), calls, string_literals, Vector{Tuple{String,MPType}}(), io)
  end

function create_id(gc::GenerationContext, str::String)
  id_string = "%$(str)_$(gc.counter)"
  gc.counter += 1
  return id_string
end

function reset_id_counter(gc::GenerationContext)
  gc.counter = 0
end

function pt(gc::GenerationContext, str, indent)
  print(gc.io, '\t'^indent * str)
end

pt(gc, str) = pt(gc, str, 0)
ptln(gc, str, indent) = pt(gc, str * '\n', indent)
ptln(gc, str) = ptln(gc, str, 0)

function generate_header(gc::GenerationContext)
  ptln(gc, "define i32 @main(i32, i8**) {")
end

function generate_strings(gc::GenerationContext)
  ptln(gc, "$INT_STR_ID = private constant [3 x i8] c\"%d\\00\"")
  ptln(gc, "$REAL_STR_ID = private constant [3 x i8] c\"%f\\00\"")
  ptln(gc, "$STR_STR_ID = private constant [3 x i8] c\"%s\\00\"")
  ptln(gc, "$TRUE_STR_ID = private constant [5 x i8] c\"true\\00\"")
  ptln(gc, "$FALSE_STR_ID = private constant [6 x i8] c\"false\\00\"")
  ptln(gc, "$EMPTY_STR_ID = private constant [1 x i8] c\"\\00\"")
  generate_string_literals(gc)
  ptln(gc, "")
end

function generate_string_literals(gc::GenerationContext)
  for literal::LiteralFactor in gc.string_literals
    str = literal.token.lexeme
    len = length(str) + 1 # Account for terminating null
    ptln(gc, "$(literal.unique_id) = private constant [$len x i8] c\"$str\\00\"")
  end
end

function generate_subroutines(gc::GenerationContext)
  for entry::SubroutineEntry in gc.subroutines
    subroutine::Subroutine = entry.node
    generate(subroutine, gc)
  end
end

function generate(s::Subroutine, gc::GenerationContext)
  # for fingerprint in keys(s.fingerprint_to_implicits_and_f_id)
  #   implicits, llvm_function_name = s.fingerprint_to_implicits_and_f_id[fingerprint]
  #   generate_subroutine(s, implicits, llvm_function_name, gc)
  # end
  if s.is_called
    generate_subroutine(s, s.llvm_function_name, gc)
  end
end

function generate_subroutine(s::Subroutine, llvm_function_name::String, gc::GenerationContext)
  llvm_ret_type = mptype_to_llvm_type[s.ret_type.scalar_type]
  args = ["$(mptype_to_llvm_type[parameter_to_mptype(param)]) %$(param.name)" for param in s.params]
  for (name, mptype) in gc.all_var_names_and_types
    as_reftype = scalar_to_ref_type[mptype]
    name_as_implicit = get_implicit_param_id(name, mptype)
    push!(args, "$(mptype_to_llvm_type[as_reftype]) %$(name_as_implicit)")
  end
  argstring = join(args, ", ")
  signature_string = "$llvm_ret_type $(s.llvm_function_name)($argstring)"
  ptln(gc, "define $signature_string {")
  generate(s.body, gc)
  if s.ret_type.scalar_type == MNothing  # Subroutine is a procedure
    ptln(gc, "ret void", 1)
  end
  ptln(gc, "}")
  ptln(gc, "")
end

function generate_footer(gc::GenerationContext)
  ptln(gc, "ret i32 0", 1) # Main returns 0
  ptln(gc, "}\n") # Close main
  ptln(gc, "declare i32 @printf(i8*, ...)")
  ptln(gc, "declare i32 @puts(i8*)")
  ptln(gc, "declare i8* @strcpy(i8*, i8* nocapture readonly)")
  ptln(gc, "declare i8* @strcat(i8*, i8* nocapture readonly)")
  ptln(gc, "declare i32 @strcmp(i8* nocapture, i8* nocapture)")
  ptln(gc, "")
  generate_helpers(gc)
end

function generate_types(gc)
  ptln(gc, "$STRING_TYPE_ID = type { i8*, i32 }")
  ptln(gc, "$ARRAY_TYPE_ID = type { i8*, i32 }")
  ptln(gc, "$INT_ARRAY_TYPE_ID = type { i32*, i32 }")
  ptln(gc, "$REAL_ARRAY_TYPE_ID = type { double*, i32 }")
  ptln(gc, "$BOOL_ARRAY_TYPE_ID = type { i1*, i32 }")
  ptln(gc, "$STRING_ARRAY_TYPE_ID = type { i8*, i32 }")
  ptln(gc, "")
end

function generate_helpers(gc)
  for helper in HELPER_FUNCTIONS
    ptln(gc, helper)
  end
end

generate(program::String) = generate(program::String, IOBuffer())

function generate(program::String, io)
  token_sequence = scan_input(program)
  DEBUG && println("scanning finished")
  pc = ParsingContext(token_sequence)
  AST::Program = parse_input(token_sequence, pc)
  DEBUG && println("parsing finished")
  ac = AnalysisContext()
  ac.all_var_names_and_types = pc.all_var_names_and_types
  static_analysis(AST, ac)
  DEBUG && println("static analysis finished")
  # DEBUG && println("subroutines vector contains ", ac.subroutines)
  # DEBUG && println("unique calls contain ", ac.unique_calls)
  gc = GenerationContext(ac.subroutines, ac.unique_calls, pc.string_literals, io)
  gc.all_var_names_and_types = pc.all_var_names_and_types
  generate(AST, gc)
  DEBUG && println("codegen finished")
  reset_id_counter(gc)
end

function generate(AST::Program, gc::GenerationContext)
  st = AnalysisContext()
  generate_types(gc)
  generate_default_values(gc)
  generate_strings(gc)
  generate_subroutines(gc)
  add_predefined_names(gc)
  generate_header(gc)
  generate(AST.main, gc)
  generate_footer(gc)
end

function generate_default_values(gc::GenerationContext)
  all_types = Vector{MPType}(unique(type for (name, type) in gc.all_var_names_and_types))
  # TODO: exclude other types if necessary
  for type in setdiff(all_types, [MString], array_types)
    ptln(gc, "@default_$(type) = global $(mptype_to_llvm_type[type]) $(default_value_strings[type])")
  end
end

function add_predefined_names(gc::GenerationContext)
  # not implemented yet
end

function generate(b::Block, gc::GenerationContext)
  for statement in b.statements
    generate(statement, gc)
  end
end

function generate(d::Declaration, gc)
  mptype = d.var_type.true_type
  llvm_type = mptype_to_llvm_type[mptype]
  if mptype in array_types
    scalar_llvm_type = mptype_to_llvm_type[d.var_type.scalar_type]
    for id in d.unique_ids
      ptln(gc, "$id = alloca $(llvm_type)", 1)
      arr_id = create_id(gc, "arr_content")
      size_id = generate(d.var_type.size, gc)
      ptln(gc, "$arr_id = alloca $(scalar_llvm_type), i32 $size_id", 1)
      arr_ptr_id = create_id(gc, "arr_ptr")
      ptln(gc, "$arr_ptr_id = getelementptr $(llvm_type), $(llvm_type)* $id, i32 0, i32 0", 1)
      ptln(gc, "store $(scalar_llvm_type)* $arr_id, $(scalar_llvm_type)** $arr_ptr_id", 1)
      size_ptr_id = create_id(gc, "size_ptr")
      ptln(gc, "$size_ptr_id = getelementptr $(llvm_type), $(llvm_type)* $id, i32 0, i32 1", 1)
      ptln(gc, "store i32 $size_id, i32* $size_ptr_id", 1)
    end
  else
    for id in d.unique_ids
      ptln(gc, "$id = alloca $(llvm_type)", 1)
    end
  end
end

function generate(i::IfThen, gc::GenerationContext)
  cond_id = generate(i.condition, gc)
  then_label = create_id(gc, "then")
  end_label = create_id(gc, "end_if")
  ptln(gc, "br i1 $cond_id, label $then_label, label $end_label", 1)
  ptln(gc, "$(then_label[2:end]):")
  cond_id = generate(i.then_stmt, gc)
  ptln(gc, "br label $end_label", 1)
  ptln(gc, "$(end_label[2:end]):")
end

function generate(i::IfThenElse, gc::GenerationContext)
  cond_id = generate(i.condition, gc)
  then_label = create_id(gc, "then_")
  else_label = create_id(gc, "else_")
  end_label = create_id(gc, "end_if_")
  ptln(gc, "br i1 $cond_id, label $then_label, label $else_label", 1)
  ptln(gc, "$(then_label[2:end]):")
  generate(i.then_stmt, gc)
  ptln(gc, "br label $end_label", 1)
  ptln(gc, "$(else_label[2:end]):")
  generate(i.else_stmt, gc)
  ptln(gc, "br label $end_label", 1)
  ptln(gc, "$(end_label[2:end]):")
end

function generate(w::While, gc::GenerationContext)
  while_label = create_id(gc, "while_")
  do_label = create_id(gc, "do_")
  end_label = create_id(gc, "end_if_")
  ptln(gc, "br label $while_label", 1)
  ptln(gc, "$(while_label[2:end]):")
  cond_id = generate(w.condition, gc)
  ptln(gc, "br i1 $cond_id, label $do_label, label $end_label", 1)
  ptln(gc, "$(do_label[2:end]):")
  generate(w.do_stmt, gc)
  ptln(gc, "br label $while_label", 1)
  ptln(gc, "$(end_label[2:end]):")
end

function generate(c::CallStatement, gc::GenerationContext)
  generate_call(c, gc)
  return
end

function generate(c::CallFactor, gc::GenerationContext)
  generate_call(c, gc)
end

function generate_call(c::Call, gc::GenerationContext)
  subroutine::Subroutine = c.subroutine
  ret_type = subroutine.ret_type.true_type

  explicit_arg_ids = [generate(arg, gc) for arg in c.arguments]
  explicit_arg_types = [mptype_to_llvm_type[arg.type] for arg in c.arguments ]

  llvm_function_name = subroutine.llvm_function_name

  implicit_arg_ids, implicit_arg_types = get_implicit_ids_and_llvm_types(c.implicit_params, gc)

  arg_ids = [explicit_arg_ids; implicit_arg_ids]
  arg_types = [explicit_arg_types; implicit_arg_types]

  arg_strs = ["$type $id" for (type, id) in zip(arg_types, arg_ids)]
  arg_string = join(arg_strs,  ", ")

  ret_id = create_id(gc, "ret")
  llvm_ret_type = mptype_to_llvm_type[ret_type]
  if ret_type == MNothing  # Subroutine is a procedure
    ptln(gc, "call $llvm_ret_type $(llvm_function_name)($arg_string)", 1)
  else
    ptln(gc, "$ret_id = call $llvm_ret_type $(llvm_function_name)($arg_string)", 1)
  end
  return ret_id
end

function get_implicit_ids_and_llvm_types(entries::Vector{SymTableEntry}, gc::GenerationContext)
  ids = []
  types = []
  for entry in entries
    if entry.var_type ∈ ref_types
      push!(ids, entry.val_identifier)
      push!(types, entry.var_type)
    else
      push!(ids, get_ref(entry.val_identifier, entry.var_type, gc))
      push!(types, entry.var_type)
    end
  end
  types = map(t->mptype_to_llvm_type[scalar_to_ref_type[t]], types)
  return ids, types
end

function get_ref(id::String, mptype::MPType, gc::GenerationContext)
  ref_id = create_id(gc, "ref_$(mptype)")
  llvm_type = mptype_to_llvm_type[mptype]
  ptln(gc, "$ref_id = alloca $llvm_type", 1)
  return ref_id
end

function generate(r::Return, gc::GenerationContext)
  val_id = generate(r.value, gc)
  llvm_ret_type = mptype_to_llvm_type[r.value.type]
  ptln(gc, "ret $llvm_ret_type $val_id", 1)
end

function generate(a::Assignment, gc::GenerationContext)
  var_type = a.var_type
  value_llvm_type = mptype_to_llvm_type[a.value.type]
  value_id = generate(a.value, gc)
  variable_pointer = a.var_unique_id
  if var_type ∈ ref_types
    ptln(gc, "store $value_llvm_type $value_id, $value_llvm_type* $variable_pointer", 1)
  elseif var_type ∈ ref_types
    ptln(gc, "$(a.var_unique_id) = $(get_identity(var_type)) $(value_id)", 1)
  else  # Array type
    scalar_type = array_type_to_scalar_type[a.var_type]
    scalar_llvm_type = mptype_to_llvm_type[scalar_type]
    array_llvm_type = mptype_to_llvm_type[a.var_type]
    index_id = generate(a.array_index, gc)
    arr_ptr_id = create_id(gc, "arr_ptr")
    cast_array_id = create_id(gc, "cast_array_ptr")
    ptln(gc, "$cast_array_id = bitcast $array_llvm_type* $variable_pointer to $ARRAY_TYPE_ID*", 1)
    ptln(gc, "$arr_ptr_id = call i8* $GET_ARRAY_PTR_ID($ARRAY_TYPE_ID* $cast_array_id)", 1)
    backcast_arr_id = create_id(gc, "backcast")
    ptln(gc, "$backcast_arr_id = bitcast i8* $arr_ptr_id to $scalar_llvm_type*", 1)
    elem_ptr_id = create_id(gc, "elem_ptr")
    ptln(gc, "$elem_ptr_id = getelementptr $scalar_llvm_type, $scalar_llvm_type* $backcast_arr_id, i32 $index_id", 1)
    ptln(gc, "store $scalar_llvm_type $value_id, $scalar_llvm_type* $elem_ptr_id", 1)
  end
end

function get_identity(type::MPType)
  if type == MInt
    return "add i32 0,"
  elseif type == MReal
    return "fadd double 0.0,"
  elseif type == MBool
    return "add i1 0,"
  elseif type == MString
    println("identity function for strings not implemented yet")
    return
  end
  println("identity function for $(type) not implemented yet")
end

function generate(r::Read, gc::GenerationContext)
  for (var::Variable, type::MPType) in zip(r.variables, r.var_types)
    throw(StaticAnalysisException(
      "Code generation for Read statement not implemented."
    ))
  end
end

function generate(p::Write, gc::GenerationContext)
  for arg in p.arguments
    arg_id = generate(arg, gc)
    if arg.type == MBool
      ptln(gc, "call void @print_bool(i1 $(arg_id))", 1)
    elseif arg.type ∈ [MInt, MReal]
      arg_type = mptype_to_llvm_type[arg.type]
      envelope_id = arg.type == MInt ? INT_STR_ID : REAL_STR_ID
      pt(gc, "call i32 (i8*, ...) ", 1)
      ptln(gc, "@printf(i8* getelementptr inbounds ([3 x i8], [3 x i8]* $(envelope_id), i32 0, i32 0), $(arg_type) $(arg_id))")
    elseif arg.type == MString
      str_ptr_id = create_id(gc, "str_ptr")
      ptln(gc, "$str_ptr_id = getelementptr $STRING_TYPE_ID, $STRING_TYPE_ID* $arg_id, i32 0, i32 0", 1)
      str_id = create_id(gc, "str")
      ptln(gc, "$str_id = load i8*, i8** $str_ptr_id", 1)
      ptln(gc, "call i32 (i8*, ...) @printf(i8* getelementptr ([3 x i8], [3 x i8]* $STR_STR_ID, i32 0, i32 0), i8* $str_id)", 1)
    end
  end
  ptln(gc, "call i32 @puts(i8* getelementptr inbounds ([1 x i8], [1 x i8]* $EMPTY_STR_ID, i32 0, i32 0))", 1)
end

function generate(b::BinaryOperation, gc::GenerationContext)
  ret_llvm_type = mptype_to_llvm_type[b.type]
  ret_id = create_id(gc, "$(b.type)")
  left = generate(b.left, gc)
  right = generate(b.right, gc)
  if b.left.type == MString return generate_string_op(b.op, left, right, gc) end
  operand_llvm_type = mptype_to_llvm_type[b.left.type]
  opcode = binary_op_and_type_to_opcode[(b.op, b.left.type)]
  ptln(gc, "$ret_id = $opcode $operand_llvm_type $left, $right", 1)
  return ret_id
end

function generate(u::UnaryOperation, gc::GenerationContext)
  ret_llvm_type = mptype_to_llvm_type[u.type]
  ret_id = create_id(gc, "$(u.type)")
  operand = generate(u.operand, gc)
  operand_llvm_type = mptype_to_llvm_type[u.operand.type]
  opcode, first_arg = unary_op_and_type_to_opcode_and_first_arg[(u.op, u.operand.type)]
  ptln(gc, "$ret_id = $opcode $operand_llvm_type $operand, $first_arg", 1)
  return ret_id
end

function generate(g::GetRef, gc::GenerationContext)
  return g.operand.variable_entry.val_identifier
end

function generate_string_op(op::TokenClass, left::String, right::String, gc::GenerationContext)
  if op == plus
    return concat_strings(left, right, gc)
  else
    return string_comparison(op, left, right, gc)
  end
end

function concat_strings(left::String, right::String, gc::GenerationContext)
  ret_id = create_id(gc, "concat")
  length_id = create_id(gc, "str_len")
  new_str_id = create_id(gc, "new_str")
  ptln(gc, "$ret_id = alloca $STRING_TYPE_ID", 1)
  ptln(gc, "$length_id = call i32 @combined_length($STRING_TYPE_ID* $left, $STRING_TYPE_ID* $right)", 1)
  ptln(gc, "$new_str_id = alloca i8, i32 $length_id", 1)
  ptln(gc, "call void $CONCAT_ID($STRING_TYPE_ID* $ret_id, i8* $new_str_id, $STRING_TYPE_ID* $left, $STRING_TYPE_ID* $right)", 1)
  return ret_id
end

function string_comparison(op::TokenClass, left::String, right::String, gc::GenerationContext)
  ret_id = create_id(gc, "str_cmp")
  comp_tmp_id = create_id(gc, "comp_tmp")
  ptln(gc, "$comp_tmp_id = call i32 $COMP_STRS_ID($STRING_TYPE_ID* $left, $STRING_TYPE_ID* $right)", 1)
  cond = op_and_type_to_cond[(op, MInt)]
  ptln(gc, "$ret_id = icmp $cond i32 $comp_tmp_id, 0", 1)
  return ret_id
end

function generate(l::LiteralFactor, gc::GenerationContext)
  ret_id = ""
  if l.type ∈ [MInt, MReal, MBool]
    return l.token.lexeme
  elseif l.type == MString
    ret_id = create_id(gc, "str_lit")
    str_len = length(l.token.lexeme) + 1  # Account for terminating null
    ptln(gc, "$ret_id = alloca $STRING_TYPE_ID", 1)
    str_ptr_id = create_id(gc, "str_ptr")
    lit_as_ptr_id = create_id(gc, "lit_ptr")
    ptln(gc, "$lit_as_ptr_id = bitcast [$str_len x i8]* $(l.unique_id) to i8*", 1)
    ptln(gc, "$str_ptr_id = getelementptr $STRING_TYPE_ID, $STRING_TYPE_ID* $ret_id, i32 0, i32 0", 1)
    ptln(gc, "store i8* $lit_as_ptr_id, i8** $str_ptr_id", 1)
    str_len_ptr_id = create_id(gc, "str_len")
    ptln(gc, "$str_len_ptr_id = getelementptr $STRING_TYPE_ID, $STRING_TYPE_ID* $ret_id, i32 0, i32 1", 1)
    ptln(gc, "store i32 $(str_len), i32* $str_len_ptr_id", 1)
  end
  return ret_id
end

function generate(p::ParenFactor, gc::GenerationContext)
  return generate(p.expression, gc)
end

function generate(v::VariableFactor, gc::GenerationContext)
  llvm_type = mptype_to_llvm_type[v.type]
  entry::SymTableEntry = v.variable_entry
  ret_id = create_id(gc, "var_val")
  if v.variable_entry.var_type ∉ ref_types
    return entry.val_identifier
  else
    ptln(gc, "$ret_id = load $llvm_type, $llvm_type* $(entry.val_identifier)", 1)
  end
  return ret_id
end

function generate(s::SizeFactor, gc::GenerationContext)
  ret_id = create_id(gc, "array_size")
  bitcast_id = create_id(gc, "cast_to_array")
  llvm_type = mptype_to_llvm_type[s.array.type]
  array_id = generate(s.array, gc)
  ptln(gc, "$bitcast_id = bitcast $(llvm_type)* $array_id to $ARRAY_TYPE_ID*" , 1)
  ptln(gc, "$ret_id = call i32 $GET_ARRAY_SIZE_ID($ARRAY_TYPE_ID* $bitcast_id)", 1)
  return ret_id
end

function generate(a::ArrayAccessFactor, gc::GenerationContext)
  var_entry::SymTableEntry = a.variable_entry
  val_id = var_entry.val_identifier
  scalar_type = array_type_to_scalar_type[var_entry.var_type]
  llvm_type = mptype_to_llvm_type[scalar_type]
  array_llvm_type = mptype_to_llvm_type[var_entry.var_type]
  index_id = generate(a.index, gc)
  arr_ptr_id = create_id(gc, "arr_ptr")
  ret_id = create_id(gc, "array_el")
  len_id = create_id(gc, "array_len_ptr")
  cast_array_id = create_id(gc, "cast_array_ptr")
  ptln(gc, "$cast_array_id = bitcast $array_llvm_type* $val_id to $ARRAY_TYPE_ID*", 1)
  ptln(gc, "$len_id = call i32 $GET_ARRAY_SIZE_ID($ARRAY_TYPE_ID* $cast_array_id)", 1)
  ptln(gc, "$arr_ptr_id = call i8* $GET_ARRAY_PTR_ID($ARRAY_TYPE_ID* $cast_array_id)", 1)
  backcast_arr_id = create_id(gc, "backcast")
  ptln(gc, "$backcast_arr_id = bitcast i8* $arr_ptr_id to $llvm_type*", 1)
  elem_ptr_id = create_id(gc, "elem_ptr")
  ptln(gc, "$elem_ptr_id = getelementptr $llvm_type, $llvm_type* $backcast_arr_id, i32 $index_id", 1)
  ptln(gc, "$ret_id = load $llvm_type, $llvm_type* $elem_ptr_id", 1)
  return ret_id
end

function generate(n::NotFactor, gc::GenerationContext)
  arg_id = generate(n.argument, gc)
  ret_id = create_id(gc, "not")
  ptln(gc, "$ret_id = add i1 $arg_id, 1", 1)
  return ret_id
end

function get_subroutine_fingerprint(s::Subroutine, implicits::Vector{SymTableEntry})
  explicits = ["$(parameter_to_llvmtype(p)) $(p.name)" for p::Parameter in s.params]
  implicits = ["$(mptype_to_llvm_type[s.var_type]) $(s.var_name)" for s::SymTableEntry in implicits]
  "$(s.name)/$(join(explicits, ", "))/$(join(implicits, ", "))"
end

max_stars_repo_name: juhamyllari/minipascal-julia

max_issues_repo_name: juhamyllari/minipascal-julia

max_forks_repo_name: juhamyllari/minipascal-julia


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

using Markdown
using InteractiveUtils

# ╔═╡ 480caab4-f7ee-4dcf-9218-a0d877eafc54
module FreeFall

using Parameters

"""Problem type"""
Base.@kwdef struct Problem{G, Y0, V0, TS}
    g::G = 9.80665
    y0::Y0 = 0.0
    v0::V0 = 30.0
    tspan::TS = (0.0, 8.0)
end

"""Algorithm types"""
Base.@kwdef struct EulerMethod{T}  dt::T = 0.1 end
Base.@kwdef struct ExactFormula{T} dt::T = 0.1 end
default_algorithm(::Problem) = EulerMethod()

"""Solution type"""
struct Solution{Y, V, T, P<:Problem, A} y::Y; v::V; t::T; prob::P; alg::A end

"""Solver function"""
solve(prob::Problem) = solve(prob, default_algorithm(prob))

function solve(prob::Problem, alg::ExactFormula)
    # (; g, y0, v0, tspan) = prob
    # (; dt) = alg
    @unpack g, y0, v0, tspan = prob
    @unpack dt = alg
    t0, t1 = tspan
    t = range(t0, t1 + dt/2; step = dt)
    y(t) = y0 + v0*(t - t0) - g*(t - t0)^2/2
    v(t) = v0 - g*(t - t0)
    Solution(y.(t), v.(t), t, prob, alg)
end

function solve(prob::Problem, alg::EulerMethod)
    # (; g, y0, v0, tspan) = prob
    # (; dt) = alg
    @unpack g, y0, v0, tspan = prob
    @unpack dt = alg
    t0, t1 = tspan
    t = range(t0, t1 + dt/2; step = dt)
    n = length(t)
    y = Vector{typeof(y0)}(undef, n)
    v = Vector{typeof(v0)}(undef, n)
    y[1] = y0
    v[1] = v0
    for i in 1:n-1
        v[i+1] = v[i] - g*dt
        y[i+1] = y[i] + v[i]*dt
    end
    Solution(y, v, t, prob, alg)
end

end

# ╔═╡ 5ff9b788-8b60-4add-acc8-1967d5d9a274
using Plots

# ╔═╡ daf3cfd2-bebe-47cd-a878-6d2ebf758557
module SomeExtension

using Parameters
using ..FreeFall
using ..FreeFall: Problem, Solution

Base.@kwdef struct Symplectic2ndOrder{T}  dt::T = 0.1 end

function FreeFall.solve(prob::Problem, alg::Symplectic2ndOrder)
    # (; g, y0, v0, tspan) = prob
    # (; dt) = alg
    @unpack g, y0, v0, tspan = prob
    @unpack dt = alg
    t0, t1 = tspan
    t = range(t0, t1 + dt/2; step = dt)
    n = length(t)
    y = Vector{typeof(y0)}(undef, n)
    v = Vector{typeof(v0)}(undef, n)
    y[1] = y0
    v[1] = v0
    for i in 1:n-1
        ytmp = y[i] + v[i]*dt/2
        v[i+1] = v[i] - g*dt
        y[i+1] = ytmp + v[i+1]*dt/2
    end
    Solution(y, v, t, prob, alg)
end

end

# ╔═╡ aa55b47a-cc03-48e6-9902-36594c323f47
using MonteCarloMeasurements

# ╔═╡ 4a53ef80-e5dd-11eb-2128-791c6f72ed29
md"""
# Problem-Algorithm-Solver pattern

* Gen Kuroki
* 2021-07-11
* https://discourse.julialang.org/t/function-depending-on-the-global-variable-inside-module/64322/10

A code will be more readable and efficient if the global variables that store the parameters of a problem are passed as arguments to functions _everytime_.

However, many people find it troublesome to pass the parameters as arguments to the function _everytime_.

In order to resolve this misunderstanding, they can try the problem-algorithm-solver pattern explained below.

In order to use the `(; a, b, c) = p` syntax, require VERSION ≥ v"1.7.0-beta" [#39285](https://github.com/JuliaLang/julia/pull/39285).
"""

# ╔═╡ 677be629-7a93-4cd7-8083-59784bc80cb1
#VERSION ≥ v"1.7.0-beta" #> true

# ╔═╡ 4b944036-f230-434d-aa3b-dca1da8b150f
md"""
## A minimal working example of the problem-algorithm-solver pattern
"""

# ╔═╡ 53fee11f-fdf9-44de-a1ed-f2fb5261d0a9
md"""
This module will calculate the free-fall motion under a uniform gravity potential.

* Problem: The parameters of problems are stored in objects of type `FreeFall.Problem`.

* Algorithm: The parameters of algorithms are stored in objects of types `FreeFall.EulerMethod` and of `FreeFall.ExactFormula`.

* Solver function: The function `FreeFall.solve(prob::FreeFall.Problem, alg)` returns the object of type `FreeFall.Solution` obtained by solving the problem `prob` with the algorithm `alg`.

Note that the `(; a, b, c) = p` syntax of Julia ≥ v1.7, is very useful for extracting the contents of the object that contains parameters. (You can also do the same thing with `@unpack` in the exellent well-known package [Parameters.jl](https://github.com/mauro3/Parameters.jl).)
"""

# ╔═╡ 09eb78f8-153c-46d2-bbf2-6b2b048e8fda
md"""
### Example 1: Compare a numerical solution and an exact one
"""

# ╔═╡ 8912cf7e-5544-4eac-b7d0-21fac0872f27
let
	earth = FreeFall.Problem()
	sol_euler = FreeFall.solve(earth)
	sol_exact = FreeFall.solve(earth, FreeFall.ExactFormula())

	plot(sol_euler.t, sol_euler.y; label="Euler's method (dt = $(sol_euler.alg.dt))", ls=:auto)
	plot!(sol_exact.t, sol_exact.y; label="exact solution", ls=:auto)
	title!("On the Earth"; xlabel="t", legend=:bottomleft)
end

# ╔═╡ c41f04d8-9d88-474b-a877-2f7ffc01aa94
md"""
Since the time step `dt = 0.1` is not enough small, the error of the Euler method is rather large.
"""

# ╔═╡ 87fcd645-a028-4360-aaa6-036f53729256
md"""
### Example 2: Change the algorithm parameter `dt` smaller
"""

# ╔═╡ f62033a4-c8c8-4975-9b83-37633f6b9d28
let
	earth = FreeFall.Problem()
	sol_euler = FreeFall.solve(earth, FreeFall.EulerMethod(dt = 0.01))
	sol_exact = FreeFall.solve(earth, FreeFall.ExactFormula())

	plot(sol_euler.t, sol_euler.y; label="Euler's method (dt = $(sol_euler.alg.dt))", ls=:auto)
	plot!(sol_exact.t, sol_exact.y; label="exact solution", ls=:auto)
	title!("On the Earth"; xlabel="t", legend=:bottomleft)
end

# ╔═╡ b154e7d1-77da-4af2-ad0b-b447977b9770
md"""
### Example 3: Change the problem parameter `g` to the gravitational acceleration on the moon
"""

# ╔═╡ 3a04adc7-e64c-4669-8e50-de2e290623e4
let
	moon = FreeFall.Problem(g = 1.62, tspan = (0.0, 40.0))
	sol_euler = FreeFall.solve(moon)
	sol_exact = FreeFall.solve(moon, FreeFall.ExactFormula(dt = sol_euler.alg.dt))

	plot(sol_euler.t, sol_euler.y; label="Euler's method (dt = $(sol_euler.alg.dt))", ls=:auto)
	plot!(sol_exact.t, sol_exact.y; label="exact solution", ls=:auto)
	title!("On the Moon"; xlabel="t", legend=:bottomleft)
end

# ╔═╡ cf7ee4ba-636b-411a-b4bd-9a92b1ba4e85
md"""
## Extensibility of the Julia ecosystem

A different author of the `FreeFall` module can define , in the `SomeExtension` module, a new algorithm type and a new method of the `FreeFall.solve` function.  Both a new type and a new method!
"""

# ╔═╡ 1ea81f0b-5152-48cd-b477-2f57d2fc89b4
md"""
__If you get an error "UndefVarError: XXX not defined" in the following cell, run it again.__
"""

# ╔═╡ 1ce33cd2-be68-4623-9ec9-fe03cc4f3769
let
	earth = FreeFall.Problem()
	sol_sympl = FreeFall.solve(earth, SomeExtension.Symplectic2ndOrder(dt = 2.0))
	sol_exact = FreeFall.solve(earth, FreeFall.ExactFormula())

	plot(sol_sympl.t, sol_sympl.y; label="2nd order symplectic (dt = $(sol_sympl.alg.dt))", ls=:auto)
	plot!(sol_exact.t, sol_exact.y; label="exact solution", ls=:auto)
	title!("On the Earth"; xlabel="t", legend=:bottomleft)
end

# ╔═╡ d7b1fbe8-b262-4c9a-b7ca-c1110844b3bd
md"""
The second-order symplectic method gives exact discretizations of the free-fall motions under uniform gravity potentials.
"""

# ╔═╡ 577e1048-46be-4089-9f7e-dd56a14e9398
md"""
## Composability of the Julia ecosystem

By combining the MonteCarloMeasurements.jl package with the modules given above, we can plot the behavior of the system for perturbations of the initial value.  We did not intend for them to be used in this way!
"""

# ╔═╡ 8fb076cd-6086-40dc-ae5b-545f01d42545
let
	earth = FreeFall.Problem(y0 = 0.0 ± 0.0, v0 = 30.0 ± 1.0)
	sol_euler = FreeFall.solve(earth)
	sol_sympl = FreeFall.solve(earth, SomeExtension.Symplectic2ndOrder(dt = 2.0))
	sol_exact = FreeFall.solve(earth, FreeFall.ExactFormula())

	ylim = (-100, 60)
	P = plot(sol_euler.t, sol_euler.y; label="Euler's method (dt = $(sol_euler.alg.dt))", ls=:auto)
	title!("On the Earth"; xlabel="t", legend=:bottomleft, ylim)
	Q = plot(sol_sympl.t, sol_sympl.y; label="2nd order symplectic (dt = $(sol_sympl.alg.dt))", ls=:auto)
	title!("On the Earth"; xlabel="t", legend=:bottomleft, ylim)
	R = plot(sol_exact.t, sol_exact.y; label="exact solution", ls=:auto)
	title!("On the Earth"; xlabel="t", legend=:bottomleft, ylim)
	plot(P, Q, R; size=(720, 600))
end

# ╔═╡ dd8f2a48-f430-49fa-8067-35e88b27a4bc

# ╔═╡ 00000000-0000-0000-0000-000000000001
PLUTO_PROJECT_TOML_CONTENTS = """
[deps]
MonteCarloMeasurements = "0987c9cc-fe09-11e8-30f0-b96dd679fdca"
Parameters = "d96e819e-fc66-5662-9728-84c9c7592b0a"
Plots = "91a5bcdd-55d7-5caf-9e0b-520d859cae80"

[compat]
MonteCarloMeasurements = "~0.10.4"
Parameters = "~0.12.2"
Plots = "~1.19.1"
"""

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

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

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

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

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

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

[[Bzip2_jll]]
deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"]
git-tree-sha1 = "c3598e525718abcc440f69cc6d5f60dda0a1b61e"
uuid = "6e34b625-4abd-537c-b88f-471c36dfa7a0"
version = "1.0.6+5"

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

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

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

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

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

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

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

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

[[DataAPI]]
git-tree-sha1 = "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"

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

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

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

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

[[Distributions]]
deps = ["FillArrays", "LinearAlgebra", "PDMats", "Printf", "QuadGK", "Random", "SparseArrays", "SpecialFunctions", "Statistics", "StatsBase", "StatsFuns"]
git-tree-sha1 = "3889f646423ce91dd1055a76317e9a1d3a23fff1"
uuid = "31c24e10-a181-5473-b8eb-7969acd0382f"
version = "0.25.11"

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

[[EarCut_jll]]
deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"]
git-tree-sha1 = "92d8f9f208637e8d2d28c664051a00569c01493d"
uuid = "5ae413db-bbd1-5e63-b57d-d24a61df00f5"
version = "2.1.5+1"

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

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

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

[[FillArrays]]
deps = ["LinearAlgebra", "Random", "SparseArrays"]
git-tree-sha1 = "25b9cc23ba3303de0ad2eac03f840de9104c9253"
uuid = "1a297f60-69ca-5386-bcde-b61e274b549b"
version = "0.12.0"

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

[[Fontconfig_jll]]
deps = ["Artifacts", "Bzip2_jll", "Expat_jll", "FreeType2_jll", "JLLWrappers", "Libdl", "Libuuid_jll", "Pkg", "Zlib_jll"]
git-tree-sha1 = "35895cf184ceaab11fd778b4590144034a167a2f"
uuid = "a3f928ae-7b40-5064-980b-68af3947d34b"
version = "2.13.1+14"

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

[[FreeType2_jll]]
deps = ["Artifacts", "Bzip2_jll", "JLLWrappers", "Libdl", "Pkg", "Zlib_jll"]
git-tree-sha1 = "cbd58c9deb1d304f5a245a0b7eb841a2560cfec6"
uuid = "d7e528f0-a631-5988-bf34-fe36492bcfd7"
version = "2.10.1+5"

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

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

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

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

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

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

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

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

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

[[Hwloc]]
deps = ["Hwloc_jll"]
git-tree-sha1 = "92d99146066c5c6888d5a3abc871e6a214388b91"
uuid = "0e44f5e4-bd66-52a0-8798-143a42290a1d"
version = "2.0.0"

[[Hwloc_jll]]
deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"]
git-tree-sha1 = "3395d4d4aeb3c9d31f5929d32760d8baeee88aaf"
uuid = "e33a78d0-f292-5ffc-b300-72abe9b543c8"
version = "2.5.0+0"

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

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

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

[[IterTools]]
git-tree-sha1 = "05110a2ab1fc5f932622ffea2a003221f4782c18"
uuid = "c8e1da08-722c-5040-9ed9-7db0dc04731e"
version = "1.3.0"

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

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

[[JSON]]
deps = ["Dates", "Mmap", "Parsers", "Unicode"]
git-tree-sha1 = "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"

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

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

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

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

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

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

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

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

[[LibVPX_jll]]
deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"]
git-tree-sha1 = "12ee7e23fa4d18361e7c2cde8f8337d4c3101bc7"
uuid = "dd192d2f-8180-539f-9fb4-cc70b1dcf69a"
version = "1.10.0+0"

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

[[MonteCarloMeasurements]]
deps = ["Distributed", "Distributions", "LinearAlgebra", "MacroTools", "Random", "RecipesBase", "Requires", "SLEEFPirates", "StaticArrays", "Statistics", "StatsBase", "Test"]
git-tree-sha1 = "cdc1eb7c5a1154c15dfe7368f1ca1f693dc40202"
uuid = "0987c9cc-fe09-11e8-30f0-b96dd679fdca"
version = "0.10.4"

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

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

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

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

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

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

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

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

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

[[PDMats]]
deps = ["LinearAlgebra", "SparseArrays", "SuiteSparse"]
git-tree-sha1 = "4dd403333bcf0909341cfe57ec115152f937d7d8"
uuid = "90014a1f-27ba-587c-ab20-58faa44d9150"
version = "0.11.1"

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

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

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

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

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

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

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

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

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

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

[[QuadGK]]
deps = ["DataStructures", "LinearAlgebra"]
git-tree-sha1 = "12fbe86da16df6679be7521dfb39fbc861e1dc7b"
uuid = "1fd47b50-473d-5c70-9696-f719f8f3bcdc"
version = "2.4.1"

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

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

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

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

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

[[Rmath]]
deps = ["Random", "Rmath_jll"]
git-tree-sha1 = "bf3188feca147ce108c76ad82c2792c57abe7b1f"
uuid = "79098fc4-a85e-5d69-aa6a-4863f24498fa"
version = "0.7.0"

[[Rmath_jll]]
deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"]
git-tree-sha1 = "68db32dff12bb6127bac73c209881191bf0efbb7"
uuid = "f50d1b31-88e8-58de-be2c-1cc44531875f"
version = "0.3.0+0"

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

[[SLEEFPirates]]
deps = ["IfElse", "Static", "VectorizationBase"]
git-tree-sha1 = "da6d214ffc85b1292f300649ef86d3c4f9aaf25d"
uuid = "476501e8-09a2-5ece-8869-fb82de89a1fa"
version = "0.6.22"

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

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

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

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

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

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

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

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

[[Static]]
deps = ["IfElse"]
git-tree-sha1 = "2740ea27b66a41f9d213561a04573da5d3823d4b"
uuid = "aedffcd0-7271-4cad-89d0-dc628f76c6d3"
version = "0.2.5"

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

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

[[StatsFuns]]
deps = ["LogExpFunctions", "Rmath", "SpecialFunctions"]
git-tree-sha1 = "30cd8c360c54081f806b1ee14d2eecbef3c04c49"
uuid = "4c63d2b9-4356-54db-8cca-17b64c39e42c"
version = "0.9.8"

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

[[SuiteSparse]]
deps = ["Libdl", "LinearAlgebra", "Serialization", "SparseArrays"]
uuid = "4607b0f0-06f3-5cda-b6b1-a6196a1729e9"

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

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

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

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

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

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

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

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

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

[[VectorizationBase]]
deps = ["ArrayInterface", "Hwloc", "IfElse", "Libdl", "LinearAlgebra", "Static"]
git-tree-sha1 = "1b5a87cc55f5e4b072eca187b115f1c01c61e092"
uuid = "3d5dd08c-fd9d-11e8-17fa-ed2836048c2f"
version = "0.20.20"

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

[[Wayland_protocols_jll]]
deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg", "Wayland_jll"]
git-tree-sha1 = "2839f1c1296940218e35df0bbb220f2a79686670"
uuid = "2381bf8a-dfd0-557d-9999-79630e7b1b91"
version = "1.18.0+4"

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

[[x264_jll]]
deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"]
git-tree-sha1 = "d713c1ce4deac133e3334ee12f4adff07f81778f"
uuid = "1270edf5-f2f9-52d2-97e9-ab00b5d0237a"
version = "2020.7.14+2"

[[x265_jll]]
deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"]
git-tree-sha1 = "487da2f8f2f0c8ee0e83f39d13037d6bbf0a45ab"
uuid = "dfaa095f-4041-5dcd-9319-2fabd8486b76"
version = "3.0.0+3"

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

# ╔═╡ Cell order:
# ╟─4a53ef80-e5dd-11eb-2128-791c6f72ed29
# ╠═677be629-7a93-4cd7-8083-59784bc80cb1
# ╟─4b944036-f230-434d-aa3b-dca1da8b150f
# ╠═480caab4-f7ee-4dcf-9218-a0d877eafc54
# ╟─53fee11f-fdf9-44de-a1ed-f2fb5261d0a9
# ╟─09eb78f8-153c-46d2-bbf2-6b2b048e8fda
# ╠═5ff9b788-8b60-4add-acc8-1967d5d9a274
# ╠═8912cf7e-5544-4eac-b7d0-21fac0872f27
# ╟─c41f04d8-9d88-474b-a877-2f7ffc01aa94
# ╟─87fcd645-a028-4360-aaa6-036f53729256
# ╠═f62033a4-c8c8-4975-9b83-37633f6b9d28
# ╟─b154e7d1-77da-4af2-ad0b-b447977b9770
# ╠═3a04adc7-e64c-4669-8e50-de2e290623e4
# ╟─cf7ee4ba-636b-411a-b4bd-9a92b1ba4e85
# ╟─1ea81f0b-5152-48cd-b477-2f57d2fc89b4
# ╠═daf3cfd2-bebe-47cd-a878-6d2ebf758557
# ╠═1ce33cd2-be68-4623-9ec9-fe03cc4f3769
# ╟─d7b1fbe8-b262-4c9a-b7ca-c1110844b3bd
# ╟─577e1048-46be-4089-9f7e-dd56a14e9398
# ╠═aa55b47a-cc03-48e6-9902-36594c323f47
# ╠═8fb076cd-6086-40dc-ae5b-545f01d42545
# ╠═dd8f2a48-f430-49fa-8067-35e88b27a4bc
# ╟─00000000-0000-0000-0000-000000000001
# ╟─00000000-0000-0000-0000-000000000002

max_stars_repo_path: 0011/Problem-Algorithm-Solver pattern - Pluto Julia 1.6.jl

max_stars_repo_name: genkuroki/public

max_issues_repo_name: genkuroki/public

max_forks_repo_name: genkuroki/public


text:
struct SettlementPhysical <: SettlementType end
struct SettlementCash <: SettlementType end

mutable struct SwaptionResults
  value::Float64
  additionalResults::Dict{String, Float64}
end

SwaptionResults() = SwaptionResults(0.0, Dict("spreadCorrection" => 0.0, "strike" => 0.0, "atmForward" => 0.0, "annuity" => 0.0, "swapLength" => 0.0, "stdDev" => 0.0,
                                              "vega" => 0.0))

function reset!(results::SwaptionResults)
  results.value = 0.0
  for k in keys(results.additionalResults)
    results.additionalResults[k] = 0.0
  end

  return results
end

mutable struct Swaption{E <: Exercise, S <: SettlementType, P <: PricingEngine, VS <: VanillaSwap} <: Option{E}
  lazyMixin::LazyMixin
  swap::VS
  exercise::E
  delivery::S
  results::SwaptionResults
  pricingEngine::P

  # function call{E, S}(::Type{Swaption}, lz::LazyMixin, swap::VanillaSwap, exercise::E, delivery::S, results::SwaptionResults)
  #   new{E, S, PricingEngine}(lz, swap, exercise, delivery, results)
  # end

  # function call{E, S, P}(::Type{Swaption}, lz::LazyMixin, swap::VanillaSwap, exercise::E, delivery::S, results::SwaptionResults, pricingEngine::P = NullSwaptionEngine())
  #   new{E, S, P}(lz, swap, exercise, delivery, results, pricingEngine)
  # end
  # Swaption{E, S}(lz::LazyMixin, swap::VanillaSwap, exercise::E, delivery::S, results::SwaptionResults, pricingEngine::P = NullSwaptionEngine()) =
  #   new{E, S, P}(lz, swap, exercise, delivery, results, pricingEngine)
end

Swaption(swap::VS, exercise::E) where {VS <: VanillaSwap, E <: Exercise} = Swaption{E, SettlementPhysical, NullSwaptionEngine, VS}(LazyMixin(), swap, exercise, SettlementPhysical(), SwaptionResults(), NullSwaptionEngine())

Swaption(swap::VS, exercise::E, pe::P) where {E <: Exercise, P <: PricingEngine, VS <: VanillaSwap} = Swaption{E, SettlementPhysical, P, VS}(LazyMixin(), swap, exercise, SettlementPhysical(), SwaptionResults(), pe)

mutable struct NonstandardSwaption{E <: Exercise, S <: SettlementType, P <: PricingEngine, NS <: NonstandardSwap} <: Option{E}
  lazyMixin::LazyMixin
  swap::NS
  exercise::E
  delivery::S
  results::SwaptionResults
  pricingEngine::P

  # function call{E, S, P}(::Type{NonstandardSwaption}, lz::LazyMixin, swap::NonstandardSwap, exercise::E, delivery::S, results::SwaptionResults, pricingEngine::P = NullSwaptionEngine())
  #   new{E, S, P}(lz, swap, exercise, delivery, results, pricingEngine)
  # end
end

NonstandardSwaption(swap::NS, exercise::E) where {E <: Exercise, NS <: NonstandardSwap} = NonstandardSwaption{E, SettlementPhysical, NullSwaptionEngine, NS}(LazyMixin(), swap, exercise,
                                                                      SettlementPhysical(), SwaptionResults(), NullSwaptionEngine())
NonstandardSwaption(swap::NS, exercise::E, pe::P) where {E <: Exercise, P <: PricingEngine, NS <: NonstandardSwap} =
    NonstandardSwaption{E, SettlementPhysical, P, NS}(LazyMixin(), swap, exercise, SettlementPhysical(), SwaptionResults(), pe)

function perform_calculations!(swaption::Union{Swaption, NonstandardSwaption})
  reset!(swaption.results)
  _calculate!(swaption.pricingEngine, swaption)

  return swaption
end

function npv(swaption::Union{Swaption, NonstandardSwaption})
  calculate!(swaption)

  return swaption.results.value
end

function calibration_basket(swaption::NonstandardSwaption, swaptionEngine::PricingEngine, swapIndex::SwapIndex, swaptionVol::SwaptionVolatilityStructure,
                            basketType::CalibrationBasketType)
  return calibration_basket(swaptionEngine, swaption, swapIndex, swaptionVol, basketType)
end

function clone(swaption::Swaption, pe::PricingEngine = swaption.pricingEngine)
  lazyMixin, res = pe == swaption.pricingEngine ? (swaption.lazyMixin, swaption.results) : (LazyMixin(), SwaptionResults())

  newSwaption = Swaption(lazyMixin, swaption.swap, swaption.exercise, swaption.delivery, res, pe)

  return newSwaption
end

get_pricing_engine_type(::Swaption{E, S, P, NS}) where {E, S, P, NS} = P