### A Pluto.jl notebook ###
# v0.17.5

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 iv = try Base.loaded_modules[Base.PkgId(Base.UUID("6e696c72-6542-2067-7265-42206c756150"), "AbstractPlutoDingetjes")].Bonds.initial_value catch; b -> missing; end
        local el = $(esc(element))
        global $(esc(def)) = Core.applicable(Base.get, el) ? Base.get(el) : iv(el)
        el
    end
end

# ╔═╡ 981ee694-d145-4d46-806d-a69c0b529c89
begin
	using PlutoUI,SymPy,Plots
	gr()
	theme(:bright)
    PlutoUI.TableOfContents()

end

# ╔═╡ 240bf15b-20e6-4b89-96ff-1dcbb862ef9b
PlutoUI.Resource("https://tva1.sinaimg.cn/thumbnail/e6c9d24egy1h2alsw1tzxj20m80gomxn.jpg")

# ╔═╡ ff3f2492-d57d-11ec-386b-ed8716f9c392
md"# Simple  Geometry with Julia:part one  basic and point plot"

# ╔═╡ 06a8c6d8-e09f-40c0-b1dd-e28dbc7baa38
md"""
!!! notice "Abstract"
    1.**This is a  tutorial for plotting  some geometry shape based vector viewpoint**

    2.**all chart made by plot vector plus some stylelish**
"""

# ╔═╡ 2b65a1d3-3454-465d-a1a7-71174ede0658
md"""
## Basic geometry chart
1. **point**
2. **line**
3. **plane**
4. **trigonometry**
5. **polar coordinate system**
6. **Solid geometry and  analytical geometry**
7. **vector and vector fields**
"""

# ╔═╡ 276112ce-f179-4022-99f4-4543e5a307aa
md"""
## Introduction
>Understanding  mathematics knowledge is vital for learning data science and ai ,but this task is very difficult. Like running, learning math requires constant training.There are many points,Firstly we talk about function and vector by using Julia and Pluto Note. when you touch Pluto, believe me ,you will wanto to make note day by day! It is amazing , it is addictive!

"""

# ╔═╡ 9f4b7a95-a2ae-4434-850f-871320642392
md""" ## Preparation

### Installation

please referece here
[Install Julia & Pluto](https://computationalthinking.mit.edu/Spring21/our_first_climate_model/#:~:text=Software%20installation)

###  Editor
 We use VSCode as editor, but we don't coding at vsc, we just need a plugin.

Here it is [Copy path](https://marketplace.visualstudio.com/items?itemName=alexdima.copy-relative-path), if we create a pluto note or download a note from  repository, pluto server need this path to open note
![](https://cloud.githubusercontent.com/assets/5047891/18313606/d284b04a-750f-11e6-9d0a-8c4f6a3a562e.gif)

###  Package
  We need some package including :LinearAlgebra,Plots,PlutoUI,CairoMakie,SymPy. That's all!

###  Working Flow

When you open a pluto note ,it is a html page based on [**Preact**](https://preactjs.com/). Note sending data to  Julia,
after computing , Julia sending results back to you html page.

We can use PlutoUI to make some interactive programming, Julia don't know this,they just proceeding computing.

This is a good method for learning programming, we change a little to watch what's going on, then we can understand  our code

"""

# ╔═╡ b2dd3dc1-5a70-4149-8f08-39cb7d7b598f
md"## 1.Plotting Point
   Firstle we talk about 2d point.

Basicly, given a point represent by vector method  eg.
$$\begin{bmatrix}
x\\
y\\
\end{bmatrix}$$ or  $\left (x,y\right )$, plot application understanding their meaning , they can do their job.

First component called  xseries(xs), Second component called yseries(xs).

They can including one point's coordiant, or serval points,lately we will see this.

**let plot origin $\left (0,0\right )$**

### first plot
"

# ╔═╡ 058d13ef-b8bc-4acb-b635-66380c459ddb
let
	#scatter is a plot function
	origin=[0 0]
	scatter(origin[:,1],origin[:,2],framestyle=:origin,label="origin:(0,0)",markersize=8,color=:red,size=(300,300))

end

# ╔═╡ 368e33ad-6f34-4150-a6d3-0dc85107f479
md"""
  ### make interaction programming

```julia
    scatter(origin[:,1],origin[:,2])
```

this snippet seems  a little tricky, we talk about later,first we make a interacting programming , we add a Slider  to change point's size

```julia
    scatter(markersize=8)
```
this attribute give information abou point'size , so we can make change it
we need a variable to store this information and binding to Slider.

"""

# ╔═╡ 4ce60bbc-bbfc-43bf-aaa8-e5e99c2dd981
let
range=3:1:14
md"""

PointSize $(@bind pointsize Slider(range; default=4, show_value=true))

we bind pointsize to Slider, when we move Slider, change pointsize, pluto note change
markersize automatically.  That is interesting , if we have a circlesize, we can change a cirle radius(smaller or bigger)?

```julia
markersize=pointsize
```

"""
end

# ╔═╡ 69251bda-d4bb-4e62-94d0-6ea09d032eb7
md"""
!!! tips "tips"
       move  your mouse pointer to right-bottom, click  Live docs, then it will open document of pluto, then click **Slider**, there are  many usage of Slider
"""

# ╔═╡ 21357fa6-c47b-463d-abd4-ddbbb5e57c96
let
	origin=[0 0]
	scatter(origin[:,1],origin[:,2],framestyle=:origin,label="origin:
    (0,0)",markersize=pointsize,color=:red,size=(300,300))

end

# ╔═╡ fbbf0fbb-e3be-4bb5-889a-dab24e47e6a4
md"""
 ###  add second point
 now let's see  origin's data structure
"""

# ╔═╡ 129625b1-5bcd-4d88-a4fa-7007514a17ef
begin
	origin=[0 0]
	@show origin
end

# ╔═╡ 951682dc-0800-48a1-85a2-d33c274ff791
md"""
```julia
    1×2 Matrix{Int64}: 0  0
```
use  [x y] style define point it represent  $m\times n$ matrix.  which is  n rows and m column
if we want to access first component we need follow  first row-first column's order.
"""

# ╔═╡ f5ffa8cb-b9d4-4e14-82a9-815217d21dfb
let
	point1=[1 2]  #  define a  1 row # 2 column matrix
	point1[1,1]   #  element  at  1 row, first column
	#point1[1,2]   #  element  at  1 row, second column
end

# ╔═╡ d2f70e5c-1ce3-43e3-b345-65d7702450c5
let

	point1=[1 2]  #  define a  1 row # 2 column matrix
	point1[:,1]   #  element  at  1 row, first column
	#point1[:,2]   #  element  at  1 row, second column
end

# ╔═╡ 6a82272c-6a6d-478b-9630-88904a48d527
md"""
```julia
point1=[1 2]
point1[:,1]
```
`point1[:,1] ` means get all  matrix first column elements form a array
for plot serval points at same time , we need this method

"""

# ╔═╡ 77ca5461-bef3-406f-bfb8-33bb9d2a9a24
let
	pointarray=[1 2;
	            3 4;
	]

end

# ╔═╡ ea9856da-afd9-43de-865c-beba9f750c42
let
	origin=[0 0]
	point1=[3 4]
	pointarray1=[origin;
		         point1]
	pointarray2=[0 0;
	            3 4;]  # same with above code

	#xs1=pointarray1[:,1]  # xs array
	#ys1=pointarray1[:,2]  # ys  array
	#xs2=pointarray2[:,1]  # xs array
	xs2=pointarray2[:,2]  # ys  array

end

# ╔═╡ 321fb637-d829-4da3-a1c4-24cda593177c
let
	origin=[0 0]
	point1=[2 1]
	pointarray1=[origin;
		         point1]
	pointarray2=[0 0;
	             2 2;]  # same with above code

	xs1=pointarray1[:,1]  # xs array
	ys1=pointarray1[:,2]  # ys  array
	xs2=pointarray2[:,1]  # xs array
	ys2=pointarray2[:,2]  # ys  array

	p1=scatter(xs1,ys1,markersize=5,color=:red,framestyle=:origin,title="pointarray1",label=false)
	p2=scatter(xs2,ys2,markersize=8,framestyle=:origin,title="pointarray2",label=false)
plot!(p1,p2,size=(500,240))

end

# ╔═╡ 27fb4e49-da99-4977-afc6-866c767bed17
md"""
  ### Taste of function plot
   basically, computer plot using point-by-point method
   for a function given a range mapping to domian , in 2d  we get xseries and yseries
   if we comput enough  point pair, we can plot a smooth curve

let's see

$$sin(θ)$$

"""

# ╔═╡ 70efb239-611d-46cd-b930-1f4800e4a29a
let

	xs=0:0.2:2π
	ys=[sin(x) for x in xs] # it is julia's method name comphrension
	scatter(xs,ys,framestyle=:origin,label="sin(θ)")

end

# ╔═╡ edf00ebe-418b-496a-8a09-c05d9a6d74d3
let
    interval=0.5  #  change this value
	xs=0:interval:2π
	ys=[sin(x) for x in xs]
	scatter(xs,ys,framestyle=:origin,markersize=4,label="sin(θ)")

end

# ╔═╡ b5e539fc-d11a-4e24-bbdc-f885a05a801d
let
 range=0.02:0.02:0.6
md"""

Sampling Interval $(@bind samplinginterval Slider(range; default=0.1, show_value=true))

```julia
interval=samplinginterval
```

"""
end

# ╔═╡ 83d2c441-c093-42f1-9dae-62e8b883ff59
md"""
  #### we get a value every $(samplinginterval) interval
"""

# ╔═╡ 8805a21c-f71a-41ed-80a2-5cfdabfe24a7
let
    interval=samplinginterval  #
	xs=0:interval:2π
	ys=[sin(x) for x in xs]
	scatter(xs,ys,framestyle=:origin,markersize=4,label="sin(θ)  every $(samplinginterval)")

end

# ╔═╡ 98b87c0a-ea05-4dfa-a278-dd32de511ff0
let

	intervalArray=[0.5,0.2,0.1,0.01]
	arrlength=length(intervalArray)
	xsArr=[0:i:2π for i in intervalArray]

	plotArr=[]
	for i in  1:arrlength
	  ys=[sin(x) for x in xsArr[i]]
	  p=scatter(xsArr[i],ys,color=:green,markersize=2,title="interval=$(intervalArray[i])",label=false,framestyle=:origin)
	  push!(plotArr,p)
	end

	plot(plotArr...,layout=(1,arrlength),size=(200*arrlength,200))

end

# ╔═╡ b88bd4ad-44a0-4e44-8b90-5cbefc822efc
md"> here we can extract code as function ,that is julia language's function ,not mathematcis function"

# ╔═╡ 9465c7bb-4261-4f3c-b7fb-f8b70945a72a
let
	function getPlotArr(arr)
		intervalArray=arr   #as we refactor code from above code,don't change internal code structure, use temp variable
     arrlength=length(intervalArray)
     xsArr=[0:i:2π for i in intervalArray]

    plotArr=[]
      for i in  1:arrlength
       ys=[sin(x) for x in xsArr[i]]
       p=scatter(xsArr[i],ys,color=:green,markersize=2,title="interval=$(intervalArray[i])",label=false,framestyle=:origin)
      push!(plotArr,p)
      end
    return  plotArr
end

	intervalArray=Float64[0.6,0.5,0.3,0.2,0.1,0.01]
    arrlength=length(intervalArray)
    plotArr=getPlotArr(intervalArray)
    plot(plotArr...,layout=(1,arrlength),size=(200*arrlength,200))

end

# ╔═╡ 5ce24cc4-0a5c-4da7-8fb9-8bef998d536b
md"""
from above  plots, you can see  with interval decrease,we get more smooth function  curve, pattern become more and more clearly.
that is  data science do . we get data sampling ,expect to find some pattern.
give you a set of data, what  can we  learining form these data?
"""

# ╔═╡ d8a42b21-0d34-4b78-b66d-1778b7af39b3
md"""
!!! notice "Conclusion"
    this part we talk about some basic operation of julia  plotting
    need more modify, please waiting.
    netx part we will introduce  line,  from point to line , we can get some feeling about vector which is the most important in this tutorial
"""

# ╔═╡ 00000000-0000-0000-0000-000000000001
PLUTO_PROJECT_TOML_CONTENTS = """
[deps]
Plots = "91a5bcdd-55d7-5caf-9e0b-520d859cae80"
PlutoUI = "7f904dfe-b85e-4ff6-b463-dae2292396a8"
SymPy = "24249f21-da20-56a4-8eb1-6a02cf4ae2e6"

[compat]
Plots = "~1.29.0"
PlutoUI = "~0.7.38"
SymPy = "~1.1.6"
"""

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

julia_version = "1.7.1"
manifest_format = "2.0"

[[deps.AbstractPlutoDingetjes]]
deps = ["Pkg"]
git-tree-sha1 = "8eaf9f1b4921132a4cff3f36a1d9ba923b14a481"
uuid = "6e696c72-6542-2067-7265-42206c756150"
version = "1.1.4"

[[deps.Adapt]]
deps = ["LinearAlgebra"]
git-tree-sha1 = "af92965fb30777147966f58acb05da51c5616b5f"
uuid = "79e6a3ab-5dfb-504d-930d-738a2a938a0e"
version = "3.3.3"

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

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

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

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

[[deps.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 = "4b859a208b2397a7a623a03449e4636bdb17bcf2"
uuid = "83423d85-b0ee-5818-9007-b63ccbeb887a"
version = "1.16.1+1"

[[deps.ChainRulesCore]]
deps = ["Compat", "LinearAlgebra", "SparseArrays"]
git-tree-sha1 = "9950387274246d08af38f6eef8cb5480862a435f"
uuid = "d360d2e6-b24c-11e9-a2a3-2a2ae2dbcce4"
version = "1.14.0"

[[deps.ChangesOfVariables]]
deps = ["ChainRulesCore", "LinearAlgebra", "Test"]
git-tree-sha1 = "1e315e3f4b0b7ce40feded39c73049692126cf53"
uuid = "9e997f8a-9a97-42d5-a9f1-ce6bfc15e2c0"
version = "0.1.3"

[[deps.ColorSchemes]]
deps = ["ColorTypes", "ColorVectorSpace", "Colors", "FixedPointNumbers", "Random"]
git-tree-sha1 = "7297381ccb5df764549818d9a7d57e45f1057d30"
uuid = "35d6a980-a343-548e-a6ea-1d62b119f2f4"
version = "3.18.0"

[[deps.ColorTypes]]
deps = ["FixedPointNumbers", "Random"]
git-tree-sha1 = "a985dc37e357a3b22b260a5def99f3530fb415d3"
uuid = "3da002f7-5984-5a60-b8a6-cbb66c0b333f"
version = "0.11.2"

[[deps.ColorVectorSpace]]
deps = ["ColorTypes", "FixedPointNumbers", "LinearAlgebra", "SpecialFunctions", "Statistics", "TensorCore"]
git-tree-sha1 = "3f1f500312161f1ae067abe07d13b40f78f32e07"
uuid = "c3611d14-8923-5661-9e6a-0046d554d3a4"
version = "0.9.8"

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

[[deps.CommonEq]]
git-tree-sha1 = "d1beba82ceee6dc0fce8cb6b80bf600bbde66381"
uuid = "3709ef60-1bee-4518-9f2f-acd86f176c50"
version = "0.2.0"

[[deps.CommonSolve]]
git-tree-sha1 = "68a0743f578349ada8bc911a5cbd5a2ef6ed6d1f"
uuid = "38540f10-b2f7-11e9-35d8-d573e4eb0ff2"
version = "0.2.0"

[[deps.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 = "b153278a25dd42c65abbf4e62344f9d22e59191b"
uuid = "34da2185-b29b-5c13-b0c7-acf172513d20"
version = "3.43.0"

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

[[deps.Conda]]
deps = ["Downloads", "JSON", "VersionParsing"]
git-tree-sha1 = "6e47d11ea2776bc5627421d59cdcc1296c058071"
uuid = "8f4d0f93-b110-5947-807f-2305c1781a2d"
version = "1.7.0"

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

[[deps.DataAPI]]
git-tree-sha1 = "fb5f5316dd3fd4c5e7c30a24d50643b73e37cd40"
uuid = "9a962f9c-6df0-11e9-0e5d-c546b8b5ee8a"
version = "1.10.0"

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

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

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

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

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

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

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

[[deps.EarCut_jll]]
deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"]
git-tree-sha1 = "3f3a2501fa7236e9b911e0f7a588c657e822bb6d"
uuid = "5ae413db-bbd1-5e63-b57d-d24a61df00f5"
version = "2.2.3+0"

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

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

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

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

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

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

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

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

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

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

[[deps.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 = "cd6efcf9dc746b06709df14e462f0a3fe0786b1e"
uuid = "d2c73de3-f751-5644-a686-071e5b155ba9"
version = "0.64.2+0"

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

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

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

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

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

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

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

[[deps.Hyperscript]]
deps = ["Test"]
git-tree-sha1 = "8d511d5b81240fc8e6802386302675bdf47737b9"
uuid = "47d2ed2b-36de-50cf-bf87-49c2cf4b8b91"
version = "0.0.4"

[[deps.HypertextLiteral]]
deps = ["Tricks"]
git-tree-sha1 = "c47c5fa4c5308f27ccaac35504858d8914e102f9"
uuid = "ac1192a8-f4b3-4bfe-ba22-af5b92cd3ab2"
version = "0.9.4"

[[deps.IOCapture]]
deps = ["Logging", "Random"]
git-tree-sha1 = "f7be53659ab06ddc986428d3a9dcc95f6fa6705a"
uuid = "b5f81e59-6552-4d32-b1f0-c071b021bf89"
version = "0.2.2"

[[deps.IniFile]]
git-tree-sha1 = "f550e6e32074c939295eb5ea6de31849ac2c9625"
uuid = "83e8ac13-25f8-5344-8a64-a9f2b223428f"
version = "0.5.1"

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

[[deps.InverseFunctions]]
deps = ["Test"]
git-tree-sha1 = "336cc738f03e069ef2cac55a104eb823455dca75"
uuid = "3587e190-3f89-42d0-90ee-14403ec27112"
version = "0.1.4"

[[deps.IrrationalConstants]]
git-tree-sha1 = "7fd44fd4ff43fc60815f8e764c0f352b83c49151"
uuid = "92d709cd-6900-40b7-9082-c6be49f344b6"
version = "0.1.1"

[[deps.IterTools]]
git-tree-sha1 = "fa6287a4469f5e048d763df38279ee729fbd44e5"
uuid = "c8e1da08-722c-5040-9ed9-7db0dc04731e"
version = "1.4.0"

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

[[deps.JLLWrappers]]
deps = ["Preferences"]
git-tree-sha1 = "abc9885a7ca2052a736a600f7fa66209f96506e1"
uuid = "692b3bcd-3c85-4b1f-b108-f13ce0eb3210"
version = "1.4.1"

[[deps.JSON]]
deps = ["Dates", "Mmap", "Parsers", "Unicode"]
git-tree-sha1 = "3c837543ddb02250ef42f4738347454f95079d4e"
uuid = "682c06a0-de6a-54ab-a142-c8b1cf79cde6"
version = "0.21.3"

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

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

[[deps.LERC_jll]]
deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"]
git-tree-sha1 = "bf36f528eec6634efc60d7ec062008f171071434"
uuid = "88015f11-f218-50d7-93a8-a6af411a945d"
version = "3.0.0+1"

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

[[deps.LaTeXStrings]]
git-tree-sha1 = "f2355693d6778a178ade15952b7ac47a4ff97996"
uuid = "b964fa9f-0449-5b57-a5c2-d3ea65f4040f"
version = "1.3.0"

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

[[deps.LogExpFunctions]]
deps = ["ChainRulesCore", "ChangesOfVariables", "DocStringExtensions", "InverseFunctions", "IrrationalConstants", "LinearAlgebra"]
git-tree-sha1 = "09e4b894ce6a976c354a69041a04748180d43637"
uuid = "2ab3a3ac-af41-5b50-aa03-7779005ae688"
version = "0.3.15"

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

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

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

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

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

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

[[deps.Missings]]
deps = ["DataAPI"]
git-tree-sha1 = "bf210ce90b6c9eed32d25dbcae1ebc565df2687f"
uuid = "e1d29d7a-bbdc-5cf2-9ac0-f12de2c33e28"
version = "1.0.2"

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

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

[[deps.NaNMath]]
git-tree-sha1 = "b086b7ea07f8e38cf122f5016af580881ac914fe"
uuid = "77ba4419-2d1f-58cd-9bb1-8ffee604a2e3"
version = "0.3.7"

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

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

[[deps.OpenBLAS_jll]]
deps = ["Artifacts", "CompilerSupportLibraries_jll", "Libdl"]
uuid = "4536629a-c528-5b80-bd46-f80d51c5b363"

[[deps.OpenLibm_jll]]
deps = ["Artifacts", "Libdl"]
uuid = "05823500-19ac-5b8b-9628-191a04bc5112"

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

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

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

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

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

[[deps.Parsers]]
deps = ["Dates"]
git-tree-sha1 = "1285416549ccfcdf0c50d4997a94331e88d68413"
uuid = "69de0a69-1ddd-5017-9359-2bf0b02dc9f0"
version = "2.3.1"

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

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

[[deps.PlotThemes]]
deps = ["PlotUtils", "Statistics"]
git-tree-sha1 = "8162b2f8547bc23876edd0c5181b27702ae58dce"
uuid = "ccf2f8ad-2431-5c83-bf29-c5338b663b6a"
version = "3.0.0"

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

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

[[deps.PlutoUI]]
deps = ["AbstractPlutoDingetjes", "Base64", "ColorTypes", "Dates", "Hyperscript", "HypertextLiteral", "IOCapture", "InteractiveUtils", "JSON", "Logging", "Markdown", "Random", "Reexport", "UUIDs"]
git-tree-sha1 = "670e559e5c8e191ded66fa9ea89c97f10376bb4c"
uuid = "7f904dfe-b85e-4ff6-b463-dae2292396a8"
version = "0.7.38"

[[deps.Preferences]]
deps = ["TOML"]
git-tree-sha1 = "47e5f437cc0e7ef2ce8406ce1e7e24d44915f88d"
uuid = "21216c6a-2e73-6563-6e65-726566657250"
version = "1.3.0"

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

[[deps.PyCall]]
deps = ["Conda", "Dates", "Libdl", "LinearAlgebra", "MacroTools", "Serialization", "VersionParsing"]
git-tree-sha1 = "1fc929f47d7c151c839c5fc1375929766fb8edcc"
uuid = "438e738f-606a-5dbb-bf0a-cddfbfd45ab0"
version = "1.93.1"

[[deps.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 = "c6c0f690d0cc7caddb74cef7aa847b824a16b256"
uuid = "ea2cea3b-5b76-57ae-a6ef-0a8af62496e1"
version = "5.15.3+1"

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

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

[[deps.RecipesBase]]
git-tree-sha1 = "6bf3f380ff52ce0832ddd3a2a7b9538ed1bcca7d"
uuid = "3cdcf5f2-1ef4-517c-9805-6587b60abb01"
version = "1.2.1"

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

[[deps.Reexport]]
git-tree-sha1 = "45e428421666073eab6f2da5c9d310d99bb12f9b"
uuid = "189a3867-3050-52da-a836-e630ba90ab69"
version = "1.2.2"

[[deps.RelocatableFolders]]
deps = ["SHA", "Scratch"]
git-tree-sha1 = "cdbd3b1338c72ce29d9584fdbe9e9b70eeb5adca"
uuid = "05181044-ff0b-4ac5-8273-598c1e38db00"
version = "0.1.3"

[[deps.Requires]]
deps = ["UUIDs"]
git-tree-sha1 = "838a3a4188e2ded87a4f9f184b4b0d78a1e91cb7"
uuid = "ae029012-a4dd-5104-9daa-d747884805df"
version = "1.3.0"

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

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

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

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

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

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

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

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

[[deps.SpecialFunctions]]
deps = ["ChainRulesCore", "IrrationalConstants", "LogExpFunctions", "OpenLibm_jll", "OpenSpecFun_jll"]
git-tree-sha1 = "5ba658aeecaaf96923dce0da9e703bd1fe7666f9"
uuid = "276daf66-3868-5448-9aa4-cd146d93841b"
version = "2.1.4"

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

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

[[deps.StatsAPI]]
deps = ["LinearAlgebra"]
git-tree-sha1 = "c82aaa13b44ea00134f8c9c89819477bd3986ecd"
uuid = "82ae8749-77ed-4fe6-ae5f-f523153014b0"
version = "1.3.0"

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

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

[[deps.SymPy]]
deps = ["CommonEq", "CommonSolve", "Latexify", "LinearAlgebra", "Markdown", "PyCall", "RecipesBase", "SpecialFunctions"]
git-tree-sha1 = "e1865ba3c44551087a04295ddc40c10edf1b24a0"
uuid = "24249f21-da20-56a4-8eb1-6a02cf4ae2e6"
version = "1.1.6"

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

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

[[deps.Tables]]
deps = ["DataAPI", "DataValueInterfaces", "IteratorInterfaceExtensions", "LinearAlgebra", "OrderedCollections", "TableTraits", "Test"]
git-tree-sha1 = "5ce79ce186cc678bbb5c5681ca3379d1ddae11a1"
uuid = "bd369af6-aec1-5ad0-b16a-f7cc5008161c"
version = "1.7.0"

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

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

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

[[deps.Tricks]]
git-tree-sha1 = "6bac775f2d42a611cdfcd1fb217ee719630c4175"
uuid = "410a4b4d-49e4-4fbc-ab6d-cb71b17b3775"
version = "0.1.6"

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

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

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

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

[[deps.Unzip]]
git-tree-sha1 = "34db80951901073501137bdbc3d5a8e7bbd06670"
uuid = "41fe7b60-77ed-43a1-b4f0-825fd5a5650d"
version = "0.1.2"

[[deps.VersionParsing]]
git-tree-sha1 = "58d6e80b4ee071f5efd07fda82cb9fbe17200868"
uuid = "81def892-9a0e-5fdd-b105-ffc91e053289"
version = "1.3.0"

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

[[deps.Wayland_protocols_jll]]
deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"]
git-tree-sha1 = "4528479aa01ee1b3b4cd0e6faef0e04cf16466da"
uuid = "2381bf8a-dfd0-557d-9999-79630e7b1b91"
version = "1.25.0+0"

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

[[deps.libblastrampoline_jll]]
deps = ["Artifacts", "Libdl", "OpenBLAS_jll"]
uuid = "8e850b90-86db-534c-a0d3-1478176c7d93"

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

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

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

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

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

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

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

[[deps.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:
# ╠═240bf15b-20e6-4b89-96ff-1dcbb862ef9b
# ╠═ff3f2492-d57d-11ec-386b-ed8716f9c392
# ╠═981ee694-d145-4d46-806d-a69c0b529c89
# ╠═06a8c6d8-e09f-40c0-b1dd-e28dbc7baa38
# ╟─2b65a1d3-3454-465d-a1a7-71174ede0658
# ╟─276112ce-f179-4022-99f4-4543e5a307aa
# ╟─9f4b7a95-a2ae-4434-850f-871320642392
# ╠═b2dd3dc1-5a70-4149-8f08-39cb7d7b598f
# ╠═058d13ef-b8bc-4acb-b635-66380c459ddb
# ╠═368e33ad-6f34-4150-a6d3-0dc85107f479
# ╠═4ce60bbc-bbfc-43bf-aaa8-e5e99c2dd981
# ╠═69251bda-d4bb-4e62-94d0-6ea09d032eb7
# ╠═21357fa6-c47b-463d-abd4-ddbbb5e57c96
# ╠═fbbf0fbb-e3be-4bb5-889a-dab24e47e6a4
# ╠═129625b1-5bcd-4d88-a4fa-7007514a17ef
# ╠═951682dc-0800-48a1-85a2-d33c274ff791
# ╠═f5ffa8cb-b9d4-4e14-82a9-815217d21dfb
# ╠═d2f70e5c-1ce3-43e3-b345-65d7702450c5
# ╠═6a82272c-6a6d-478b-9630-88904a48d527
# ╠═77ca5461-bef3-406f-bfb8-33bb9d2a9a24
# ╠═ea9856da-afd9-43de-865c-beba9f750c42
# ╠═321fb637-d829-4da3-a1c4-24cda593177c
# ╠═27fb4e49-da99-4977-afc6-866c767bed17
# ╠═70efb239-611d-46cd-b930-1f4800e4a29a
# ╠═edf00ebe-418b-496a-8a09-c05d9a6d74d3
# ╟─b5e539fc-d11a-4e24-bbdc-f885a05a801d
# ╠═83d2c441-c093-42f1-9dae-62e8b883ff59
# ╠═8805a21c-f71a-41ed-80a2-5cfdabfe24a7
# ╠═98b87c0a-ea05-4dfa-a278-dd32de511ff0
# ╟─b88bd4ad-44a0-4e44-8b90-5cbefc822efc
# ╠═9465c7bb-4261-4f3c-b7fb-f8b70945a72a
# ╠═5ce24cc4-0a5c-4da7-8fb9-8bef998d536b
# ╠═d8a42b21-0d34-4b78-b66d-1778b7af39b3
# ╟─00000000-0000-0000-0000-000000000001
# ╟─00000000-0000-0000-0000-000000000002


# -*- coding: utf-8 -*-
# ---
# jupyter:
#   jupytext:
#     formats: ipynb,jl:hydrogen
#     text_representation:
#       extension: .jl
#       format_name: hydrogen
#       format_version: '1.3'
#       jupytext_version: 1.11.2
#   kernelspec:
#     display_name: Julia 1.8.0-DEV
#     language: julia
#     name: julia-1.8
# ---

# %%
using LinearAlgebra
using LoopVectorization
using LinearMaps
using ArnoldiMethod

function prepareDiag(L)
    diag = zeros(2^L)
    for state = 1:2^L
        for i = 1:L
            j = i==L ? 1 : i+1
            @inbounds diag[state] -= (((state >> (i-1))&1) == ((state >> (j-1))&1)) ? 1 : -1
        end
    end
    diag
end

function Hfunc!(C, B, diag, L)
    N = length(diag)
    @tturbo for state = 1:N
        C[state] = diag[state] * B[state]
    end
    for i = 1:L
        @tturbo for state = 1:N
            newstate = (state&(~(2^L))) ⊻ (1<<(i-1))
            c = newstate == 0
            newstate = !c*newstate + c*N # remove if statement
            C[newstate] -= B[state]
        end
    end
end
prepareHfunc!(L) = (C, B) -> Hfunc!(C, B, prepareDiag(L), L)

function f_LinearMap(L)
    H_LinearMap = LinearMap(prepareHfunc!(L), 2^L, ismutating=true, issymmetric=true, isposdef=false)
    d_LM, h_LM = partialschur(H_LinearMap, nev=1, which=SR())
    d_LM.eigenvalues[1]
end

f_exact(L) = -2sum(abs(sin((n-1/2) * pi/L)) for n in 1:L)

# %%
# https://juliaphysics.github.io/PhysicsTutorials.jl/tutorials/general/quantum_ising/quantum_ising.html

using LinearAlgebra
using SparseArrays
using ArnoldiMethod
⊗(x,y) = kron(x,y)

function TransverseFieldIsing_sparse(;N,h)
    id = [1 0; 0 1] |> sparse
    σˣ = [0 1; 1 0] |> sparse
    σᶻ = [1 0; 0 -1] |> sparse

    first_term_ops = fill(id, N)
    first_term_ops[1] = σᶻ
    first_term_ops[2] = σᶻ

    second_term_ops = fill(id, N)
    second_term_ops[1] = σˣ

    H = spzeros(Int, 2^N, 2^N) # note the spzeros instead of zeros here
    #for i in 1:N-1
    for i in 1:N # periodic boundary condition
        H -= foldl(⊗, first_term_ops)
        first_term_ops = circshift(first_term_ops,1)
    end

    for i in 1:N
        H -= h*foldl(⊗, second_term_ops)
        second_term_ops = circshift(second_term_ops,1)
    end
    H
end

function f_sparse(L)
    H = TransverseFieldIsing_sparse(N=L, h=1)
    d, h = partialschur(H; nev=1, which=SR())
    d.eigenvalues[1]
end

# %%
f_exact(20)

# %%
@time f_LinearMap(20)
@time f_LinearMap(20)

# %%
@time f_sparse(20)
@time f_sparse(20)

# %%
@time H = TransverseFieldIsing_sparse(N=20, h=1)
@time d, h = partialschur(H; nev=1, which=SR())

# %%
@time f_LinearMap(10)
@time f_LinearMap(10)

# %%
@time f_sparse(10)
@time f_sparse(10)

# %%
L = 10
@time H_LM = LinearMap(prepareHfunc!(L), 2^L, ismutating=true, issymmetric=true, isposdef=false)
@time d_LM, h_LM = partialschur(H_LM, nev=1, which=SR())
@time d_LM, h_LM = partialschur(H_LM, nev=1, which=SR())

# %%
@time H_sparse = TransverseFieldIsing_sparse(N=10, h=1)
@time d, h = partialschur(H_sparse; nev=1, which=SR())
@time d, h = partialschur(H_sparse; nev=1, which=SR())

# %%
using BenchmarkTools

L = 10
@show L
println("LinearMaps:")
H_LM = @btime LinearMap(prepareHfunc!(L), 2^L, ismutating=true, issymmetric=true, isposdef=false)
@btime d_LM, h_LM = partialschur($H_LM, nev=1, which=$(SR()))
println("SparseArrays:")
H_sparse = @btime TransverseFieldIsing_sparse(N=L, h=1)
@btime d, h = partialschur($H_sparse; nev=1, which=$(SR()));

# %%
using BenchmarkTools

L = 20
@show L
println("LinearMaps:")
H_LM = @btime LinearMap(prepareHfunc!(L), 2^L, ismutating=true, issymmetric=true, isposdef=false)
@btime d_LM, h_LM = partialschur($H_LM, nev=1, which=$(SR()))
println("SparseArrays:")
H_sparse = @btime TransverseFieldIsing_sparse(N=L, h=1)
@btime d, h = partialschur($H_sparse; nev=1, which=$(SR()));

# %%


# Taken from https://github.com/JuliaMath/KahanSummation.jl/pull/7/files
#   and make the follwoing changes: (1) Comment out the first two lines
#   ("__precom.." and "module...") and comment out all the lines after Line
#   100. (2) Update the function name `mapreduce_single` to `mapreduce_first`.
# Use `include("KahanSummation_patch.jl")` to load the patch.

# This file contains code that was formerly a part of Julia.
# License is MIT: https://julialang.org/license

# __precompile__(true)
# module KahanSummation

if VERSION >= v"0.7.0-DEV.3000" # TODO: More specific bound
    if isdefined(Base, :sum_kbn) # Deprecated
        import Base: sum_kbn, cumsum_kbn
    else
        export sum_kbn, cumsum_kbn
    end
end
if isdefined(Base, Symbol("@default_eltype"))
    using Base: @default_eltype
else
    macro default_eltype(itr)
        quote
            Core.Inference.return_type(first, Tuple{$(esc(itr))})
        end
    end
end
if isdefined(Base, :promote_sys_size_add)
    using Base: promote_sys_size_add
else
    promote_sys_size_add(x::T) where {T} = Base.r_promote(+, zero(T)::T)
end

"""
    TwicePrecisionN{T}
Represents an extended precision number as `x.hi - x.nlo`.
We store the lower order component as the negation to avoid problems when `x.hi == -0.0`.
"""
struct TwicePrecisionN{T}
    hi::T
    nlo::T
end

@inline function plus_kbn(x::T, y::T) where {T}
    hi = x + y
    nlo = abs(x) > abs(y) ? (hi - x ) - y : (hi - y) - x
    TwicePrecisionN(hi, nlo)
end
@inline function plus_kbn(x::T, y::TwicePrecisionN{T}) where {T}
    hi = x + y.hi
    if abs(x) > abs(y.hi)
        nlo = ((hi - x) - y.hi) + y.nlo
    else
        nlo = ((hi - y.hi) - x) + y.nlo
    end
    TwicePrecisionN(hi, nlo)
end
@inline plus_kbn(x::TwicePrecisionN{T}, y::T) where {T} = plus_kbn(y, x)

@inline function plus_kbn(x::TwicePrecisionN{T}, y::TwicePrecisionN{T}) where {T}
    hi = x.hi + y.hi
    if abs(x.hi) > abs(y.hi)
        nlo = (((hi - x.hi) - y.hi) + y.nlo) + x.nlo
    else
        nlo = (((hi - y.hi) - x.hi) + x.nlo) + y.nlo
    end
    TwicePrecisionN(hi, nlo)
end

Base.convert(::Type{TwicePrecisionN{T}}, x::Number) where {T} =
    TwicePrecisionN{T}(convert(T, x), zero(T))
Base.convert(::Type{T}, x::TwicePrecisionN) where {T} =
    convert(T, x.hi - x.nlo)

@static if VERSION >= v"0.7.0-"
    Base.mapreduce_empty(f, ::typeof(plus_kbn), T) = TwicePrecisionN(zero(T),zero(T))
    Base.mapreduce_empty(::typeof(identity), ::typeof(plus_kbn), T) = TwicePrecisionN(zero(T),zero(T)) # disambiguate
    Base.mapreduce_first(f, ::typeof(plus_kbn), x) = TwicePrecisionN(x, zero(x))
else
    Base.r_promote_type(::typeof(plus_kbn), ::Type{T}) where {T} =
        TwicePrecisionN{T}
    Base.mr_empty(f, ::typeof(plus_kbn), T) = TwicePrecisionN(zero(T),zero(T))
end

singleprec(x::TwicePrecisionN{T}) where {T} = convert(T, x)

"""
    sum_kbn([f,] A)
Return the sum of all elements of `A`, using the Kahan-Babuska-Neumaier compensated
summation algorithm for additional accuracy.
"""
sum_kbn(f, X) = singleprec(mapreduce(f, plus_kbn, X))
sum_kbn(X) = sum_kbn(identity, X)

#=

"""
    cumsum_kbn(A, dim::Integer)
@@ -85,32 +157,4 @@ function cumsum_kbn(v::AbstractVector{T}) where T<:AbstractFloat
    return r
end

"""
    sum_kbn(A)
Return the sum of all elements of `A`, using the Kahan-Babuska-Neumaier compensated
summation algorithm for additional accuracy.
"""
function sum_kbn(A)
    T = @default_eltype(typeof(A))
    c = promote_sys_size_add(zero(T)::T)
    i = start(A)
    if done(A, i)
        return c
    end
    Ai, i = next(A, i)
    s = Ai - c
    while !(done(A, i))
        Ai, i = next(A, i)
        t = s + Ai
        if abs(s) >= abs(Ai)
            c -= ((s-t) + Ai)
        else
            c -= ((Ai-t) + s)
        end
        s = t
    end
    s - c
end

end # module
=#


"""
This module contains various statistical models in the form of [`NestedModel`](@ref)s. These models can be used for examples and for testing.

* [`Models.GaussianShells`](@ref)
* [`Models.CorrelatedGaussian`](@ref)
"""
module Models

using ..NestedSamplers

using Distributions
using LinearAlgebra
using LogExpFunctions

include("shells.jl")
include("correlated.jl")
include("eggbox.jl")

end # module


module Elements

using FastGaussQuadrature
import Base.Iterators: product

import ..Transforms: TransformChain

export Simplex, Element, Tensor, quadrule

abstract type ReferenceElement end

struct Simplex{NDim} <: ReferenceElement end

Base.ndims(::Simplex{n}) where n = n

quadrule(::Simplex{0}, npts::Int) = zeros(Float64, 1, 0), [1.0]

function quadrule(::Simplex{N}, npts::Int) where N
    (pts, wts) = gausslegendre(npts)
    pts = (pts .+ 1.0) ./ 2
    wts = wts ./ 2

    pts = hcat([collect(k) for k in product((pts for _ in 1:N)...)]...)
    wts = .*((reshape(wts, fill(1, k)..., npts) for k in 0:N-1)...)
    pts', vec(wts)
end

struct Tensor <: ReferenceElement
    terms :: Array{Simplex}
end

Base.ndims(self::Tensor) = sum(ndims(term) for term in self.terms)

struct Element
    reference :: ReferenceElement
    index :: Int
    transform :: Tuple{TransformChain, TransformChain}
end

Element(ref::ReferenceElement, index::Int; transform::TransformChain=(), dimcorr::TransformChain=()) =
    Element(ref, index, (dimcorr, transform))

Base.ndims(self::Element) = ndims(self.reference)

end # module


"""
    module Shapes

Definitions of various shapes.

All shapes have the signature: `shape(parameters::Tuple, x0, y0, θ=0)`, and some
have keyword argument `reference` which determines which point `(x0,y0)` is referring to.

The length of `parameters` depends on the shape. For example, `Circle` has just one
parameter, the radius, while `Ellipse` has two, the semi-major and semi-minor axes.
"""
module Shapes
abstract type AbstractDisk <: AbstractShape{2,1} end
abstract type AbstractQuadrilateral <: AbstractShape{2,4} end

export Circle,
Ellipse,
DeformedDisk,
Annulus,
Square,
Rectangle,
Parallelogram,
normal_distance,
get_number_of_sides,
rotate,
unrotate

export AbstractShape,
AbstractDisk,
AbstractQuadrilateral

using ...Defaults
using Formatting
using LinearAlgebra
using NLopt
using RecipesBase
using StaticArrays

abstract type AbstractShape{N} end # N is the number of sides
abstract type AbstractDisk <: AbstractShape{1} end
abstract type AbstractQuadrilateral <: AbstractShape{4} end

get_number_of_sides(s::AbstractShape{N}) where N = N

"""
    struct Circle <: AbstractDisk <: AbstractShape{1}

    Circle((R,), x0, y0, θ=0; reference=:center) -> circle
    Circle(R, x0, y0, θ=0; reference=:center) -> circle

`R` radius
`x0, y0` is location of `reference`

-------------------

    (::Circle)(x,y) --> is_in_circle::Bool
"""
struct Circle <: AbstractDisk
    R::Float64
    x0::Float64
    y0::Float64
    θ::Float64
    sinθ::Float64
    cosθ::Float64
    corners::Tuple{}
    models::Array{Opt,1}

    Circle((R,), args...; kwargs...) = Circle(R[1], args...; kwargs...)
    function Circle(R::Real,x0::Real,y0::Real,θ::Real=0; reference::Symbol=:center)
        if reference ∈ [:bottom, :Bottom, :b, :B]
            x0,y0 = (x0,y0) .+ rotate( 0, R,cos(θ),sin(θ))
        elseif reference ∈ [:top, :Top, :T, :t]
            x0,y0 = (x0,y0) .+ rotate( 0,-R,cos(θ),sin(θ))
        elseif reference ∈ [:left, :Left, :L, :l]
            x0,y0 = (x0,y0) .+ rotate( R, 0,cos(θ),sin(θ))
        elseif reference ∈ [:right, :Right, :R, :r]
            x0,y0 = (x0,y0) .+ rotate(-R, 0,cos(θ),sin(θ))
        end
        c = new(R,x0,y0,θ,sin(θ),cos(θ))
        models = build_models(c)
        return new(R,x0,y0,θ,sin(θ),cos(θ),(),models)
    end

    (c::Circle)(x,y) = hypot((x-c.x0), (y-c.y0)) < c.R-eps(c.R)

    function Base.show(io::IO, circle::Circle)
        print(io, "Circle(R=", fmt("2.2f",circle.R), ", x0=", fmt("2.2f",circle.x0), ", y0=", fmt("2.2f",circle.y0), ")")
    end
end
"""
    perimeter(t,shape,side) -> x,y,dx/dt,dy/dt
"""
function perimeter(t::Number,c::Circle,side::Int)
    sn,cs = sincos(t)
    x = c.x0 + c.R*cs
    y = c.y0 + c.R*sn
    ẋ = -c.R*sn
    ẏ =  c.R*cs
    return x,y,ẋ,ẏ
end

"""
    struct Ellipse <: AbstractDisk <: AbstractShape{1}

    Ellipse((a,b), x0, y0, θ=0; reference=:center) -> ellipse
    Ellipse(a, b, x0, y0, θ=0; reference=:center) -> ellipse

`a, b` axis lengths
`x0, y0` is location of `reference`
`θ` angle of `a` axis

----------------

    (::Ellipse)(x,y) -> is_in_ellipse::Bool
"""
struct Ellipse <: AbstractDisk
    a::Float64
    b::Float64
    x0::Float64
    y0::Float64
    θ::Float64
    sinθ::Float64
    cosθ::Float64
    corners::Tuple{}
    models::Array{Opt,1}

    Ellipse((a,b),args...;kwargs...) = Ellipse(a,b,args...;kwargs...)
    function Ellipse(a::Number,b::Number,x0::Number,y0::Number,θ::Number=0; reference::Symbol=:center)
        if reference ∈ [:bottom, :Bottom, :b, :B]
            x0,y0 = (x0,y0) .+ rotate( 0, b,cos(θ),sin(θ))
        elseif reference ∈ [:top, :Top, :T, :t]
            x0,y0 = (x0,y0) .+ rotate( 0,-b,cos(θ),sin(θ))
        elseif reference ∈ [:left, :Left, :L, :l]
            x0,y0 = (x0,y0) .+ rotate( a, 0,cos(θ),sin(θ))
        elseif reference ∈ [:right, :Right, :R, :r]
            x0,y0 = (x0,y0) .+ rotate(-a, 0,cos(θ),sin(θ))
        end
         e = new(a,b,x0,y0,θ,sin(θ),cos(θ))
         models = build_models(e)
         return new(a,b,x0,y0,θ,sin(θ),cos(θ),(),models)
    end

    Ellipse(c::Circle) = Ellipse(c.R,c.R,c.x0,c.y0,c.θ)

    function (e::Ellipse)(x,y)
        xurot, yurot = unrotate(x, y, e)
        return hypot((xurot-e.x0)/e.a, (yurot-e.y0)/e.b) < 1-eps()
    end

    function Base.show(io::IO, ellipse::Ellipse)
        print(io, "Ellipse(a=", fmt("2.2f",ellipse.a), ", b=", fmt("2.2f",ellipse.b), ", x0=", fmt("2.2f",ellipse.x0), ", y0=", fmt("2.2f",ellipse.y0), ", θ=∠", fmt("3.2f",(mod2pi(ellipse.θ))*180/π), "°)")
    end
end
function perimeter(t::Number,e::Ellipse,side::Int)
    sn,cs = sincos(t)
    xur,yur =    e.a*cs, e.b*sn
    xpur,ypur = -e.a*sn, e.b*cs
    x,y = (e.x0,e.y0) .+ rotate(xur,yur,e.cosθ,e.sinθ)
    ẋ,ẏ = rotate(xpur,ypur,e.cosθ,e.sinθ)
    return x,y,ẋ,ẏ
end

"""
    struct DeformedDisk{N} <: AbstractDisk <: AbstractShape{1}

    DeformedDisk{N}(R, x0, y0, M, a, φ, θ=0) -> deformeddisk
    DeformedDisk{N}((R,M,a,φ), x0, y0, θ=0) -> deformeddisk

`R` is radius,
`x0` and `y0` is center of circle of radius `R`,
`M` is array of length `N` of multipole integers,
`a` is array of length `N` amplitudes,
`φ` is array of length `N` of angles
`θ` is overall rotation angle

-----------------

    (::DeformedDisk)(x,y) -> is_in_disk::Bool
"""
struct DeformedDisk{N} <: AbstractDisk
    R::Float64
    x0::Float64
    y0::Float64
    θ::Float64
    M::SArray{Tuple{N},Int,1,N}
    a::SArray{Tuple{N},Float64,1,N}
    φ::SArray{Tuple{N},Float64,1,N}
    sinθ::Float64
    cosθ::Float64
    corners::Tuple{}
    models::Array{Opt,1}

    DeformedDisk{n}((R,M,a,φ),x0,y0,args...;kwargs...) where n= DeformedDisk{n}(R,x0,y0,M,a,φ,args...;kwargs...)
    function DeformedDisk{n}(R::Number,x0::Number,y0::Number,M,a,φ,θ=0) where n
        @assert n==length(M)==length(a)==length(φ) "parameter N in DeformedDisk{N}(...) must be equal to length(M)"
        d = new{n}(R,x0,y0,θ,SVector{n}(M),SVector{n}(a),SVector{n}(φ.+θ),sin(θ),cos(θ))
        models = build_models(d)
        return new{n}(R,x0,y0,d.θ,d.M,d.a,d.φ,d.sinθ,d.cosθ,(),models)
    end

    DeformedDisk(c::Circle) = DeformedDisk{0}(c.R,c.x0,c.y0,[],[],[],c.θ)

    function (d::DeformedDisk)(x,y)
        θ = atan(y-d.y0,x-d.x0)
        r = hypot(x-d.x0,y-d.y0) < d.R + sum(d.a.*(map((m,φ)->cos(m*(θ-φ)),d.M,d.φ))) - eps(d.R)
        return r
    end

    function Base.show(io::IO, d::DeformedDisk{N}) where N
         print(io,"DeformedDisk with $N multipoles")
    end
end
function perimeter(t::Number,d::DeformedDisk,side::Int)
    r = d.R + sum(d.a.*(map((m,φ)->cos(m*(t-φ)),d.M,d.φ)))
    ṙ = sum(d.a.*(map((m,φ)->-m*sin(m*(t-φ)),d.M,d.φ)))
    sn,cs = sincos(t)
    x,y = d.x0 + r*cs, d.y0 + r*sn
    ẋ = ṙ*cs - r*sn
    ẏ = ṙ*sn + r*cs
    return x,y,ẋ,ẏ
end

# plotting for all disks
@recipe function f(d::AbstractDisk)
    θ = LinRange(0,2π,201)
    x = Array{Float64,1}(undef,length(θ))
    y = Array{Float64,1}(undef,length(θ))
    for i ∈ eachindex(θ)
        x[i],y[i] = perimeter(θ[i],d,1)
    end
    alpha --> 0
    seriestype --> :path
    fillcolor --> SHAPE_COLOR
    fillrange --> d.y0
    fillalpha --> SHAPE_FILL_ALPHA
    aspect_ratio --> 1
    legend-->false
    (x,y)
end

"""
    struct Annulus <: AbstractShape{2}

    Annulus((R1,R2),x0,y0,θ=0) -> annulus
    Annulus(R1, R2, x0, y0, θ=0) -> annulus

`R1` is inner radius, `R2` outer
`x0, y0` is center

-------------
    (::Annulus)(x,y) -> is_in_annulus::Bool
"""
struct Annulus <: AbstractShape{2}
    R1::Float64
    R2::Float64
    x0::Float64
    y0::Float64
    θ::Float64
    sinθ::Float64
    cosθ::Float64
    corners::Tuple{}
    models::Array{Opt,1}

    Annulus((R1,R2),args...;kwargs...) = Annulus(R1,R2,args...;kwargs...)
    function Annulus(R1::Number,R2::Number,x0::Number,y0::Number,θ::Number=0)
        @assert R1≤R2 "R1=$R1, R2=$R2 must satisfy R1≤R2"
        a = new(R1,R2,x0,y0,0,0,1)
        models = build_models(a)
        return new(R1,R2,x0,y0,0,sin(θ),cos(θ),(),models)
    end

    (c::Annulus)(x,y) = c.R1+eps(c.R1) < hypot( (x-c.x0),(y-c.y0) ) < c.R2-eps(c.R2)

    function Base.show(io::IO, a::Annulus)
        print(io, "Annulus(R1=$(fmt("2.2f",a.R1)), R2=$(fmt("2.2f",a.R2)), x0=$(fmt("2.2f",a.x0)), y0=$(fmt("2.2f",a.y0)))")
    end
end
function perimeter(t::Number,a::Annulus,side::Int)
    sn,cs = sincos(t)
    R = side==1 ? a.R1 : a.R2
    x = a.x0 + R*cs
    y = a.y0 + R*sn
    ẋ = -R*sn
    ẏ =  R*cs
    return x,y,ẋ,ẏ
end
@recipe function f(cr::Annulus)
    @series Circle(cr.R1,cr.x0,cr.y0)
    @series Circle(cr.R2,cr.x0,cr.y0)
end

"""
    struct Square <: AbstractQuadrilateral <: AbstractShape{4}

    Square((a,),x0,y0,θ=0; reference=:center) -> square
    Square(a,x0,y0,θ=0); reference=:center -> square

`a` length of sides
`x0, y0` is location of `reference`
`θ` angle of `a` side

------------

    (::Square)(x,y) -> is_in_square::Bool
"""
struct Square <: AbstractQuadrilateral
    a::Float64
    x0::Float64
    y0::Float64
    θ::Float64
    sinθ::Float64
    cosθ::Float64
    corners::NTuple{4,NTuple{2,Float64}}
    models::Array{Opt,1}

    Square((a,),args...;kwargs...) = Square(a,args...;kwargs...)
    function Square(a::Number,x0::Number,y0::Number,θ::Number=0; reference::Symbol=:center)
        if reference ∈ [:bottom, :Bottom, :b, :B]
            x0,y0 = (x0,y0) .+ rotate( 0, a/2,cos(θ),sin(θ))
        elseif reference ∈ [:top, :Top, :T, :t]
            x0,y0 = (x0,y0) .+ rotate( 0,-a/2,cos(θ),sin(θ))
        elseif reference ∈ [:left, :Left, :L, :l]
            x0,y0 = (x0,y0) .+ rotate( a/2, 0,cos(θ),sin(θ))
        elseif reference ∈ [:right, :Right, :R, :r]
            x0,y0 = (x0,y0) .+ rotate(-a/2, 0,cos(θ),sin(θ))
        elseif reference ∈ [:topright, :TopRight, :TR, :tr, :ne, :NE]
            x0,y0 = (x0,y0) .+ rotate(-a/2,-a/2,cos(θ),sin(θ))
        elseif reference ∈ [:topleft, :TopLeft, :TL, :tl, :nw, :NW]
            x0,y0 = (x0,y0) .+ rotate( a/2,-a/2,cos(θ),sin(θ))
        elseif reference ∈ [:bottomleft, :BottomLeft, :BL, :bl, :sw, :SW]
            x0,y0 = (x0,y0) .+ rotate( a/2, a/2,cos(θ),sin(θ))
        elseif reference ∈ [:bottomright, :BottomRight, :BR, :br, :se, :SE]
            x0,y0 = (x0,y0) .+ rotate(-a/2, a/2,cos(θ),sin(θ))
        end
        s = new(a,x0,y0,θ,sin(θ),cos(θ))
        c1 = rotate(s.x0+s.a/2,s.y0+s.a/2, s)
        c2 = rotate(s.x0-s.a/2,s.y0+s.a/2, s)
        c3 = rotate(s.x0-s.a/2,s.y0-s.a/2, s)
        c4 = rotate(s.x0+s.a/2,s.y0-s.a/2, s)
        models = build_models(s)
        return new(a,x0,y0,θ,sin(θ),cos(θ),(c1,c2,c3,c4),models)
    end

    function (s::Square)(x,y)
        xurot, yurot = unrotate(x, y, s)
        return -s.a/2+10eps(s.a/2) < xurot-s.x0 < s.a/2-10eps(s.a/2)  &&  -s.a/2+10eps(s.a/2) < yurot-s.y0 < s.a/2-10eps(s.a/2)
    end

    function Base.show(io::IO, square::Square)
        print(io, "Square(a=$(fmt("2.2f",square.a)), x0=$(fmt("2.2f",square.x0)), y0=$(fmt("2.2f",square.y0)), θ=∠", fmt("3.2f",(mod2pi(square.θ))*180/π), "°)")
    end
end
function perimeter(t::Number,s::Square,side::Int)
    if side==1
        x = -s.a/2
        y = -s.a/2 + t*s.a
        ẋ = 0
        ẏ = s.a
    elseif side==2
        x =  s.a/2
        y = -s.a/2 + t*s.a
        ẋ = 0
        ẏ = s.a
    elseif side==3
        x = -s.a/2 + t*s.a
        y = -s.a/2
        ẋ = s.a
        ẏ = 0
    elseif side==4
        x = -s.a/2 + t*s.a
        y =  s.a/2
        ẋ = s.a
        ẏ = 0
    end
    x += s.x0
    y += s.y0
    xrot,yrot = rotate(x,y,s)
    ẋrot,ẏrot = rotate(ẋ,ẏ,s.cosθ,s.sinθ)
    return xrot,yrot,ẋrot,ẏrot
end

"""
    struct Rectangle <: AbstractQuadrilateral <: AbstractShape{4}

    Rectangle((a,b), x0, y0, θ=0; reference=:center) -> rectangle
    Rectangle(a, b, x0, y0, θ=0; reference=:center) -> rectangle

`a, b` length of sides
`x0, y0` is location of `reference`
`θ` angle of `a` side

-----------------------

    (::Rectangle)(x,y) -> is_in_rectangle::Bool
"""
struct Rectangle <: AbstractQuadrilateral
    a::Float64
    b::Float64
    x0::Float64
    y0::Float64
    θ::Float64
    sinθ::Float64
    cosθ::Float64
    corners::NTuple{4,NTuple{2,Float64}}
    models::Array{Opt,1}

    Rectangle((a,b),args...;kwargs...) = Rectangle(a,b,args...;kwargs...)
    function Rectangle(a::Number,b::Number,x0::Number,y0::Number,θ::Number=0; reference::Symbol=:center)
        if reference ∈ [:bottom, :Bottom, :b, :B]
            x0,y0 = (x0,y0) .+ rotate( 0, b/2,cos(θ),sin(θ))
        elseif reference ∈ [:top, :Top, :T, :t]
            x0,y0 = (x0,y0) .+ rotate( 0,-b/2,cos(θ),sin(θ))
        elseif reference ∈ [:left, :Left, :L, :l]
            x0,y0 = (x0,y0) .+ rotate( a/2, 0,cos(θ),sin(θ))
        elseif reference ∈ [:right, :Right, :R, :r]
            x0,y0 = (x0,y0) .+ rotate(-a/2, 0,cos(θ),sin(θ))
        elseif reference ∈ [:topright, :TopRight, :TR, :tr, :ne, :NE]
            x0,y0 = (x0,y0) .+ rotate(-a/2,-b/2,cos(θ),sin(θ))
        elseif reference ∈ [:topleft, :TopLeft, :TL, :tl, :nw, :NW]
            x0,y0 = (x0,y0) .+ rotate( a/2,-b/2,cos(θ),sin(θ))
        elseif reference ∈ [:bottomleft, :BottomLeft, :BL, :bl, :sw, :SW]
            x0,y0 = (x0,y0) .+ rotate( a/2, b/2,cos(θ),sin(θ))
        elseif reference ∈ [:bottomright, :BottomRight, :BR, :br, :se, :SE]
            x0,y0 = (x0,y0) .+ rotate(-a/2, b/2,cos(θ),sin(θ))
        end
        r = new(a,b,x0,y0,θ,sin(θ),cos(θ))
        models = build_models(r)
        corners_x, corners_y = Array{Float64}(undef,4), Array{Float64}(undef,4)
        c1 = (r.x0,r.y0) .+ rotate( r.a/2, r.b/2,r.cosθ,r.sinθ)
        c2 = (r.x0,r.y0) .+ rotate(-r.a/2, r.b/2,r.cosθ,r.sinθ)
        c3 = (r.x0,r.y0) .+ rotate(-r.a/2,-r.b/2,r.cosθ,r.sinθ)
        c4 = (r.x0,r.y0) .+ rotate( r.a/2,-r.b/2,r.cosθ,r.sinθ)
        return new(a,b,x0,y0,θ,sin(θ),cos(θ),(c1,c2,c3,c4),models)
    end

    Rectangle(s::Square) = Rectangle(s.a,s.a,s.x0,s.y0,s.θ)

    function (r::Rectangle)(x,y)
        xurot, yurot = unrotate(x, y, r)
        return -r.a/2+20eps(r.a/2) < xurot-r.x0 < r.a/2-20eps(r.a/2)  &&  -r.b/2+20eps(r.b/2) < yurot-r.y0 < r.b/2-20eps(r.b/2)
    end

    function Base.show(io::IO, rect::Rectangle)
        print(io, "Rectangle(a=$(fmt("2.2f",rect.a)), b=$(fmt("2.2f",rect.b)), x0=$(fmt("2.2f",rect.x0)), y0=$(fmt("2.2f",rect.y0)), θ=∠", fmt("3.2f",(mod2pi(rect.θ))*180/π), "°)")
    end
end
function perimeter(t::Number,s::Rectangle,side::Int)
    if side==1
        x = -s.a/2
        y = -s.b/2 + t*s.b
        ẋ = 0
        ẏ = s.b
    elseif side==2
        x =  s.a/2
        y = -s.b/2 + t*s.b
        ẋ = 0
        ẏ = s.b
    elseif side==3
        x = -s.a/2 + t*s.a
        y = -s.b/2
        ẋ = s.a
        ẏ = 0
    elseif side==4
        x = -s.a/2 + t*s.a
        y =  s.b/2
        ẋ = s.a
        ẏ = 0
    end
    x += s.x0
    y += s.y0
    xrot,yrot = rotate(x,y,s)
    ẋrot,ẏrot = rotate(ẋ,ẏ,s.cosθ,s.sinθ)
    return xrot,yrot,ẋrot,ẏrot
end

# """
#     Parallelogram(a, b, α, x0, y0, θ)
# """
# struct Parallelogram <: AbstractParallelogram
#     a::Float64
#     b::Float64
#     α::Float64
#     x0::Float64
#     y0::Float64
#     θ::Float64
#     sinθ::Float64
#     cosθ::Float64
#     tanα::Float64
#     cosα::Float64
#     models::Array{Opt,1}
#
#     function Parallelogram(a::Number, b::Number, α::Number, x0::Number, y0::Number, θ::Number)
#         new(a,b,α,x0,y0,θ,cos(θ),sin(θ),tan(α),cos(α))
#     end
#
#     function (p::Parallelogram)(x,y)
#         xrot, yrot = rotate(x-p.x0, y-p.y0, p.cosθ, p.sinθ)
#         return p.tanα*(xrot-p.a) < yrot < p.tanα*xrot  &&  0 < yrot < p.cosα*p.b
#     end
#
#     Base.show(io::IO, par::Parallelogram) = print(io, "Parallelogram(a=$(fmt("2.2f",par.a)), b=$(fmt("2.2f",par.b)), α=$(fmt("2.2f",par.α)), x0=$(fmt("2.2f",par.x0)), y0=$(fmt("2.2f",par.y0)), θ=$(fmt("2.2f",par.θ)))")
#
#     @recipe function f(pg::Parallelogram)
#         x = cumsum([0, pg.a, +pg.b*cos(pg.α), -pg.a, -pg.b*cos(pg.α)])
#         y = cumsum([0, 0, +pg.b*sin(pg.α), 0, -pg.b*sin(pg.α)])
#         alpha --> 0
#         seriestype --> :path
#         fillcolor --> SHAPE_COLOR
#         fillrange --> 0
#         fillalpha --> SHAPE_FILL_ALPHA
#         aspect_ratio --> 1
#         legend --> false
#         xrot, yrot = rotate(x,y,pg.cosθ,-pg.sinθ)
#         pg.x0 .+ xrot, pg.y0 .+ yrot
#     end
# end

# plot all quadrilaterals
@recipe function f(q::AbstractQuadrilateral)
    t = LinRange(0,1,201)
    x1,y1 = Array{Float64}(undef,length(t)), Array{Float64}(undef,length(t))
    x2,y2 = Array{Float64}(undef,length(t)), Array{Float64}(undef,length(t))
    x3,y3 = Array{Float64}(undef,length(t)), Array{Float64}(undef,length(t))
    x4, y4 = Array{Float64}(undef,length(t)), Array{Float64}(undef,length(t))
    for i ∈ eachindex(t)
        x1[i],y1[i] = perimeter(t[i],q,1)
        x2[i],y2[i] = perimeter(t[i],q,2)
        x3[i],y3[i] = perimeter(t[i],q,3)
        x4[i],y4[i] = perimeter(t[i],q,4)
    end
    alpha --> 0
    seriestype --> :path
    fillcolor --> SHAPE_COLOR
    fillrange --> q.y0
    fillalpha --> SHAPE_FILL_ALPHA
    aspect_ratio --> 1
    legend --> false
    vcat(x1,x4,reverse(x3)),vcat(y1,y4,reverse(y3))
end

"""
    rotate(x,y,cosθ,sinθ) -> xrot, yrot

rotate by `θ` about origin

----------------
    rotate(x,y,shape) -> xrot, yrot

rotate about `shape`'s center by `shape`'s angle
"""
rotate(x::Real,y::Real,cosθ::Real,sinθ::Real) = iszero(sinθ) ? (x,y) : (cosθ*x-sinθ*y, sinθ*x+cosθ*y)
rotate(x::Real,y::Real,s::AbstractShape) = (s.x0,s.y0) .+ rotate(x-s.x0,y-s.y0,s.cosθ,s.sinθ)
function rotate(x::AbstractArray,y::AbstractArray,cosθ::Real,sinθ::Real)
    xrot = Array{Float64}(undef,size(xyrot))
    yrot = Array{Float64}(undef,size(xyrot))
    rotate!(xrot,yrot,x,y,cosθ,sinθ)
    return xrot, yrot
end
function rotate(x::AbstractArray,y::AbstractArray,s::AbstractShape)
    xrot,yrot = rotate(x.-s.x0,y.-y.s0,s.cosθ,s.sinθ)
    for i ∈ eachindex(xrot)
        xrot[i] += s.x0
        yrot[i] += s.y0
    end
    return xrot,yrot
end
function rotate!(xrot::AbstractArray,yrot::AbstractArray,x::AbstractArray,y::AbstractArray,cosθ::Real,sinθ::Real)
    for i ∈ eachindex(x)
        xrot[i],yrot[i] = rotate(x[i],y[i],cosθ,sinθ)
    end
    return nothing
end

"""
    unrotate(x,y,cosθ,sinθ) -> xrot, yrot

rotate by `-θ` about origin

----------------
    unrotate(x,y,shape) -> xrot, yrot

rotate about `shape`'s center by the negative of `shape`'s angle
"""
unrotate(x,y,cosθ::Real,sinθ::Real) = rotate(x,y,cosθ,-sinθ)
unrotate(x::Real,y::Real,s::AbstractShape) = (s.x0,s.y0) .+ unrotate(x-s.x0,y-s.y0,s.cosθ,s.sinθ)
function unrotate(x::AbstractArray,y::AbstractArray,s::AbstractShape)
    xrot,yrot = unrotate(x.-s.x0,y.-y.s0,s.cosθ,s.sinθ)
    for i ∈ eachindex(xrot)
        xrot[i] += s.x0
        yrot[i] += s.y0
    end
    return xrot,yrot
end

function build_models(s::AbstractShape{N}) where N
    models = Array{Opt}(undef,N)
    for i ∈ eachindex(models)
        models[i] = Opt(NL_NORMAL_ALGORITHM, 1)
        models[i].xtol_rel = NL_NORMAL_XTOL
        models[i].ftol_rel = NL_NORMAL_FTOL
        models[i].maxeval = NL_NORMAL_MAXEVAL
        if typeof(s)<:AbstractQuadrilateral
            models[i].lower_bounds = 0
            models[i].upper_bounds = 1
        end
    end
    return models
end

function model_objective(v::Vector,grad::Vector,X::Number,Y::Number,s::AbstractShape,side::Int)
    x,y,ẋ,ẏ = perimeter(v[1],s,side)
    if length(grad) > 0
        grad[1] = 2(x-X)*ẋ + 2(y-Y)*ẏ
    end
    return (x-X)^2 + (y-Y)^2
end

"""
    normal_distance(shape,x,y) -> normal, tangent, distance, side

Get the `normal` vector from the surface of `shape` to a point `x,y`,
and the associated `tangent` vector and distance from surface `side`.

The returned arguments are sorted in ascending order by `distance`, so that, e.g.,
`normal[1]` is the normal vector associated with the closest side.
"""
function normal_distance(s::TS,x::Number,y::Number) where TS<:AbstractShape
    n = Array{Array{Float64,1}}(undef,length(s.models))
    d = Array{Float64}(undef,length(s.models))
    sides = 1:length(s.models)
    for i ∈ eachindex(s.models)
        model = s.models[i]
        model.min_objective = (v,g)->model_objective(v,g,x,y,s,i)
        minf,mint,ret = optimize(model,[t0_shape(x,y,s)])
        X,Y = perimeter(mint[1],s,i)
        n[i] = [x-X, y-Y]
        n[i] = n[i]/norm(n[i])
        d[i] = sqrt(minf)
    end
    perm = sortperm(d)
    n, d = n[perm], d[perm]
    NORMAL = Array{Array{Float64,1}}(undef,length(s.models))
    TANGENT = Array{Array{Float64,1}}(undef,length(s.models))
    for i ∈ eachindex(NORMAL)
        nx, ny = n[i][1],n[i][2]
        NORMAL[i] = [nx, ny]
        TANGENT[i] = [ny,-nx]
    end
    return NORMAL, TANGENT, d, sides[perm]
end

t0_shape(x::Number,y::Number,s::AbstractQuadrilateral) = .5
t0_shape(x::Number,y::Number,s::AbstractDisk) = atan(y-s.y0,x-s.x0)

end #module


corr_from_chol(w) = w*w'

function chol_from_vec(x, K)
    w = vec_to_lt(x, K)
    for i in 1:K
        if sum(w[i,:].^2) > 1
            w[i,i] = 0.0
        else
            w[i,i] = sqrt(1 - sum(w[i,:].^2))
        end
    end
    LowerTriangular(w)
end

## transform cholesky factor (vector repr) to KxK corr matrix (vector repr)
function chol_vec_to_corr_vec(y, K)
    lt_to_vec(LowerTriangular(corr_from_chol(chol_from_vec(y, K))))
end

function log_jacobian_det_chol_to_corr(w::LowerTriangular)
    K = size(w,1)
    sum((K .- (2:K)).*log.(diag(w)[2:K]))
end

function log_jacobian_det_free_to_corr(y, K)
    w = free_to_chol(y, K)
    log_jacobian_det_free_to_chol(y, K) + log_jacobian_det_chol_to_corr(w)
end

function lt_to_vec(w::LowerTriangular)
    K = size(w)[1]
    x = Vector{eltype(w)}(undef, Int(K*(K-1)/2))
    k = 1
    for i in 2:K
        for j in 1:(i-1)
            x[k] = w[i,j]
            k += 1
        end
    end
    x
end

function vec_to_lt(y, K)
    w = zeros(eltype(y), K, K)
    k = 1
    for i in 2:K
        for j in 1:(i-1)
            w[i,j] = y[k]
            k += 1
        end
    end
    LowerTriangular(w)
end

ut_to_vec(w::UpperTriangular) = lt_to_vec(LowerTriangular(w'))
vec_to_ut(y, K) = UpperTriangular(vec_to_lt(y, K)')

function corr_to_free(w::UpperTriangular)
    K = size(w,1)

    z = UpperTriangular(zeros(eltype(w), K, K))
    z[1,2:end] = w[1,2:end]

    for i in 2:K
        for j in (i+1):K
            ##z[i,j] = w[i,j] / sqrt(prod(1 .- z[1:(i-1),j].^2))
            z[i,j] = w[i,j] / sqrt(1 - sum(w[1:(i-1),j].^2))
        end
    end
    atanh.(z)
end

function free_to_corr(y::UpperTriangular)
    z = tanh.(y)

    K = size(z)[1]
    w = zeros(eltype(y), K, K)

    w[1,1] = 1
    w[1,2:end] = z[1,2:end]

    for i in 2:K
        w[i,i] = sqrt(prod(1 .- z[1:(i-1),i].^2))
        for j in (i+1):K
            w[i,j] = z[i,j]*sqrt(prod(1 .- z[1:(i-1),j].^2))
        end
    end

    UpperTriangular(w) ##w'*w
end

## p_y(y) \propto |J|p_w(w) \propto |J'|p_x(x)
function logpdf_free(y::Vector, K::Int, eta::Float64)
    w = free_to_corr(y, K)
    d = LKJcorrChol(K, eta)
    logpdf(d, w) + log_jacobian_det_free_to_chol(y, K)
end


# apollonian gasket constructed in sketch environment
# please dont` use large subdivision levels if you cant` afford supercomputer
#

using ShapeFactory

app = dispatch("CATIA.Application")
prt = app.ActiveDocument.Part

xy = prt.FindObjectByName("xy plane")

gst = prt.HybridBodies.Add()
gst.Name = "Output"

sk1 = gst.HybridSketches.Add(xy)
f2d = sk1.OpenEdition()
con = sk1.Constraints

function base_circle(rad)
    pt0 = f2d.CreatePoint(0, 0)
    con.AddMonoEltCst.(0, pt0)

    ax0 = f2d.CreateLine(0, 0, 0, rad * 3)
    ax0.StartPoint = pt0
    ax0.Construction = true
    con.AddMonoEltCst(13, ax0)

    c0 = f2d.CreateClosedCircle(0, 0, rad)
    c0.CenterPoint = pt0
    rad = con.AddMonoEltCst.(14, c0)
    rad.Dimension.ValuateFromString("$(rad)mm")

    return c0, ax0
end

function base_triad(cc0, ax0)

    cx = f2d.CreateClosedCircle(0, 0, cc0.Radius - 1)
    cx.CenterPoint = cc0.CenterPoint
    cx.Construction = true

    tri = map((2pi/3):(2pi/3):2pi) do ang
        pt = f2d.CreatePoint(sin(ang) * cx.Radius, cos(ang)*cx.Radius)
        pt.Construction = true
        cc = f2d.CreateClosedCircle(sin(ang), cos(ang), 1)
        cc.CenterPoint = pt
        rad = con.AddMonoEltCst.(14, cc)
        rad.Dimension.ValuateFromString("$(cc0.Radius / 2)mm")
        cc => rad
    end

    tri, rad = first.(tri), last.(tri)

    con.AddBiEltCst.(2, getproperty.(tri, :CenterPoint), cx)

    push!(tri, tri[1])
    for n in 1:3
        con.AddBiEltCst(4, tri[n], tri[n+1])
    end

    con.AddBiEltCst(2, tri[1].CenterPoint, ax0)
    map(x -> x.Deactivate(), rad)
    con.AddBiEltCst.(4, tri[1:end-1], cc0)

    return tri[1:end-1]
end

function circle_tri(a, b, c)
    loop = (a, b, c, a)
    cntr = [0, 0, 0]
    for n in 1:3
        pt    = f2d.CreatePoint(0, 0)
        pc    = con.AddBiEltCst.(2, pt, (loop[n], loop[n+1]))
        cntr += pt.GetCoordinates([0, 0, 0])[2] ./ 3
        map(x -> x.Deactivate(), pc)
    end
    cir = f2d.CreateClosedCircle(cntr[1], cntr[2], 0.01)
    con.AddBiEltCst.(4, cir, sort([a, b, c], by=x -> x.Radius, rev=true))
    return cir
end

function subdivide(a, b, c, stop)
    loop = (a, b, c, a)
    x = circle_tri(a, b, c)
    if stop > 1
        for n in 1:3
            subdivide(loop[n], loop[n+1], x, stop-1)
        end
    end
end

c0, ax = base_circle(10)
a, b, c = base_triad(c0, ax)
subdivide(a, b, c, 2)

loop = (a, b, c, a)
for n in 1:3
    subdivide(loop[n], loop[n+1], c0, 3)
end

sk1.CloseEdition()
prt.Update()


using JuMP, GLPK

const no_strat = vec([1.0])

function strat_vec(l::Int64, i::Int64)
    to_return = zeros(l)
    @inbounds to_return[i] = 1.0
    return to_return
end

minmax(R::Matrix{Float64}, m::Int64) =
    @inbounds mapreduce(x -> maximum(R[:, x]), min, 1:m)
maxmin(R::Matrix{Float64}, n::Int64) =
    @inbounds mapreduce(x -> minimum(R[x, :]), max, 1:n)
findminmax(R::Matrix{Float64}, n::Int64) =
    @inbounds strat_vec(n, argmax(map(x -> minimum(R[x, :]), 1:n)))
findmaxmin(R::Matrix{Float64}, m::Int64) =
    @inbounds strat_vec(m, argmin(map(x -> maximum(R[:, x]), 1:m)))

struct NashResult
    payoff::Float64
    row_strategy::Vector{Float64}
    column_strategy::Vector{Float64}
end

function nash(R::Matrix{Float64})
    n, m = size(R)

    # Check if we have to do linear programming
    n == 1 && return NashResult(
        minimum(R), no_strat, strat_vec(m, argmin(R)[2]))
    m == 1 && return NashResult(
        maximum(R), strat_vec(n, argmax(R)[1]), no_strat)
    minmax(R, m) == maxmin(R, n) && return NashResult(
        minmax(R, m), findminmax(R, n), findmaxmin(R, m))

    # Set up model and payoff
    model = direct_model(GLPK.Optimizer())
    @variable(model, z)
    @objective(model, Max, 1.0 * z)

    # Solve for row player
    @variable(model, x[1:n], lower_bound = 0.0)
    @constraint(model, c1, x' * R .>= z)
    @constraint(model, sum(x) == 1.0)

    optimize!(model)

    return NashResult(JuMP.value(z), JuMP.value.(x), vec(shadow_price.(c1)))
end

function SM(state::DynamicState, static_state::StaticState, depth::Int64;
    allow_nothing::Bool = false, allow_overfarming::Bool = false,
    sim_to_end::Bool = false)
    A, B = get_possible_decisions(state, static_state,
        allow_nothing = allow_nothing, allow_overfarming = allow_overfarming)

    (Base.ctpop_int(A) == 0x00 || Base.ctpop_int(B) == 0x00 || depth == 0) &&
        return sim_to_end ?
            NashResult(sum(battle_scores(state, static_state, 100)
                / 100) - 0.5, no_strat, no_strat) :
            NashResult(battle_score(state, static_state) - 0.5,
                no_strat, no_strat)

    payoffs = zeros(Float64, Base.ctpop_int(A), Base.ctpop_int(B))

    for i = 0x01:Base.ctpop_int(A), j = 0x01:Base.ctpop_int(B)
        turn_output = play_turn(state, static_state, get_decisions(A, B, i, j))
        if turn_output.odds == 1.0
            @inbounds payoffs[i, j] = SM(turn_output.next_state_1,
                static_state, depth - 1,
                allow_nothing = allow_nothing,
                allow_overfarming = allow_overfarming,
                sim_to_end = sim_to_end).payoff
        else
            @inbounds payoffs[i, j] = turn_output.odds *
                SM(turn_output.next_state_1,
                    static_state, depth - 1,
                    allow_nothing = allow_nothing,
                    allow_overfarming = allow_overfarming,
                    sim_to_end = sim_to_end).payoff + (1 - turn_output.odds) *
                SM(turn_output.next_state_2, static_state, depth - 1,
                    allow_nothing = allow_nothing,
                    allow_overfarming = allow_overfarming,
                    sim_to_end = sim_to_end).payoff
        end
    end
    return nash(payoffs)
end

function solve_battle(s::DynamicState, static_s::StaticState, depth::Int64;
    allow_nothing::Bool = false, allow_overfarming::Bool = false,
    sim_to_end::Bool = false)
    value = 0.0
    decision = 0, 0
    strat = Strategy([], [], [], [])
    while true
        A, B = get_possible_decisions(s, static_s,
            allow_nothing = allow_nothing,
            allow_overfarming = allow_overfarming)

        (Base.ctpop_int(A) == 0x00 || Base.ctpop_int(B) == 0x00) &&
            return value, strat
        if Base.ctpop_int(A) == 0x01 && Base.ctpop_int(B) == 0x01
            decision = get_decisions(A, B, 0x01, 0x01)
        else
            nash_result = SM(s, static_s, depth, sim_to_end = sim_to_end)
            value = nash_result.payoff
            d1, d2 = rand(rb_rng), rand(rb_rng)
            decision1, decision2 = UInt8(length(nash_result.row_strategy)),
                UInt8(length(nash_result.column_strategy))
            j = 0.0
            for i = 0x01:(UInt8(length(nash_result.row_strategy)) - 0x01)
                @inbounds j += nash_result.row_strategy[i]
                if d1 < j
                    decision1 = i
                    break
                end
            end
            j = 0.0
            for i = 0x01:(UInt8(length(nash_result.column_strategy)) - 0x01)
                @inbounds j += nash_result.column_strategy[i]
                if d2 < j
                    decision2 = i
                    break
                end
            end
            decision = get_decisions(A, B, decision1, decision2)
        end
        s = select_next_state(play_turn(s, static_s, decision))
        push!(strat.decisions, decision)
        push!(strat.scores, value + 0.5)
        push!(strat.activeMons, get_active(s))
        push!(strat.hps, ((get_hp(s[0x01][0x01]), get_hp(s[0x01][0x02]),
                           get_hp(s[0x01][0x03])), (get_hp(s[0x02][0x01]),
                           get_hp(s[0x02][0x02]), get_hp(s[0x02][0x03]))))
    end
end


@reexport module ModelKit

export @var,
    @unique_var,
    Variable,
    Expression,
    coefficients,
    degree,
    degrees,
    differentiate,
    dense_poly,
    evaluate,
    expand,
    exponents_coefficients,
    expressions,
    horner,
    is_homogeneous,
    multi_degrees,
    parameters,
    nparameters,
    nvariables,
    monomials,
    optimize,
    parameters,
    subs,
    support_coefficients,
    rand_poly,
    to_dict,
    to_number,
    variables,
    System,
    Homotopy,
    TaylorVector,
    variable_groups,
    vectors

import LinearAlgebra
import MultivariatePolynomials:
    MultivariatePolynomials,
    coefficients,
    degree,
    differentiate,
    nvariables,
    monomials,
    subs,
    variables
const MP = MultivariatePolynomials

include("model_kit/symengine.jl")
include("model_kit/symbolic.jl")
include("model_kit/instructions.jl")
include("model_kit/codegen.jl")

end # module


# type Gate{T<:AbstractArray}
#     Wx::T
#     Wh::T
#     b::T
# end

# function lstm_cell(input::Gate, forget::Gate, output::Gate, candidate::Gate, ct, ht)
#     ft = sigmoid(xt * forget.Wx + ht * forget.Wh .+ forget.b)
#     it = sigmoid(xt * input.Wx + ht * input.Wh .+ input.b)
#     ot = sigmoid(xt * output.Wx + ht * output.Wh .+ output.b)
#     candidate_memory = tanh(xt * candidate.Wx + ht * candidate.Wh .+ candidate.b)
#     ct = ft * ct + it * candidate_memory
#     ht = ot * tanh(ct)
#     return (ht, ct, ht)
# end


export qsfitversion, spectral_coverage

qsfitversion() = v"0.1.0"
qsfit_data() = artifact"qsfit_data"

gauss(x, μ, σ) = exp.(-0.5 .* ((x .- μ) ./ σ).^2) ./ sqrt(2pi) ./ σ

function planck(λ, T)
    h = 6.6260755  * 1e-27  # Planck's constant [erg s]
    c = 2.99792458 * 1e10   # Vacuum speed of light [cm / s]
    k = 1.380658   * 1e-16  # Boltzmann constant [erg / K]
    b = 2 * h * c^2. ./ (λ.^5.)
    d = h * c ./ (λ .* k .* T)
    return b ./ (exp.(d) .- 1)
end

function convol(v, _k)
    k = reverse(_k)
    nk = length(k)
    @assert nk < length(v)
    @assert mod(nk, 2) == 1
    r = div(nk, 2)
    out = fill(0., length(v))
    for i in r+1:length(v)-r
        out[i-r:i+r] .+= v[i] .* k
    end
    return out ./ sum(abs.(k))
end

function estimate_fwhm(λ, f)
    i = argmax(f)
    maxv = f[i]
    i = findall(f .>= (maxv/2))
    (i1, i2) = extrema(i)
    fwhm = λ[i2] - λ[i1]
    return fwhm
end

# Wavelength span is assumed to be equal to the initial FWHM plus the
# spectral resolution (in quadrature)
spectral_coverage(spec_λ, resolution, line::Union{SpecLineGauss, SpecLineLorentz, SpecLineVoigt}; kw...) =
    spectral_coverage(spec_λ, resolution, line.center.val, line.center.val * sqrt(line.fwhm.val^2 + resolution^2) / 3.e5; kw...)

function spectral_coverage(spec_λ, resolution, comp::ironuv; kw...)
    rr = extrema(ironuv_read()[1])
    return spectral_coverage(spec_λ, resolution, mean(rr), rr[2]-rr[1]; kw...)
end

function spectral_coverage(spec_λ, resolution, comp::ironopt; kw...)
    rr = extrema(QSFit.ironopt_read(comp.file)[:wavelength])
    return spectral_coverage(spec_λ, resolution, mean(rr), rr[2]-rr[1]; kw...)
end

function spectral_coverage(spec_λ::Vector{Float64}, resolution::Float64,
                           center_λ::Float64, span_λ::Float64; min_steps::Int=5)

    # Identify min/max wavelengths
    λmin = center_λ - span_λ / 2.
    λmax = center_λ + span_λ / 2.

    # Calculate step corresponding to the spectral resolution
    δ = resolution / 3e5 * center_λ

    # How many steps should be considered?
    steps = Int(ceil((λmax - λmin) / δ))
    (steps < min_steps)  &&  (steps = min_steps)
    bin_edges = range(λmin, λmax, length=steps+1)

    # How many intervals are sampled?
    good = 0
    for i in 1:steps
        good += sign(count(bin_edges[i] .<= spec_λ .< bin_edges[i+1]))
    end

    return (λmin, λmax, good / steps)
end