#========================================================================================#
#	Laboratory 1
#
# Introduction to Julia, variables and data.
#
# Author: Niall Palfreyman, 22/12/2021
#========================================================================================#
[
	Activity(
		"""
		Hi! In this laboratory we use activities and questions that introduce us to the basics
		of the Julia language. Feel free to experiment at any time by entering your own
		commands at the Julia prompt.

		I will interpret anything you enter as an argument of the function reply() as an
		answer to the current question. Try this now - at the Julia prompt, enter the
		following code:

		city = "Ingolstadt"

		Then enter your answer as "reply(city)":
		""",
		"We're still getting acquainted - simply follow the above instructions precisely. :-)",
		x -> (x=="Ingolstadt")
	),
	Activity(
		"""
		You can use the Julia REPL like a calculator. Arithmetic operations have their usual
		priority, so tell me the result you get if you enter "(3+4)*5":
		""",
		"35",
		x -> x==35
	),
	Activity(
		"""
		A very useful feature of the Julia REPL is that you can always retrieve the result of
		your previous calculation by entering "ans" at the Julia prompt. Press up-arrow a few
		times until you see your previous calculation "(3+4)*5". Enter this calculation, and
		then enter "ans" at the Julia prompt. What value is returned?
		""",
		"35",
		x -> x==35
	),
	Activity(
		"""
		Remove the brackets: \"3+4*5\". First calculate this value, then retrieve it again
		by entering the variable \"ans\". Finally, show me the value using \"reply(ans)\":
		""",
		"23",
		x -> x==23
	),
	Activity(
		"What answer do you get if you remove the multiplication sign: \"5(3+4)\"?",
		"35",
		x -> x==35
	),
	Activity(
		"What do you get if you use floating-point division \"1 + 1/7\"?",
		"$(1+1/7)",
		x -> abs(x-(1+1/7)) < 0.01
	),
	Activity(
		"Or you can use exact division to produce a rational number \"a = 1 + 1//7\":",
		"$(1+1//7)",
		x -> x == 1+1//7
	),
	Activity(
		"You can then convert this to a floating-point number: \"Float64(a)\"",
		"$(1+1/7)",
		x -> abs(x-(1+1/7)) < 0.01
	),
	Activity(
		"""
		Let's try working with Vectors. A Vector is a linearly ordered list in square
		brackets. They can contain all kinds of elements. Enter this definition of the
		Vector b: \"b = [3,5,"4",[1,2]]\". Then use length() to find the length of b:
		""",
		"length(b)",
		x -> x == 4
	),
	Activity(
		"Use b[2] to access the 2nd element of b, and use the function isodd() to see if it is odd:",
		"isodd(b[2])",
		x -> x == true
	),
	Activity(
		"What is the type of (typeof) the 4th element of b?",
		"Vector{Int64}",
		x -> (x <: Vector)
	),
	Activity(
		"Change the 3rd element of b to my name \"Niall\" and find the length of that 3rd element",
		"b[3] = \"Niall\"",
		x -> x == 5
	),
	Activity(
		"""
		Ranges are closely related to Vectors. Investigate the expression \"2:4\", then use
		collect() to convert it into a Vector. Finally, use b[2:4] to extract the last three
		elements of b:
		""",
		"[5,\"4\",[1,2]",
		x -> x == [5,"Niall",[1,2]]
	),
	Activity(
		"""
		We can also work with BigInt numbers: \"c = factorial(big(100))\". Find out how
		many digits this number has by using digits() to convert it into a Vector.
		""",
		"158",
		x -> x == 158
	),
	Activity(
		"""
		Functions are centrally important in Julia, and the language offers us three
		different ways of defining them. Use this format to define your own squaring
		function, then use it to compute the square of -253:
		function sq1( x)
			x * x
		end
		""",
		"64009",
		x -> x == 64009
	),
	Activity(
		"""
		Now use this shorter format to define a new squaring function, and use it to
		compute the square of the imaginary number 5im:
		sq2( x) = x * x
		""",
		"-25",
		x -> x == -25
	),
	Activity(
		"""
		Finally, functions are so important in Julia that we often want to create an
		anonymous function quickly in the middle of a calculation. Use the following
		anonymous notation to compute the square of the complex number 1+im:
		(x -> x*x)(1+im)
		""",
		"2im",
		x -> x == 2im
	),
	Activity(
		"""
		Use tuples to assign values in parallel to the variables x and y:
		(x,y) = (2,3)
		Then give me (as a string) the code to swap the values of x and y:
		""",
		"(x,y) = (y,x)",
		x -> (r=replace(x," "=>""); r == "(x,y)=(y,x)" || r == "(y,x)=(x,y)")
	),
	Activity(
		"""
		You can use conditionals in the usual way, for example:

			function f(x)
				if x ≥ 0
					x
				else
					-x
				end
			end

		Now use the shorter notation "x ≥ 0 ? x : -x", and assign g equal to an anonymous function
		that calculates the factorial of its argument. That is, g(n) returns 1 if n ≤ 1, and
		returns n * g(n-1) otherwise. Use g to calculate 9!:
		""",
		"n -> (n ≤ 1 ? 1 : n * g(n-1))",
		x -> (x == 362880)
	),
	Activity(
		"""
		We use double quotation marks to denote Strings. Find out the typeof() "Niall":
		""",
		"typeof(\"Niall\")",
		x -> (x == String)
	),
	Activity(
		"""
		We can concatenate two or more Strings into a single String using the operator *.
		Define the two strings:

		name1 = \"Niall\"
		name2 = \"Palfreyman\"

		What result do you get if you concatenate these two Strings?
		""",
		"name1 * name2",
		x -> (x == "NiallPalfreyman")
	),
	Activity(
		"""
		I prefer my name with a space between the first and second names. We can do this
		using the call join([name1,name2],\" \"). What String results from this?
		""",
		"\"Niall Palfreyman\"",
		x -> (x == "Niall Palfreyman")
	),
	Activity(
		"""
		We can pattern text more simply using STRING INTERPOLATION. Enter this code:

		\"\$name1 \$name2\"

		The dollar sign (\$) interpolates the values of name1 and name2 into the string.
		Enter the following code and then see if you can work out the return value of
		the function println():

		x = π/2; println("The sine of \$x is \$(sin(x)).")

		(Note: You can enter the symbol π by typing "\\pi" and then pressing Tab.
		Alternatively, you can replace this symbol by the named constant "pi".)
		""",
		"\"typeof(ans)\"",
		x -> (x == Nothing)
	),
	Activity(
		"""
		Julia offers us many different functions for manipulating strings. Let's start
		by creating a String that we can play around with:

		a_string = "We are one and we are many, and from all the lands on Earth we come."

		Now ask whether the substring "Earth" occurs in the string a_quote: What is the
		return value of the function call occursin("Earth",a_string)?
		""",
		"",
		x -> x == true
	),
	Activity(
		"""
		What value does the function call startswith("Earth",a_string) return?
		""",
		"",
		x -> x == false
	),
	Activity(
		"""
		What value does the function call endswith("Earth",a_string) return?
		""",
		"",
		x -> x == false
	),
	Activity(
		"""
		Try out the following function calls: lowercase(a_string), uppercase(a_string)
		and titlecase(a_string).
		""",
		"",
		x -> true
	),
	Activity(
		"""
		Try out the call: replace(a_string, "lands" => "countries"). Then find out the
		type of the second argument of this function call:
		""",
		"typeof(\"lands\" => \"countries\")",
		x -> (x <: Pair)
	),
	Activity(
		"""
		The call split(a_string, " ") divides a_string into tokens separated by the
		space symbol " ". How many tokens arise if we split a_string using the
		separator "an"?
		""",
		"split(a_string,\"an\")",
		x -> x == 5
	),
	Activity(
		"""
		We can convert numbers to strings. For example, what result do you get if
		you enter string(2.718)?
		""",
		"",
		x -> x == "2.718"
	),
	Activity(
		"""
		And converting back again, what result do you get if you enter
		parse(Float64,"2.718")?
		""",
		"parse() parses argument 2 according to the type of argument 1.",
		x -> x == 2.718
	),
]


#=
Author:= Shreeda Bhat
License:= MIT
UTF-8 =#

# julia has many logical operator, !, && and ||
#=
! - Logical Not
&& - Logical AND
|| - Logical OR
=#

###### NOT operator########
println("##################Let's start with Not operator#########")

println(!true)# contradicts as True i.e False

println(!false)

########### AND operator##############
println("#######################AND operator###################")

println(true && true)# True

println(true && false)# False

println(false && false)# False

################OR operator############

println("#######################OR operator###################")

println(true || true )# True

println(true || false)# True

println(false || false)# False

#*********************#Comparision operator#**************#
#=
> - Greater Than
< - Lesser Than
== - Equal To
=#
println("#*********************#Comparision operator#**************#")
println(2<56)#True

print(25>23)#True

print(25<2)#False

println(23>25)#False

println(25==25)#True

println(25==50-25)#True

println(25==20)#False

println((10-56>1)&& (10==10)||(10-6>1))#True

#Julia evaluates from left to right for logical expressions
println(true&&false||true) # True

#=

&&  and || are the boolean operators

| and & are the bitwise operators =#


# Before running this, please make sure to activate and instantiate the environment
# corresponding to [this `Project.toml`](https://raw.githubusercontent.com/alan-turing-institute/DataScienceTutorials.jl/master/Project.toml) and [this `Manifest.toml`](https://raw.githubusercontent.com/alan-turing-institute/DataScienceTutorials.jl/master/Manifest.toml)
# so that you get an environment which matches the one used to generate the tutorials:
#
# ```julia
# cd("DataScienceTutorials") # cd to folder with the *.toml
# using Pkg; Pkg.activate("."); Pkg.instantiate()
# ```

x = [1, 3, 2, 5]
@show x
@show length(x)

y = [4, 5, 6, 1]
z = x .+ y # elementwise operation

X = [1  2; 3 4]

X = reshape([1, 2, 3, 4], 2, 2)

X = permutedims(reshape([1, 2, 3, 4], 2, 2))

X = reshape([1,2,3,4], 2, 2) |> permutedims

x = 4
@show x^2
@show sqrt(x)

sqrt.([4, 9, 16])

using Statistics, StatsBase

x = randn(1_000) # 1_000 points iid from a N(0, 1)
μ = mean(x)
σ = std(x)
@show (μ, σ)

X = [1 2; 3 4; 5 6]
@show X[1, 2]
@show X[:, 1]
@show X[1, :]
@show X[[1, 2], [1, 2]]

size(X)

using CSV

using RDatasets

using DataFrames

auto = dataset("ISLR", "Auto")
first(auto, 3)

describe(auto, :mean, :median, :std)

names(auto)

mpg = auto.MPG
mpg = auto[:, 1]
mpg = auto[:, :MPG]
mpg |> mean

@show size(auto)
@show nrow(auto)
@show ncol(auto)

using PyPlot

figure(figsize=(8,6))
plot(mpg)

# This file was generated using Literate.jl, https://github.com/fredrikekre/Literate.jl


# tools for displaying latin squares

# alphabets

abc = [ "$ch" for ch in 'a':'z']
ABC = uppercase.(abc)
greek = [ "$(Char(t))" for t in 945:969 if t != 962 ]
GREEK = uppercase.(greek)

export print_latin

"""
`print_latin(A)` prints out the Latin square `A` using uppercase
Latin letters.

`print_latin(A,B)` prints out the pair of (presumably orthogonal)
Latin squares using upper case Latin letters for `A` and lower case Greek
letters for `B`.

Both versions permit alternate character sets:
* `print_latin(A,charlist)`
* `print_latin(A,B,charlist1,charlist2)`

The following pre-built character sets are available:
* `LatinSquares.abc`: lower case Latin letters
* `LatinSquares.ABC`: upper case Latin letters
* `LatinSquares.greek`: lower case Greek letters
* `LatinSquares.GREEK`: upper case Greek letters
"""

function print_latin(A::Matrix{Int}, chars::Array{String,1}=ABC)
    r,c = size(A)
    for i=1:r
        for j=1:c
            print(chars[A[i,j]])
            if j < c
                print(" ")
            else
                println()
            end
        end
    end
    nothing
end

function print_latin(A::Matrix{Int}, B::Matrix{Int}, chars1::Array{String}=ABC, chars2::Array{String}=greek)
    r,c = size(A)
    for i=1:r
        for j=1:c
            print(chars1[A[i,j]]*chars2[B[i,j]])
            if j<c
                print(" ")
            else
                println()
            end
        end
    end
    nothing
end


immutable HammingDistance <: Distance
end

"""
The hamming distance between two sequenced collections, or strings, of equal
length is measured as the number of differences between them at each index.
"""
hamming() = HammingDistance()

# Shouldn't be repeating code here.
# Need to find a way to unify these two method headers.
function dist(::HammingDistance, x::DirectIndexString, y::DirectIndexString)
  dst = 0
  for i in 1 : min(length(x), length(y))
    @inbounds if x[i] != y[i]
      dst += 1
    end
  end
  return dst
end

function dist{T}(::HammingDistance, x::Vector{T}, y::Vector{T})
  dst = 0
  for i in 1 : min(length(x), length(y))
    @inbounds if x[i] != y[i]
      dst += 1
    end
  end
  return dst
end


# Age ranges
ages = ["$age0-$(age0+4)" for age0 = 0:5:95]
push!(ages, "100+")
female = [328, 319, 307, 290, 284, 293, 293, 263, 241, 235,
    218, 191, 161, 135, 95, 66, 47, 26, 11.3, 3.1, 0.4]
male = [350, 341, 325, 310, 303, 310, 305, 270, 246, 239,
    218, 187, 153, 124, 83, 53, 34, 16, 5.7, 1.2, 0.1]
# Plot as bars - with negative values for female
barplot(ages, -female, horizontal=true, color=0xff8080)
hold(true)
barplot(ages, male, horizontal=true, color=0x8080ff)
# Change to absolute figures
xticklabels(x -> abs(x))
# Add labels
title("World population by age in 2019 (https://www.populationpyramid.net/)")
xlabel("Million people")
legend("female", "male")


include("prime-factors.jl")
using Test

@testset "no factors" begin
    @test prime_factors(1) == []
end

@testset "prime number" begin
    @test prime_factors(2) == [2]
end

@testset "square of a prime" begin
    @test prime_factors(9) == [3, 3]
end

@testset "cube of a prime" begin
    @test prime_factors(8) == [2, 2, 2]
end

@testset "product of primes and non-primes" begin
    @test prime_factors(12) == [2, 2, 3]
end

@testset "product of primes" begin
    @test prime_factors(901255) == [5, 17, 23, 461]
end

@testset "factors include a large prime" begin
    @test prime_factors(93819012551) == [11, 9539, 894119]
end


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

using Markdown
using InteractiveUtils

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

# ╔═╡ affd15dc-f0c1-11eb-37e2-7f6df754d6bd
begin
	using Agents, Random
	using CairoMakie
	using InteractiveDynamics
	using CSV
	using PlutoUI
end

# ╔═╡ 43ed6789-c001-4364-aa0f-aa8b6ddb81f9
md"""
# Elementary celular automata

Here, we implement cellular automata in julia using Agents.jl.
"""

# ╔═╡ 9d0552b3-9823-469d-9c45-1d13c4bb871b
md"""
We're going to use these packages
"""

# ╔═╡ bbe62cc6-c9c5-42c0-b7c3-69c7c0535cd8
md"""
First, we define the automaton living in a 1D space. We're using the package Agents.jl to create the model
"""

# ╔═╡ c290b55b-dfab-40bd-b07f-56e57dc7811a
"""
The automaton living in a 1D space
"""
mutable struct Cell <: AbstractAgent
    id::Int
    pos::Dims{1}
    status::Int # either 0 or 1, where 1 is 'alive'
end

# ╔═╡ 9891246c-cd91-410e-90ec-e897a05d1a56
md"""
A single rule on an elementary automaton can be ...
# TODO
"""

# ╔═╡ 68b11c3b-7cb9-4bea-8806-f668be9861ec
"""
Returns an array with the rule for the next status configurations, 1-indexed.

Thus, the first position corresponds to the rule for the cell neighborhood status (0,0,0)
and the last to (1,1,1)
"""
function rule_from_code(wolfram_code)
    return digits(wolfram_code, base=2, pad=8)
end

# ╔═╡ 202dda7b-7b1e-4902-a1f5-75e4a8b76761
"""
Takes the status of a neighborhood and returns the corresponding
index in the model rule (0,0,0) -> 1 (0,1,0) -> 3
"""
function configuration_index(cell_statuses)
    # takes the tuples and forms a string (0,1,1) -> "011"
    binary_code = string(cell_statuses...)
    # turns the string binary code into the integer
    index = parse(Int, binary_code, base=2) + 1
    return index
end

# ╔═╡ a3c6d798-a256-461a-920e-a7672f40c9d5
"""
Given a cell checks its neighbors and
decides the next status of the cell based on the model rule
"""
function next_status(cell, model)
    neighbors = collect(nearby_agents(cell, model))
    cell_statuses = (c.status for c in [neighbors[1], cell, neighbors[2]])
    index = configuration_index(cell_statuses)
    return model.rule[index]
end

# ╔═╡ 061f8ad5-7643-4c3d-a1b5-f42acb707e1a
"""
Initializes the ABM

# Arguments
- `n_cells::Int`: total of cell automaton
- `wolfram_code::Int`: wolfram code for the rule
- `seed::Int`: random seed
"""
function build_model(; n_cells = 101, wolfram_code=30, random_start=true, seed = 30)
    space = GridSpace((n_cells,); metric=:chebyshev)

    properties = Dict(:rule => rule_from_code(wolfram_code),)
    model = ABM(
        Cell,
        space; properties,
        rng = MersenneTwister(seed))

	if random_start
		for x in 1:n_cells
			cell = Cell(nextid(model), (x,), rand([0,1]))
			add_agent_pos!(cell, model)
		end
	else
		for x in 1:n_cells
			initial_state = x == div(n_cells, 2) + 1 ? 1 : 0
			cell = Cell(nextid(model), (x,), initial_state)
        	add_agent_pos!(cell, model)
		end
	end

    return model
end

# ╔═╡ c28393f5-f267-4f18-87e3-3b372b3de83b
"""
Dummy update of the cells
"""
function cell_step!(_, _)

end

# ╔═╡ ed4c31ac-59f8-4f51-8759-6dc0f7b63075
"""
Performs a synchronous update of all cells
"""
function ca_step!(model)
    new_statuses = fill(0, nagents(model))
    for agent in allagents(model)
        new_statuses[agent.id] = next_status(agent, model)
    end

    for id in allids(model)
        model[id].status = new_statuses[id]
    end
end

# ╔═╡ 7be6dfb6-23bc-4a37-afba-f1a69ef85c2b
md"""
## Running the model

Lets setup some initialization variables, namely the number of cells, and the wolfram_code to use.

Wolfram code(0-255): $(@bind wolfram_code PlutoUI.NumberField(0:255, default=30))

N cells(3-1001): $(@bind n_cells PlutoUI.NumberField(3:1001, default=101))

Max steps(10-500): $(@bind max_steps PlutoUI.NumberField(10:500, default=100))

Random start: $(@bind random_start PlutoUI.CheckBox(default=true))
"""

# ╔═╡ eeb17b56-000b-4023-b84c-1dcb735a51f2
md"""
Here we run the automata and collect data
"""

# ╔═╡ 365ee475-4252-49d3-870e-2b7828429c50
begin
	model = build_model(
		n_cells=n_cells, wolfram_code=wolfram_code, random_start=random_start)

	# Runs the model and collects data
	data, _ = run!(model, cell_step!, ca_step!, max_steps; adata=[:status])
end;

# ╔═╡ f456fa04-32c7-4bb2-b62b-3e3f74ba9a27
md"""
Lets plot the time evolution in the y axis
"""

# ╔═╡ 39b73998-5238-4f11-8126-b0e970edcfce
begin
	function plot_automata(data)
		fig, ax = heatmap(data.id, data.step, data.status, colormap=:Blues_3)
		ax.yreversed = true
		return fig
	end

	plot_automata(data)
end

# ╔═╡ 60418c85-7e06-4c8b-bc02-6f86d85d9d01
md"""
The plot was possible because the data contains the step, id/position and status of the cell (1/0)
"""

# ╔═╡ 13a7809b-5f19-457d-aa1c-8b7329e1c59f
first(data, 10)

# ╔═╡ 00000000-0000-0000-0000-000000000001
PLUTO_PROJECT_TOML_CONTENTS = """
[deps]
Agents = "46ada45e-f475-11e8-01d0-f70cc89e6671"
CSV = "336ed68f-0bac-5ca0-87d4-7b16caf5d00b"
CairoMakie = "13f3f980-e62b-5c42-98c6-ff1f3baf88f0"
InteractiveDynamics = "ec714cd0-5f51-11eb-0b6e-452e7367ff84"
PlutoUI = "7f904dfe-b85e-4ff6-b463-dae2292396a8"
Random = "9a3f8284-a2c9-5f02-9a11-845980a1fd5c"

[compat]
Agents = "~4.5.0"
CSV = "~0.8.5"
CairoMakie = "~0.6.3"
InteractiveDynamics = "~0.16.2"
PlutoUI = "~0.7.9"
"""

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

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

[[AbstractTrees]]
git-tree-sha1 = "03e0550477d86222521d254b741d470ba17ea0b5"
uuid = "1520ce14-60c1-5f80-bbc7-55ef81b5835c"
version = "0.3.4"

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

[[Agents]]
deps = ["CSV", "DataFrames", "DataStructures", "Distributed", "GraphRecipes", "JLD2", "LightGraphs", "LinearAlgebra", "OpenStreetMapX", "ProgressMeter", "Random", "RecipesBase", "Requires", "Scratch", "StatsBase"]
git-tree-sha1 = "42dfaaefcd4031a81597b86387470769098d8189"
uuid = "46ada45e-f475-11e8-01d0-f70cc89e6671"
version = "4.5.0"

[[Animations]]
deps = ["Colors"]
git-tree-sha1 = "e81c509d2c8e49592413bfb0bb3b08150056c79d"
uuid = "27a7e980-b3e6-11e9-2bcd-0b925532e340"
version = "0.4.1"

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

[[ArnoldiMethod]]
deps = ["LinearAlgebra", "Random", "StaticArrays"]
git-tree-sha1 = "f87e559f87a45bece9c9ed97458d3afe98b1ebb9"
uuid = "ec485272-7323-5ecc-a04f-4719b315124d"
version = "0.1.0"

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

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

[[Automa]]
deps = ["Printf", "ScanByte", "TranscodingStreams"]
git-tree-sha1 = "d50976f217489ce799e366d9561d56a98a30d7fe"
uuid = "67c07d97-cdcb-5c2c-af73-a7f9c32a568b"
version = "0.8.2"

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

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

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

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

[[CSV]]
deps = ["Dates", "Mmap", "Parsers", "PooledArrays", "SentinelArrays", "Tables", "Unicode"]
git-tree-sha1 = "b83aa3f513be680454437a0eee21001607e5d983"
uuid = "336ed68f-0bac-5ca0-87d4-7b16caf5d00b"
version = "0.8.5"

[[Cairo]]
deps = ["Cairo_jll", "Colors", "Glib_jll", "Graphics", "Libdl", "Pango_jll"]
git-tree-sha1 = "d0b3f8b4ad16cb0a2988c6788646a5e6a17b6b1b"
uuid = "159f3aea-2a34-519c-b102-8c37f9878175"
version = "1.0.5"

[[CairoMakie]]
deps = ["Base64", "Cairo", "Colors", "FFTW", "FileIO", "FreeType", "GeometryBasics", "LinearAlgebra", "Makie", "SHA", "StaticArrays"]
git-tree-sha1 = "7d37b0bd71e7f3397004b925927dfa8dd263439c"
uuid = "13f3f980-e62b-5c42-98c6-ff1f3baf88f0"
version = "0.6.3"

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

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

[[ColorBrewer]]
deps = ["Colors", "JSON", "Test"]
git-tree-sha1 = "61c5334f33d91e570e1d0c3eb5465835242582c4"
uuid = "a2cac450-b92f-5266-8821-25eda20663c8"
version = "0.4.0"

[[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 = "32a2b8af383f11cbb65803883837a149d10dfe8a"
uuid = "3da002f7-5984-5a60-b8a6-cbb66c0b333f"
version = "0.10.12"

[[ColorVectorSpace]]
deps = ["ColorTypes", "Colors", "FixedPointNumbers", "LinearAlgebra", "SpecialFunctions", "Statistics", "StatsBase"]
git-tree-sha1 = "4d17724e99f357bfd32afa0a9e2dda2af31a9aea"
uuid = "c3611d14-8923-5661-9e6a-0046d554d3a4"
version = "0.8.7"

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

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

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

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

[[Crayons]]
git-tree-sha1 = "3f71217b538d7aaee0b69ab47d9b7724ca8afa0d"
uuid = "a8cc5b0e-0ffa-5ad4-8c14-923d3ee1735f"
version = "4.0.4"

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

[[DataFrames]]
deps = ["Compat", "DataAPI", "Future", "InvertedIndices", "IteratorInterfaceExtensions", "LinearAlgebra", "Markdown", "Missings", "PooledArrays", "PrettyTables", "Printf", "REPL", "Reexport", "SortingAlgorithms", "Statistics", "TableTraits", "Tables", "Unicode"]
git-tree-sha1 = "a19645616f37a2c2c3077a44bc0d3e73e13441d7"
uuid = "a93c6f00-e57d-5684-b7b6-d8193f3e46c0"
version = "1.2.1"

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

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

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

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

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

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

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

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

[[FillArrays]]
deps = ["LinearAlgebra", "Random", "SparseArrays", "Statistics"]
git-tree-sha1 = "8c8eac2af06ce35973c3eadb4ab3243076a408e7"
uuid = "1a297f60-69ca-5386-bcde-b61e274b549b"
version = "0.12.1"

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

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

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

[[FreeType]]
deps = ["CEnum", "FreeType2_jll"]
git-tree-sha1 = "cabd77ab6a6fdff49bfd24af2ebe76e6e018a2b4"
uuid = "b38be410-82b0-50bf-ab77-7b57e271db43"
version = "4.0.0"

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

[[FreeTypeAbstraction]]
deps = ["ColorVectorSpace", "Colors", "FreeType", "GeometryBasics", "StaticArrays"]
git-tree-sha1 = "d51e69f0a2f8a3842bca4183b700cf3d9acce626"
uuid = "663a7486-cb36-511b-a19d-713bb74d65c9"
version = "0.9.1"

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

[[Future]]
deps = ["Random"]
uuid = "9fa8497b-333b-5362-9e8d-4d0656e87820"

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

[[GeometryTypes]]
deps = ["ColorTypes", "FixedPointNumbers", "LinearAlgebra", "StaticArrays"]
git-tree-sha1 = "07194161fe4e181c6bf51ef2e329ec4e7d050fc4"
uuid = "4d00f742-c7ba-57c2-abde-4428a4b178cb"
version = "0.8.4"

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

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

[[GraphRecipes]]
deps = ["AbstractTrees", "GeometryTypes", "InteractiveUtils", "Interpolations", "LightGraphs", "LinearAlgebra", "NaNMath", "NetworkLayout", "PlotUtils", "RecipesBase", "SparseArrays", "Statistics"]
git-tree-sha1 = "7269dc06b8cd8d16fc2b1756cf7f41901bbc3c52"
uuid = "bd48cda9-67a9-57be-86fa-5b3c104eda73"
version = "0.5.7"

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

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

[[GridLayoutBase]]
deps = ["GeometryBasics", "InteractiveUtils", "Match", "Observables"]
git-tree-sha1 = "d44945bdc7a462fa68bb847759294669352bd0a4"
uuid = "3955a311-db13-416c-9275-1d80ed98e5e9"
version = "0.5.7"

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

[[HTTP]]
deps = ["Base64", "Dates", "IniFile", "MbedTLS", "Sockets"]
git-tree-sha1 = "c7ec02c4c6a039a98a15f955462cd7aea5df4508"
uuid = "cd3eb016-35fb-5094-929b-558a96fad6f3"
version = "0.8.19"

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

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

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

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

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

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

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

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

[[InteractiveDynamics]]
deps = ["DataStructures", "Makie", "Observables", "Requires", "StatsBase", "Test"]
git-tree-sha1 = "8c77c74af4e149611b55b40f0b3085fe2b9d8ccf"
uuid = "ec714cd0-5f51-11eb-0b6e-452e7367ff84"
version = "0.16.2"

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

[[Interpolations]]
deps = ["AxisAlgorithms", "LinearAlgebra", "OffsetArrays", "Random", "Ratios", "SharedArrays", "SparseArrays", "StaticArrays", "WoodburyMatrices"]
git-tree-sha1 = "1e0e51692a3a77f1eeb51bf741bdd0439ed210e7"
uuid = "a98d9a8b-a2ab-59e6-89dd-64a1c18fca59"
version = "0.13.2"

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

[[InvertedIndices]]
deps = ["Test"]
git-tree-sha1 = "15732c475062348b0165684ffe28e85ea8396afc"
uuid = "41ab1584-1d38-5bbf-9106-f11c6c58b48f"
version = "1.0.0"

[[Isoband]]
deps = ["isoband_jll"]
git-tree-sha1 = "f9b6d97355599074dc867318950adaa6f9946137"
uuid = "f1662d9f-8043-43de-a69a-05efc1cc6ff4"
version = "0.1.1"

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

[[JLD2]]
deps = ["DataStructures", "FileIO", "MacroTools", "Mmap", "Pkg", "Printf", "Reexport", "TranscodingStreams", "UUIDs"]
git-tree-sha1 = "59ee430ac5dc87bc3eec833cc2a37853425750b4"
uuid = "033835bb-8acc-5ee8-8aae-3f567f8a3819"
version = "0.4.13"

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

[[KernelDensity]]
deps = ["Distributions", "DocStringExtensions", "FFTW", "Interpolations", "StatsBase"]
git-tree-sha1 = "591e8dc09ad18386189610acafb970032c519707"
uuid = "5ab0869b-81aa-558d-bb23-cbf5423bbe9b"
version = "0.6.3"

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

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

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

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

[[LibExpat]]
deps = ["Expat_jll", "Pkg"]
git-tree-sha1 = "27dc51f94ceb107fd53b367431a638b430e01e81"
uuid = "522f3ed2-3f36-55e3-b6df-e94fee9b0c07"
version = "0.6.1"

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

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

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

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

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

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

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

[[LightGraphs]]
deps = ["ArnoldiMethod", "DataStructures", "Distributed", "Inflate", "LinearAlgebra", "Random", "SharedArrays", "SimpleTraits", "SparseArrays", "Statistics"]
git-tree-sha1 = "432428df5f360964040ed60418dd5601ecd240b6"
uuid = "093fc24a-ae57-5d10-9952-331d41423f4d"
version = "1.3.5"

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

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

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

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

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

[[Makie]]
deps = ["Animations", "Artifacts", "Base64", "ColorBrewer", "ColorSchemes", "ColorTypes", "Colors", "Contour", "Distributions", "DocStringExtensions", "FFMPEG", "FileIO", "FixedPointNumbers", "Formatting", "FreeType", "FreeTypeAbstraction", "GeometryBasics", "GridLayoutBase", "ImageIO", "IntervalSets", "Isoband", "KernelDensity", "LaTeXStrings", "LinearAlgebra", "MakieCore", "Markdown", "Match", "MathTeXEngine", "Observables", "Packing", "PlotUtils", "PolygonOps", "Printf", "Random", "Serialization", "Showoff", "SignedDistanceFields", "SparseArrays", "StaticArrays", "Statistics", "StatsBase", "StatsFuns", "StructArrays", "UnicodeFun"]
git-tree-sha1 = "5761bfd21ad271efd7e134879e39a2289a032fc8"
uuid = "ee78f7c6-11fb-53f2-987a-cfe4a2b5a57a"
version = "0.15.0"

[[MakieCore]]
deps = ["Observables"]
git-tree-sha1 = "7bcc8323fb37523a6a51ade2234eee27a11114c8"
uuid = "20f20a25-4f0e-4fdf-b5d1-57303727442b"
version = "0.1.3"

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

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

[[Match]]
git-tree-sha1 = "5cf525d97caf86d29307150fcba763a64eaa9cbe"
uuid = "7eb4fadd-790c-5f42-8a69-bfa0b872bfbf"
version = "1.1.0"

[[MathTeXEngine]]
deps = ["AbstractTrees", "Automa", "DataStructures", "FreeTypeAbstraction", "GeometryBasics", "LaTeXStrings", "REPL", "Test"]
git-tree-sha1 = "69b565c0ca7bf9dae18498b52431f854147ecbf3"
uuid = "0a4f8689-d25c-4efe-a92b-7142dfc1aa53"
version = "0.1.2"

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

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

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

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

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

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

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

[[NetworkLayout]]
deps = ["GeometryBasics", "LinearAlgebra", "Random", "Requires", "SparseArrays"]
git-tree-sha1 = "76bbbe01d2e582213e656688e63707d94aaadd15"
uuid = "46757867-2c16-5918-afeb-47bfcb05e46a"
version = "0.4.0"

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

[[Observables]]
git-tree-sha1 = "fe29afdef3d0c4a8286128d4e45cc50621b1e43d"
uuid = "510215fc-4207-5dde-b226-833fc4488ee2"
version = "0.4.0"

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

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

[[OpenStreetMapX]]
deps = ["DataStructures", "HTTP", "JSON", "LibExpat", "LightGraphs", "Random", "Serialization", "SparseArrays", "Statistics"]
git-tree-sha1 = "71fe65bce368557f5b94079ac709a9c08f46f720"
uuid = "86cd37e6-c0ff-550b-95fe-21d72c8d4fc9"
version = "0.2.3"

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

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

[[Packing]]
deps = ["GeometryBasics"]
git-tree-sha1 = "f4049d379326c2c7aa875c702ad19346ecb2b004"
uuid = "19eb6ba3-879d-56ad-ad62-d5c202156566"
version = "0.4.1"

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

[[Pango_jll]]
deps = ["Artifacts", "Cairo_jll", "Fontconfig_jll", "FreeType2_jll", "FriBidi_jll", "Glib_jll", "HarfBuzz_jll", "JLLWrappers", "Libdl", "Pkg"]
git-tree-sha1 = "9bc1871464b12ed19297fbc56c4fb4ba84988b0d"
uuid = "36c8627f-9965-5494-a995-c6b170f724f3"
version = "1.47.0+0"

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

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

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

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

[[PolygonOps]]
git-tree-sha1 = "c031d2332c9a8e1c90eca239385815dc271abb22"
uuid = "647866c9-e3ac-4575-94e7-e3d426903924"
version = "0.1.1"

[[PooledArrays]]
deps = ["DataAPI", "Future"]
git-tree-sha1 = "cde4ce9d6f33219465b55162811d8de8139c0414"
uuid = "2dfb63ee-cc39-5dd5-95bd-886bf059d720"
version = "1.2.1"

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

[[PrettyTables]]
deps = ["Crayons", "Formatting", "Markdown", "Reexport", "Tables"]
git-tree-sha1 = "0d1245a357cc61c8cd61934c07447aa569ff22e6"
uuid = "08abe8d2-0d0c-5749-adfa-8a2ac140af0d"
version = "1.1.0"

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

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

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

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

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

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

[[SIMD]]
git-tree-sha1 = "9ba33637b24341aba594a2783a502760aa0bff04"
uuid = "fdea26ae-647d-5447-a871-4b548cad5224"
version = "3.3.1"

[[ScanByte]]
deps = ["Libdl", "SIMD"]
git-tree-sha1 = "9cc2955f2a254b18be655a4ee70bc4031b2b189e"
uuid = "7b38b023-a4d7-4c5e-8d43-3f3097f304eb"
version = "0.3.0"

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

[[SentinelArrays]]
deps = ["Dates", "Random"]
git-tree-sha1 = "35927c2c11da0a86bcd482464b93dadd09ce420f"
uuid = "91c51154-3ec4-41a3-a24f-3f23e20d615c"
version = "1.3.5"

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

[[SignedDistanceFields]]
deps = ["Random", "Statistics", "Test"]
git-tree-sha1 = "d263a08ec505853a5ff1c1ebde2070419e3f28e9"
uuid = "73760f76-fbc4-59ce-8f25-708e95d2df96"
version = "0.4.0"

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

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

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

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

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

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

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

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

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

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

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

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

[[StructArrays]]
deps = ["DataAPI", "Tables"]
git-tree-sha1 = "ad1f5fd155426dcc879ec6ede9f74eb3a2d582df"
uuid = "09ab397b-f2b6-538f-b94a-2f83cf4a842a"
version = "0.4.2"

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

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

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

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

[[TranscodingStreams]]
deps = ["Random", "Test"]
git-tree-sha1 = "7c53c35547de1c5b9d46a4797cf6d8253807108c"
uuid = "3bb67fe8-82b1-5028-8e26-92a6c54297fa"
version = "0.9.5"

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

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

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

[[WoodburyMatrices]]
deps = ["LinearAlgebra", "SparseArrays"]
git-tree-sha1 = "59e2ad8fd1591ea019a5259bd012d7aee15f995c"
uuid = "efce3f68-66dc-5838-9240-27a6d6f5f9b6"
version = "0.5.3"

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

[[isoband_jll]]
deps = ["Libdl", "Pkg"]
git-tree-sha1 = "a1ac99674715995a536bbce674b068ec1b7d893d"
uuid = "9a68df92-36a6-505f-a73e-abb412b6bfb4"
version = "0.2.2+0"

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

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

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

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

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

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

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

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

# ╔═╡ Cell order:
# ╟─43ed6789-c001-4364-aa0f-aa8b6ddb81f9
# ╟─9d0552b3-9823-469d-9c45-1d13c4bb871b
# ╠═affd15dc-f0c1-11eb-37e2-7f6df754d6bd
# ╟─bbe62cc6-c9c5-42c0-b7c3-69c7c0535cd8
# ╠═c290b55b-dfab-40bd-b07f-56e57dc7811a
# ╠═9891246c-cd91-410e-90ec-e897a05d1a56
# ╠═68b11c3b-7cb9-4bea-8806-f668be9861ec
# ╠═202dda7b-7b1e-4902-a1f5-75e4a8b76761
# ╠═a3c6d798-a256-461a-920e-a7672f40c9d5
# ╠═061f8ad5-7643-4c3d-a1b5-f42acb707e1a
# ╠═c28393f5-f267-4f18-87e3-3b372b3de83b
# ╠═ed4c31ac-59f8-4f51-8759-6dc0f7b63075
# ╟─7be6dfb6-23bc-4a37-afba-f1a69ef85c2b
# ╟─eeb17b56-000b-4023-b84c-1dcb735a51f2
# ╠═365ee475-4252-49d3-870e-2b7828429c50
# ╟─f456fa04-32c7-4bb2-b62b-3e3f74ba9a27
# ╠═39b73998-5238-4f11-8126-b0e970edcfce
# ╟─60418c85-7e06-4c8b-bc02-6f86d85d9d01
# ╠═13a7809b-5f19-457d-aa1c-8b7329e1c59f
# ╟─00000000-0000-0000-0000-000000000001
# ╟─00000000-0000-0000-0000-000000000002


function get_window_bounds(img::GenericGrayImage, pixel::CartesianIndex{2}, w::Integer)
    row, col = pixel.I
    min_row, min_col = first.(axes(img))
    max_row, max_col = last.(axes(img))

    row₀ = max(row - w, min_row)
    col₀ = max(col - w, min_col)
    row₁ = min(row + w, max_row)
    col₁ = min(col + w, max_col)

    return row₀, col₀, row₁, col₁
end

function μ_in_window(I::AbstractArray{T,2}, row₀::Integer, col₀::Integer, row₁::Integer, col₁::Integer) where T <: Real
    μ = boxdiff(I, row₀:row₁, col₀:col₁) / ((row₁-row₀+1)*(col₁-col₀+1))
    return μ < eps() ? zero(eltype(I)) : μ
end

function σ²_in_window(I²::AbstractArray{T,2}, μ::Real, row₀::Integer, col₀::Integer, row₁::Integer, col₁::Integer) where T <: Real
    σ² = μ_in_window(I², row₀, col₀, row₁, col₁) - μ^2
    return σ² < eps() ? zero(eltype(I²)) : σ²
end

function σ_in_window(I²::AbstractArray{T,2}, μ::Real, row₀::Integer,  col₀::Integer, row₁::Integer, col₁::Integer) where T <: Real
    σ² = σ²_in_window(I², μ, row₀, col₀, row₁, col₁)
    return σ² < eps() ? zero(eltype(I²)) : sqrt(σ²)
end


function bfgs_trace!(tr::OptimizationTrace,
                     x_old::Vector,
                     f_x::Real,
                     iteration::Integer,
                     gradient_new::Vector,
                     B::Matrix,
                     store_trace::Bool,
                     show_trace::Bool)
    dt = Dict()
    dt["g(x_new)"] = copy(gradient_new)
    dt["~inv(H)"] = copy(B)
    os = OptimizationState(copy(x_old), f_x, iteration, dt)
    if store_trace
        push!(tr, os)
    end
    if show_trace
        println(os)
    end
end

function bfgs(d::DifferentiableFunction,
              initial_x::Vector;
              initial_B::Matrix = eye(length(initial_x)),
              tolerance::Real = 1e-8,
              iterations::Integer = 1_000,
              store_trace::Bool = false,
              show_trace::Bool = false)

    # Keep track of the number of iterations
    iteration = 0

    # Count function and gradient calls
    f_calls = 0
    g_calls = 0

    # Keep a record of our current position
    x_old = copy(initial_x)
    x_new = copy(initial_x)

    # Determine number of parameters
    n = length(initial_x)
    gradient_old = Array(Float64, n)
    gradient_new = Array(Float64, n)

    # Keep a record of the current gradient
    f_calls += 1
    g_calls += 1
    f_x = d.fg!(x_old, gradient_old)
    copy!(gradient_new, gradient_old)

    # Initialize our approximate inverse Hessian
    B = copy(initial_B)

    # Iterate until convergence
    converged = false

    # Show state of the system
    tr = OptimizationTrace()
    if store_trace || show_trace
        bfgs_trace!(tr, x_old, f_x, iteration, gradient_new, B, store_trace, show_trace)
    end

    # Maintain arrays for position and gradient changes
    s = Array(Float64, n)
    y = Array(Float64, n)

    # Reuse arrays during every line search
    ls_x = Array(Float64, n)
    ls_gradient = Array(Float64, n)

    while !converged && iteration < iterations
        # Increment the iteration counter
        iteration += 1

        # Set the search direction
        p = -B * gradient_new

        # Calculate a step-size
        # TODO: Clean up backtracking_line_search
        alpha, f_update, g_update =
          backtracking_line_search!(d, x_new, p, ls_x, ls_gradient)
        f_calls += f_update
        g_calls += g_update

        # Update our position
        copy!(x_old, x_new)
        for i in 1:n
            s[i] = alpha * p[i]
            x_new[i] = x_old[i] + s[i]
        end

        # Update the gradient
        copy!(gradient_old, gradient_new)
        f_x = d.fg!(x_new, gradient_new)
        f_calls += 1
        g_calls += 1

        # Update the change in the gradient
        for i in 1:n
            y[i] = gradient_new[i] - gradient_old[i]
        end

        # Update the inverse Hessian approximation
        rho = 1.0 / dot(y, s)
        if isinf(rho)
           @printf "Cannot decrease the objective function along the current search direction\n"
           break
        end
        # TODO: Reuse storage here
        v = eye(size(B, 1)) - rho * y * s'
        if iteration == 1
            B = v'v * dot(y, s) / dot(y, y)
        else
            B = v' * B * v + rho * s * s'
        end

        # Show state of the system
        if store_trace || show_trace
            bfgs_trace!(tr, x_old, f_x, iteration, gradient_new, B, store_trace, show_trace)
        end

        # Assess convergence
        if norm(gradient_new, Inf) <= tolerance
           converged = true
        end
    end

    OptimizationResults("BFGS",
                        initial_x,
                        x_new,
                        f_x,
                        iteration,
                        converged,
                        tr,
                        f_calls,
                        g_calls)
end


@testset begin
    @test find_smallest_set_of_vertices(6, [[0,1],[0,2],[2,5],[3,4],[4,2]]) == [0, 3]
    @test find_smallest_set_of_vertices(5, [[0,1],[2,1],[3,1],[1,4],[2,4]]) == [0, 2, 3]
end


using MyInterpolations
using Base.Test

const _lin_interp = LinearInterpolation

@testset "Testing linear interporation" begin
    @testset "case with two grid points" begin
        grid = [1, 2]
        vals = [2, 0]
        itp = _lin_interp(grid, vals)

        @testset "scalar input" begin
            x, y = 1.25, 1.5
            @test isapprox(itp(x), y)
        end

        @testset "end points" begin
            for (x, y) in [(grid[1], grid[end]), (vals[1], vals[end])]
                @test itp(x) == y
            end
        end

        @testset "array inputs" begin
            xys = [
                ([4/3, 1.8], [4/3, 0.4]),
                (linspace(1, 2, 5), [2.0 - 0.5*i for i in 0:4])
            ]
            for (x, y) in xys
                @test isapprox(itp.(x), y)
            end
        end
    end

    @testset "general case" begin
        grid = 0:2
        vals = [1, 3, 2.5]
        itp = _lin_interp(grid, vals)

        @testset "scalar input" begin
            x, y = 1.2, 2.9
            @test isapprox(itp(x), y)
        end

        @testset "end points" begin
            for (x, y) in [(grid[1], vals[1]), (grid[end], vals[end])]
                @test itp(x) == y
            end
        end

        @testset "array inputs" begin
            xys = [
                ([0.5, 1.2], [2, 2.9]),  # Vector
                (grid, vals),  # UnitRange
                (0:2:2, vals[[1, 3]]),  # StepRange
                (0:0.5:1.5, [1, 2, 3, 2.75]),  # FloatRange
                (linspace(0, 1.5, 4), [1, 2, 3, 2.75])  # LinSpace
            ]
            for (x, y) in xys
                @test isapprox(itp.(x), y)
            end
        end
    end
end


# Create example data
x = LinRange(0, 1, 11)
y = LinRange(0, 1, 11)
s = LinRange(50, 400, 11)
c = LinRange(0, 255, 11)
# Plot the points with increasing size and color
scatter(x, y, s, c)


# Point in 1D space

export Point1D

mutable struct Point1D <: Point
    x :: Float64
end

point(x) = Point1D(x)
zero(::Type{Point1D}) = Point1D(0.0)
rand(r::AbstractRNG, ::Random.SamplerType{Point1D}) = Point1D(rand(r, [1.0, -1.0]))

+(lhs :: Point1D, rhs :: Point1D) = Point1D(lhs.x + rhs.x)
-(lhs :: Point1D, rhs :: Point1D) = Point1D(lhs.x - rhs.x)
-(p :: Point1D) = Point1D(-p.x)
*(a :: Real, p :: Point1D) = Point1D(a*p.x)
/(p :: Point1D, a :: Real) = Point1D(p.x/a)

abs(p :: Point1D) = abs(p.x)
abs2(p :: Point1D) = p.x*p.x

plot_str(p::Point1D) = string(p.x)


"""
Uniformed Matrix for CTMC
"""

macro unif(Q, ufact)
    expr = quote
        qv = maximum(abs.(spdiag($Q))) * $ufact
        if iszero(qv)
            qv = 1.0e-12
        end
        P = $Q / qv
        d = spdiag(P)
        d .+= 1
        (P, qv)
    end
    esc(expr)
end

"""
unif(Q::AbstractSparseM{Tv,Ti}, ufact::Tv = 1.01)
unif(Q::Matrix{Tv}, ufact::Tv = 1.01)

Get an uniformed transition probability matrix from a CTMC kernel.

   P = I + Q / qv
   qv = max(abs(diag(Q))) * ufact

Parameters:
- Q: CTMC Kernel
- ufact: uniformization factor
Return value:
A tuple of
- P: The uniformed transition probability matrix
- qv: The maximum event rate

"""

function unif(Q::SparseMatrixCSC{Tv,Ti}, ufact::Tv = 1.01) where {Tv, Ti}
    @unif(Q, ufact)
end

function unif(Q::SparseCSR{Tv,Ti}, ufact::Tv = 1.01) where {Tv, Ti}
    @unif(Q, ufact)
end

function unif(Q::SparseCSC{Tv,Ti}, ufact::Tv = 1.01) where {Tv, Ti}
    @unif(Q, ufact)
end

function unif(Q::SparseCOO{Tv,Ti}, ufact::Tv = 1.01) where {Tv, Ti}
    @unif(Q, ufact)
end

function unif(Q::Matrix{Tv}, ufact::Tv = 1.01) where {Tv, Ti}
    @unif(Q, ufact)
end


"""
  meshgrid(vx::AbstractVector{T}, vy::AbstractVector{T}) where T<:Real

Replicates matlab function for generating structured meshes
"""
function meshgrid(vx::AbstractVector{T}, vy::AbstractVector{T}) where T<:Real
	m, n = length(vy), length(vx)
  vx = reshape(vx, 1, n)
  vy = reshape(vy, m, 1)

  return (repeat(vx, m, 1)', repeat(vy, 1, n)')
end

# square [x0, x1, y0, y1]
function squareMesh(square::Vector{T},N::Int,order::Int) where T<:Real
  x = range(square[1],stop=square[2],length=N)
  y = range(square[3],stop=square[4],length=N)

  # create nodes
  X,Y = meshgrid(x,y)
  xy = [X[:] Y[:]]

  # create element connectivity
  ni = size(X,1); # number of rows
  nNodes = size(xy,1);
  t2nidxMap = 1:nNodes-ni;
  topNode = ni:ni:nNodes-ni;
  t2nidxMap = deleteat!(collect(t2nidxMap),collect(topNode))
  k = t2nidxMap;
  cm = [k k.+1 k.+ni.+1 k.+ni]

  if order==2
    xy,cm = Order1ToOrder2(xy,cm)
    xy,cm = deleteDuplicates(xy,cm)
  end

  # create boundary nodes
  left=Int[]; top=Int[]; right=Int[]; bottom=Int[]
  for i=1:size(xy,1)
    # left
    if isMachZero(xy[i,1],square[1])
      push!(left,i)
    end
    # right
    if isMachZero(xy[i,1],square[2])
      push!(right,i)
    end
    # bottom
    if isMachZero(xy[i,2],square[3])
      push!(bottom,i)
    end
    # top
    if isMachZero(xy[i,2],square[4])
      push!(top,i)
    end
  end

  # generate bdry object
  bndNames = Index([:left,:top,:right,:bottom])
  bdry = DictList{Int}([left,top,right,bottom],bndNames)

  (order==1 ? o=:Linear : o=:Quadratic)

  # Generate Mesh!
  return ScalarMesh(ArrayToNodes(xy),ArrayToElements(cm),bdry,o)
end

function squareMeshFluid(square,N)
  mesh = squareMesh(square,N,2)

  xy = NodesToArray(mesh.xy)
  cm = ElementsToArray(mesh.cm)

  xyptemp,cmptemp = pMesh(xy,cm)

  xyp = ArrayToNodes(xyptemp)
  cmp = ArrayToElements(cmptemp)

  return FluidMesh(mesh.xy,mesh.cm,mesh.bdry,:Quadratic,xyp,cmp)
end

function axisymMesh(Nr,Nt,Rmax,order;phi=(x -> x))
  xi = linspace(0,1,Nr)
  t = linspace(0,pi,Nt)

  # create nodes
  XI,THETA = meshgrid(xi,t)
  xy = [XI[:] THETA[:]]

  # create element connectivity
  ni = size(XI,1); # number of rows
  nNodes = size(xy,1)
  t2nidxMap = 1:nNodes-ni;
  topNode = ni:ni:nNodes-ni;
  t2nidxMap = deleteat!(collect(t2nidxMap),collect(topNode))
  k = t2nidxMap;
  cm = [k k+1 k+ni+1 k+ni]

  if order==2
    xy,cm = Order1ToOrder2(xy,cm)
    xy,cm = deleteDuplicates(xy,cm)
  end

  # create boundary nodes
  inflow=Int[]; outflow=Int[]; axis=Int[]; sphere=Int[]
  for i=1:size(xy,1)
    # axis
    if isMachZero(xy[i,2],0.0) || isMachZero(xy[i,2],Float64(pi))
      push!(axis,i)
    end
    # inflow
    if isMachZero(xy[i,1],1) && xy[i,2]<=Float64(pi)/2
      push!(inflow,i)
    end
    # outflow
    if isMachZero(xy[i,1],1) && xy[i,2]>Float64(pi)/2
      push!(outflow,i)
    end
    # sphere
    if isMachZero(xy[i,1],0.0)
      push!(sphere,i)
    end
  end

  # transform xy nodes
  transform(r) = exp(log(Rmax)*phi(r))

  XI2    = xy[:,1]
  THETA2 = xy[:,2]

  R = transform.(XI2)
  X = R.*cos.(THETA2)
  Y = R.*sin.(THETA2)
  xy = [X[:] Y[:]]

  # generate bdry object
  bndNames = Index([:inflow,:outflow,:axis,:sphere])
  bdry = DictList{Int}([inflow,outflow,axis,sphere],bndNames)

  (order==1 ? o=:Linear : o=:Quadratic)

  # Generate Mesh!
  return ScalarMesh(ArrayToNodes(xy),ArrayToElements(cm),bdry,o)
end

function axisymFluid(Nr,Nt,Rmax;phi=(x -> x))
  mesh = axisymMesh(Nr,Nt,Rmax,2;phi=phi)

  xy = NodesToArray(mesh.xy)
  cm = ElementsToArray(mesh.cm)

  xyptemp,cmptemp = pMesh(xy,cm)

  xyp = ArrayToNodes(xyptemp)
  cmp = ArrayToElements(cmptemp)

  return FluidMesh(mesh.xy,mesh.cm,mesh.bdry,:Quadratic,xyp,cmp)
end

# function to convert array to Nodes
function ArrayToNodes(xy)
  return [Node(xy[i,1],xy[i,2]) for i=1:size(xy,1)]
end

# function to convert Nodes to array form
function NodesToArray(xy)
    # get xy into correct form
  x = [xy[i].x for i=1:length(xy)]
  y = [xy[i].y for i=1:length(xy)]
  return hcat(x,y)
end

# function to convert array to elements
function ArrayToElements(cm)
  return [Element(cm[i,:]) for i in 1:size(cm,1)]
end

# function to convert Elements to array form
function ElementsToArray(cm)
  return [cm[i].NodeList[j] for i=1:length(cm), j=1:length(cm[1].NodeList)]
end

# converts order 1 meshes to order 2 meshes
function Order1ToOrder2(xy,cm)
  nNodesOld = size(xy,1)
  nElm      = size(cm,1)

  xNew = Float64[]
  yNew = Float64[]
  cmNew = Array{Int}(undef,nElm,9)

  cmNew[:,1:4] = cm   # will throw error if cm isn't quad

  # for each element
  newNodeInd = nNodesOld + 1
  for el=1:nElm
    # for nodes 5 - 9
    for i=5:9
      x=0.0; y=0.0
      if i==5
        x = (xy[cm[el,1],1] + xy[cm[el,2],1])/2
        y = (xy[cm[el,1],2] + xy[cm[el,2],2])/2
      elseif i==6
        x = (xy[cm[el,2],1] + xy[cm[el,3],1])/2
        y = (xy[cm[el,2],2] + xy[cm[el,3],2])/2
      elseif i==7
        x = (xy[cm[el,3],1] + xy[cm[el,4],1])/2
        y = (xy[cm[el,3],2] + xy[cm[el,4],2])/2
      elseif i==8
        x = (xy[cm[el,4],1] + xy[cm[el,1],1])/2
        y = (xy[cm[el,4],2] + xy[cm[el,1],2])/2
      elseif i==9
        x = (xy[cm[el,1],1] + xy[cm[el,2],1] +
             xy[cm[el,3],1] + xy[cm[el,4],1])/4
        y = (xy[cm[el,1],2] + xy[cm[el,2],2] +
             xy[cm[el,3],2] + xy[cm[el,4],2])/4
      end
      # add node to xy
      push!(xNew,x)
      push!(yNew,y)

      # add node index to cm
      cmNew[el,i] = newNodeInd
      newNodeInd += 1
    end
  end

  xyNew = Array{Float64}(undef,newNodeInd-1,2)

  for i=1:(newNodeInd-1)
    if i <= nNodesOld
      xyNew[i,1] = xy[i,1]; xyNew[i,2] = xy[i,2]
    else
      k = i-nNodesOld
      xyNew[i,1] = xNew[k]; xyNew[i,2] = yNew[k]
    end
  end

  return xyNew, cmNew
end

function deleteDuplicates!(mesh)
  # transition data to arrays
  xyold = NodesToArray(mesh.xy)
  cmold = ElementsToArray(mesh.cm)

  # delete duplicates and reassign cm
  xynew,cmnew = deleteDuplicates(xyold,cmold)

  # create new mesh out of this

end

# need to make function that replaces/deletes duplicate nodes
# for the love of all that is holy, do not change anything in here. the indices
# are a nightmare
function deleteDuplicates(xy,cm)
  nNodesOld = size(xy,1)
  nElm   = size(cm,1)

  nodeIndPos5 = view(cm,:,5)
  nodeIndPos6 = view(cm,:,6)
  nodeIndPos7 = view(cm,:,7)
  nodeIndPos8 = view(cm,:,8)

  xyNew = Float64[]
  skipList = Int[]
  cmNew = copy(cm)

  # for each node in 5 or 7 position
  for ti=1:length(nodeIndPos5)
    i = nodeIndPos5[ti]
    if !(i in skipList)
      for tj=1:length(nodeIndPos7)
        j = nodeIndPos7[tj]
        # check whether x AND y match within epsilon
        if isMachZero(xy[i,1],xy[j,1]) &&
           isMachZero(xy[i,2],xy[j,2]) &&
           i!=j
          # find other index for node j
          kjfind = something(findfirst(isequal(j), cm), 0)
          # 1) assign corresponding node index to cm of other node
          cmNew[kjfind] = i
          # 3) add other node to list so that it isn't crossed later on
          push!(skipList,j)
          break
        end
      end
    end
  end

  # for each node in 6 or 8 position
  for ti=1:length(nodeIndPos6)
    i = nodeIndPos6[ti]
    if !(i in skipList)
      for tj=1:length(nodeIndPos8)
        j = nodeIndPos8[tj]
        # check whether x AND y match within epsilon
        if isMachZero(xy[i,1],xy[j,1]) &&
           isMachZero(xy[i,2],xy[j,2]) &&
           i!=j
          # find other index for node j
          kjfind = something(findfirst(isequal(j), cmNew), 0)
          # 1) assign corresponding node index to cm of other node
          cmNew[kjfind] = i
          # 3) add other node to list so that it isn't crossed later on
          push!(skipList,j)
          break
        end
      end
    end
  end

  cmsubtract = zeros(Int,(length(cm)))
  # forward sweep to decide how many to subtract from each cm
  tempind=1
  for i in cmNew
    for k in skipList
      if i>=k
        cmsubtract[tempind]+=1
      end
    end
    tempind+=1
  end

  # backward sweep to actually take off those deleted indices
  for i=1:length(cm)
    cmNew[i] -= cmsubtract[i]
  end

  xyNew = xy[setdiff(1:end, skipList), :]

  return xyNew, cmNew
end

# function to check whether two numbers are within machine epsilon
function isMachZero(x,y)
  ep = eps()
  if abs(x-y) < ep
    return true
  else
    return false
  end
end

"""
  pMesh(xy,cm) -> xyp, cmp

Generates pressure mesh equivalents given a velocity mesh (xy,cm).

Might need to generate map between velocity and pressure nodes ....?
"""
function pMesh(xy,cm)
  Nel         = size(cm,1)
  NumPNodes   = length(unique(@view cm[:,1:4]))
  xyp         = zeros(NumPNodes,2)
  cmp         = zeros(Int,(Nel,4))

  nodeCheckarr::Array{Int,1} = []

  elCounter = 1
  nodeCounter = 1
  nodeCheck = 1
  for el=1:Nel
    for i=1:4
      c = cm[el,i]
      if c in nodeCheckarr
        cmp[elCounter,i] = something(findfirst(isequal(c),nodeCheckarr),0)
      else
        push!(nodeCheckarr,c)
        xyp[nodeCounter,:] = @view xy[c,:]
        cmp[elCounter,i] = nodeCounter
        nodeCounter += 1
      end

      nodeCheck += 1
    end

    elCounter += 1
  end

  return xyp,cmp
end

"""
  lowerUV(xy,cm,u,v) -> u,v

computes velocity on coarser mesh using p-mesh
"""
function lowerUV(xy,cm,u,v)
  Nel       = size(cm,1)
  NumPNodes = length(unique(cm[:,1:4]))
  q1u   = zeros(NumPNodes,1)
  q1v   = zeros(NumPNodes,1)
  cmp = 0*Array{Int}(undef,Nel,4)
  nodeCheckarr::Array{Int,1} = []

  elCounter = 1
  nodeCounter = 1
  nodeCheck = 1
  for el=1:Nel
    for i=1:4
      c = cm[el,i]
      if c in nodeCheckarr
        cmp[elCounter,i] = something(findfirst(isequal(c),nodeCheckarr),0)
      else
        push!(nodeCheckarr,c)
        q1u[nodeCounter] = u[c]
        q1v[nodeCounter] = v[c]
        nodeCounter += 1
      end

      nodeCheck += 1
    end

    elCounter += 1
  end

  return q1u,q1v
end

"""
  lowerScalar(xy,cm,q2s) -> q1s

computes scalar on coarser mesh using p-mesh
"""
function lowerScalar(xy,cm,q2s)
  Nel       = size(cm,1)
  NumQ1Nodes = length(unique(cm[:,1:4]))
  q1s   = zeros(NumQ1Nodes,1)
  #cmp = 0*Array{Int}(Nel,4)
  nodeCheckarr::Array{Int,1} = []

  elCounter = 1
  nodeCounter = 1
  nodeCheck = 1
  for el=1:Nel
    for i=1:4
      c = cm[el,i]
      if c in nodeCheckarr
        #cmp[elCounter,i] = findfirst(nodeCheckarr,c)
      else
        push!(nodeCheckarr,c)
        q1s[nodeCounter] = q2s[c]
        nodeCounter += 1
      end

      nodeCheck += 1
    end

    elCounter += 1
  end

  return q1s
end


"""

This function fills a pre-allocated square matrix `D` with the taxonomic
distance between taxa in a vector `tax`. The keywords areguments are `d` (the
distance as a function to closest matching level), `strict` (a boolean to decide
whether the distance of a taxon with itself should be read from the distance
dictionary, or set to 0), and other keywords passed to the `lineage` function.

Because the distances are symetrical, there are only (n(n-1))/2 measurements to
do.

By default, this function uses the distances from Shimatani (2001):

| equal rank  | distance |
|-------------|----------|
| species     | 0        |
| genus       | 1        |
| family      | 2        |
| subclass    | 3        |
| `:fallback` | 4        |

Shimatani, K. 2001. On the measurement of species diversity incorporating
species differences. Oikos 93:135–147.
"""
function taxonomicdistance!(D::Matrix{Float64}, tax::Vector{NCBITaxon}; d::Dict{Symbol,Float64}=Dict(:species=>0.0, :genus=>1.0, :family=>2.0, :subclass=>3.0, :fallback=>4.0), strict::Bool=true, kwargs...)
    if size(D,1) != size(D,2)
        throw(DimensionMismatch("The matrix `D` ($(size(D))) must be square" ))
    end
    if size(D,1) != length(tax)
        throw(DimensionMismatch("The matrix `D` ($(size(D,1))) must have the same sizes as the length of `tax` ($(length(tax)))."))
    end
    ancestry = lineage.(tax; kwargs...)
    for i in 1:(length(tax)-1)
        D[i,i] = strict ? get(d, rank(tax[i]), d[:fallback]) : 0.0
        ai = ancestry[i]
        for j in (i+1):length(tax)
            aj = ancestry[j]
            shared = rank.(ai ∩ aj)
            D[i,j] = D[j,i] = minimum([get(d, s, d[:fallback]) for s in shared])
        end
    end
    return D
end

"""
    taxonomicdistance(tax::Vector{NCBITaxon}; kwargs...)

See `taxonomicdistance!`.
"""
function taxonomicdistance(tax::Vector{NCBITaxon}; kwargs...)
    D = zeros(Float64, length(tax), length(tax))
    taxonomicdistance!(D, tax; kwargs...)
    return D
end


######################
# Auxilary functions #
######################

"""
    outer(vecs::Array{Array{T,1},1}) where T<:Real

Takes vector of vectors and return outer product tensor

**Input parameters**
* `vecs::Array{Array{T,1},1}` - array of vectors to use in outer product
"""
function outer(vecs::Array{Array{T,1},1}) where T<:Real
    vecs_len = length.(vecs)
    dims = length(vecs_len)
    reshaped = [ones(Int,dims) for i in 1:dims]
    for i in 1:dims
        reshaped[i][i]=vecs_len[i]
    end
    return .*([reshape(v,reshaped[i]...) for (i,v) in enumerate(vecs)]...)
end

"""
    outer(vecs::Array{Array{SymPy.Sym,1},1})

Takes vector of symbolic vectors and return outer product tensor

**Input parameters**
* `vecs::Array{Array{SymPy.Sym,1},1}` - array of symbolic vectors to use in outer product
"""
function outer(vecs::Array{Array{SymPy.Sym,1},1})
    vecs_len = length.(vecs)
    dims = length(vecs_len)
    reshaped = [ones(Int,dims) for i in 1:dims]
    for i in 1:dims
        reshaped[i][i]=vecs_len[i]
    end
    return .*([reshape(v,reshaped[i]...) for (i,v) in enumerate(vecs)]...)
end

"""
    _next_state(game::Dict{String,<:Array}, s::Vector{Vector{T}},
            states_dict::Dict{<:Array, Int64}) where T<:Real

Internal function finding the index of next state (next simplex vertex)

**Input parameters**
* `game::Dict{String,<:Array}` - dictionary of players and their payoff matrices
* `s::Vector{Vector{T}},` - vector of actions probabilities
* `states_dict::Dict{<:Array, Int64}` - a dictionary of simplex vertices (states)
"""
function _next_state(game::Dict{String,<:Array},
            s::Vector{Vector{T}},
            states_dict::Dict{<:Array, Int64}) where T<:Real
    br = Array{Int64}[]
    for i in 1:length(game)
        push!(br,best_reply(game, s, i))
    end
    next = _select_random(game, s, br)
    get(states_dict, next, 0)
end

"""
    select_random(game::Dict{String,<:Array}, s::Vector{Vector{T}},
            br::Array{Array{Int64}}) where T<:Real

Internal function for best reply iteration.
Returns a random point on the given best reply simplex

**Input parameters**
* `game::Dict{String,<:Array}` - dictionary of players and their payoff matrices
* `s::Vector{Vector{T}}` - vector of actions probabilities
* `br::Array{Array{Int64}}` - array of best reply
"""
function _select_random(game::Dict{String,<:Array},
            s::Vector{Vector{T}},
            br::Array{Array{Int64}}) where T<:Real
    s_iter = [] #TODO: not Any type
    for i in 1:length(game)
            s_iter_temp = fill(0.0, size(s[i]))' #size: number of actions
            findzero = mapslices(sum, convert(Array{Int,2},br[i]), dims = 1)
            s_iter_temp[findzero .!= 0] = rand(sum(findzero))  #where best reply is non-zero
            s_iter_temp = s_iter_temp ./ sum(s_iter_temp)
            s_iter_temp = reshape(s_iter_temp, size(s[i]))
            push!(s_iter, s_iter_temp)
    end
    s_iter = convert(typeof(s), s_iter)
    return s_iter
end

"""
    plot_br(br::CDDLib.Polyhedron{T}) where T<:Real

Plots best reply in the form of simplex (3d and above)

**Input parameters**
* `br::CDDLib.Polyhedron{T})` - best_reply function (with return_val="chull") output
"""
function plot_br(br::CDDLib.Polyhedron{T}) where T<:Real
    br_mesh=Polyhedra.Mesh(br)
    vis = MeshCat.Visualizer()
    setobject!(vis, br_mesh)
    IJuliaCell(vis)
end


function sdpt1_test()
    # min X[1,1] + X[2,2]    max y
    #     X[2,1] = 1         [0   y/2     [ 1  0
    #                         y/2 0    <=   0  1]
    #     X >= 0              y free
    # Optimal solution:
    #     ⎛ 1   1 ⎞
    # X = ⎜       ⎟           y = 2
    #     ⎝ 1   1 ⎠

    model = TestModel{Float64}()

    X = MOI.add_variables(model, 3)

    vov = MOI.VectorOfVariables(X)

    cX = MOI.add_constraint(model, vov, MOI.PositiveSemidefiniteConeTriangle(2))

    c = MOI.add_constraint(model, MOI.ScalarAffineFunction([MOI.ScalarAffineTerm(1.0, X[2])], 0.0), MOI.EqualTo(1.0))

    MOI.set(model, MOI.ObjectiveFunction{MOI.ScalarAffineFunction{Float64}}(), MOI.ScalarAffineFunction(MOI.ScalarAffineTerm.(1.0, [X[1], X[end]]), 0.0))
    MOI.set(model, MOI.ObjectiveSense(), MOI.MIN_SENSE)

    return  model
end

function sdpt2_test()
    #=
        min TR(X)
    s.t.
        X[2,1] = 1
        X in PSD
    =#

    model = TestModel{Float64}()

    X = MOI.add_variables(model, 3)

    vov = MOI.VectorOfVariables(X)

    cX = MOI.add_constraint(model, MOI.VectorAffineFunction{Float64}(vov), MOI.PositiveSemidefiniteConeTriangle(2))

    c = MOI.add_constraint(model, MOI.ScalarAffineFunction([MOI.ScalarAffineTerm(1.0, X[2])], 0.0), MOI.EqualTo(1.0))

    MOI.set(model, MOI.ObjectiveFunction{MOI.ScalarAffineFunction{Float64}}(), MOI.ScalarAffineFunction(MOI.ScalarAffineTerm.(1.0, [X[1], X[end]]), 0.0))
    MOI.set(model, MOI.ObjectiveSense(), MOI.MIN_SENSE)

    return  model
end

function sdpt3_test()
    # Problem SDP1 - sdo1 from MOSEK docs
    # From Mosek.jl/test/mathprogtestextra.jl, under license:
    #   Copyright (c) 2013 Ulf Worsoe, Mosek ApS
    #   Permission is hereby granted, free of charge, to any person obtaining a copy of this
    #   software and associated documentation files (the "Software"), to deal in the Software
    #   without restriction, including without limitation the rights to use, copy, modify, merge,
    #   publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons
    #   to whom the Software is furnished to do so, subject to the following conditions:
    #   The above copyright notice and this permission notice shall be included in all copies or
    #   substantial portions of the Software.
    #
    #     | 2 1 0 |
    # min | 1 2 1 | . X + x1
    #     | 0 1 2 |
    #
    #
    # s.t. | 1 0 0 |
    #      | 0 1 0 | . X + x1 = 1
    #      | 0 0 1 |
    #
    #      | 1 1 1 |
    #      | 1 1 1 | . X + x2 + x3 = 1/2
    #      | 1 1 1 |
    #
    #      (x1,x2,x3) in C^3_q
    #      X in C_psd
    #
    # The dual is
    # max y1 + y2/2
    #
    # s.t. | y1+y2    y2    y2 |
    #      |    y2 y1+y2    y2 | in C_psd
    #      |    y2    y2 y1+y2 |
    #
    #      (1-y1, -y2, -y2) in C^3_q
    #
    # The dual of the SDP constraint is rank two of the form
    # [γ, 0, -γ] * [γ, 0, γ'] + [δ, ε, δ] * [δ, ε, δ]'
    # and the dual of the SOC constraint is of the form (√2*y2, -y2, -y2)
    #
    # The feasible set of the constraint dual contains only four points.
    # Eliminating, y1, y2 and γ from the dual constraints gives
    # -ε^2 + -εδ + 2δ^2 + 1
    # (√2-2)ε^2 + (-2√2+2)δ^2 + 1
    # Eliminating ε from this set of equation give
    # (-6√2+4)δ^4 + (3√2-2)δ^2 + (2√2-3)
    # from which we find the solution
    # δ = √(1 + (3*√2+2)*√(-116*√2+166) / 14) / 2
    # which is optimal
    # ε = √((1 - 2*(√2-1)*δ^2) / (2-√2))
    # y2 = 1 - ε*δ
    # y1 = 1 - √2*y2
    # obj = y1 + y2/2
    # The primal solution is rank one of the form
    # X = [α, β, α] * [α, β, α]'
    # and by complementary slackness, x is of the form (√2*x2, x2, x2)
    # The primal reduces to
    #      4α^2+4αβ+2β^2+√2*x2= obj
    #      2α^2    + β^2+√2*x2 = 1 (1)
    #      8α^2+8αβ+2β^2+ 4 x2 = 1
    # Eliminating β, we get
    # 4α^2 + 4x2 = 3 - 2obj (2)
    # By complementary slackness, we have β = kα where
    # k = -2*δ/ε
    # Replacing β by kα in (1) allows to eliminate α^2 in (2) to get
    # x2 = ((3-2obj)*(2+k^2)-4) / (4*(2+k^2)-4*√2)
    # With (2) we get
    # α = √(3-2obj-4x2)/2
    # β = k*α

   model = TestModel{Float64}()

    X = MOI.add_variables(model, 6)
    x = MOI.add_variables(model, 3)

    vov = MOI.VectorOfVariables(X)

    cX = MOI.add_constraint(model, vov, MOI.PositiveSemidefiniteConeTriangle(3))
    cx = MOI.add_constraint(model, MOI.VectorOfVariables(x), MOI.SecondOrderCone(3))

    c1 = MOI.add_constraint(model, MOI.ScalarAffineFunction(MOI.ScalarAffineTerm.([1., 1, 1, 1], [X[1], X[3], X[end], x[1]]), 0.), MOI.EqualTo(1.))
    c2 = MOI.add_constraint(model, MOI.ScalarAffineFunction(MOI.ScalarAffineTerm.([1., 2, 1, 2, 2, 1, 1, 1], [X; x[2]; x[3]]), 0.), MOI.EqualTo(1/2))

    objXidx = [1:3; 5:6]
    objXcoefs = 2*ones(5)
    MOI.set(model, MOI.ObjectiveFunction{MOI.ScalarAffineFunction{Float64}}(), MOI.ScalarAffineFunction(MOI.ScalarAffineTerm.([objXcoefs; 1.0], [X[objXidx]; x[1]]), 0.0))
    MOI.set(model, MOI.ObjectiveSense(), MOI.MIN_SENSE)

    return model
end

function sdpt4_test()
    # Problem SDP1 - sdo1 from MOSEK docs
    # From Mosek.jl/test/mathprogtestextra.jl, under license:
    #   Copyright (c) 2013 Ulf Worsoe, Mosek ApS
    #   Permission is hereby granted, free of charge, to any person obtaining a copy of this
    #   software and associated documentation files (the "Software"), to deal in the Software
    #   without restriction, including without limitation the rights to use, copy, modify, merge,
    #   publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons
    #   to whom the Software is furnished to do so, subject to the following conditions:
    #   The above copyright notice and this permission notice shall be included in all copies or
    #   substantial portions of the Software.
    #
    #     | 2 1 0 |
    # min | 1 2 1 | . X + x1
    #     | 0 1 2 |
    #
    #
    # s.t. | 1 0 0 |
    #      | 0 1 0 | . X + x1 = 1
    #      | 0 0 1 |
    #
    #      | 1 1 1 |
    #      | 1 1 1 | . X + x2 + x3 = 1/2
    #      | 1 1 1 |
    #
    #      (x1,x2,x3) in C^3_q
    #      X in C_psd
    #
    # The dual is
    # max y1 + y2/2
    #
    # s.t. | y1+y2    y2    y2 |
    #      |    y2 y1+y2    y2 | in C_psd
    #      |    y2    y2 y1+y2 |
    #
    #      (1-y1, -y2, -y2) in C^3_q
    #
    # The dual of the SDP constraint is rank two of the form
    # [γ, 0, -γ] * [γ, 0, γ'] + [δ, ε, δ] * [δ, ε, δ]'
    # and the dual of the SOC constraint is of the form (√2*y2, -y2, -y2)
    #
    # The feasible set of the constraint dual contains only four points.
    # Eliminating, y1, y2 and γ from the dual constraints gives
    # -ε^2 + -εδ + 2δ^2 + 1
    # (√2-2)ε^2 + (-2√2+2)δ^2 + 1
    # Eliminating ε from this set of equation give
    # (-6√2+4)δ^4 + (3√2-2)δ^2 + (2√2-3)
    # from which we find the solution
    # δ = √(1 + (3*√2+2)*√(-116*√2+166) / 14) / 2
    # which is optimal
    # ε = √((1 - 2*(√2-1)*δ^2) / (2-√2))
    # y2 = 1 - ε*δ
    # y1 = 1 - √2*y2
    # obj = y1 + y2/2
    # The primal solution is rank one of the form
    # X = [α, β, α] * [α, β, α]'
    # and by complementary slackness, x is of the form (√2*x2, x2, x2)
    # The primal reduces to
    #      4α^2+4αβ+2β^2+√2*x2= obj
    #      2α^2    + β^2+√2*x2 = 1 (1)
    #      8α^2+8αβ+2β^2+ 4 x2 = 1
    # Eliminating β, we get
    # 4α^2 + 4x2 = 3 - 2obj (2)
    # By complementary slackness, we have β = kα where
    # k = -2*δ/ε
    # Replacing β by kα in (1) allows to eliminate α^2 in (2) to get
    # x2 = ((3-2obj)*(2+k^2)-4) / (4*(2+k^2)-4*√2)
    # With (2) we get
    # α = √(3-2obj-4x2)/2
    # β = k*α

    model = TestModel{Float64}()

    X = MOI.add_variables(model, 6)
    x = MOI.add_variables(model, 3)

    vov = MOI.VectorOfVariables(X)
    cX = MOI.add_constraint(model, MOI.VectorAffineFunction{Float64}(vov), MOI.PositiveSemidefiniteConeTriangle(3))
    cx = MOI.add_constraint(model, MOI.VectorOfVariables(x), MOI.SecondOrderCone(3))

    c1 = MOI.add_constraint(model, MOI.ScalarAffineFunction(MOI.ScalarAffineTerm.([1., 1, 1, 1], [X[1], X[3], X[end], x[1]]), 0.), MOI.EqualTo(1.))
    c2 = MOI.add_constraint(model, MOI.ScalarAffineFunction(MOI.ScalarAffineTerm.([1., 2, 1, 2, 2, 1, 1, 1], [X; x[2]; x[3]]), 0.), MOI.EqualTo(1/2))

    objXidx = [1:3; 5:6]
    objXcoefs = 2*ones(5)
    MOI.set(model, MOI.ObjectiveFunction{MOI.ScalarAffineFunction{Float64}}(), MOI.ScalarAffineFunction(MOI.ScalarAffineTerm.([objXcoefs; 1.0], [X[objXidx]; x[1]]), 0.0))
    MOI.set(model, MOI.ObjectiveSense(), MOI.MIN_SENSE)

    return model
end


@reexport module TSMLDemo

using Dates
using DataFrames
using Random
using Statistics
using Plots

using TSML: Statifier
using TSML.BaseFilters: Pipeline
using TSML.ValDateFilters: DateValgator, DateValNNer
using TSML.TSMLTypes
import TSML.TSMLTypes.fit! # to overload
import TSML.TSMLTypes.transform! # to overload
using TSML.Utils
using TSML: Plotter

export tsml_demo

function pauseme(msg)
  println(msg)
  println(">>> enter key to continue <<<")
  read(stdin,Char);
  nothing
end

function generateX()
    Random.seed!(123)
    gdate = DateTime(2014,1,1):Dates.Minute(15):DateTime(2014,1,3)
    gval = Array{Union{Missing,Float64}}(rand(length(gdate)))
    gmissing = 0.30 * length(gdate) |> floor |> Integer
    gndxmissing = Random.shuffle(1:length(gdate))[1:gmissing]
    X = DataFrame(Date=gdate,Value=gval)
    X.Value[gndxmissing] .= missing
    return X
end

function tsml_demo()
  println("\n")
  println(">>>>>>>>>><<<<<<<<<<")
  println("Welcome to TSML demo")
  println(">>>>>>>>>><<<<<<<<<<")
  println("\n")
  println("Let's start by generating random Time Series")
  pauseme("data and output the first 20 rows")
  data=generateX()
  println(first(data,20))
  println("...")
  println("...")
  pauseme("Notice the presence of missing values.")

  println("Let's create a pipeline for plotting")
  println(" ")
  pauseme("
  julia code:
      pltr = Plotter()
      mpipeline = Pipeline(Dict(
            :transformers => [pltr]
           )
      )
      fit!(mpipeline,data)
      transform!(mpipeline,data)
  ")
  println("Please wait until the plot is shown before continuing.")

  pltr = Plotter()
  xpipeline = Pipeline(Dict(
          :transformers => [pltr]
       )
  )
  fit!(xpipeline,data)
  res=transform!(xpipeline,data)
  display(res)
  println("Notice the breaks in the plot due to missing data.\n")
  println("Let's create a pipeline to aggregate and")
  println("measure data quality including missing values")
  println(" ")
  pauseme("
  julia code:
      valgator = DateValgator(Dict(:dateinterval => Dates.Hour(1)))
      stfier = Statifier(Dict(:processmissing => true))
      mpipeline = Pipeline(Dict(
            :transformers => [valgator,stfier]
           )
      )
      fit!(mpipeline,data)
      res = transform!(mpipeline,data)
  ")

  valgator = DateValgator(Dict(:dateinterval => Dates.Hour(1)))
  stfier = Statifier(Dict(:processmissing => true))
  mpipeline = Pipeline(Dict(
        :transformers => [valgator,stfier]
       )
  )
  fit!(mpipeline,data)
  res = transform!(mpipeline,data)
  show(res,allcols=true)
  println("\n")
  println("Notice the stats of blocks of missing values denoted by fieldnames starting with b.")
  println(" ")
  println("Let's create a pipeline for aggregation with imputation and get data quality")
  pauseme("
  julia code:
      valgator = DateValgator(Dict(:dateinterval => Dates.Hour(1)))
      valnner = DateValNNer(Dict(:dateinterval => Dates.Hour(1)))
      stfier = Statifier(Dict(:processmissing => true))
      mpipeline = Pipeline(Dict(
            :transformers => [valgator,valnner,stfier]
           )
      )
      fit!(mpipeline,data)
      res = transform!(mpipeline,data)
  ")
  valgator = DateValgator(Dict(:dateinterval => Dates.Hour(1)))
  valnner = DateValNNer(Dict(:dateinterval => Dates.Hour(1)))
  stfier = Statifier(Dict(:processmissing => true))
  mpipeline = Pipeline(Dict(
          :transformers => [valgator,valnner,stfier]
       )
  )
  fit!(mpipeline,data)
  res = transform!(mpipeline,data)
  show(res,allcols=true)
  println("\n")
  println("Notice that the imputation causes the statistics of missing values return NaN.")
  println("")
  pauseme("Let's create a pipeline to plot the cleaned and imputed data")
  pauseme("
  julia code:
      mpipeline = Pipeline(Dict(
              :transformers => [valgator,valnner,pltr]
           )
      )
      fit!(mpipeline,data)
      res=transform!(mpipeline,data)
  ")
  mpipeline = Pipeline(Dict(
          :transformers => [valgator,valnner,pltr]
       )
  )
  fit!(mpipeline,data)
  res=transform!(mpipeline,data)
  display(res)
  nothing
end

end