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

using Markdown
using InteractiveUtils

# ╔═╡ 75ae3354-2aaa-11ec-1805-d1efd04acf08
begin
	using LinearAlgebra
	using Plots
	using PlutoUI
	using WAV
	using SpeechFeatures
	using FFTW
	using PaddedViews
end

# ╔═╡ f87589e3-c5d7-41b5-b376-bcf9eec006d1
md"""
# MFCC features explained
*[Lucas Ondel](https://lucasondel.github.io/), October 2021*

In this notebook, we show step-by-step how to compute the Mel-Frequency Cepstral Coefficient (MFCC) features.
"""

# ╔═╡ d9026f53-756d-4862-a258-f9663a9a76a2
md"""
We will use mostly two packages: [WAV.jl](https://github.com/dancasimiro/WAV.jl) to load the WAV data and [SpeechFeatures.jl](https://github.com/lucasondel/SpeechFeatures.jl) which provides utility function to extract the features.
"""

# ╔═╡ 319b69f9-6c9d-4d22-9896-055800cf5de8
TableOfContents()

# ╔═╡ 844d4433-bc74-472b-9723-d4136bf56f0f
md"""
## Loading the data

We will work with a sample from the TIMIT corpus freely available on the LDC website. First, we download it in the directory of this notebook.
"""

# ╔═╡ 86a58676-7f23-4e45-8ffb-0413e00e3237
wavpath = download("https://catalog.ldc.upenn.edu/desc/addenda/LDC93S1.wav",
		 		   joinpath(@__DIR__, "sample.wav"))

# ╔═╡ f6647baa-e24a-4c67-9c1c-ae95cd9239e4
md"""
Now, we read it using the `wavread` function. This function returns the channel matrix of size $S \times C$ and the sampling frequency.
"""

# ╔═╡ 4cd9e50b-6e12-48e0-812d-00af1598b32c
channels, fs = wavread(wavpath; format="double")

# ╔═╡ f2227028-3926-4864-9330-33cacc6349be
md"""
Each channel corresponds to the recording of one microphone. Concretely, if you have a mono recording you will have one channel, if you have stereo recording you will have 2 channels, etc.

Here, we only take the first channel (the data is mono anyway).
"""

# ╔═╡ ab6e2ce4-5941-4441-ae1d-7417a9b2b84e
x = channels[:, 1]

# ╔═╡ 786d833c-4a58-48d3-9e6e-b7869fd02a2e
md"""
Now we can plot the waveform.
"""

# ╔═╡ 8d116895-703f-4fd5-a3a9-aa8925ef7461
plot((1:length(x)) ./ fs, x; xlabel="Time (s)", legend=false)

# ╔═╡ 8daea702-d679-4ef0-96d5-230f597889a6
md"""
## Dithering

To avoid having frequency components with 0 energy, we add a tiny bit of white noise to the signal.
"""

# ╔═╡ db90b23f-d363-432d-a2e2-5772bf1657ba
dithering = 1e-12

# ╔═╡ 0a9c2db4-bd6e-42e5-874f-28f75b5385c5
x .+= randn(length(x)) .* dithering

# ╔═╡ 0a2780df-8fee-4b27-a944-3e0c7f2aa053
md"""
## DC removal

In general, most signals will be centered around zero. However, if this is not the case, this can introduce an undesired bias. To avoid this, we simply remove the Direct Component (DC) as a first step.
"""

# ╔═╡ da662210-d760-4989-b6c3-99c58395514f
x .-= mean(x)

# ╔═╡ 8bbd7a37-c714-4f64-81d0-48a18717336b
md"""
## Getting frames

The MFCC features are based on short-term spectral representation of the signal. Therefore, we need to divide the signal into overlapping frames that will be later transformed to the spectral domain.

The frame extraction has 2 parameters:
  * the frame duration
  * the step, i.e. the time between the beginning of two adjacent frames
"""

# ╔═╡ 70ef4159-f09a-4e2d-a266-c86972a6a611
frameduration = 0.025 # in second

# ╔═╡ ce94d7c3-5814-4805-a5c5-bf6e56c412ff
framestep = 0.01 # in second

# ╔═╡ b457c84c-50aa-43aa-84d6-d38cff22883b
md"""
To get the size of a frame in terms of number of samples, we multiply the frame duration and the sampling frequency.
"""

# ╔═╡ 2623d5d5-ba06-4247-8929-5d98d8c65c89
framesize = Int64(frameduration * fs)

# ╔═╡ 65e7fb61-4fb5-4826-8487-2c613b782773
X = hcat(SpeechFeatures.eachframe(x; srate=fs, frameduration, framestep)...)

# ╔═╡ 045b825e-47ed-462f-912d-3954812651a8
md"""
## Pre-emphasis

The purpose of this step is to increase the dynamic range of the high-frequency components. The pre-emphasis filter is defined as:
```math
y[n] = x[n] - k \cdot x[n-1],
```
where $k \in [0, 1]$. In general, we set $k = 0.97$.
"""

# ╔═╡ de44637f-2f24-4a5a-b1f3-1f5dd90f85af
function preemphasis(x; k = 0.97)
	y = similar(x)
	y[1] = x[1]
	prev = x[1]
	for i in 2:length(x)
		y[i] = x[i] - k*prev
		prev = x[i]
	end
	y
end

# ╔═╡ 8f876646-f9bf-489a-8002-607d38eee4e9
prmX = hcat(map(preemphasis, eachcol(X))...)

# ╔═╡ d49fd625-0415-44b4-94a9-94d2780aa0c3
md"""
## Windowing

Each frame, will be multiplied by a windowing function. Here we compare different type of windows.
"""

# ╔═╡ dfa1065d-29ad-443e-930c-33ae740652d7
# The exponent is used in the Kaldi features extraction.
# The idea is to get a Hamming-like window which goes to 0
# at the edge.
hann = SpeechFeatures.HannWindow(framesize) .^ 0.85

# ╔═╡ b1b54b9e-bca0-4e65-901d-6cf690331e2c
hamming = SpeechFeatures.HammingWindow(framesize)

# ╔═╡ 9fd33feb-e0c2-45fc-80a2-baa9fe9bbcd3
rectangular = SpeechFeatures.RectangularWindow(framesize)

# ╔═╡ 717bf350-954e-4e33-95ef-063c89fe90ae
begin
	plot((1:framesize) ./ fs, hann; linewidth=2, yrange=(0,1), label = "Hann")
	plot!((1:framesize) ./ fs, hamming; linewidth=2, yrange=(0,1), label = "Hamming")
	plot!((1:framesize) ./ fs, rectangular; linewidth=2, yrange=(0,1), label = "Rectangular")
end

# ╔═╡ 19148014-f27e-4821-946e-fb68345a7641
md"""
Change the line below to select the window.
"""

# ╔═╡ f283f94f-993a-4156-b606-8014aae341ca
window = hann

# ╔═╡ 95b9d153-4934-45d8-b9f3-138d93757bfb
md"""
Finally, we multiply (in-place) the window on each frame.
"""

# ╔═╡ f4f33068-88f2-4b23-bb7b-47abc9e34bac
wX = hcat(map(x -> x .* window, eachcol(prmX))...)

# ╔═╡ e2b3d74c-9199-4c03-8405-fb19f171fd05
md"""
## Short-term spectrum

Now, we have compute the Fourier transform for each fame.

For efficiency reason, it is common that we compute the Fast Fourier Transform (FFT) on vector of length being a power of two. For this, we simply take the first power of two larger than the current frame size.
"""

# ╔═╡ d4cb69e2-fd46-4bc6-ae1b-8e041e015f76
fftlen = Int64(2^ceil(log2(size(wX, 1))))

# ╔═╡ d18010a6-6ac5-493f-92fc-590bf6bd6fe3
md"""
We pad the frames with zeros at the end to match the size of the FFT.
"""

# ╔═╡ 3ae4936f-faa9-45ac-90cd-c2a1bc782550
pX = PaddedView(0, wX, (fftlen, size(wX, 2)))[1:end,:]

# ╔═╡ 831accf5-fa88-492c-9a9e-6e7e58a6ce91
md"""
Get the log-magnitude spectrum of each frame.
"""

# ╔═╡ 68ef4c2f-eb3c-458f-96ad-a301754bc469
S = abs.(rfft(pX, 1)[1:end-1,:])

# ╔═╡ 013a0118-8181-461e-bc8f-fb6479787383
heatmap((1:size(S,2)) ./ 100, (fs/2) .* (0:size(S,1)-1) ./ size(S,1), S;
		xlabel="time (s)", ylabel="frequency (Hz)")

# ╔═╡ 6c94c82f-837b-4bcc-8db8-79ad8a0382d4
md"""
## Filter bank

The short-term spectrum we have extracted is useful but it is not very faithful of what humans actually perceive. Actually, our spectral resolution is much lower and it maps non-linearly with the frequency range.

The mel-scale is an approximation of the human frequency-perception. It is given by:
```math
m = 1127 \ln(1 + \frac{f}{700})
```
"""

# ╔═╡ 5de5c24d-5407-4001-96a1-21094719c65f
plot(0.1:0.1:8000, SpeechFeatures.freq2mel.(0.1:0.1:8000);
	 xlabel="Hertz", ylabel="Mel", legend=false)

# ╔═╡ 6398bf0b-295e-4c6d-a9fa-0df8c1bd2807
md"""
We create a set of 26 filters whose centers are equally spaced on the mel-scale.
"""

# ╔═╡ 20c2333c-f368-4077-86ef-794e849adb0a
fbank = SpeechFeatures.FilterBank(26; srate=fs, fftlen, lofreq=20, hifreq=7600)

# ╔═╡ 406bce61-409c-4d3e-8d50-930f4b48387b
plot((fs/2) .* (1:size(fbank,2)) ./ size(fbank,2), fbank';
	 size=(800, 400), legend=false, xlabel="frequency (Hz)")

# ╔═╡ df2ee608-be7f-44d6-bab8-41a67fbe9e48
md"""
Applying the filter bank in the spectral domain amounts to multily the frame matrix with the filter bank matrix.
"""

# ╔═╡ 39b9c15e-8459-45a3-b4de-9bada6203580
fS = fbank * S

# ╔═╡ f19ba4e7-8625-43b9-a989-95e3f7ab1825
heatmap((1:size(fS,2)) ./ 100, 1:size(fS,1), fS;
		xlabel="time (s)")

# ╔═╡ 664a2a9b-d12a-4230-b0bb-c4eb32dbd253
md"""
Furthermore, we get our spectrum in the log-domain to reduce the dynamic range.
"""

# ╔═╡ 07c8b5e6-0f95-49ea-8fb8-1aa24c6bc11c
lS = log.(fS)

# ╔═╡ 82550777-784f-4c97-86e2-1e0bad53f9ae
heatmap((1:size(fS,2)) ./ 100, 1:size(fS,1), lS;
		xlabel="time (s)")

# ╔═╡ b0f01b5a-40a8-4bc1-bab6-ad9ea1daff73
md"""
## DCT

We can decorrelate and reduce the dimension of the the features by applying a Discrete  Cosine Transform (DCT). By doing so, our features are now in the "cepstral" domain.
"""

# ╔═╡ ce9a8367-48d0-4947-8499-50b674d763ea
nceps = 13

# ╔═╡ d2c573f1-58b2-4104-b619-56cfbb522063
C = dct(lS, 1)[1:nceps,:]

# ╔═╡ 057c98a6-9878-4d51-aede-f77603af7e16
heatmap((1:size(C,2)) ./ 100, 1:size(C,1), C;
		xlabel="time (s)")

# ╔═╡ 09528ac5-ffda-4d0a-b7ce-522722593644
md"""
## Liftering

Now we "lifter" (i.e. filtering in the cepstral domain) to even the dynamic ranges across cepstral coefficients.
"""

# ╔═╡ 9c9d7293-68c7-4d66-bea0-1743019bf9dc
function makelifter(N, L)
	t = Vector(1:N)
	1 .+ L/2 * sin.(π * t / L)
end

# ╔═╡ f97d744f-be53-42a4-8800-e83d4440b0e6
lifter = makelifter(size(C,1), 22)

# ╔═╡ 857e1fa9-6997-4c96-90fa-ae0fbb9e8cc2
plot(lifter, legend=false)

# ╔═╡ 83af226d-f60f-461e-8c28-835160d5c270
lC = hcat(map(x -> x .* lifter, eachcol(C))...)

# ╔═╡ b8180e1c-d698-44ec-9372-a7d8f133b3f1
heatmap((1:size(lC,2)) ./ 100, 1:size(lC,1), lC;
		xlabel="time (s)")

# ╔═╡ 0582ef6f-dcd2-42c2-a1bd-8aac011cf166
md"""
## Dynamic features

Finally, we add the first and second derivatives to the signal. The derivatives are
calculated as:
```math
\dot{x} \approx \frac{\sum_{k=1}^K k \cdot (x[n+k] - x[n-k]) }{2\sum_{k=1}^K k^2}
```
"""

# ╔═╡ 09ede491-cb56-4327-b2e0-6e10b3a5483d
Δ = SpeechFeatures.delta(C, 2)

# ╔═╡ 9c0fd83e-9217-4516-a4e6-9566a7e78b31
ΔΔ = SpeechFeatures.delta(Δ, 2)

# ╔═╡ af564d77-dc11-4125-bde3-1f07c4521937
features = vcat(C, Δ, ΔΔ)

# ╔═╡ 00000000-0000-0000-0000-000000000001
PLUTO_PROJECT_TOML_CONTENTS = """
[deps]
FFTW = "7a1cc6ca-52ef-59f5-83cd-3a7055c09341"
LinearAlgebra = "37e2e46d-f89d-539d-b4ee-838fcccc9c8e"
PaddedViews = "5432bcbf-9aad-5242-b902-cca2824c8663"
Plots = "91a5bcdd-55d7-5caf-9e0b-520d859cae80"
PlutoUI = "7f904dfe-b85e-4ff6-b463-dae2292396a8"
SpeechFeatures = "6f3487c4-5ca2-4050-bfeb-2cf56df92307"
WAV = "8149f6b0-98f6-5db9-b78f-408fbbb8ef88"

[compat]
FFTW = "~1.4.5"
PaddedViews = "~0.5.10"
Plots = "~1.22.5"
PlutoUI = "~0.7.16"
SpeechFeatures = "~0.3.0"
WAV = "~1.1.1"
"""

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

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

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

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

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

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

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

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

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

[[ColorSchemes]]
deps = ["ColorTypes", "Colors", "FixedPointNumbers", "Random"]
git-tree-sha1 = "a851fec56cb73cfdf43762999ec72eff5b86882a"
uuid = "35d6a980-a343-548e-a6ea-1d62b119f2f4"
version = "3.15.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 = "31d0151f5716b655421d9d75b7fa74cc4e744df2"
uuid = "34da2185-b29b-5c13-b0c7-acf172513d20"
version = "3.39.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 = "cc70b17275652eb47bc9e5f81635981f13cea5c8"
uuid = "9a962f9c-6df0-11e9-0e5d-c546b8b5ee8a"
version = "1.9.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"

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

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

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

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

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

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

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

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

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

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

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

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

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

[[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 = "d189c6d2004f63fd3c91748c458b09f26de0efaa"
uuid = "28b8d3ca-fb5f-59d9-8090-bfdbd6d07a71"
version = "0.61.0"

[[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 = "cafe0823979a5c9bff86224b3b8de29ea5a44b2e"
uuid = "d2c73de3-f751-5644-a686-071e5b155ba9"
version = "0.61.0+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"

[[Graphite2_jll]]
deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"]
git-tree-sha1 = "344bf40dcab1073aca04aa0df4fb092f920e4011"
uuid = "3b182d85-2403-5c21-9c21-1e1f0cc25472"
version = "1.3.14+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 = "14eece7a3308b4d8be910e265c724a6ba51a9798"
uuid = "cd3eb016-35fb-5094-929b-558a96fad6f3"
version = "0.9.16"

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

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

[[HypertextLiteral]]
git-tree-sha1 = "f6532909bf3d40b308a0f360b6a0e626c0e263a8"
uuid = "ac1192a8-f4b3-4bfe-ba22-af5b92cd3ab2"
version = "0.9.1"

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

[[LogExpFunctions]]
deps = ["ChainRulesCore", "DocStringExtensions", "IrrationalConstants", "LinearAlgebra"]
git-tree-sha1 = "34dc30f868e368f8a17b728a1238f3fcda43931a"
uuid = "2ab3a3ac-af41-5b50-aa03-7779005ae688"
version = "0.3.3"

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

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

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

[[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 = "bf210ce90b6c9eed32d25dbcae1ebc565df2687f"
uuid = "e1d29d7a-bbdc-5cf2-9ac0-f12de2c33e28"
version = "1.0.2"

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

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

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

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

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

[[PaddedViews]]
deps = ["OffsetArrays"]
git-tree-sha1 = "646eed6f6a5d8df6708f15ea7e02a7a2c4fe4800"
uuid = "5432bcbf-9aad-5242-b902-cca2824c8663"
version = "0.5.10"

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

[[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 = "b084324b4af5a438cd63619fd006614b3b20b87b"
uuid = "995b91a9-d308-5afd-9ec6-746e21dbc043"
version = "1.0.15"

[[Plots]]
deps = ["Base64", "Contour", "Dates", "Downloads", "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 = "bc70e31a04f22780b57ad399ff94f9b78a1e7b39"
uuid = "91a5bcdd-55d7-5caf-9e0b-520d859cae80"
version = "1.22.5"

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

[[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 = "44a75aa7a527910ee3d1751d1f0e4148698add9e"
uuid = "3cdcf5f2-1ef4-517c-9805-6587b60abb01"
version = "1.1.2"

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

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

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

[[SpeechFeatures]]
deps = ["FFTW", "LinearAlgebra", "PaddedViews"]
git-tree-sha1 = "2a489b75dfb511e6ddfaaaebec85f5df9a2b496d"
uuid = "6f3487c4-5ca2-4050-bfeb-2cf56df92307"
version = "0.3.0"

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

[[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", "LogExpFunctions", "Missings", "Printf", "Random", "SortingAlgorithms", "SparseArrays", "Statistics", "StatsAPI"]
git-tree-sha1 = "65fb73045d0e9aaa39ea9a29a5e7506d9ef6511f"
uuid = "2913bbd2-ae8a-5f71-8c99-4fb6c76f3a91"
version = "0.33.11"

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

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

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

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

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

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

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

[[WAV]]
deps = ["Base64", "FileIO", "Libdl", "Logging"]
git-tree-sha1 = "1d5dc6568ab6b2846efd10cc4d070bb6be73a6b8"
uuid = "8149f6b0-98f6-5db9-b78f-408fbbb8ef88"
version = "1.1.1"

[[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", "HarfBuzz_jll", "JLLWrappers", "Libdl", "Pkg", "Zlib_jll"]
git-tree-sha1 = "5982a94fcba20f02f42ace44b9894ee2b140fe47"
uuid = "0ac62f75-1d6f-5e53-bd7c-93b484bb37c0"
version = "0.15.1+0"

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

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

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

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

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

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

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

[[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:
# ╟─f87589e3-c5d7-41b5-b376-bcf9eec006d1
# ╟─d9026f53-756d-4862-a258-f9663a9a76a2
# ╠═75ae3354-2aaa-11ec-1805-d1efd04acf08
# ╠═319b69f9-6c9d-4d22-9896-055800cf5de8
# ╟─844d4433-bc74-472b-9723-d4136bf56f0f
# ╠═86a58676-7f23-4e45-8ffb-0413e00e3237
# ╟─f6647baa-e24a-4c67-9c1c-ae95cd9239e4
# ╠═4cd9e50b-6e12-48e0-812d-00af1598b32c
# ╟─f2227028-3926-4864-9330-33cacc6349be
# ╠═ab6e2ce4-5941-4441-ae1d-7417a9b2b84e
# ╟─786d833c-4a58-48d3-9e6e-b7869fd02a2e
# ╠═8d116895-703f-4fd5-a3a9-aa8925ef7461
# ╟─8daea702-d679-4ef0-96d5-230f597889a6
# ╠═db90b23f-d363-432d-a2e2-5772bf1657ba
# ╠═0a9c2db4-bd6e-42e5-874f-28f75b5385c5
# ╟─0a2780df-8fee-4b27-a944-3e0c7f2aa053
# ╠═da662210-d760-4989-b6c3-99c58395514f
# ╟─8bbd7a37-c714-4f64-81d0-48a18717336b
# ╠═70ef4159-f09a-4e2d-a266-c86972a6a611
# ╠═ce94d7c3-5814-4805-a5c5-bf6e56c412ff
# ╟─b457c84c-50aa-43aa-84d6-d38cff22883b
# ╠═2623d5d5-ba06-4247-8929-5d98d8c65c89
# ╠═65e7fb61-4fb5-4826-8487-2c613b782773
# ╟─045b825e-47ed-462f-912d-3954812651a8
# ╠═de44637f-2f24-4a5a-b1f3-1f5dd90f85af
# ╠═8f876646-f9bf-489a-8002-607d38eee4e9
# ╟─d49fd625-0415-44b4-94a9-94d2780aa0c3
# ╟─dfa1065d-29ad-443e-930c-33ae740652d7
# ╠═b1b54b9e-bca0-4e65-901d-6cf690331e2c
# ╠═9fd33feb-e0c2-45fc-80a2-baa9fe9bbcd3
# ╠═717bf350-954e-4e33-95ef-063c89fe90ae
# ╟─19148014-f27e-4821-946e-fb68345a7641
# ╠═f283f94f-993a-4156-b606-8014aae341ca
# ╟─95b9d153-4934-45d8-b9f3-138d93757bfb
# ╠═f4f33068-88f2-4b23-bb7b-47abc9e34bac
# ╟─e2b3d74c-9199-4c03-8405-fb19f171fd05
# ╠═d4cb69e2-fd46-4bc6-ae1b-8e041e015f76
# ╟─d18010a6-6ac5-493f-92fc-590bf6bd6fe3
# ╠═3ae4936f-faa9-45ac-90cd-c2a1bc782550
# ╟─831accf5-fa88-492c-9a9e-6e7e58a6ce91
# ╠═68ef4c2f-eb3c-458f-96ad-a301754bc469
# ╠═013a0118-8181-461e-bc8f-fb6479787383
# ╟─6c94c82f-837b-4bcc-8db8-79ad8a0382d4
# ╠═5de5c24d-5407-4001-96a1-21094719c65f
# ╟─6398bf0b-295e-4c6d-a9fa-0df8c1bd2807
# ╠═20c2333c-f368-4077-86ef-794e849adb0a
# ╠═406bce61-409c-4d3e-8d50-930f4b48387b
# ╟─df2ee608-be7f-44d6-bab8-41a67fbe9e48
# ╠═39b9c15e-8459-45a3-b4de-9bada6203580
# ╠═f19ba4e7-8625-43b9-a989-95e3f7ab1825
# ╟─664a2a9b-d12a-4230-b0bb-c4eb32dbd253
# ╠═07c8b5e6-0f95-49ea-8fb8-1aa24c6bc11c
# ╠═82550777-784f-4c97-86e2-1e0bad53f9ae
# ╟─b0f01b5a-40a8-4bc1-bab6-ad9ea1daff73
# ╠═ce9a8367-48d0-4947-8499-50b674d763ea
# ╠═d2c573f1-58b2-4104-b619-56cfbb522063
# ╠═057c98a6-9878-4d51-aede-f77603af7e16
# ╟─09528ac5-ffda-4d0a-b7ce-522722593644
# ╠═9c9d7293-68c7-4d66-bea0-1743019bf9dc
# ╠═f97d744f-be53-42a4-8800-e83d4440b0e6
# ╠═857e1fa9-6997-4c96-90fa-ae0fbb9e8cc2
# ╠═83af226d-f60f-461e-8c28-835160d5c270
# ╠═b8180e1c-d698-44ec-9372-a7d8f133b3f1
# ╟─0582ef6f-dcd2-42c2-a1bd-8aac011cf166
# ╠═09ede491-cb56-4327-b2e0-6e10b3a5483d
# ╠═9c0fd83e-9217-4516-a4e6-9566a7e78b31
# ╠═af564d77-dc11-4125-bde3-1f07c4521937
# ╟─00000000-0000-0000-0000-000000000001
# ╟─00000000-0000-0000-0000-000000000002

max_stars_repo_path: examples/MFCC features explained.jl

max_stars_repo_name: lucasondel/SpeechFeatures

max_issues_repo_path: examples/MFCC features explained.jl

max_issues_repo_name: BUTSpeechFIT/SpeechFeatures

max_forks_repo_path: examples/MFCC features explained.jl

max_forks_repo_name: BUTSpeechFIT/SpeechFeatures


text:
# Compute Post-Widder coefficients Vk
function _PWcoeffs(N::Integer)
    if isodd(N)
        N += 1
        @warn "N must be even... increment N += 1"
    end
    v = zeros(BigFloat, N)
    aux = zero(eltype(v))
    for k in 1:N
        for j in fld(k + 1, 2):minimum(Int, [k, div(N, 2)])
            aux = big(j)^(div(N, 2)) * factorial(big(2 * j))
            aux /= factorial(big(div(N, 2) - j)) * factorial(big(j)) * factorial(big(j - 1))
            aux /= factorial(big(k - j)) * factorial(big(2 * j - k))
            v[k] += aux
        end
        v[k] *= (-1)^(k + div(N, 2))
    end
    return v
end

# compute the laplace transform using Post-Widder for a single time pt
function postwid(func::Function, t::AbstractFloat; v = _PWcoeffs(18))
    N = length(v)
    a = zero(eltype(v))
    for k in 1:N
        bk = convert(eltype(v), k)
        a += v[k] * func(bk * log(convert(eltype(a), 2)) / t)
    end
    return a * log(convert(eltype(a), 2)) / t
end

# for an array of t using multithreading
function postwid(f::Function, t::AbstractArray; v = _PWcoeffs(18))
    N = length(v)
    a = zeros(eltype(v), length(t))
    Threads.@threads for ind in eachindex(t)
        a[ind] = postwid(f, t[ind], v = v)
    end
    return a
end

# Implement Laplace transfrom along a hyperbola contour

## adaptive contour for each time point (can't precompute f(ω) or sk)

function s(θ, N, t)
    μ = 4.492075287 * N / t
    ϕ = 1.172104229
    return μ + im * μ * sinh(θ + im * ϕ)
end
# derivitive of hyperbola contour
function ds(θ, N, t)
    μ = 4.492075287 * N / t
    ϕ = 1.172104229
    return im * μ * cosh(θ + im * ϕ)
end

# compute the laplace transform along a hyperbola contour fixed time point
function hyperbola(f::Function, t::AbstractFloat; N = 16)
    a =  zero(Complex{eltype(t)})
    N = convert(eltype(t), N)
    h = 1.081792140 / N
    for k in 0:N-1
        sk = s((k + 1/2) * h, N, t)
        dsk = ds((k + 1/2) * h, N, t)
        a += f(sk) * exp(sk * t) * dsk
    end
    return imag(a) * h / pi
end

function hyperbola(f::Function, t::AbstractArray; N = 16)
    out = fill(f(t[1]), length(t))
    Threads.@threads for ind in 2:length(t)
        out[ind] = hyperbola(f, t[ind], N = N)
    end
    return out
end

#### fixed integration path (can precompute)
# get the fixed integration components

# get the fixed integration components
function hyper_coef(N, t::AbstractVector; ϕ = 1.09)
    A = acosh(((π - 2 * ϕ) * t[end] / t[1] + 4 * ϕ - π) / ((4 * ϕ - π) * sin(ϕ)))
    μ = (4 * π * ϕ - π^2) * N / t[end] / A
    h = A / N
    return μ, h
end

# compute the fixed hyperbola contour
s_fixed(θ, μ; ϕ = 1.09) = μ + im * μ * sinh(θ + im * ϕ)
ds_fixed(θ, μ; ϕ = 1.09) = im * μ * cosh(θ + im * ϕ)

# compute the function values over the fixed contour nodes
function fixed_sk(f::Function, N, t::AbstractVector, T)
    μ, h = hyper_coef(N, t)
    a = zeros(Complex{eltype(T)}, N)
    sk = zeros(Complex{eltype(t)}, N)
    Threads.@threads for k in 0:Int(N)-1
        sk[k+1] = s_fixed((k + 1/2) * h, μ)
        dsk = ds_fixed((k + 1/2) * h, μ)
        a[k+1] = f(sk[k + 1]) * dsk
    end
    return a, sk, h
end

function hyper_fixed_points(a, sk, h, t::AbstractFloat)
    b = zero(eltype(a))
    for ind in eachindex(sk)
        b += a[ind] * exp(sk[ind] * t)
    end
    return imag(b) * h / π
end

function hyper_fixed(f::Function, t::AbstractVector; N = 24, T = eltype(t))
    a, sk, h = fixed_sk(f, N, t, T)
    out = zeros(T, length(t))
    Threads.@threads for ind in eachindex(t)
        out[ind] = hyper_fixed_points(a, sk, h, t[ind])
    end
    return out
end

ext: jl

max_issues_repo_name: heltonmc/LightPropagation


text:
using GAP, Hecke, Printf, ArgParse

include(joinpath(Hecke.pkgdir,"examples/FieldEnumeration/FieldEnumeration.jl"))

###############################################################################
#
#  Read-Write
#
###############################################################################

function ArgParse.parse_item(::Type{fmpz}, x::AbstractString)
  if in('^', x)
    l = split(x, '^')
    if length(l) != 2
      throw(error("Could not parse $x as fmpz"))
    end
    l = string.(l)
    return (parse(fmpz, l[1]))^parse(Int, l[2])
  else
    return parse(fmpz, string(x))
  end
end

function parse_commandline()
  s = ArgParseSettings()

  @add_arg_table s begin
    "--order", "-o"
      help = "Order of the group"
      arg_type = Int
      default = -1
    "--id", "-i"
      help = "Id of the group"
      arg_type = Int
      default = -1
    "--number", "-n"
      help = "Number of the group"
      arg_type = Int
      default = -1
    "--tamely-ramified"
      help = "Only tamely ramified fields"
      action = :store_true
    "--only-real"
      help = "Only totally real fields"
      action = :store_true
    "--disc-bound"
      help = "Discriminant bound"
      arg_type = fmpz
      required = true
    "--rootdisc-bound"
      help = "Discriminant bound"
      arg_type = Float64
    "--only-cm"
      help = "Only CM fields"
      action = :store_true
    "--max-ab-subfields"
      help = "File containing maximal abelian subextensions"
    "--simplify"
      help = "Simplify the field"
      action = :store_true
    "--output"
      help = "Output file"
      arg_type = String
      default = ""
    "--output-type"
      help = "Type of output file. Options are default (uses _write_fields), polys, nfdb"
      default = "default"
  end

  return parse_args(s)
end

function main()
  parsed_args = parse_commandline()

  local grp_order::Int
  local grp_id::Int
  local dbound::fmpz
  local only_real::Bool
  local only_tame::Bool
  local grp_no::Int
  local only_cm::Bool
  local maxabsubfields::Union{String, Nothing}
  local simplify::Bool
  local out::String

  for (arg, val) in parsed_args
    println("$arg => $val")
    if arg == "order"
      grp_order = val
    elseif arg == "id"
      grp_id = val
    elseif arg == "disc-bound"
      dbound = val
    elseif arg == "only-real"
      only_real = val
    elseif arg == "tamely-ramified"
      only_tame = val
    elseif arg == "number"
      grp_no = val
    elseif arg == "only-cm"
      only_cm = val
    elseif arg == "max-ab-subfields"
      maxabsubfields = val
    elseif arg == "simplify"
      simplify = val
    elseif arg == "out"
      out = val
    end
  end

  sprint_formatted(fmt, args...) = @eval @sprintf($fmt, $(args...))

  if grp_id != -1
    n = grp_order
    i = grp_id
  end

  if out == ""
    if grp_id != -1
      @assert grp_order != -1
      n = grp_order
      i = grp_id
      grp_no = findfirst(isequal((n, i)), small_solvable_groups)
    else
      @assert grp_no != -1
      n, i = small_solvable_groups[grp_no]
    end
  end

  _n = clog(dbound, fmpz(10))
  if out == ""
    if fmpz(10)^_n == dbound
      file = sprint_formatted("%0$(length(string(length(small_solvable_groups))))d", grp_no) * "-$n-$i-10^$(_n)"
    else
      file = sprint_formatted("%0$(length(string(length(small_solvable_groups))))d", grp_no) * "-$n-$i-$dbound"
    end

    if maxabsubfields isa String
      file = file * "_" * maxabsubfields
    end
    file = file * ".log"
  else
    file = out
  end

  @show grp_order
  @show grp_id
  @show grp_no
  @show dbound
  @show only_real
  @show only_tame
  @show only_cm
  @show file
  @show simplify

  if maxabsubfields isa String
    @show maxabsubfields
  end

  flush(stdout)

  if isfile(file)
    throw(error("File $file does already exist"))
  end

  println("========================================")
  println("Group: $n $i")
  println("========================================")

  println("========================================")
  println("Discriminant bound: $dbound")
  println("========================================")

  flush(stdout)

  set_verbose_level(:FieldsNonFancy, 3)

  if maxabsubfields isa String
    maxabsub = Hecke._read_from_file(maxabsubfields)
    l = fields(n, i, maxabsub, dbound, only_real = only_real, simplify = simplify)
  else
    l = fields(n, i, dbound, only_real = only_real, simplify = simplify)
  end

  flush(stdout)

  # Determine the automorphism groups
  if only_cm
    Hecke.assure_automorphisms.(l)
  end

  flush(stdout)

  ll = map(v -> v.field, l)

  flush(stdout)

  if only_cm
    ffields = [ (x, discriminant(maximal_order(x))) for x in ll if Hecke.iscm_field(x)[1]]
  else
    ffields = [ (x, discriminant(maximal_order(x))) for x in ll ]
  end

  flush(stdout)

  sort!(ffields, lt = (x, y) -> abs(x[2]) <= abs(y[2]))

  @show length(ffields)

  _write_fields(ffields, file)
end

main()


text:
# If number has 3 as a factor, output 'Pling'
# If number has 5 as a factor, output 'Plang'
# 7 as a factor, output 'Plong'

function raindrops(number::Int)
    # Handy for checking we've covered every case:
    # julia> Iterators.product(("3",""),("5",""),("7","")) |> collect
    if number % 3 == 0
        if number % 5 == 0
            if number % 7 == 0
                "PlingPlangPlong"
            else
                "PlingPlang"
            end
        elseif number % 7 == 0
            "PlingPlong"
        else
            "Pling"
        end
    elseif number % 5 == 0
        if number % 7 == 0
            "PlangPlong"
        else
            "Plang"
        end
    elseif number % 7 == 0
        "Plong"
    else
        string(number)
    end
end

# This was much faster to write and much more extensible
# It's about 30x slower if all 3 factors match, 10x slower if two factors match, but the same speed for every other number
using StaticArrays: @SVector

⊙(s::AbstractString,::Nothing) = s
⊙(::Nothing,::Nothing) = nothing
⊙(n::Nothing,s::AbstractString) = s⊙n
⊙(l::AbstractString,r::AbstractString) = l*r

function raindrops_nothing(number::Int)
    fungen(fact,sound) = x -> rem(x,fact) == 0 ? sound : nothing
    funs = @SVector [
        fungen(3,"Pling"),
        fungen(5,"Plang"),
        fungen(7,"Plong"),
    ]
   res = mapreduce(f->f(number),⊙,funs)
   isnothing(res) ? string(number) : res
end


text:
@testset "Testing Sort" begin
    # Need to pass 50 words at a time as records. First 5 words are the key
    # and the last 45 words are the payload.

    # Instantiate a core. For now, only test a single core, so mark it as both
    # the first and the last core.

    # Create three 50 word records.
    makerecord = TestPrograms.makerecord
    records = [makerecord(i, zerofirst = true) for i in 1:3]

    # In all cases, the output result should be the record with the minimujm
    # key.
    #
    # Make sure to strip off the leading 1.
    expected_result = records[1][2:end]

    # We're going to feed these into the test core in all permutations of the
    # three records. In each case, the record with Key 0 0 0 0 1 should come out
    # since it has the lowest key.
    perms = (
        [1, 2, 3],
        [1, 3, 2],
        [2, 1, 3],
        [2, 3, 1],
        [3, 1, 2],
        [3, 2, 1],
    )

    for perm in perms
        core = testfifo_core(TestPrograms.snakesort(true, true, false))
        TestPrograms.initialize_snakesort(core)

        # Create an output to attach to the core.
        output = AsapSim.TestFifo{Int16}()
        core.outputs[1] = output
        # Index the "records" vector according to the permutation chosen.
        core.fifos[1].read_data = vcat(records[perm]...)

        # Run this test until 50 bytes of data has been recieved or until timeout.
        @time runfor(core, 1000)
        @test output.write_data == expected_result
    end
end

@testset "Testing SnakeSort" begin
    # Test a system consisting of
    # input -> sort -> sort -> sort -> sort -> output
    #
    # This will test all of the sort variations.
    Random.seed!(2)

    num_records = 8
    records = [TestPrograms.makerecord(i, zerofirst = true) for i in 1:num_records]

    # Expected results are these records in order - strip the learding zeros.
    expected_results = vcat((r[2:end] for r in records)...)

    # Iterate through several permutations.
    failing_permutations = []

    for _ in 1:50
        # Generate a random permutatation for the records.
        perm = randperm(num_records)

        # Create 4 sort cores.
        #
        # NOTE: These could be put in a vector and a lot of this done
        # automatically. I'm doing it by hand just for clarity sake.
        core_1 = test_core(TestPrograms.snakesort(true, false, false))
        core_2 = test_core(TestPrograms.snakesort(false, false,false))
        core_3 = test_core(TestPrograms.snakesort(false, false, false))
        core_4 = test_core(TestPrograms.snakesort(false, true, false))

        # Initialize all cores.
        TestPrograms.initialize_snakesort(core_1)
        TestPrograms.initialize_snakesort(core_2)
        TestPrograms.initialize_snakesort(core_3)
        TestPrograms.initialize_snakesort(core_4)

        # Connect outputs to inputs.
        core_1.outputs[1] = core_2.fifos[1]
        core_1.outputs[2] = core_2.fifos[2]

        core_2.outputs[1] = core_3.fifos[1]
        core_2.outputs[2] = core_3.fifos[2]

        core_3.outputs[1] = core_4.fifos[1]
        core_3.outputs[2] = core_4.fifos[2]

        # Create a fifo for the output handler
        output_fifo = AsapSim.DualClockFifo(Int16, 32)
        output_handler = AsapSim.OutputHandler{Int16, typeof(output_fifo)}(
            input = output_fifo
        )
        core_4.outputs[1] = output_fifo

        # Create an input handler and connect it to the ibuf of core 1
        input_handler = AsapSim.InputHandler{Int16,typeof(output_fifo)}(
            # End data with -1 to tell first core that we're done.
            data = vcat(records[perm]..., [-1, num_records]),
            output = core_1.fifos[1],
        )

        # Clock the whole system several times.
        for i in 1:2000
            update!(output_handler)
            update!(core_4)
            update!(core_3)
            update!(core_2)
            update!(core_1)
            update!(input_handler)
        end

        passed = output_handler.data == expected_results
        @test passed
        if !passed
            push!(failing_permutations, perm)
        end

        @test length(output_handler.data) == length(expected_results)
    end

    # Print out failing permutations if any were encountered.
    if length(failing_permutations) > 0
        display(failing_permutations)
    end
end


text:
using BayesNegativeBinomial
using Distributions
using LinearAlgebra
using Random
using Test
const BNB = BayesNegativeBinomial

function simulate_sample(rng, N, D)
    X = [ones(N) rand(N, D - 1)]
    β = [-6.0; 10.0 * ones(D - 1)]
    g = [true, false, true]
    mapping = [[1], collect(2:D-1), [D]]
    gexp = zeros(Bool, D)
    for d in 1:length(mapping)
        gexp[mapping[d]] .= g[d]
    end
    p = 1 ./ (1 .+ exp.(X[:, gexp] * β[gexp]))
    y = [rand(rng, NegativeBinomial(2, p[i])) for i in 1:N]
    return p, y, X, mapping
end

begin
    N, D = 2000, 5
    rng = MersenneTwister(1)
    p, y, X, mapping = simulate_sample(rng, N, D)
    println(mean(y))
    sampler = BayesNegativeBinomial.Sampler(y, X; mapping, Σ0β = Matrix(1000.0 * I(D)), update_g = [false, true, false])
    chain = BayesNegativeBinomial.sample(rng, sampler, mcmcsize = 10000, burnin = 5000)
    mean(chain)
end
# @testset "BayesNegativeBinomial.jl" begin
#     N, D = 2000, 4
#     rng = MersenneTwister(1)
#     p, y, X, mapping = simulate_sample(rng, N, D)
#     sampler = BayesNegativeBinomial.Sampler(y, X; mapping)
#     BayesNegativeBinomial.step!(rng, sampler)
#     sampler = BayesNegativeBinomial.Sampler(y, X)
#     chain = BayesNegativeBinomial.sample(rng, sampler)
#     mean(chain)
#     chain_array = hcat(chain...)
#     println(mean(chain_array .== 0.0, dims = 2))
# end


text:
"""
	MultipleRepresentation(extractors::Tuple)

Extractor extracts item to a `ProductNode` where each item is different extractor and item is
extracted by all extractors in multirepresentation.

# Examples

## Example of both categorical and string representation

One of usecases is to use string representation for strings and categorical variable representation for most frequent values.
This allows model to more easily learn frequent or somehow else significant values, which creating meaningful representation
for previously unseen inputs.

```jldoctest
julia> e = MultipleRepresentation((ExtractString(), ExtractCategorical(["tcp", "udp", "dhcp"])));

julia> s1 = e("tcp")
ProductNode with 1 obs
  ├── e1: ArrayNode(2053×1 NGramMatrix with Int64 elements) with 1 obs
  └── e2: ArrayNode(4×1 MaybeHotMatrix with Bool elements) with 1 obs

julia> s1[:e1]
2053×1 Mill.ArrayNode{Mill.NGramMatrix{String,Array{String,1},Int64},Nothing}:
 "tcp"

julia> s1[:e2]
4×1 Mill.ArrayNode{Mill.MaybeHotMatrix{Int64,Array{Int64,1},Int64,Bool},Nothing}:
 0
 1
 0
 0

julia> s2 = e("http")
ProductNode with 1 obs
  ├── e1: ArrayNode(2053×1 NGramMatrix with Int64 elements) with 1 obs
  └── e2: ArrayNode(4×1 MaybeHotMatrix with Bool elements) with 1 obs

julia> s2[:e1]
2053×1 Mill.ArrayNode{Mill.NGramMatrix{String,Array{String,1},Int64},Nothing}:
 "http"

julia> s2[:e2]
4×1 Mill.ArrayNode{Mill.MaybeHotMatrix{Int64,Array{Int64,1},Int64,Bool},Nothing}:
 0
 0
 0
 1
```

## Example of irregular schema representation

The other usecase is to handle irregular schema, where extractor returns `missing` representation if it's unable to
extract it properly. Of course there do not have to be only leaf value extractors, some extractors may be ExtractDict,
while other are extracting leaves etc.

```jldoctest
julia> e = MultipleRepresentation((ExtractString(), ExtractScalar(Float32, 2, 3)));

julia> s1 = e(5)
ProductNode with 1 obs
  ├── e1: ArrayNode(2053×1 NGramMatrix with Missing elements) with 1 obs
  └── e2: ArrayNode(1×1 Array with Float32 elements) with 1 obs

julia> s1[:e1]
2053×1 Mill.ArrayNode{Mill.NGramMatrix{Missing,Array{Missing,1},Missing},Nothing}:
 missing

julia> s1[:e2]
1×1 Mill.ArrayNode{Array{Float32,2},Nothing}:
 9.0

julia> s2 = e("hi")
ProductNode with 1 obs
  ├── e1: ArrayNode(2053×1 NGramMatrix with Int64 elements) with 1 obs
  └── e2: ArrayNode(1×1 Array with Missing elements) with 1 obs

julia> s2[:e1]
2053×1 Mill.ArrayNode{Mill.NGramMatrix{String,Array{String,1},Int64},Nothing}:
 "hi"

julia> s2[:e2]
1×1 Mill.ArrayNode{Array{Missing,2},Nothing}:
 missing

```
"""
struct MultipleRepresentation{E<:Union{NamedTuple, Tuple}}
	extractors::E
end
# todo: add jldoctest example
MultipleRepresentation(vs::Vector) = MultipleRepresentation((;[Symbol("e$(i)") => v for (i,v) in enumerate(vs)]...))
MultipleRepresentation(vs::Tuple) = MultipleRepresentation((;[Symbol("e$(i)") => v for (i,v) in enumerate(vs)]...))
(m::MultipleRepresentation)(x) = ProductNode(map(e -> e(x), m.extractors))
Base.keys(e::MultipleRepresentation) = keys(e.extractors)

Base.hash(e::MultipleRepresentation, h::UInt) = hash(e.extractors, h)
Base.:(==)(e1::MultipleRepresentation, e2::MultipleRepresentation) = e1.extractors == e2.extractors
Base.getindex(e::MultipleRepresentation, i::Int) = e.extractors[i]


text:
mutable struct Drawing
    width::Float64
    height::Float64
    filename::AbstractString
    surface::CairoSurface
    cr::CairoContext
    surfacetype::Symbol
    redvalue::Float64
    greenvalue::Float64
    bluevalue::Float64
    alpha::Float64
    buffer::IOBuffer # Keeping both buffer and data because I think the buffer might get GC'ed otherwise
    bufferdata::Array{UInt8, 1} # Direct access to data

    function Drawing(w, h, stype::Symbol, f::AbstractString="")
        bufdata = UInt8[]
        iobuf = IOBuffer(bufdata, read=true, write=true)
        the_surfacetype = stype
        if stype == :pdf
            the_surface     = Cairo.CairoPDFSurface(iobuf, w, h)
        elseif stype == :png # default to PNG
            the_surface     = Cairo.CairoARGBSurface(w, h)
        elseif stype == :eps
            the_surface     = Cairo.CairoEPSSurface(iobuf, w, h)
        elseif stype == :svg
            the_surface     = Cairo.CairoSVGSurface(iobuf, w, h)
        elseif stype == :image
            the_surface     = Cairo.CairoImageSurface(w, h, Cairo.FORMAT_ARGB32)
        else
            error("Unknown Luxor surface type" \"$stype\"")
        end
        the_cr  = Cairo.CairoContext(the_surface)
        # @info("drawing '$f' ($w w x $h h) created in $(pwd())")
        currentdrawing      = new(w, h, f, the_surface, the_cr, the_surfacetype, 0.0, 0.0, 0.0, 1.0, iobuf, bufdata)
        if isempty(CURRENTDRAWING)
            push!(CURRENTDRAWING, currentdrawing)
        else
            CURRENTDRAWING[1] = currentdrawing
        end
        return currentdrawing
    end
end

const CURRENTDRAWING = Array{Drawing, 1}()

# utility functions that access the internal current Cairo drawing object, which is
# stored as item 1 in a constant global array

function get_current_cr()
    try
        getfield(CURRENTDRAWING[1], :cr)
    catch
        error("There is no current drawing.")
    end
end

get_current_redvalue()    = getfield(CURRENTDRAWING[1], :redvalue)
get_current_greenvalue()  = getfield(CURRENTDRAWING[1], :greenvalue)
get_current_bluevalue()   = getfield(CURRENTDRAWING[1], :bluevalue)
get_current_alpha()       = getfield(CURRENTDRAWING[1], :alpha)

set_current_redvalue(r)   = setfield!(CURRENTDRAWING[1], :redvalue, convert(Float64, r))
set_current_greenvalue(g) = setfield!(CURRENTDRAWING[1], :greenvalue, convert(Float64, g))
set_current_bluevalue(b)  = setfield!(CURRENTDRAWING[1], :bluevalue, convert(Float64, b))
set_current_alpha(a)      = setfield!(CURRENTDRAWING[1], :alpha, convert(Float64, a))

current_filename()        = getfield(CURRENTDRAWING[1], :filename)
current_width()           = getfield(CURRENTDRAWING[1], :width)
current_height()          = getfield(CURRENTDRAWING[1], :height)
current_surface()         = getfield(CURRENTDRAWING[1], :surface)
current_surface_ptr()     = getfield(getfield(CURRENTDRAWING[1], :surface), :ptr)
current_surface_type()    = getfield(CURRENTDRAWING[1], :surfacetype)

current_buffer()          = getfield(CURRENTDRAWING[1], :buffer)
current_bufferdata()      = getfield(CURRENTDRAWING[1], :bufferdata)

"""
    currentdrawing()

Return the current Luxor drawing, if there currently is one.
"""
function currentdrawing()
    if isempty(CURRENTDRAWING) || current_surface_ptr() == C_NULL
        # Already finished or not even started
        @info "There is no current drawing"
        return false
    else
        return CURRENTDRAWING[1]
    end
end

# How Luxor output works. You start by creating a drawing
# either aimed at a file (PDF, EPS, PNG, SVG) or aimed at an
# in-memory buffer (:svg, :png, or :image); you could be
# working in Jupyter or Pluto or Atom, or a terminal, and on
# either Mac, Linux, or Windows.  (The @svg/@png/@pdf macros
# are shortcuts to file-based drawings.) When a drawing is
# finished, you go `finish()` (that's the last line of the
# @... macros.). Then, if you want to actually see it, you
# go `preview()`, which returns the current drawing.

# Then the code has to decide where you're working, and what
# type of file it is, then sends it to the right place,
# depending on the OS.

function Base.show(io::IO, ::MIME"text/plain", d::Drawing)
    @debug "show MIME:text/plain"
    returnvalue = d.filename
    # IJulia and Juno call the `show` function twice: once for
    # the image MIME and a second time for the text/plain MIME.
    # We check if this is such a 'second call':
    if (get(io, :jupyter, false) || Juno.isactive()) && (d.surfacetype == :svg || d.surfacetype == :png)
        return d.filename
    end
    # perhaps drawing hasn't started yet, eg in the REPL
    if !ispath(d.filename)
        location = !isempty(d.filename) ? d.filename : "in memory"
        println(" Luxor drawing: (type = :$(d.surfacetype), width = $(d.width), height = $(d.height), location = $(location))")
    else
        # open the image file
        if Sys.isapple()
            run(`open $(returnvalue)`)
        elseif Sys.iswindows()
            cmd = get(ENV, "COMSPEC", "cmd")
            run(`$(ENV["COMSPEC"]) /c start $(returnvalue)`)
        elseif Sys.isunix()
            run(`xdg-open $(returnvalue)`)
        end
    end
end

"""
    tidysvg(fname)

Read the SVG image in `fname` and write it to a file
`fname-tidy.svg` with modified glyph names.

Return the name of the modified file.

SVG images use named defs for text, which cause errors
problem when used in a notebook.
[See](https://github.com/jupyter/notebook/issues/333) for
example.

A kludgy workround is to rename the elements...
"""
function tidysvg(fname)
    # I pinched this from Simon's RCall.jl
    path, ext = splitext(fname)
    outfile = ""
    if ext == ".svg"
        outfile = "$(path * "-tidy" * ext)"
        open(fname) do f
            r = string(rand(100000:999999))
            d = read(f, String)
            d = replace(d, "id=\"glyph" => "id=\"glyph"*r)
            d = replace(d, "href=\"#glyph" => "href=\"#glyph"*r)
            open(outfile, "w") do out
                write(out, d)
            end
            @info "modified SVG file copied to $(outfile)"
        end
    end
    return outfile
end

# in memory:

Base.showable(::MIME"image/svg+xml", d::Luxor.Drawing) = d.surfacetype == :svg
Base.showable(::MIME"image/png", d::Luxor.Drawing) = d.surfacetype == :png

function Base.show(f::IO, ::MIME"image/svg+xml", d::Luxor.Drawing)
    @debug "show MIME:svg "
    write(f, d.bufferdata)
end

function Base.show(f::IO, ::MIME"image/png", d::Luxor.Drawing)
    @debug "show MIME:png "
    write(f, d.bufferdata)
end

"""
    paper_sizes

The `paper_sizes` Dictionary holds a few paper sizes, width is first, so default is Portrait:

```
"A0"      => (2384, 3370),
"A1"      => (1684, 2384),
"A2"      => (1191, 1684),
"A3"      => (842, 1191),
"A4"      => (595, 842),
"A5"      => (420, 595),
"A6"      => (298, 420),
"A"       => (612, 792),
"Letter"  => (612, 792),
"Legal"   => (612, 1008),
"Ledger"  => (792, 1224),
"B"       => (612, 1008),
"C"       => (1584, 1224),
"D"       => (2448, 1584),
"E"       => (3168, 2448))
```
"""
const paper_sizes = Dict{String, Tuple}(
  "A0"     => (2384, 3370),
  "A1"     => (1684, 2384),
  "A2"     => (1191, 1684),
  "A3"     => (842, 1191),
  "A4"     => (595, 842),
  "A5"     => (420, 595),
  "A6"     => (298, 420),
  "A"      => (612, 792),
  "Letter" => (612, 792),
  "Legal"  => (612, 1008),
  "Ledger" => (792, 1224),
  "B"      => (612, 1008),
  "C"      => (1584, 1224),
  "D"      => (2448, 1584),
  "E"      => (3168, 2448))

"""
Create a new drawing, and optionally specify file type (PNG, PDF, SVG, EPS),
file-based or in-memory, and dimensions.

    Drawing(width=600, height=600, file="luxor-drawing.png")

# Extended help

    Drawing()

creates a drawing, defaulting to PNG format, default filename "luxor-drawing.png",
default size 800 pixels square.

You can specify dimensions, and assume the default output filename:

    Drawing(400, 300)

creates a drawing 400 pixels wide by 300 pixels high, defaulting to PNG format, default
filename "luxor-drawing.png".

    Drawing(400, 300, "my-drawing.pdf")

creates a PDF drawing in the file "my-drawing.pdf", 400 by 300 pixels.

    Drawing(1200, 800, "my-drawing.svg")

creates an SVG drawing in the file "my-drawing.svg", 1200 by 800 pixels.

    Drawing(width, height, surfacetype | filename)

creates a new drawing of the given surface type (e.g. :svg, :png), storing the picture
only in memory if no filename is provided.

    Drawing(1200, 1200/Base.Mathconstants.golden, "my-drawing.eps")

creates an EPS drawing in the file "my-drawing.eps", 1200 wide by 741.8 pixels (= 1200 ÷ ϕ)
high. Only for PNG files must the dimensions be integers.

    Drawing("A4", "my-drawing.pdf")

creates a drawing in ISO A4 size (595 wide by 842 high) in the file "my-drawing.pdf".
Other sizes available are: "A0", "A1", "A2", "A3", "A4", "A5", "A6", "Letter", "Legal",
"A", "B", "C", "D", "E". Append "landscape" to get the landscape version.

    Drawing("A4landscape")

creates the drawing A4 landscape size.

PDF files default to a white background, but PNG defaults to transparent, unless you specify
one using `background()`.

    Drawing(width, height, :image)

creates the drawing in an image buffer in memory. You can obtain the data as a matrix with
`image_as_matrix()`.

"""
function Drawing(w=800.0, h=800.0, f::AbstractString="luxor-drawing.png")
    (path, ext)         = splitext(f)
    currentdrawing = Drawing(w, h, Symbol(ext[2:end]), f)
    if isempty(CURRENTDRAWING)
        push!(CURRENTDRAWING, currentdrawing)
    else
        CURRENTDRAWING[1] = currentdrawing
    end
    return currentdrawing
end

function Drawing(paper_size::AbstractString, f="luxor-drawing.png")
  if occursin("landscape", paper_size)
    psize = replace(paper_size, "landscape" => "")
    h, w = paper_sizes[psize]
  else
    w, h = paper_sizes[paper_size]
  end
  Drawing(w, h, f)
end

"""
    finish()

Finish the drawing, and close the file. You may be able to open it in an
external viewer application with `preview()`.
"""
function finish()
    if current_surface_ptr() == C_NULL
        # Already finished
        return false
    end
    if current_surface_type() == :png
        Cairo.write_to_png(current_surface(), current_buffer())
    end

    Cairo.finish(current_surface())
    Cairo.destroy(current_surface())

    if current_filename() != ""
        write(current_filename(), current_bufferdata())
    end

    return true
end

"""
    preview()

If working in a notebook (eg Jupyter/IJulia), display a PNG or SVG file in the notebook.

If working in Juno, display a PNG or SVG file in the Plot pane.

Drawings of type :image should be converted to a matrix with `image_as_matrix()`
before calling `finish()`.

Otherwise:

- on macOS, open the file in the default application, which is probably the Preview.app for
  PNG and PDF, and Safari for SVG
- on Unix, open the file with `xdg-open`
- on Windows, refer to `COMSPEC`.
"""
function preview()
    @debug "preview()"
    return CURRENTDRAWING[1]
end

# for filenames, the @pdf/png/svg macros may pass either
# a string or an expression (with
# interpolation) which may or may not contain a valid
# extension ... yikes

function _add_ext(fname, ext)
    if isa(fname, Expr)
        # fname is an expression
        if endswith(string(last(fname.args)), string(ext))
            # suffix has been passed
            return fname
        else
            # there was no suffix
            push!(fname.args, string(".", ext))
            return fname
        end
    else
        # fname is a string
        if endswith(fname, string(ext))
            # file had a suffix
            return fname
        else
            # file did not have a suffix
            return string(fname, ".", ext)
        end
    end
end

"""
    @svg drawing-instructions [width] [height] [filename]

Create and preview an SVG drawing, optionally specifying width and height (the
default is 600 by 600). The file is saved in the current working directory as
`filename` if supplied, or `luxor-drawing-(timestamp).svg`.

### Examples

```
@svg circle(O, 20, :fill)

@svg circle(O, 20, :fill) 400

@svg circle(O, 20, :fill) 400 1200

@svg circle(O, 20, :fill) 400 1200 "/tmp/test"

@svg circle(O, 20, :fill) 400 1200 "/tmp/test.svg"

@svg begin
        setline(10)
        sethue("purple")
        circle(O, 20, :fill)
     end

@svg begin
        setline(10)
        sethue("purple")
        circle(O, 20, :fill)
     end 1200 1200
```
"""
macro svg(body, width=600, height=600, fname="luxor-drawing-$(Dates.format(Dates.now(), "HHMMSS_s")).svg")
    quote
        local lfname = _add_ext($(esc(fname)), :svg)
        Drawing($(esc(width)), $(esc(height)), lfname)
        origin()
        background("white")
        sethue("black")
        $(esc(body))
        finish()
        preview()
    end
end

"""
    @png drawing-instructions [width] [height] [filename]

Create and preview an PNG drawing, optionally specifying width and height (the
default is 600 by 600). The file is saved in the current working directory as
`filename`, if supplied, or `luxor-drawing(timestamp).png`.

### Examples

```
@png circle(O, 20, :fill)

@png circle(O, 20, :fill) 400

@png circle(O, 20, :fill) 400 1200

@png circle(O, 20, :fill) 400 1200 "/tmp/round"

@png circle(O, 20, :fill) 400 1200 "/tmp/round.png"

@png begin
        setline(10)
        sethue("purple")
        circle(O, 20, :fill)
     end

@png begin
        setline(10)
        sethue("purple")
        circle(O, 20, :fill)
     end 1200 1200
```
"""
macro png(body, width=600, height=600, fname="luxor-drawing-$(Dates.format(Dates.now(), "HHMMSS_s")).png")
    quote
        local lfname = _add_ext($(esc(fname)), :png)
        Drawing($(esc(width)), $(esc(height)), lfname)
        origin()
        background("white")
        sethue("black")
        $(esc(body))
        finish()
        preview()
    end
end

"""
    @pdf drawing-instructions [width] [height] [filename]

Create and preview an PDF drawing, optionally specifying width and height (the
default is 600 by 600). The file is saved in the current working directory as
`filename` if supplied, or `luxor-drawing(timestamp).pdf`.

### Examples

```
@pdf circle(O, 20, :fill)

@pdf circle(O, 20, :fill) 400

@pdf circle(O, 20, :fill) 400 1200

@pdf circle(O, 20, :fill) 400 1200 "/tmp/A0-version"

@pdf circle(O, 20, :fill) 400 1200 "/tmp/A0-version.pdf"

@pdf begin
        setline(10)
        sethue("purple")
        circle(O, 20, :fill)
     end

@pdf begin
        setline(10)
        sethue("purple")
        circle(O, 20, :fill)
     end 1200 1200
```
"""
macro pdf(body, width=600, height=600, fname="luxor-drawing-$(Dates.format(Dates.now(), "HHMMSS_s")).pdf")
     quote
        local lfname = _add_ext($(esc(fname)), :pdf)
        Drawing($(esc(width)), $(esc(height)), lfname)
        origin()
        background("white")
        sethue("black")
        $(esc(body))
        finish()
        preview()
    end
end

"""
    @eps drawing-instructions [width] [height] [filename]

Create and preview an EPS drawing, optionally specifying width and height (the
default is 600 by 600). The file is saved in the current working directory as
`filename` if supplied, or `luxor-drawing(timestamp).eps`.

On some platforms, EPS files are converted automatically to PDF when previewed.

### Examples

```
@eps circle(O, 20, :fill)

@eps circle(O, 20, :fill) 400

@eps circle(O, 20, :fill) 400 1200

@eps circle(O, 20, :fill) 400 1200 "/tmp/A0-version"

@eps circle(O, 20, :fill) 400 1200 "/tmp/A0-version.eps"

@eps begin
        setline(10)
        sethue("purple")
        circle(O, 20, :fill)
     end

@eps begin
        setline(10)
        sethue("purple")
        circle(O, 20, :fill)
     end 1200 1200
```
"""
macro eps(body, width=600, height=600, fname="luxor-drawing-$(Dates.format(Dates.now(), "HHMMSS_s")).eps")
    quote
       local lfname = _add_ext($(esc(fname)), :eps)
        Drawing($(esc(width)), $(esc(height)), lfname)
        origin()
        background("white")
        sethue("black")
        $(esc(body))
        finish()
        preview()
    end
end

"""
    @draw drawing-instructions [width] [height]

Preview an PNG drawing, optionally specifying width and height (the
default is 600 by 600). The drawing is stored in memory, not in a file on disk.

### Examples

```
@draw circle(O, 20, :fill)

@draw circle(O, 20, :fill) 400

@draw circle(O, 20, :fill) 400 1200

@draw begin
         setline(10)
         sethue("purple")
         circle(O, 20, :fill)
      end

@draw begin
         setline(10)
         sethue("purple")
         circle(O, 20, :fill)
      end 1200 1200
```
"""
macro draw(body, width=600, height=600)
    quote
        Drawing($(esc(width)), $(esc(height)), :png)
        origin()
        background("white")
        sethue("black")
        $(esc(body))
        finish()
        preview()
    end
end

"""
    _argb32_to_rgba(i)

Convert a 32bit ARGB Int to a four value array:

```
_argb32_to_rgba(0xFF800000)

4-element Array{Float64,1}:
 1.0
 0.5019607843137255
 0.0
 0.0
```

"""
function _argb32_to_rgba(k)
    reverse(reinterpret(UInt8, [k]) ./ 0xFF)
end

"""
    unpremultiplyalpha(a)

Given an array of UInt32 values, divide each value by the
alpha value. See alphadivide or reversing premultiplied
alpha values.

Returns an array of arrays, where each array has four
Float64 values.

In a premultiplied image array, a 50% transparent red pixel
is stored as 0x80800000, rather than not 0x80ff0000. This
function reverses the process,  dividing each RGB value by
the alpha value.

The highest two digits of each incoming element is
interpreted as the alpha value.

```
unpremultiplyalpha([0x80800000])
 1-element Array{Array{Float64,1},1}:
 [1.0, 0.0, 0.0, 0.5019607843137255]
```

Notice the arithmetic errors introduced as 0x80 gets
converted to 0.5019.
"""
function unpremultiplyalpha(a)
    af = _argb32_to_rgba.(a)
    for i in eachindex(af)
        af[i] = circshift(af[i], -1)
        α = af[i][4]
        if ! iszero(α) # don't ÷ by 0
            af[i][1] /= α  # red
            af[i][2] /= α  # green
            af[i][3] /= α  # blue
        end
    end
    return af
end

"""
    image_as_matrix()

Return an Array of the current state of the picture as an
array of ARGB32.

A matrix 50 wide and 30 high => a table 30 rows by 50 cols

```
using Luxor, Images

Drawing(50, 50, :png)
origin()
background(randomhue()...)
sethue("white")
fontsize(40)
fontface("Georgia")
text("42", halign=:center, valign=:middle)
mat = image_as_matrix()
finish()
```
"""
function image_as_matrix()
    if length(CURRENTDRAWING) != 1
        error("no current drawing")
    end
    w = Int(current_surface().width)
    h = Int(current_surface().height)
    z = zeros(UInt32, w, h)
    # create a new image surface to receive the data from the current drawing
    # flipxy: see issue https://github.com/Wikunia/Javis.jl/pull/149
    imagesurface = CairoImageSurface(z, Cairo.FORMAT_ARGB32, flipxy=false)
    cr = Cairo.CairoContext(imagesurface)
    Cairo.set_source_surface(cr, current_surface(), 0, 0)
    Cairo.paint(cr)
    data = imagesurface.data
    Cairo.finish(imagesurface)
    Cairo.destroy(imagesurface)
    return reinterpret(ARGB32, permutedims(data, (2, 1)))
end

"""
    @imagematrix(body, width=256, height=256)

Create a drawing and return a matrix of the image.

This macro returns a matrix of pixels that represent the drawing
produced by the vector graphics instructions. It uses the `image_as_matrix()`
function.

The default drawing is 256 by 256 points.

You don't need `finish()` (the macro calls it), and it's not previewed by `preview()`.
```
m = @imagematrix begin
        sethue("red")
        box(O, 20, 20, :fill)
    end 60 60

julia>  m[1220:1224] |> show
    ARGB32[ARGB32(0.0N0f8,0.0N0f8,0.0N0f8,0.0N0f8),
           ARGB32(1.0N0f8,0.0N0f8,0.0N0f8,1.0N0f8),
           ARGB32(1.0N0f8,0.0N0f8,0.0N0f8,1.0N0f8),
           ARGB32(1.0N0f8,0.0N0f8,0.0N0f8,1.0N0f8),
           ARGB32(1.0N0f8,0.0N0f8,0.0N0f8,1.0N0f8)]

```

If, for some strange reason you want to draw the matrix as another
Luxor drawing again, use code such as this:

```
m = @imagematrix begin
        sethue("red")
        box(O, 20, 20, :fill)
        sethue("blue")
        box(O, 10, 40, :fill)
    end 60 60

function convertmatrixtocolors(m)
    return convert.(Colors.RGBA, m)
end

function drawimagematrix(m)
    d = Drawing(500, 500, "/tmp/temp.png")
    origin()
    w, h = size(m)
    t = Tiler(500, 500, w, h)
    mi = convertmatrixtocolors(m)
    @show mi[30, 30]
    for (pos, n) in t
        c = mi[t.currentrow, t.currentcol]
        setcolor(c)
        box(pos, t.tilewidth -1, t.tileheight - 1, :fill)
    end
    finish()
    return d
end

drawimagematrix(m)
```

Transparency

The default value for the cells in an image matrix is
transparent black. (Luxor's default color is opaque black.)

```
julia> @imagematrix begin
       end 2 2
2×2 reinterpret(ARGB32, ::Array{UInt32,2}):
 ARGB32(0.0,0.0,0.0,0.0)  ARGB32(0.0,0.0,0.0,0.0)
 ARGB32(0.0,0.0,0.0,0.0)  ARGB32(0.0,0.0,0.0,0.0)
```

Setting the background to a partially or completely
transparent value may give unexpected results:

```
julia> @imagematrix begin
       background(1, 0.5, 0.0, 0.5) # semi-transparent orange
       end 2 2
2×2 reinterpret(ARGB32, ::Array{UInt32,2}):
 ARGB32(0.502,0.251,0.0,0.502)  ARGB32(0.502,0.251,0.0,0.502)
 ARGB32(0.502,0.251,0.0,0.502)  ARGB32(0.502,0.251,0.0,0.502)
```

here the semi-transparent orange color has been partially
applied to the transparent background.

```
julia> @imagematrix begin
           sethue(1., 0.5, 0.0)
       paint()
       end 2 2
2×2 reinterpret(ARGB32, ::Array{UInt32,2}):
 ARGB32(1.0,0.502,0.0,1.0)  ARGB32(1.0,0.502,0.0,1.0)
 ARGB32(1.0,0.502,0.0,1.0)  ARGB32(1.0,0.502,0.0,1.0)
```

picks up the default alpha of 1.0.
"""
macro imagematrix(body, width=256, height=256)
    quote
        Drawing($width, $height, :image)
        origin()
        $(esc(body))
        m = image_as_matrix()
        finish()
        m
    end
end

"""
    image_as_matrix!(buffer)

Like `image_as_matrix()`, but use an existing UInt32 buffer.

`buffer` is a buffer of UInt32.

```
w = 200
h = 150
buffer = zeros(UInt32, w, h)
Drawing(w, h, :image)
origin()
juliacircles(50)
m = image_as_matrix!(buffer)
finish()
# collect(m)) is Array{ARGB32,2}
Images.RGB.(m)
```
"""
function image_as_matrix!(buffer)
    if length(Luxor.CURRENTDRAWING) != 1
        error("no current drawing")
    end
    # create a new image surface to receive the data from the current drawing
    # flipxy: see issue https://github.com/Wikunia/Javis.jl/pull/149
    imagesurface = Cairo.CairoImageSurface(buffer, Cairo.FORMAT_ARGB32, flipxy=false)
    cr = Cairo.CairoContext(imagesurface)
    Cairo.set_source_surface(cr, Luxor.current_surface(), 0, 0)
    Cairo.paint(cr)
    data = imagesurface.data
    Cairo.finish(imagesurface)
    Cairo.destroy(imagesurface)
    return reinterpret(ARGB32, permutedims(data, (2, 1)))
end

"""
    @imagematrix!(buffer, body, width=256, height=256)

Like `@imagematrix`, but use an existing UInt32 buffer.

```
w = 200
h  = 150
buffer = zeros(UInt32, w, h)
m = @imagematrix! buffer juliacircles(40) 200 150;
Images.RGB.(m)
```
"""
macro imagematrix!(buffer, body, width=256, height=256)
    quote
        Drawing($width, $height, :image)
        origin()
        $(esc(body))
        m = image_as_matrix!($(esc(buffer)))
        finish()
        m
    end
end


text:
struct IMDB{S}
    filepaths::Vector{S}    #paths to all the files of specified category
end

function IMDB(category = "train_pos")
    paths = Dict{String, Vector{String}}()
    folder, subfolder = split(category, "_")
    basepath = joinpath(datadep"IMDB movie reviews dataset", "aclImdb", "$folder", "$subfolder")
    paths = joinpath.(basepath, readdir(basepath))
    IMDB(paths)
end

MultiResolutionIterators.levelname_map(::Type{IMDB}) = [
    :documents => 1,
    :sentences => 2,
    :words => 3, :tokens => 3,
    :characters => 4]

function load(dataset::IMDB)
    Channel(ctype=@NestedVector(String, 2), csize=4) do docs
        for path in dataset.filepaths   #extract data from the files in directory and put into channel
            open(path) do fileio
                cur_text = read(fileio, String)
                sents = [intern.(tokenize(sent)) for sent in split_sentences(cur_text)]
                put!(docs, sents)
            end #open
        end #for
    end #channel
end


text:
# Rayleigh.jl
# Rayleigh test of randomness against a unimodal alternative
#
# For reference see:
# Fisher, N. I., & Lee, A. J. (1983). A correlation coefficient for
#     circular data. Biometrika, 70(2), 327–332. doi:10.2307/2335547
# Fisher, N. I. Statistical Analysis of Circular Data. Cambridge:
#     Cambridge University Press, 1995.
# Jammalamadaka, S. R. & SenGupta, A. Topics in circular statistics
#     vol. 5.  World Scientific, 2001.
#
# Copyright (C) 2012   Simon Kornblith
#
# Permission is hereby granted, free of charge, to any person obtaining
# a copy of this software and associated documentation files (the
# "Software"), to deal in the Software without restriction, including
# without limitation the rights to use, copy, modify, merge, publish,
# distribute, sublicense, and/or sell copies of the Software, and to
# permit persons to whom the Software is furnished to do so, subject to
# the following conditions:
#
# The above copyright notice and this permission notice shall be
# included in all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

export RayleighTest, FisherTLinearAssociation, JammalamadakaCircularCorrelation

## RAYLEIGH TEST OF UNIFORMITY AGAINST AN UNSPECIFIED UNIMODAL ALTERNATIVE

immutable RayleighTest <: HypothesisTest
    Rbar::Float64 # mean resultant length
    n::Int        # number of observations
end
function RayleighTest{S <: Complex}(samples::Vector{S})
    s = @compat Float64(abs(sum(samples./abs(samples))))
    n = length(samples)
    Rbar = s/n
    RayleighTest(Rbar, n)
end
function RayleighTest{S <: Real}(samples::Vector{S})
    s = @compat Float64(abs(sum(exp(im*samples))))
    n = length(samples)
    Rbar = s/n
    RayleighTest(Rbar, n)
end

testname(::RayleighTest) = "Rayleigh test"
population_param_of_interest(x::RayleighTest) = ("Mean resultant length", 0, x.Rbar) # parameter of interest: name, value under h0, point estimate

function show_params(io::IO, x::RayleighTest, ident="")
    println(io, ident, "number of observations: $(x.n)")
    println(io, ident, "test statistic:         $(x.Rbar^2 * x.n)")
end

function pvalue(x::RayleighTest)
    Z = x.Rbar^2 * x.n
    x.n > 1e6 ? exp(-Z) :
        exp(-Z)*(1+(2*Z-Z^2)/(4*x.n)-(24*Z - 132*Z^2 + 76*Z^3 - 9*Z^4)/(288*x.n^2))
end

## N.I. FISHER'S TEST OF T-LINEAR CIRCULAR-CIRCULAR ASSOCIATION

immutable FisherTLinearAssociation{S <: Real, T <: Real} <: HypothesisTest
    rho_t::Float64                              # circular correlation coefficient
    theta::Vector{S}                            # radians of group 1
    phi::Vector{T}                              # radians of group 2
    uniformly_distributed::@compat(Union{Bool,Void}) # is distribution of theta and phi uniform?
end
function FisherTLinearAssociation{Stheta <: Real, Sphi <: Real}(theta::Vector{Stheta},
        phi::Vector{Sphi}, uniformly_distributed::@compat(Union{Bool,Void}))
    check_same_length(theta, phi)

    A = sum(cos(theta).*cos(phi))
    B = sum(sin(theta).*sin(phi))
    C = sum(cos(theta).*sin(phi))
    D = sum(sin(theta).*cos(phi))
    T = A*B-C*D

    # Notation drawn from Fisher, 1993
    n = length(theta)
    E = sum(cos(2*theta))
    F = sum(sin(2*theta))
    G = sum(cos(2*phi))
    H = sum(sin(2*phi))
    rho_t = 4*T/sqrt((n^2 - E^2 - F^2)*(n^2-G^2-H^2))
    FisherTLinearAssociation(rho_t, theta, phi, uniformly_distributed)
end
FisherTLinearAssociation{S <: Real, T <: Real}(theta::Vector{S},
        phi::Vector{T}) = FisherTLinearAssociation(theta, phi, nothing)

testname(::FisherTLinearAssociation) =
    "T-linear test of circular-circular association"
population_param_of_interest(x::FisherTLinearAssociation) = ("Circular correlation coefficient", 0, x.rho_t) # parameter of interest: name, value under h0, point estimate
function show_params(io::IO, x::FisherTLinearAssociation, ident="")
    println(io, ident, "number of observations: [$(length(x.theta)),$(length(x.phi))]")
end

# For large samples, compute the distribution and statistic of T
function tlinear_Z(x::FisherTLinearAssociation)
    n = length(x.theta)
    theta_resultant = sum(exp(im*x.theta))
    phi_resultant = sum(exp(im*x.phi))
    theta_resultant_angle = angle(theta_resultant)
    phi_resultant_angle = angle(phi_resultant)
    alpha_2_theta = mean(cos(2*(x.theta-theta_resultant_angle)))
    beta_2_theta = mean(sin(2*(x.theta-theta_resultant_angle)))
    alpha_2_phi = mean(cos(2*(x.phi-phi_resultant_angle)))
    beta_2_phi = mean(sin(2*(x.phi-phi_resultant_angle)))
    U_theta = (1-alpha_2_theta^2-beta_2_theta^2)/2
    U_phi = (1-alpha_2_phi^2-beta_2_phi^2)/2
    V_theta = (abs2(theta_resultant)/n^2)*(1-alpha_2_theta)
    V_phi = (abs2(phi_resultant)/n^2)*(1-alpha_2_phi)
    sqrt(n)*U_theta*U_phi*rho_t/sqrt(V_theta*V_phi)

    # Alternative computational strategy from Fisher and Lee (1983)
    # a1 = [mean(cos(theta)), mean(cos(phi))]
    # b1 = [mean(sin(theta)), mean(sin(phi))]
    # a2 = [mean(cos(2*(theta))), mean(cos(2*(phi)))]
    # b2 = [mean(sin(2*(theta))), mean(sin(2*(phi)))]

    # mu = (1-a2.^2-b2.^2)/2
    # A = a1.^2 + b1.^2 + a2.*b1.^2 - a1.^2.*a2 - 2a1.*b1.*b2

    # Z = sqrt(n)*prod(mu)*rho_t/sqrt(prod(A))
end

# p-values
function pvalue(x::FisherTLinearAssociation; tail=:both)
    n = length(x.theta)
    if n == 0
        return NaN
    elseif n < 25 || x.uniformly_distributed == nothing
        # If the number of samples is small, or if we don't know whether the
        # distribution is uniform, use a permutation test.

        # "For n < 25, use a randomisation test based on the quantity T = AB - CD"
        ct = cos(x.theta)
        st = sin(x.theta)
        cp = cos(x.phi)
        sp = sin(x.phi)
        T = sum(ct.*cp)*sum(st.*sp)-sum(ct.*sp)*sum(st.*cp)
        greater = 0

        exact = n <= 8
        nperms = exact ? factorial(n) : 100000
        indices = [1:n;]
        for i = 1:nperms
            tp = exact ? nthperm(indices, i) : shuffle!(indices)
            a = 0.0
            b = 0.0
            c = 0.0
            d = 0.0
            for j = 1:n
                a += ct[tp[j]]*cp[j]
                b += st[tp[j]]*sp[j]
                c += ct[tp[j]]*sp[j]
                d += st[tp[j]]*cp[j]
            end
            Tp = a*b-c*d
            greater += tail == :both ? abs(Tp) > T :
                       tail == :left ? Tp < T :
                       tail == :right ? Tp > T :
                       throw(ArgumentError("tail=$(tail) is invalid"))
        end
        greater / nperms
    else
        # Use approximate distribution of test statistic. This only works if we
        # know whether the distributions of theta and phi are uniform. Otherwise,
        # the provided p-values are not conservative.

        # "If either distribution has a mean resultant length 0...the statistic has
        # approximately a double exponential distribution with density 1/2*exp(-abs(x))""
        (dist, stat) = x.uniformly_distributed ? (Laplace(), n*x.rho_t) :
            (Normal(), tlinear_Z(x.rho_t, x.theta, x.phi))
        return pvalue(dist, stat; tail=tail)
    end
end

## JAMMALAMADAKA'S CIRCULAR CORRELATION

immutable JammalamadakaCircularCorrelation <: HypothesisTest
    r::Float64  # circular-circular correlation coefficient
    Z::Float64  # test statistic
end
function JammalamadakaCircularCorrelation{S <: Real, T <: Real}(alpha::Vector{S}, beta::Vector{T})
    check_same_length(alpha, beta)
    # calculate sample mean directions
    alpha_bar = angle(sum(exp(im*alpha)))
    beta_bar = angle(sum(exp(im*beta)))
    r = sum(sin(alpha .- alpha_bar).*sin(beta .- beta_bar))/sqrt(sum(sin(alpha .- alpha_bar).^2)*sum(sin(beta .- beta_bar).^2))

    sin2_alpha = sin(alpha .- alpha_bar).^2
    sin2_beta = sin(beta .- beta_bar).^2
    lambda_20 = mean(sin2_alpha)
    lambda_02 = mean(sin2_beta)
    lambda_22 = mean(sin2_alpha.*sin2_beta)
    Z = sqrt(length(alpha)*lambda_20*lambda_02/lambda_22)*r

    JammalamadakaCircularCorrelation(r, Z)
end

testname(::JammalamadakaCircularCorrelation) = "Jammalamadaka circular correlation"
population_param_of_interest(x::JammalamadakaCircularCorrelation) = ("Circular-circular correlation coefficient", 0, x.r) # parameter of interest: name, value under h0, point estimate
function show_params(io::IO, x::JammalamadakaCircularCorrelation, ident="")
    println(io, ident, "test statistic: $(x.Z)")
end

pvalue(x::JammalamadakaCircularCorrelation; tail=:both) = pvalue(Normal(), x.Z; tail=tail)

## GENERAL

# Complex numbers
for fn in (:JammalamadakaCircularCorrelation, :FisherTLinearAssociation)
    @eval begin
        $(fn){S <: Complex, T <: Complex}(x::Vector{S}, y::Vector{T}) =
            $(fn)(angle(x), angle(y))
    end
end


text:
module BoundaryConditions

export
    BCType, Flux, Gradient, Value, NormalFlow,

    BoundaryCondition, bctype, getbc, setbc!,

    PeriodicBoundaryCondition, NormalFlowBoundaryCondition, NoFluxBoundaryCondition,
    FluxBoundaryCondition, ValueBoundaryCondition, GradientBoundaryCondition,

    CoordinateBoundaryConditions,

    FieldBoundaryConditions, UVelocityBoundaryConditions, VVelocityBoundaryConditions,
    WVelocityBoundaryConditions, TracerBoundaryConditions, PressureBoundaryConditions,

    DiffusivityBoundaryConditions,

    apply_x_bcs!, apply_y_bcs!, apply_z_bcs!,

    fill_halo_regions!

using CUDA
using KernelAbstractions

using Oceananigans.Architectures: device
using Oceananigans.Utils: work_layout, launch!
using Oceananigans.Grids

include("boundary_condition_types.jl")
include("boundary_condition.jl")
include("discrete_boundary_function.jl")
include("continuous_boundary_function.jl")
include("coordinate_boundary_conditions.jl")
include("field_boundary_conditions.jl")
include("show_boundary_conditions.jl")

include("fill_halo_regions.jl")
include("fill_halo_regions_value_gradient.jl")
include("fill_halo_regions_normal_flow.jl")
include("fill_halo_regions_periodic.jl")
include("fill_halo_regions_flux.jl")
include("fill_halo_regions_nothing.jl")

include("apply_flux_bcs.jl")

end


text:
"""GenerateRandom function can give any random Integer between min and max except values in x """

function generate_random(min::Int, max::Int, x...)

   r = rand(min:max)
   if r in Set(x)
      return generate_random(min, max, x...)
   else
      return r
   end
end


text:
lst = readlines("data/unixdict.txt")
filter!(issorted, lst)
filter!(x -> length(x) == maximum(length, lst), lst)
println.(lst)

max_stars_repo_name: ethansaxenian/RosettaDecode

max_issues_repo_name: ethansaxenian/RosettaDecode

max_forks_repo_name: ethansaxenian/RosettaDecode


text:
using DiffOpt
using Test
using OSQP
using Ipopt
using Clp
using Random
using SCS
using LinearAlgebra
using DelimitedFiles
using GLPK

using SparseArrays: spzeros

import MathOptInterface
const MOI = MathOptInterface
const MOIU = MathOptInterface.Utilities
const MOIT = MathOptInterface.Test

import MatrixOptInterface
const MatOI = MatrixOptInterface

const ATOL = 1e-4
const RTOL = 1e-4

@testset "Examples" begin
    include(joinpath(@__DIR__, "../examples/solve-LP.jl"))
    include(joinpath(@__DIR__, "../examples/solve-QP.jl"))
end

@testset "Generate random problems" begin
    include("gen_random_problem.jl")
end

@testset "MOI_wrapper" begin
    include("MOI_wrapper.jl")
end

@testset "JuMP wrapper" begin
    include("jump.jl")
end

@testset "Solver Interface" begin
    include("solver_interface.jl")
end

@testset "Singular error with deleted variables" begin
    include("singular_exception.jl")
end