text:
#####################################################################################################
# Partition nanolish file
#####################################################################################################
using CpelNano

## Regular mode
pkg_dir = dirname(dirname(pathof(CpelNano)))

# IO
dataDir = "$(pkg_dir)/examples/full_example/"
nano = "$(dataDir)/nanopolish/full_example_noise2.0_methylation_calls.sorted.tsv"

# Partition nanopolish file into 5 files
CpelNano.split_nanopolish_file(nano,5)

#####################################################################################################
# Get model dictionary nanopolish model file
#####################################################################################################
using CpelNano

# IO
dataDir = "/Users/jordiabante/OneDrive - Johns Hopkins/CpelNano/Data/Real-Data/Nanopolish-Pore-Model/"
nano = "$(dataDir)/r9.4_450bps.cpg.6mer.template.model"

# Get nanopolish model dictionary
nano_dic = CpelNano.get_nano_pore_mod(nano)

#####################################################################################################
# Marginal model
#####################################################################################################
using CpelNano

## Regular mode
pkg_dir = dirname(dirname(pathof(CpelNano)))

# IO
dataDir = "$(pkg_dir)/examples/full_example/"
nano = "$(dataDir)/nanopolish/full_example_noise2.0_methylation_calls.sorted.tsv"
fasta = "$(dataDir)/reference/hg38_chr17_43023997_43145780.fa"
outdir = "$(dataDir)/cpelnano/"
outdir = "/Users/jordiabante/Desktop/"

# IO
config = CpelNano.CpelNanoConfig();
config.out_prefix = "full_example_sample";
config.out_dir = outdir;
config.verbose = false; # true;

# Analyze nano
CpelNano.nano_smp_est(nano,fasta,config)

#####################################################################################################
# Process nano file
#####################################################################################################
using CpelNano

## Regular mode
pkg_dir = dirname(dirname(pathof(CpelNano)))

# IO
dataDir = "$(pkg_dir)/examples/full_example/"
nano = "$(dataDir)/nanopolish/full_example_noise2.0_methylation_calls.sorted.tsv"
fasta = "$(dataDir)/reference/hg38_chr17_43023997_43145780.fa"
outdir = "$(dataDir)/cpelnano/"

# IO
config = CpelNano.CpelNanoConfig();
config.mod_nse = true;
config.verbose = false; # true;
config.out_dir = outdir;
config.out_prefix = "full_example_mod_nse_$(config.mod_nse)";

# Analyze nano
CpelNano.analyze_nano(nano,fasta,config)

## Targeted mode

# IO
dataDir = "$(pkg_dir)/examples/targeted_example/"
nano = "$(dataDir)/nanopolish/targeted_example_noise2.0_sorted_methylation_calls.sorted.tsv"
fasta = "$(dataDir)/reference/hg38_chr17_42879379_43290399.fa"
bed = "$(dataDir)/regions_of_interest/brca1.bed"
outdir = "$(dataDir)/cpelnano/"
outdir = "/Users/jordiabante/Desktop/"

# IO
config = CpelNano.CpelNanoConfig();
config.out_prefix = "targeted_example";
config.out_dir = outdir;
config.verbose = true;
config.bed_reg = bed;

# Analyze nano
CpelNano.analyze_nano(nano,fasta,config)

#####################################################################################################
# ESTIMATION
#####################################################################################################

## Process chromosome
using CpelNano

# IO
dataDir = "$(dirname(dirname(pathof(CpelNano))))/examples/full_example/"
nano = "$(dataDir)/nanopolish/full_example_noise2.0_methylation_calls.sorted.tsv"
fasta = "$(dataDir)/reference/hg38_chr17_43023997_43145780.fa"
outdir = "$(dataDir)/cpelnano/"

# Configuration
min_cov = 10.0; max_size_subreg = 500; size_est_reg = 4000; max_em_init = 10; max_em_iters = 20;
config = CpelNano.CpelNanoConfig(min_cov, max_size_subreg, size_est_reg, max_em_init, max_em_iters);
config.out_dir = outdir; config.out_prefix = "test";

# Analyze each region in chr
CpelNano.analyze_nano(nano,fasta,config)

# Output files
mml_path = "$(dataDir)/cpelnano/test_mml.bedGraph"
nme_path = "$(dataDir)/cpelnano/test_nme.bedGraph"
theta_path = "$(dataDir)/cpelnano/test_theta.bedGraph"

# Print files
# println(String(read(mml_path)))
# println(String(read(nme_path)))
println(String(read(theta_path)))

#####################################################################################################
# DIFFERENTIAL ANALYSIS
#####################################################################################################

# Read in model file
models = CpelNano.read_mod_file_chr(theta_path, "Reference")
CpelNano.comp_gjsd(models["Reference_1_4000"],models["Reference_1_4000"])

# Get all models
mod_files = fill(theta_path, 5)
mods = CpelNano.get_all_models_chr(mod_files, "Reference")
CpelNano.get_unique_ids(mods)
uniq_ids = unique(vcat(CpelNano.get_unique_ids(mods), CpelNano.get_unique_ids(mods)))


text:
### 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

# ╔═╡ 77dbc46f-1f30-47b1-b121-5811a903b57b
begin
	using LinearAlgebra
	using PlutoUI
	using Plots
	using TikzPictures
end

# ╔═╡ 701c45a0-e449-42f2-b056-99c371320fad
md"""
# DMRG Example
**Stefan Bringuier**

**[stefanbringuier@gmail.com](mailto:stefanbringuier@gmail.com)**
"""

# ╔═╡ fd9fa7a3-ab72-4131-89ed-11f154d3fb3c
md"""
!!! note "Note"
    Julia packages required for this notebook.
"""

# ╔═╡ 13be2e26-6ba1-49d1-a3cd-770679fc7ff9
PlutoUI.TableOfContents()

# ╔═╡ 35ce17c0-d377-11eb-2c48-a5e04a48dacb
md"""
## Heisenberg Antiferromagnetic Infinite Chain

Assuming a 1-D chain of spin ½ particles with nearest neighbor interactions the Hamiltonian takes the form:

```math
H = \sum^{L-1}_{i=1} \vec{S}_i \cdot \vec{S}_{i+1}
```

This two-state quantum system therefore has a Hilbert space of dimension $2^L$, where $L$ corresponds to the number of particles which given a thermodynamic ensemble, beomces an untractable problem to solve.

The operators $\vec{S}$ are the spin-operators:

```math
\begin{align}
\vec{S} &= \hat{S}^x + \hat{S}^y + \hat{S}^z \\
\\
\hat{S}^{x} &= \frac{\hbar}{2} \begin{bmatrix} 0 & 1 \\ 1 & 0 \end{bmatrix} \\
\hat{S}^{y} &= \frac{\hbar}{2} \begin{bmatrix} 0 & -i \\ i & 0 \end{bmatrix} \\
\hat{S}^{z} &= \frac{\hbar}{2} \begin{bmatrix} 1 & 0 \\ 0 & -1 \end{bmatrix}

\end{align}
```

"""

# ╔═╡ f694e40b-538d-490d-b891-10c7110d1913
md"""
## Density Matrix Renormalization Group

The idea behind DMRG is that we can use a variational scheme to extract the correct many-body quantum state for the low energy spectrum by limiting the scope of the Hilbert space explored.

In 1D infinite DMRG one starts with some minimal number of states suitable for obtaining the ground-state via *block decimation*. This is usually done by having two blocks initial with a single site. Then in the process below the blocks grow by adding sites and increasing the block Hilbert space.

"""

# ╔═╡ 957d98b0-f6fc-4cca-b21c-39c0c7d27246
TikzPicture(L"""
			\node at (3.0,0.75) {$Block\;1$};
			\draw[fill=blue!20] (-0.5,-0.5) rectangle (6.5,0.5);
			\draw (0,0) -- (6,0);
			\foreach \x in {0,...,6}
				\draw[fill] (\x,0) circle (0.25);
			\node at (11.0,0.75) {$Block\;2$};
			\draw [fill=blue!20] (7.5,-0.5) rectangle (14.5,0.5);
			\draw (8,0) -- (14,0);
			\foreach \x in {8,...,14}
				\draw[fill] (\x,0) circle (0.25);
			""",
			options="",preamble="",width="300mm")

# ╔═╡ 52f904fd-806d-487d-84bb-febf7ac63bcf
TikzPicture(L"""
			\draw[fill=blue!20] (-0.5,-0.5) rectangle (6.5,0.5);
			\foreach \x in {0,...,7}
				\draw[fill] (\x,0) circle (0.25);
			\draw [fill=blue!20] (8.5,-0.5) rectangle (15.5,0.5);
			\foreach \x in {8,...,15}
				\draw[fill] (\x,0) circle (0.25);
			\draw (0,0) -- (15,0);
			\node at (7.5,0.5) {$2\;sites$};
			""",
			options="",preamble="",width="300mm")

# ╔═╡ aa3dd19a-c9de-44cf-be35-93ca34144442
TikzPicture(L"""
			\draw[fill=blue!20] (-0.5,-0.5) rectangle (7.5,0.5);
			\foreach \x in {0,...,7}
				\draw[fill] (\x,0) circle (0.25);
			\draw [fill=blue!20] (7.5,-0.5) rectangle (15.5,0.5);
			\foreach \x in {8,...,15}
				\draw[fill] (\x,0) circle (0.25);
			\draw (0,0) -- (15,0);
			""",
			options="",preamble="",width="300mm")

# ╔═╡ 22e404fc-5908-4c6b-bfff-dcbbec5733fa
TikzPicture(L"""
			\node at (3.5,0.75) {$New\;block\;1$};
			\draw[fill=blue!20] (-0.5,-0.5) rectangle (7.5,0.5);
			\foreach \x in {0,...,7}
				\draw[fill] (\x,0) circle (0.25);
			\draw (0,0) -- (7,0);
			\node at (12.5,0.75) {$New\;block\;2$};
			\draw [fill=blue!20] (8.5,-0.5) rectangle (16.5,0.5);
			\foreach \x in {9,...,16}
				\draw[fill] (\x,0) circle (0.25);
		    \draw (9,0) -- (16,0);
			""",
			options="",preamble="",width="300mm")

# ╔═╡ 3b344b18-e496-4be1-969e-a97b4e3909eb
md"""
In the case of the infinite Heisenberg spin chain, there is really on one block given that at the boundaries its the same block. This makes the implementation a bit more concise since we only need to add a single site and then just treat the block as doubly.
"""

# ╔═╡ 6e30235e-2fb3-4280-acff-d1ec196aad4c
md"""
## Simple Code Outline

The steps below show how to perform the iterative DMRG process on an infinite chain of interaction spin sites.
"""

# ╔═╡ 068e86f8-ffab-46a4-a9eb-e8ed17d00fbd
begin
	𝑖 = im;
	ħ = 1.0;
	⋅ = *;
	⊗ = kron; #tensor product
end;

# ╔═╡ 792b1531-01db-44fa-af8e-26f77690c9f8
md"""
Now defining the different operator matrices. The notation may be inconsistant with typical use.
"""

# ╔═╡ 8a1f7b8a-f69b-4a23-ae50-edd7f6317643
begin
	I₂ = [1 0;
		  0 1];
	S₁ = ħ/2 ⋅ [0 1;
		        1 0];
	X = S₁;
	S₂ = ħ/2 ⋅ [0 -𝑖;
		        𝑖 0];
	Y = S₂
	S₃ = ħ/2 ⋅ [1  0;
		        0 -1];
	Z = S₃;
end;

# ╔═╡ 8e107e94-eecc-46f7-ac6a-e585fca30214
md"""
The antiferromagnetic Heisenberg hamiltonian can be written in a more compact form:

```math
H = \sum_i^{L-1} \hat{S}_i^{z}\,\hat{S}_{i+1}^{z} + \frac{1}{2}\left(\hat{S}_i^{+}\,\hat{S}_{i+1}^{-} + \hat{S}_{i}^{-}\,\hat{S}_{i+1}^{+}\right)
```
"""

# ╔═╡ 22923198-f790-4753-91cc-a53a37b49eda
md"""
The operators $\hat{S}^{+}$ and $\hat{S}^{-}$ are commonly called the hopping operators since they move up and dow spins from one site to another.
"""

# ╔═╡ 27558d8d-1779-494c-87ce-f9f24d5c1d8f
begin
	Sᵖ = S₁ + 𝑖⋅S₂
	Sᵐ = S₁ - 𝑖⋅S₂
end;

# ╔═╡ 5fd3ff9f-1aaa-41c3-9e65-9a9bf8781cd0
md"""
Now specifiying block operator and hamiltonian with a initial single site in each block.

To do this it is convienient to create composite data types so that function calls act on them accordingly (i.e. multiple dispatch paradigm in Julia).

The parent data type is `Block` and then there is a subtype called `InfiniteBlock` which is just a `Julia` dictionary.
"""

# ╔═╡ 8a7cb9f5-3f35-452e-8679-ca24f461a5bc
abstract type Block1D end

# ╔═╡ f9356bf2-fc71-4839-a98c-dea87cbf7534
let
	struct InfiniteBlock{T<:AbstractDict} <: Block1D
		dict::T
		InfiniteBlock(ps...) = new{AbstractDict}(Dict(ps))
	end
	Base.getindex(h::Block1D,key) = Base.getindex(h.dict,key)
	Base.setindex!(h::Block1D,v0,key) = Base.setindex!(h.dict,v0,key)
end

# ╔═╡ 8faa032f-c3d8-4e14-b0bd-2bcdb42c85b6
md"""
The function below creates a dictionary of operators that are in the Hamiltonian. The keys are of type `Symbol` and are:
`:I`
`:Z`
`:-`
`:+`
"""

# ╔═╡ ef841d81-c366-4a4a-aa36-ee8ae94b8831
function cleanblock() :: InfiniteBlock
	block = InfiniteBlock(:I => I₂,:Z => S₃, :- => Sᵐ, :+ => Sᵖ,:H => zeros(2,2));
	return block
end;

# ╔═╡ 4087e02f-47a4-46df-af30-ee99db0b4159
md"""
Lets create two blocks, each with  single site, and calculate the energy.
"""

# ╔═╡ c7acd076-38dc-4f62-accf-6ec4feeb7104
begin
	b₁,b₂ = cleanblock(),cleanblock()
	λ,𝐯= 	eigen(b₁[:Z]⊗b₂[:Z]+0.5⋅(b₁[:+]⊗b₁[:-]+b₁[:-]⊗b₂[:+]));
	gs = λ[1]/2
end;

# ╔═╡ b741f177-a6ee-41fd-9fcb-3331c810aa35
md"""
Ground-state energy between pair: $(gs)

Exact ground-state energy for infinite chain: $(-log(2)+0.25)
"""

# ╔═╡ 50055698-09f2-485e-9320-829133df52bb
function addsite!(block::InfiniteBlock)

		#Adding 1 site to right of block Hamiltonian
		block[:H] = block[:H] ⊗ I₂ +
		             block[:Z] ⊗ Z + 0.5 ⋅ ( block[:+] ⊗ Sᵐ + block[:-] ⊗ Sᵖ );

		#Update block operators after adding site
		block[:Z] = block[:I] ⊗ Z
		block[:-] = block[:I] ⊗ Sᵐ
		block[:+] = block[:I] ⊗ Sᵖ
		block[:I] = block[:I] ⊗ I₂
end;

# ╔═╡ b202ebb8-bbbd-4d1c-8885-3940509a5079
function getrdm(ψ::Array{T,1},dims::Tuple{Int,Int}) where T<:Number
	# Form RDM for left block
	ψₘ = reshape(ψ,dims)
	ρ = ψₘ ⋅ ψₘ';
	return ρ
end;

# ╔═╡ 6cdf071a-1daa-4544-9140-f611ccf66aa7
function projector(ρ::Array{T,2},dim::Int;n=1::Int) where T<:Number
	v = eigvecs(ρ);
	P = v[:,max(1,dim-n):dim]
	return P
end;

# ╔═╡ 288e51ac-cf02-4457-9eaf-144bb25e4f3c
function truncate!(block::InfiniteBlock,P::Array{T,2}) where T<:Number
		block[:Z] = P' ⋅ block[:Z] ⋅ P
		block[:-] = P' ⋅ block[:-] ⋅ P
		block[:+] = P' ⋅ block[:+] ⋅ P
		block[:I] = P' ⋅ block[:I] ⋅ P
		block[:H] = P' ⋅ block[:H] ⋅ P
end	;

# ╔═╡ 7bf05f01-59c0-4614-ac27-17ea6aa65722
md"""
!!! note "Note"
     A data type pertaining to `Model` can be found be clicking th show code icon below as well as a `rundmrg` function.
"""

# ╔═╡ b111dc67-d5ac-4991-8cf3-6b524fffd5f7
begin
	mutable struct Model{T<:Number}
		nsites::Int
		nstates::Int
		sites::Array{Int}
		E::Array{T}
		Eb::Array{T}
		ΔE::Array{T}
		Eₑ::Float64
		block::InfiniteBlock
	end
	function Model(;nstates=10,
		        nsites= 25,
			    sites=zeros(Int,25),
		        E=[],
			    Eb=[],
				ΔE=[],
				Eₑ=-log(2)+0.25,
				block=cleanblock())
		return Model(nstates,nsites,sites,E,Eb,ΔE,Eₑ,block)
	end
end

# ╔═╡ 59d8bcb8-4b1b-49f0-8635-873d147d3d42
function rundmrg!(m::Model)
	block1 = m.block :: InfiniteBlock
	nsites = m.nsites
	for i=2:nsites
		@inbounds @fastmath begin
			addsite!(block1)

			systemblock = block1[:H] ⊗ block1[:I] + block1[:I] ⊗ block1[:H] +
						 block1[:Z] ⊗ block1[:Z] +
						 0.5 ⋅ ( block1[:+] ⊗ block1[:-] + block1[:-] ⊗ block1[:+]);

			result = eigen(systemblock)
			λ,ψ = result.values[1],result.vectors[:,1]

			#Store results
			m.E[i]= real(λ)
			m.Eb[i]= (real(λ)-m.E[i-1]) / 2
			m.ΔE[i]= m.Eₑ - m.Eb[i]
			m.sites[i] = 2+2*i

			#Construct block RDM
			blocksize = size(block1[:H]);
			ρ = getrdm(ψ,blocksize);

			# Projector for n-saved eigenvalues of ρ
			P = projector(ρ,blocksize[2];n=m.nstates);

			#Now truncate the block operators
			truncate!(block1,P)

		end
	end
	m.block= block1;
	return nothing
end;

# ╔═╡ 48d2f11c-d79e-4920-b0a9-d4971cba7ad5
md"""
## Configure model
Creating a model with configuration:

number of states to keep
$(@bind y Slider(1:10,show_value=true))
number of total sites to add
$(@bind z Slider(1:50,show_value=true))
"""

# ╔═╡ 7d29cf6e-580d-481c-a8e1-0a1fa576ba1d
begin
	e1,e2,e3 = [zeros(z) for i=1:3];
	e1[1],e2[1],e3[1] = gs, gs/2, 0.0e0 #Energy per pair and error.
	s = zeros(Int,z); s[1] = 2;
	m = Model(;nstates=z,nsites=y,sites=s,
			  E=e1,Eb=e2,ΔE=e3);
	md"""Model (show code to view)"""
end

# ╔═╡ 6f749a1c-ac4f-4223-9526-2b1dff7705c3
rundmrg!(m);

# ╔═╡ 923018cb-3a30-4131-976f-30846f4286ea
md"""
## Results
"""

# ╔═╡ 73966067-1c42-4d06-93ef-1405b42dca44
begin
	p1 = plot(m.sites,m.E,ylabel="Total energy");
	p2 = plot(m.sites,m.ΔE,ylabel="ΔE",ylim=(-0.005,0.005));
	p3 = plot(m.sites,m.Eb,ylabel="Bond energy",ylim=(-0.445,-0.435));
	plot(p1,p2,p3,legend=false,
		markershape=[:circle,:circle,:circle],
		xlabel=["" "" "Sites added"],guidefont=(10,"arial"),
		layout=(3,1),size=(700,400),
		link=:x, titlefont=(12,"arial"),
		title=["Truncated at $(y) states" "" ""])
end

# ╔═╡ 00000000-0000-0000-0000-000000000001
PLUTO_PROJECT_TOML_CONTENTS = """
[deps]
LinearAlgebra = "37e2e46d-f89d-539d-b4ee-838fcccc9c8e"
Plots = "91a5bcdd-55d7-5caf-9e0b-520d859cae80"
PlutoUI = "7f904dfe-b85e-4ff6-b463-dae2292396a8"
TikzPictures = "37f6aa50-8035-52d0-81c2-5a1d08754b2d"

[compat]
Plots = "~1.20.0"
PlutoUI = "~0.7.9"
TikzPictures = "~3.3.3"
"""

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

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

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

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

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

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

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

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

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

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

[[Compat]]
deps = ["Base64", "Dates", "DelimitedFiles", "Distributed", "InteractiveUtils", "LibGit2", "Libdl", "LinearAlgebra", "Markdown", "Mmap", "Pkg", "Printf", "REPL", "Random", "SHA", "Serialization", "SharedArrays", "Sockets", "SparseArrays", "Statistics", "Test", "UUIDs", "Unicode"]
git-tree-sha1 = "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"

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

[[LittleCMS_jll]]
deps = ["Artifacts", "JLLWrappers", "JpegTurbo_jll", "Libdl", "Libtiff_jll", "Pkg"]
git-tree-sha1 = "110897e7db2d6836be22c18bffd9422218ee6284"
uuid = "d3a379c0-f9a3-5b72-a4c0-6bf4d2e8af0f"
version = "2.12.0+0"

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

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

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

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

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

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

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

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

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

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

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

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

[[OpenJpeg_jll]]
deps = ["Artifacts", "JLLWrappers", "Libdl", "Libtiff_jll", "LittleCMS_jll", "Pkg", "libpng_jll"]
git-tree-sha1 = "76374b6e7f632c130e78100b166e5a48464256f8"
uuid = "643b3616-a352-519d-856d-80112ee9badc"
version = "2.4.0+0"

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

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

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

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

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

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

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

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

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

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

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

[[Poppler_jll]]
deps = ["Artifacts", "Cairo_jll", "Fontconfig_jll", "Glib_jll", "JLLWrappers", "JpegTurbo_jll", "Libdl", "Libtiff_jll", "OpenJpeg_jll", "Pkg", "libpng_jll"]
git-tree-sha1 = "e11443687ac151ac6ef6699eb75f964bed8e1faa"
uuid = "9c32591e-4766-534b-9725-b71a8799265b"
version = "0.87.0+2"

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

[[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 = "d0c690d37c73aeb5ca063056283fde5585a41710"
uuid = "bd369af6-aec1-5ad0-b16a-f7cc5008161c"
version = "1.5.0"

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

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

[[TikzPictures]]
deps = ["LaTeXStrings", "Poppler_jll", "Requires"]
git-tree-sha1 = "06b36e2baa9b97814ef1993207b71e2e23e9efb5"
uuid = "37f6aa50-8035-52d0-81c2-5a1d08754b2d"
version = "3.3.3"

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

# ╔═╡ Cell order:
# ╟─701c45a0-e449-42f2-b056-99c371320fad
# ╟─fd9fa7a3-ab72-4131-89ed-11f154d3fb3c
# ╠═77dbc46f-1f30-47b1-b121-5811a903b57b
# ╠═13be2e26-6ba1-49d1-a3cd-770679fc7ff9
# ╟─35ce17c0-d377-11eb-2c48-a5e04a48dacb
# ╟─f694e40b-538d-490d-b891-10c7110d1913
# ╟─957d98b0-f6fc-4cca-b21c-39c0c7d27246
# ╟─52f904fd-806d-487d-84bb-febf7ac63bcf
# ╟─aa3dd19a-c9de-44cf-be35-93ca34144442
# ╟─22e404fc-5908-4c6b-bfff-dcbbec5733fa
# ╟─3b344b18-e496-4be1-969e-a97b4e3909eb
# ╟─6e30235e-2fb3-4280-acff-d1ec196aad4c
# ╠═068e86f8-ffab-46a4-a9eb-e8ed17d00fbd
# ╟─792b1531-01db-44fa-af8e-26f77690c9f8
# ╠═8a1f7b8a-f69b-4a23-ae50-edd7f6317643
# ╟─8e107e94-eecc-46f7-ac6a-e585fca30214
# ╟─22923198-f790-4753-91cc-a53a37b49eda
# ╠═27558d8d-1779-494c-87ce-f9f24d5c1d8f
# ╟─5fd3ff9f-1aaa-41c3-9e65-9a9bf8781cd0
# ╠═8a7cb9f5-3f35-452e-8679-ca24f461a5bc
# ╠═f9356bf2-fc71-4839-a98c-dea87cbf7534
# ╟─8faa032f-c3d8-4e14-b0bd-2bcdb42c85b6
# ╠═ef841d81-c366-4a4a-aa36-ee8ae94b8831
# ╟─4087e02f-47a4-46df-af30-ee99db0b4159
# ╠═c7acd076-38dc-4f62-accf-6ec4feeb7104
# ╟─b741f177-a6ee-41fd-9fcb-3331c810aa35
# ╠═50055698-09f2-485e-9320-829133df52bb
# ╠═b202ebb8-bbbd-4d1c-8885-3940509a5079
# ╠═6cdf071a-1daa-4544-9140-f611ccf66aa7
# ╠═288e51ac-cf02-4457-9eaf-144bb25e4f3c
# ╟─7bf05f01-59c0-4614-ac27-17ea6aa65722
# ╠═b111dc67-d5ac-4991-8cf3-6b524fffd5f7
# ╠═59d8bcb8-4b1b-49f0-8635-873d147d3d42
# ╟─48d2f11c-d79e-4920-b0a9-d4971cba7ad5
# ╠═7d29cf6e-580d-481c-a8e1-0a1fa576ba1d
# ╠═6f749a1c-ac4f-4223-9526-2b1dff7705c3
# ╟─923018cb-3a30-4131-976f-30846f4286ea
# ╟─73966067-1c42-4d06-93ef-1405b42dca44
# ╟─00000000-0000-0000-0000-000000000001
# ╟─00000000-0000-0000-0000-000000000002

ext: jl

max_stars_repo_name: stefanbringuier/PlutoNotebookBlogging

max_issues_repo_name: stefanbringuier/PlutoNotebookBlogging

max_forks_repo_name: stefanbringuier/PlutoNotebookBlogging


text:
import .Cairo: CairoContext, CairoSurface, CairoARGBSurface, CairoEPSSurface,
CairoPDFSurface, CairoSVGSurface, CairoImageSurface

abstract type ImageBackend end
abstract type PNGBackend <: ImageBackend end

abstract type VectorImageBackend <: ImageBackend end
abstract type SVGBackend <: VectorImageBackend end
abstract type PDFBackend <: VectorImageBackend end
abstract type PSBackend  <: VectorImageBackend end
abstract type CairoBackend <: VectorImageBackend end

mutable struct ImagePropertyState
    stroke::RGBA{Float64}
    fill::RGBA{Float64}
    stroke_dash::Array{Float64,1}
    stroke_linecap::LineCap
    stroke_linejoin::LineJoin
    visible::Bool
    linewidth::AbsoluteLength
    fontsize::AbsoluteLength
    font::AbstractString
    clip::Union{ClipPrimitive, Nothing}
    arrow::Bool
end

mutable struct ImagePropertyFrame
    # Vector properties in this frame.
    vector_properties::Dict{Type, Property}

    # True if this property frame has scalar properties. Scalar properties are
    # emitted as a group (<g> tag) that must be closed when the frame is popped.
    has_scalar_properties::Bool
end
ImagePropertyFrame() = ImagePropertyFrame(Dict{Type, Property}(), false)

mutable struct Image{B<:ImageBackend} <: Backend
    out::IO
    surface::CairoSurface
    ctx::CairoContext

    width::Float64
    height::Float64

    # Current state
    stroke::RGBA{Float64}
    fill::RGBA{Float64}
    stroke_dash::Array{Float64,1}
    stroke_linecap::LineCap
    stroke_linejoin::LineJoin
    visible::Bool
    linewidth::AbsoluteLength
    fontsize::AbsoluteLength
    font::AbstractString
    clip::Union{ClipPrimitive, Nothing}
    arrow::Bool

    # Keep track of property
    state_stack::Vector{ImagePropertyState}
    property_stack::Vector{ImagePropertyFrame}
    vector_properties::Dict{Type, Union{Property, Nothing}}

    # Close the surface when finished
    owns_surface::Bool

    # Backend is responsible for opening/closing the file
    ownedfile::Bool

    # Filename when ownedfile is true
    filename::Union{AbstractString, Nothing}

    # True when finish has been called and no more drawing should occur
    finished::Bool

    # Emit on finish
    emit_on_finish::Bool

    # Points (or pixels for PNG) per mm
    ppmm::Float64

    # For use with the t/T and s/S commands in SVG-style paths
    last_ctrl1_point::Union{AbsoluteVec2, Nothing}
    last_ctrl2_point::Union{AbsoluteVec2, Nothing}
end

function Image{B}(surface::CairoSurface,
               ctx::CairoContext,
               out::IO;

               width = 0,
               height = 0,
               stroke = default_stroke_color == nothing ?
                   RGBA{Float64}(0, 0, 0, 0) : convert(RGBA{Float64}, default_stroke_color),
               fill = default_fill_color == nothing ?
                   RGBA{Float64}(0, 0, 0, 0) : convert(RGBA{Float64}, default_fill_color),
               stroke_dash = [],
               stroke_linecap = LineCapButt(),
               stroke_linejoin = LineJoinMiter(),
               visible = true,
               linewidth = default_line_width,
               fontsize = default_font_size,
               font = default_font_family,
               clip = nothing,
               arrow = false,
               state_stack = Array{ImagePropertyState}(undef, 0),
               property_stack = Array{ImagePropertyFrame}(undef, 0),
               vector_properties = Dict{Type, Union{Property, Nothing}}(),
               owns_surface = false,
               ownedfile = false,
               filename = nothing,
               finished = false,
               emit_on_finish = false,
               ppmm = 72 / 25.4,
               last_ctrl1_point = nothing,
               last_ctrl2_point = nothing) where B<:ImageBackend

    Image{B}(out,
          surface,
          ctx,
          width,
          height,
          stroke,
          fill,
          stroke_dash,
          stroke_linecap,
          stroke_linejoin,
          visible,
          linewidth,
          fontsize,
          font,
          clip,
          arrow,
          state_stack,
          property_stack,
          vector_properties,
          owns_surface,
          ownedfile,
          filename,
          finished,
          emit_on_finish,
          ppmm,
          last_ctrl1_point,
          last_ctrl2_point)
end

Image{B}(surface::CairoSurface, ctx::CairoContext) where {B<:ImageBackend} =
        Image{B}(surface, ctx, IOBuffer())
Image{B}(surface::CairoSurface) where {B<:ImageBackend} =
        Image{B}(surface, CairoContext(surface))

function Image{B}(out::IO,
            width::MeasureOrNumber=default_graphic_width,
            height::MeasureOrNumber=default_graphic_height,
            emit_on_finish::Bool=true;
            dpi = (B==PNGBackend ? 96 : 72),
            kwargs...) where B<:ImageBackend
    width = size_measure(width)
    height = size_measure(height)

    (!isa(width, AbsoluteLength) || !isa(height, AbsoluteLength)) &&
                error("Image size must be specificed in absolute units.")

    ppmm = dpi / 25.4
    width = width.value * ppmm
    height = height.value * ppmm

    surface = newsurface(B, out, width, height)
    Image{B}(surface, CairoContext(surface), out;
                width=width, height=height,
                owns_surface=true, emit_on_finish=emit_on_finish, ppmm=ppmm,
                kwargs...)
end

Image{B}(filename::AbstractString,
            width::MeasureOrNumber=default_graphic_width,
            height::MeasureOrNumber=default_graphic_height;
            dpi = (B==PNGBackend ? 96 : 72)) where {B<:ImageBackend} =
        Image{B}(open(filename, "w"), width, height, dpi=dpi; ownedfile=true, filename=filename)

Image{B}(width::MeasureOrNumber=default_graphic_width,
            height::MeasureOrNumber=default_graphic_height,
            emit_on_finish::Bool=true;
            dpi = (B==PNGBackend ? 96 : 72)) where {B<:ImageBackend} =
        Image{B}(IOBuffer(), width, height, emit_on_finish, dpi=dpi)

PNG(args...; kwargs...) = Image{PNGBackend}(args...; kwargs...)
PDF(args...; kwargs...) = Image{PDFBackend}(args...; kwargs...)
PS(args...; kwargs...) = Image{PSBackend}(args...; kwargs...)

const CAIROSURFACE = Image{CairoBackend}

function (img::Image)(x)
    draw(img, x)
end

function canbatch(img::Image)
    for vp in values(img.vector_properties)
        (vp === nothing) || return false
    end
    return true
end

# convert compose absolute units (millimeters) to the absolute units used by the
# cairo surface (pixels for PNG, points for all others)

absolute_native_units(img::Image, u::Float64) = img.ppmm * u

surface(img::Image) = img.surface

Measures.width(img::Image) = (Cairo.width(img.surface) / img.ppmm) * mm
Measures.height(img::Image) = (Cairo.height(img.surface) / img.ppmm) * mm

iswithjs(img::Image) = false
iswithousjs(img::Image) = true

finish(::Type{B}, img::Image) where {B<:ImageBackend} = nothing
finish(::Type{PNGBackend}, img::Image) = Cairo.write_to_png(img.surface, img.out)

function finish(img::Image{B}) where B<:ImageBackend
    img.finished && return

    img.owns_surface && Cairo.destroy(img.ctx)
    finish(B, img)
    img.owns_surface && Cairo.destroy(img.surface)

    img.finished = true
    img.emit_on_finish && typeof(img.out) == IOBuffer && display(img)
    hasmethod(flush, (typeof(img.out),)) && flush(img.out)
    img.ownedfile && close(img.out)
end

function newsurface(::Type{B}, out, width, height) where B
    local surface::CairoSurface
    if B == SVGBackend
        surface = CairoSVGSurface(out, width, height)
    elseif B == PNGBackend
        surface = CairoARGBSurface(round(Integer, width), round(Integer, height))
    elseif B == PDFBackend
        surface = CairoPDFSurface(out, width, height)
    elseif B == PSBackend
        surface = CairoEPSSurface(out, width, height)
    elseif B == CairoBackend
        surface = out
    else
        error("Unkown Cairo backend.")
    end

    Cairo.status(surface) == Cairo.STATUS_SUCCESS || error("Unable to create cairo surface.")

    surface
end

function reset(img::Image{B}) where B
    img.owns_surface ||
            error("Backend can't be reused since an external cairo surface is being used.")

    if img.ownedfile
        img.out = open(img.filename, "w")
    else
        try
            seekstart(img.out)
        catch
            error("Backend can't be reused, since the output stream is not seekable.")
        end
    end

    img.surface = newsurface(B, img.out, img.width, img.height)
    img.ctx = CairoContext(img.surface)
    img.finished = false
end

isfinished(img::Image) = img.finished

root_box(img::Image) = BoundingBox(width(img), height(img))

show(io::IO, ::MIME"image/png", img::Image{PNGBackend}) = write(io, String(take!(img.out)))
show(io::IO, ::MIME"application/pdf", img::Image{PDFBackend}) = write(io, String(take!(img.out)))
show(io::IO, ::MIME"application/postscript", img::Image{PSBackend}) = write(io, String(take!(img.out)))

# Applying Properties
# -------------------

function push_property_frame(img::Image, properties::Vector{Property})
    isempty(properties) && return

    frame = ImagePropertyFrame()
    isp = isscalar.(properties)
    scalar_properties = Dict{Type, Property}(typeof(x)=>x for x in properties[isp])
    vector_properties = Dict{Type, Property}(typeof(x)=>x for x in properties[.!isp])

    kt = [Property{FillOpacityPrimitive}, Property{FillPrimitive}]
    if haskey(scalar_properties, kt[1]) && haskey(vector_properties, kt[2])
        alpha = scalar_properties[kt[1]].primitives[1].value
        vector_properties[kt[1]] = fillopacity(fill(alpha, length(vector_properties[kt[2]].primitives)))
        pop!(scalar_properties, kt[1])
    end

    frame.has_scalar_properties = !isempty(scalar_properties)
    img.vector_properties = vector_properties
    frame.vector_properties = vector_properties
    push!(img.property_stack, frame)
    isempty(scalar_properties) && return

    save_property_state(img)
    for (_, property) in scalar_properties
        apply_property(img, property.primitives[1])
    end
    haskey(scalar_properties, kt[1]) && apply_property(img, scalar_properties[kt[1]].primitives[1])
end

function pop_property_frame(img::Image)
    @assert !isempty(img.property_stack)
    frame = pop!(img.property_stack)

    frame.has_scalar_properties && restore_property_state(img)

    for (propertytype, property) in frame.vector_properties
        img.vector_properties[propertytype] = nothing
        for i in length(img.property_stack):-1:1
            if haskey(img.property_stack[i].vector_properties, propertytype)
                img.vector_properties[propertytype] =
                    img.property_stack[i].vector_properties[propertytype]
            end
        end
    end
end

function save_property_state(img::Image)
    push!(img.state_stack,
        ImagePropertyState(
            img.stroke,
            img.fill,
            img.stroke_dash,
            img.stroke_linecap,
            img.stroke_linejoin,
            img.visible,
            img.linewidth,
            img.fontsize,
            img.font,
            img.clip,
            img.arrow))
    Cairo.save(img.ctx)
end

function restore_property_state(img::Image)
    state = pop!(img.state_stack)
    img.stroke = state.stroke
    img.fill = state.fill
    img.stroke_dash = state.stroke_dash
    img.stroke_linecap = state.stroke_linecap
    img.stroke_linejoin = state.stroke_linejoin
    img.visible = state.visible
    img.linewidth = state.linewidth
    img.fontsize = state.fontsize
    img.font = state.font
    img.clip = state.clip
    img.arrow = state.arrow
    Cairo.restore(img.ctx)
end

# Return true if the vector properties need to be pushed and popped, rather
# than simply applied.
function vector_properties_require_push_pop(img::Image)
    for (propertytype, property) in img.vector_properties
        in(propertytype, [Property{FontPrimitive},
                          Property{FontSizePrimitive},
                          Property{ClipPrimitive}]) && return true
    end
    return false
end

function push_vector_properties(img::Image, idx::Int)
    save_property_state(img)
    for (propertytype, property) in img.vector_properties
        (property === nothing) && continue
        primitives = property.primitives
        idx > length(primitives) &&
                error("Vector form and vector property differ in length. Can't distribute.")
        apply_property(img, primitives[idx])
    end
end

pop_vector_properties(img::Image) = restore_property_state(img)
apply_property(img::Image, p::StrokePrimitive) =
        img.stroke = p.color
apply_property(img::Image, p::FillPrimitive) =
        img.fill = p.color
apply_property(img::Image, p::FillOpacityPrimitive) =
        img.fill = RGBA{Float64}(color(img.fill), p.value)
apply_property(img::Image, p::StrokeOpacityPrimitive) =
        img.stroke = RGBA{Float64}(color(img.stroke), p.value)
apply_property(img::Image, p::StrokeDashPrimitive) =
        img.stroke_dash = map(v -> absolute_native_units(img, v.value), p.value)
apply_property(img::Image, p::StrokeLineCapPrimitive) =
        img.stroke_linecap = p.value
apply_property(img::Image, p::StrokeLineJoinPrimitive) =
        img.stroke_linejoin = p.value
apply_property(img::Image, p::VisiblePrimitive) =
        img.visible = p.value

function apply_property(img::Image, property::LineWidthPrimitive)
    img.linewidth = property.value
    Cairo.set_line_width(img.ctx,
        absolute_native_units(img, property.value.value))
end

function apply_property(img::Image, property::FontPrimitive)
    img.font = property.family

    font_desc = ccall((:pango_layout_get_font_description, Cairo._jl_libpango),
                      Ptr{Cvoid}, (Ptr{Cvoid},), img.ctx.layout)

    if font_desc == C_NULL
        size = absolute_native_units(img, default_font_size.value)
    else
        size = ccall((:pango_font_description_get_size, Cairo._jl_libpango),
                     Cint, (Ptr{Cvoid},), font_desc)
    end

    Cairo.set_font_face(img.ctx,
        @sprintf("%s %0.2fpx", property.family, size / PANGO_SCALE))
end

function apply_property(img::Image, property::FontSizePrimitive)
    img.fontsize = property.value

    font_desc = ccall((:pango_layout_get_font_description, Cairo._jl_libpango),
                      Ptr{Cvoid}, (Ptr{Cvoid},), img.ctx.layout)

    if font_desc == C_NULL
        family = "sans"
    else
        family = ccall((:pango_font_description_get_family, Cairo._jl_libpango),
                       Ptr{UInt8}, (Ptr{Cvoid},), font_desc)
        family = unsafe_string(family)
    end

    Cairo.set_font_face(img.ctx,
        @sprintf("%s %.2fpx",
            family, absolute_native_units(img, property.value.value)))
end

function apply_property(img::Image, property::ClipPrimitive)
    if isempty(property.points); return; end
    move_to(img, property.points[1])
    for point in property.points[2:end]
        line_to(img, point)
    end
    close_path(img)
    Cairo.clip(img.ctx)
    img.clip = property
end

# No-op SVG+JS only properties
apply_property(img::Image, property::JSIncludePrimitive) = nothing
apply_property(img::Image, property::JSCallPrimitive) = nothing
apply_property(img::Image, property::SVGIDPrimitive) = nothing
apply_property(img::Image, property::SVGClassPrimitive) = nothing
apply_property(img::Image, property::SVGAttributePrimitive) = nothing

# Cairo Wrappers
# --------------

function current_point(img::Image)
    x = Array{Float64}(undef, 1)
    y = Array{Float64}(undef, 1)
    ccall((:cairo_get_current_point, Cairo._jl_libcairo), Cvoid,
          (Ptr{Cvoid}, Ptr{Float64}, Ptr{Float64}), img.ctx.ptr, x, y)
    return ((x[1] / img.ppmm)*mm, (x[2] / img.ppmm)*mm)
end

move_to(img::Image, point::AbsoluteVec2) = Cairo.move_to(img.ctx,
        absolute_native_units(img, point[1].value),
        absolute_native_units(img, point[2].value))

rel_move_to(img::Image, point::AbsoluteVec2) = Cairo.rel_move_to(img.ctx,
        absolute_native_units(img, point[1].value),
        absolute_native_units(img, point[2].value))

line_to(img::Image, point::AbsoluteVec2) = Cairo.line_to(img.ctx,
        absolute_native_units(img, point[1].value),
        absolute_native_units(img, point[2].value))

rel_line_to(img::Image, point::AbsoluteVec2) = Cairo.rel_line_to(img.ctx,
        absolute_native_units(img, point[1].value),
        absolute_native_units(img, point[2].value))

rel_curve_to(img::Image, ctrl1::AbsoluteVec2, ctrl2::AbsoluteVec2, to::AbsoluteVec2) =
        Cairo.rel_curve_to(img.ctx,
            absolute_native_units(img, ctrl1[1].value),
            absolute_native_units(img, ctrl1[2].value),
            absolute_native_units(img, ctrl2[1].value),
            absolute_native_units(img, ctrl2[2].value),
            absolute_native_units(img, to[1].value),
            absolute_native_units(img, to[2].value),)

rectangle(img::Image, corner::AbsoluteVec2, width::AbsoluteLength, height::AbsoluteLength) =
        Cairo.rectangle(img.ctx,
            absolute_native_units(img, corner[1].value),
            absolute_native_units(img, corner[2].value),
            absolute_native_units(img, width.value),
            absolute_native_units(img, height.value))

circle(img::Image, center::AbsoluteVec2, radius::AbsoluteLength) =
        Cairo.circle(img.ctx,
            absolute_native_units(img, center[1].value),
            absolute_native_units(img, center[2].value),
            absolute_native_units(img, radius.value))

curve_to(img::Image, ctrl1::AbsoluteVec2, ctrl2::AbsoluteVec2, anchor::AbsoluteVec2) =
        Cairo.curve_to(img.ctx,
            absolute_native_units(img, ctrl1[1].value),
            absolute_native_units(img, ctrl1[2].value),
            absolute_native_units(img, ctrl2[1].value),
            absolute_native_units(img, ctrl2[2].value),
            absolute_native_units(img, anchor[1].value),
            absolute_native_units(img, anchor[2].value))

close_path(img::Image) = Cairo.close_path(img.ctx)
new_sub_path(img::Image) = Cairo.new_sub_path(img.ctx)

arc(img::Image, x::Float64, y::Float64, radius::Float64, angle1::Float64, angle2::Float64) =
        Cairo.arc(img.ctx,
            absolute_native_units(img, x),
            absolute_native_units(img, y),
            absolute_native_units(img, radius),
            angle1, angle2)

arc_negative(img::Image, x::Float64, y::Float64, radius::Float64,
                      angle1::Float64, angle2::Float64) =
        Cairo.arc_negative(img.ctx,
            absolute_native_units(img, x),
            absolute_native_units(img, y),
            absolute_native_units(img, radius),
            angle1, angle2)

translate(img::Image, tx::Float64, ty::Float64) = Cairo.translate(img.ctx,
        absolute_native_units(img, tx),
        absolute_native_units(img, ty))

scale(img::Image, sx::Float64, sy::Float64) = Cairo.scale(img.ctx, sx, sy)
rotate(img::Image, theta::Float64) = Cairo.rotate(img.ctx, theta)

function rotate(img::Image, theta::Float64, x::Float64, y::Float64)
    ct = cos(theta)
    st = sin(theta)

    x′ = x - (ct * x - st * y)
    y′ = y - (st * x + ct * y)

    translate(img, x′, y′)
    rotate(img, theta)
end

# Convert native linecap/linejoin enums to the Cairo values.
cairo_linecap(::LineCapButt) = Cairo.CAIRO_LINE_CAP_BUTT
cairo_linecap(::LineCapRound) = Cairo.CAIRO_LINE_CAP_ROUND
cairo_linecap(::LineCapSquare) = Cairo.CAIRO_LINE_CAP_SQUARE
cairo_linejoin(::LineJoinMiter) = Cairo.CAIRO_LINE_JOIN_MITER
cairo_linejoin(::LineJoinBevel) = Cairo.CAIRO_LINE_JOIN_BEVEL
cairo_linejoin(::LineJoinRound) = Cairo.CAIRO_LINE_JOIN_ROUND

function fillstroke(img::Image, strokeonly::Bool=false)
    img.visible || return

    if img.fill.alpha > 0.0 && !strokeonly
        Cairo.set_source_rgba(img.ctx, img.fill.r, img.fill.g, img.fill.b, img.fill.alpha)

        if img.stroke.alpha > 0.0
            Cairo.fill_preserve(img.ctx)
        else
            Cairo.fill(img.ctx)
        end
    end

    if img.stroke.alpha > 0.0
        Cairo.set_source_rgba(img.ctx, img.stroke.r, img.stroke.g, img.stroke.b, img.stroke.alpha)
        Cairo.set_dash(img.ctx, img.stroke_dash)
        Cairo.set_line_cap(img.ctx, cairo_linecap(img.stroke_linecap))
        Cairo.set_line_join(img.ctx, cairo_linejoin(img.stroke_linejoin))

        Cairo.stroke(img.ctx)
    end

    # if the path wasn't stroked or filled, we should still clear it
    Cairo.new_path(img.ctx)
end

# Form Drawing
# ------------

function draw(img::Image, form::Form)
    if vector_properties_require_push_pop(img)
        for (idx, primitive) in enumerate(form.primitives)
            push_vector_properties(img, idx)
            draw(img, primitive)
            pop_vector_properties(img)
        end
    else
        for (idx, primitive) in enumerate(form.primitives)
            for (propertytype, property) in img.vector_properties
                (property === nothing) && continue
                primitives = property.primitives
                idx > length(primitives) &&
                        error("Vector form and vector property differ in length. Can't distribute.")
                apply_property(img, primitives[idx])
            end
            kt = [Property{FillOpacityPrimitive}]
            haskey(img.vector_properties, kt[1]) && apply_property(img, img.vector_properties[kt[1]].primitives[idx])
            draw(img, primitive)
        end
    end
end

function draw(img::Image, prim::RectanglePrimitive)
    rectangle(img, prim.corner, prim.width, prim.height)
    fillstroke(img)
end

function draw(img::Image, prim::PolygonPrimitive)
    length(prim.points) <= 1 && return
    prev_ok = false
    for (i,p) in enumerate(prim.points)
        ok = isfinite(p[1].value) && isfinite(p[2].value)
        if ok && prev_ok
            line_to(img, p)
        elseif !ok && prev_ok
            close_path(img)
            fillstroke(img)
        else
            move_to(img, p)
        end
        prev_ok = ok
    end
    close_path(img)
    fillstroke(img)
end

function draw(img::Image, prim::Compose.ComplexPolygonPrimitive)
    isempty(prim.rings) && return

    for ring in prim.rings
        move_to(img, ring[1])
        for point in ring[2:end]
            line_to(img, point)
        end
        close_path(img)
    end
    fillstroke(img)
end

function draw(img::Image, prim::CirclePrimitive)
    new_sub_path(img)
    circle(img, prim.center, prim.radius)
    fillstroke(img)
end

function draw(img::Image, prim::EllipsePrimitive)
    new_sub_path(img)
    cx = prim.center[1].value
    cy = prim.center[2].value
    rx = sqrt((prim.x_point[1].value - cx)^2 +
              (prim.x_point[2].value - cy)^2)
    ry = sqrt((prim.y_point[1].value - cx)^2 +
              (prim.y_point[2].value - cy)^2)
    theta = atan(prim.x_point[2].value - cy,
                 prim.x_point[1].value - cx)

    all(isfinite,[cx, cy, rx, ry, theta]) || return

    save_property_state(img)
    translate(img, cx, cy)
    rotate(img, theta)
    translate(img, -rx, -ry)
    scale(img, 2rx, 2ry)
    arc(img, 0.5, 0.5, 0.5, 0.0, 2pi)
    restore_property_state(img)
    fillstroke(img)
end

function draw(img::Image, prim::LinePrimitive)
    length(prim.points) <= 1 && return
    prev_ok = false
    for (i,p) in enumerate(prim.points)
        ok = isfinite(p[1].value) && isfinite(p[2].value)
        if ok && prev_ok
            line_to(img, p)
        else
            move_to(img, p)
        end
        prev_ok = ok
    end
    img.arrow && arrow(img, prim.points)
    fillstroke(img, true)
end

function get_layout_size(img::Image)
    width, height = Cairo.get_layout_size(img.ctx)
    return ((width / img.ppmm) * mm, (height / img.ppmm) * mm)
end

show(io::IO, ::MIME"image/png", ctx::Context) =
    draw(PNG(io, default_graphic_width, default_graphic_height), ctx)

function draw(img::Image, prim::TextPrimitive)
    (!img.visible || (img.fill.alpha == 0.0 && img.stroke.alpha == 0.0)) && return

    Cairo.set_text(img.ctx, prim.value, true)
    width, height = get_layout_size(img)
    pos = (prim.position[1]+prim.offset[1], prim.position[2]+prim.offset[2] - height)

    if prim.halign != hleft || prim.valign != vbottom
        if prim.halign == hcenter
            pos = (pos[1] - width/2, pos[2])
        elseif prim.halign == hright
            pos = (pos[1] - width, pos[2])
        end

        if prim.valign == vcenter
            pos = (pos[1], pos[2] + height/2)
        elseif prim.valign == vtop
            pos = (pos[1], pos[2] + height)
        end
    end

    Cairo.set_source_rgba(img.ctx, img.fill.r, img.fill.g, img.fill.b, img.fill.alpha)

    if prim.rot.theta != 0.0
        save_property_state(img)
        rotate(img, prim.rot.theta, prim.rot.offset[1].value, prim.rot.offset[2].value)
    end

    move_to(img, pos)
    Cairo.show_layout(img.ctx)

    prim.rot.theta == 0.0 || restore_property_state(img)
end

function draw(img::Image, prim::CurvePrimitive)
    move_to(img, prim.anchor0)
    curve_to(img, prim.ctrl0, prim.ctrl1, prim.anchor1)
    fillstroke(img, true)
end

draw(img::Image, prim::BitmapPrimitive) =
        error("Embedding bitmaps in Cairo backends (i.e. PNG, PDF, PS) is not supported.")

function draw(img::Image, batch::FormBatch)
    bounds = boundingbox(batch.primitive, img.linewidth, img.font, img.fontsize)
    width = bounds.a[1].value * img.ppmm
    height = bounds.a[2].value * img.ppmm

    xt = -bounds.x0[1].value * img.ppmm
    yt = -bounds.x0[2].value * img.ppmm

    Cairo.save(img.ctx)
    Cairo.reset_clip(img.ctx)
    Cairo.translate(img.ctx, xt, yt)
    Cairo.push_group(img.ctx)
    draw(img, batch.primitive)
    pattern = Cairo.pop_group(img.ctx)
    surface = Cairo.pattern_get_surface(pattern)
    Cairo.restore(img.ctx)

    # reapply the clipping region we just reset
    if img.clip !== nothing
        apply_property(img, img.clip)
    end

    Cairo.set_antialias(img.ctx, Cairo.ANTIALIAS_NONE)
    for offset in batch.offsets
        x = offset[1].value * img.ppmm - xt
        y = offset[2].value * img.ppmm - yt
        Cairo.set_source_surface(img.ctx, surface, x, y)
        Cairo.rectangle(img.ctx, x, y, width, height)
        Cairo.fill(img.ctx)
    end
    Cairo.set_antialias(img.ctx, Cairo.ANTIALIAS_DEFAULT)
end

function draw(img::Compose.Image, prim::ArcPrimitive)
    new_sub_path(img)
    xc = prim.center[1]
    yc = prim.center[2]
    prim.sector && move_to(img, (xc, yc))
    arc(img, xc.value, yc.value, prim.radius.value, prim.angle1, prim.angle2)
    prim.sector && line_to(img, (xc, yc))
    img.arrow && arrow(img, prim)
    fillstroke(img)
end

# Arrows

apply_property(img::Image, p::ArrowPrimitive) =
        img.arrow = p.value

function arrow(img::Image, points::Vector{<:Vec})
    xmax, ymax = points[end]
    dxy = points[end] .- points[end-1]
    dx, dy = dxy[1].value,  dxy[2].value
    vl, θ = 0.225*hypot(dy,dx), atan(dy, dx)
    arrowhead(img, (xmax,ymax), vl, θ)
end

function arrow(img::Image, prim::ArcPrimitive)
    xy = prim.center .+ prim.radius.*(cos(prim.angle2), sin(prim.angle2))
    θ = prim.angle2 + 0.45π
   arrowhead(img, xy, 0.5*prim.radius.value, θ)
end

function arrowhead(img::Image, point::Vec, vl::Float64, θ::Float64)
    xmax, ymax = point
    ϕ  =  pi/15
    xr = -vl*[cos(θ+ϕ), cos(θ-ϕ)].*1mm
    yr = -vl*[sin(θ+ϕ), sin(θ-ϕ)].*1mm
    move_to(img, (xmax+xr[1], ymax+yr[1]))
    line_to(img, (xmax, ymax))
    line_to(img, (xmax+xr[2], ymax+yr[2]))
end


text:
# Test @assume and macros on a model with conditioning.

using Turing
using Test

@model test() = begin
  z ~ Normal(0,1)
  x ~ Bernoulli(1)
  1 ~ Bernoulli(x / 2)
  0 ~ Bernoulli(x / 2)
  x
end

is  = IS(10000)
smc = SMC(10000)
pg  = PG(100,10)

res = sample(test(), is)

@test reduce(&, res[:x]) == 1  #c heck that x is always 1
@test res[:logevidence] ≈ 2 * log(0.5)

res = sample(test(), smc)

@test reduce(&, res[:x]) == 1  #c heck that x is always 1
@test res[:logevidence] ≈ 2 * log(0.5)

res = sample(test(), pg)

@test reduce(&, res[:x]) == 1  # check that x is always 1
# PG does not provide logevidence estimate


text:
struct AutoModelingToolkit <: AbstractADType
    obj_sparse::Bool
    cons_sparse::Bool
end

AutoModelingToolkit() = AutoModelingToolkit(false, false)

function instantiate_function(f, x, ad::AutoModelingToolkit, p, num_cons=0)
    p = isnothing(p) ? SciMLBase.NullParameters() : p
    sys = ModelingToolkit.modelingtoolkitize(OptimizationProblem(f, x, p))

    if f.grad === nothing
        grad_oop, grad_iip = ModelingToolkit.generate_gradient(sys, expression=Val{false})
        grad(J, u) = (grad_iip(J, u, p); J)
    else
        grad = f.grad
    end

    if f.hess === nothing
        hess_oop, hess_iip = ModelingToolkit.generate_hessian(sys, expression=Val{false}, sparse = ad.obj_sparse)
        hess(H, u) = (hess_iip(H, u, p); H)
    else
        hess = f.hess
    end

    if f.hv === nothing
        hv = function (H, θ, v, args...)
            res = ArrayInterfaceCore.zeromatrix(θ)
            hess(res, θ, args...)
            H .= res * v
        end
    else
        hv = f.hv
    end

    if f.cons === nothing
        cons = nothing
    else
        cons = (θ) -> f.cons(θ, p)
        cons_sys = ModelingToolkit.modelingtoolkitize(NonlinearProblem(f.cons, x, p))
    end

    if f.cons !== nothing && f.cons_j === nothing
        jac_oop, jac_iip = ModelingToolkit.generate_jacobian(cons_sys, expression=Val{false}, sparse=ad.cons_sparse)
        cons_j = function (J, θ)
            jac_iip(J, θ, p)
        end
    else
        cons_j = f.cons_j
    end

    if f.cons !== nothing && f.cons_h === nothing
        cons_hess_oop, cons_hess_iip = ModelingToolkit.generate_hessian(cons_sys, expression=Val{false}, sparse=ad.cons_sparse)
        cons_h = function (res, θ)
            cons_hess_iip(res, θ, p)
        end
    else
        cons_h = f.cons_h
    end

    return OptimizationFunction{true,AutoModelingToolkit,typeof(f.f),typeof(grad),typeof(hess),typeof(hv),typeof(cons),typeof(cons_j),typeof(cons_h)}(f.f, AutoModelingToolkit(), grad, hess, hv, cons, cons_j, cons_h)
end


text:
using .DualNumbers, FFTW
import FFTW.fft!;

function fft!(x::AbstractArray{T}) where {T <: Dual{cpx}} where {cpx <: Complex{num}} where {num <: Number}
    Xcomplex, Xder = DualNumbers.value.(x), DualNumbers.epsilon.(x)
    planned_fft = plan_fft!(similar(Xcomplex))
    Xcomplex .= planned_fft * Xcomplex
    Xder .= planned_fft * Xder
    @. x = dual(Xcomplex, Xder)
    nothing
end


text:
function fit_mode_quantile(::Type{LogitNormal}, mode::Real, qp::QuantilePoint)
    matchModeUpper(mode, qp.q, Val(40); perc = qp.p)
end

function matchModeUpper(
    mode::T, upper::T
    , ::Val{nTry}
    ; perc::Real = 0.99
) where {nTry, T<:Real}
    mode == 0.5 && return matchMedianUpper(LogitNormal, 0.5, upper; perc=perc)
    # for given mu we can compute sigma by mode and upper quantile
    # hence univariate search for mu
    # we now that mu is in (\code{logit(mode)},0) for \code{mode < 0.5} and in
    # \code{(0,logit(mode))} for \code{mode > 0.5} for unimodal distribution
    # there might be a maximum in the middle and optimized misses the low part
    # hence, first get near the global minimum by a evaluating the cost at a
    # grid that is spaced narrower at the edge
    #
    logitMode = logit(mode)
    logitUpper = logit(upper)
    upperMu = abs(logitMode) - eps()
    muTry = SVector{nTry}(
        sign(logitMode) .* log.(range(1,stop=exp(upperMu),length=nTry)))
    oF(mu) = ofLogitNormalModeUpper(mu, mode, logitMode, logitUpper, perc)
    ofMuTry = oF.(muTry)
    iMin = argmin(ofMuTry)
    # on postive side muTry are increasing, on negative side muTry decreasing
    # neet to have the lower value at the beginning of the interval
    interval = (logitMode >= 0) ?
        (muTry[max(1,iMin-1)], muTry[min(nTry,iMin+1)]) :
        (muTry[max(1,iMin+1)], muTry[min(nTry,max(1,iMin-1))])
    resOpt = optimize(oF, interval...)
    Optim.converged(resOpt) || error("could not find minimum")
    μ = Optim.minimizer(resOpt)
    σ = sqrt((logitMode - μ)/(2*mode - 1))
    LogitNormal{T}(μ,σ)
end

function matchModeUpper(
    mode::S, upper::T, ::Val{nTry}; kwargs...
) where {nTry, T<:Real, S<:Real}
    p = promote(mode, upper)
    matchModeUpper(p[1], p[2], Val(nTry); kwargs...)
end

"objective function used by `matchModeUpper(LogitNormal,...)`"
function ofLogitNormalModeUpper(mu, mode, logitMode, logitUpper, perc)
  # given mu and mode, we can calculate sigma,
  # predict the percentile of logitUpper
  # and return the squared difference as cost to be minimized
  sigma2 = (logitMode - mu)/(2.0*mode - 1.0)
  sigma2 > 0.0 || return prevfloat(Inf)
  predp = normcdf(mu, sqrt(sigma2), logitUpper)
  diff = predp - perc
  diff*diff
end

function fit_mode_flat(::Type{LogitNormal}, mode::T, ::Val{nTry} = Val(40)) where {T<:Real,nTry}
    mode == 0.5 && return(LogitNormal{T}(0.0, sqrt(2)))
    is_right = mode > 0.5
    mode_r = is_right ? mode : 1.0 - mode
    res_opt = optimize(x -> of_mode_flat(x, mode_r, logit(mode_r)), 0.0, 0.5)
    Optim.converged(res_opt) || error("could not find minimum")
    xt = res_opt.minimizer
    σ2 = (1/xt + 1/(1-xt))/2
    μr = logit(mode_r) - σ2*(2.0*mode_r - 1.0)
    μ = is_right ? μr : -μr
    LogitNormal{T}(μ,sqrt(σ2))
end

function of_mode_flat(x, m, logitm = logit(m))
  # lhs = 1/x + 1/(1-x)
  # rhs = (logitm - logit(x))/(m-x)
  # lhs = (m-x)/x + (m-x)/(1-x)
  # rhs = (logitm - logit(x))
  lhs = m/x + (m-1)/(1-x)
  rhs = logitm - logit(x)
  d = lhs - rhs
  d*d
end


text:
using OceanTurb, Printf

using OceanTurb.ModularKPP: DiagnosticPlumeModel

@use_pyplot_utils

usecmbright()

modelsetup = (N=256, L=256, stepper=:BackwardEuler)

Fb = 1e-7
Fu = 0.0
N² = 1e-5
Δt = 2minute
t_plot = (0hour, 12hour, 48hour)

name = "Free convection
    \\small{with \$ \\overline{w b} |_{z=0} = 10^{-7} \\, \\mathrm{m^2 \\, s^{-3}}\$}"

# The standard setup except with a plume model rather than a counter-gradient flux model.
model = ModularKPP.Model(; modelsetup...,
                           nonlocalflux = DiagnosticPlumeModel())

# Initial condition and fluxes
dTdz = N² / (model.constants.α * model.constants.g)
T₀(z) = 20 + dTdz*z

model.solution.T = T₀

Fθ = Fb / (model.constants.α * model.constants.g)
model.bcs.U.top = FluxBoundaryCondition(Fu)

model.bcs.T.top = FluxBoundaryCondition(Fθ)
model.bcs.T.bottom = GradientBoundaryCondition(dTdz)

iterate!(model, Δt)

#ModularKPP.update_state!(model)

ΔT = model.state.plumes.T - model.solution.T

fig, axs = subplots(ncols=3)

sca(axs[1])
plot(ΔT)

sca(axs[2])
plot(model.solution.T)
plot(model.state.plumes.T)

sca(axs[3])
plot(model.state.plumes.W²)