# 1d climate
using DrWatson
@quickactivate "NonlinearDynamicsTextbook"
include(srcdir("style.jl"))
using DynamicalSystems, PyPlot, Random

using DynamicalSystems, PyPlot, Roots

αtan(T) = 0.5 - 0.2*tanh((T-263)/4)
dTdt(T, ε = 0.65, α=αtan, s= 1.0) = s*(1 - α(T)) - 1.6e-10 * ε * T^4
dTdt(T; ε = 0.65, α=αtan, s = 1.0) = dTdt(T, ε, α, s)

# Ts = 200:400.0
# plot(Ts, dTdt.(Ts))
# plot(Ts, dTdt.(Ts, 0.2))
# plot(Ts, dTdt.(Ts, 0.9))
# axhline(0)

fig = figure(figsize = (figx/2, figy))
Ts = 200:0.5:320.0
arrows = 210:10:300 |> collect
deleteat!(arrows, findfirst(isequal(260), arrows))
roots = Roots.find_zeros(dTdt, Ts[1], Ts[end])
plot(Ts, dTdt.(Ts), color = "C0", label = "\$dT/dt\$")
axhline(0; lw = 2.0, zorder = 1, color = "C2", ls = "--")
xlim(Ts[1], Ts[end])
ylim(-0.2, 0.2)
yticks([-0.1, 0, 0.1])
tight_layout()
for (i, r) in enumerate(roots)
    plot(r, 0, marker = "o", markeredgecolor = "k", markerfacecolor = iseven(i) ? "w" : "k",
    markersize = 15, mew = 2)
end
for r in arrows
    f = dTdt(r)
    x, dx = f > 0 ? (r - 5, 5) : (r+5, -5)
    ff = abs(1.2f)^2 + 0.01
    arrow(x, 0, dx, 0; color = "C1", width = ff, length_includes_head = false,
    head_width = 1.5ff, head_length = 100ff, zorder = 99
    )
end
legend()
xticks(200:40:320)
xlabel("\$T\$"; labelpad = -15)
tight_layout(pad=0.3)
wsave(plotsdir("2", "1dstatespace"), fig)


# Jonas August, 2/2022

#=
This problem was asked by Microsoft.

A number is considered perfect if its digits sum up to exactly 10.

Given a positive integer n, return the n-th perfect number.

For example, given 1, you should return 19. Given 2, you should return 28.
=#

" Returns n-th perfect number. "
function perfect10(n::Int)::Int
    @assert n > 0
    i = 0   # number being checked
    m = 0   # current count of perfect numbers ≤ i CHECK
    while m < n
        i += 1
        # Convert number to digits by converting to string and then converting each digit to Int individually.
        s = 0  # sum of the converted digits
        for digit in string(i)
            s += parse(Int, digit)
        end
        if s == 10
            m += 1
        end
    end
    i
end

using Test
@test perfect10(1) == 19
@test perfect10(2) == 28

for n in 1:100
    @show n, perfect10(n)
end


# ---
# layout: post
# title: "π day"
# date: 2019-03-13 00:00:00 +0000
# categories: blog
# mathjax: true
# ---

# >In the UK we have started to celebrate π day (the 3rd month's 14th day) every year, even though we don't use the USA's date formatting convention of `monthnumber` followed by `daynumber`. But we can't really celebrate the 31st of April (31/4) or the 3rd of Quatember (?) (3/14), so we'll happily celebrate π day on 14/3 along with everyone else!

# >I set myself a challenge at the beginning of March: make a π-related image using Julia and the Luxor.jl package every day until π day. Some days it worked out well, others didn't, but I've gathered them all here anyway. This post has a fair few images, but not very much code or mathematical content.

# The images here are in low-resolution: they should be available on my [Flickr page](https://www.flickr.com/photos/153311384@N03/) at their full resolution if you want to download or re-use them.

# ### Day 1: Circle packing

# Circle packing may be a well-trodden path, but it always looks neat, and it's a nice easy start. You maintain a list of circles (center point and radius). Then you create a random circle, check it against all the other ones, draw it if it doesn't overlap, or reduce the radius and try again. It's not very efficient but you can set it going and go and make some coffee.

# To make the π shape appear, the code creates a path:

#md fontsize(480)
#md textoutlines("π", O, :path, halign=:center, valign=:middle)
#md πoutline = first(pathtopoly())

# then checks whether each circle's centerpoint is inside or outside the outline of the π shape:

#md isinside(pt, πoutline)

# and colors it accordingly.

# ![image label](IMAGEFOLDER/t-800.png)

# ### Day 2: Dry and wet

# I repeated myself today, thinking I could develop the circles a bit more, and ended up with this glossier wet-look version. The apparently random-looking shapes in the background are Bézier splodges that are supposed to be splashes...

# ![image label](IMAGEFOLDER/pi-reds-balls-wet-800.png)

# ### Day 3: π packing

# This is π packing rather than circle packing, although the code is again quite similar in outline: choose a point at random, find the largest font size at which the π character fits without overlapping others in the list, and then place it and add it to the list. The colors are a bit murky though.

# ![image label](IMAGEFOLDER/pi-swarm-3-800.png)

# ### Day 4: Rainbow

# Combining concentric circles and rainbow colors, this image shows about 350 digits of π.

# ![image label](IMAGEFOLDER/digits-of-pi-avenir-800.png)

# To generate the digits of π, I use this function:

function pidigits(n)
    result = BigInt[]
    k, a, b, a1, b1 = big.([2, 4, 1, 12, 4])
    while n > 0
        p, q, k = k^2, 2k + 1, k + 1
        a, b, a1, b1 = a1, b1, p * a  +  q * a1, p * b  +  q * b1
        d, d1 = a ÷ b, a1 ÷ b1
        while d == d1
            push!(result, d)
            n -= 1
            a, a1 = 10(a % b), 10(a1 % b1)
            d, d1 = a ÷ b, a1 ÷ b1
        end
    end
    return result
end

# It looks like witchcraft to me, but I understand that it's a "spigot" algorithm. I was hoping for a while that it was named after a Professor Spigot, but in fact it's describing the way the digits trickle out one by one like drops of water. It's quick enough for a thousand digits or so, but slows down a lot when you ask for 100_000 or more, probably due to the hard work that the big integer library has to do: even when you're just calculating the first 15 digits of π, the values of `a1` and `b1` are way over the limits of Int64s.

#md julia-1.1> @time pidigits(1000);
#md  0.014522 seconds (44.90 k allocations: 9.425 MiB, 28.97% gc time)

# The image might work better on white:

# ![image label](IMAGEFOLDER/digits-of-pi-avenir-on-white-800.png)

# Sometimes I wanted to check where certain sequences of digits appeared. I couldn't find a built-in function that looked for a sequence of digits in an array, but this worked well enough for my purposes:

function findsubsequence(needle, haystack)
    result = Int64[]
    for k in 1:length(haystack) - length(needle)
        if needle == view(haystack, k:k + length(needle) - 1)
            push!(result, k)
        end
    end
    return result
end

findsubsequence(str::String, digits) =
    findsubsequence(map(x -> parse(Int, x), split(str, "")), digits)

findsubsequence("999999", pidigits(2000)) # => [763]

# ### Day 5: Low-fat

# A chunky typeface like the Avenir Heavy I used yesterday is good for masking and clipping. But I wondered how the narrowest typeface would look. I found Briem Akademi, designed by Gunnlaugur Briem at the Royal Academy of Fine Arts in Copenhagen. Adobe's description says:

# >The most compressed version works best where legibility is less important than dramatic visual effect.

# and I like the abstract look even though it's almost illegible... Would this make nice bathroom tiles?

# ![image label](IMAGEFOLDER/many-digits-of-pi-briem-800.png)

# ### Day 6 Breakfast and Tiffany

# I'm still thinking about using typefaces. I'm a fan of Ed Benguiat's ITC Tiffany font, his nostalgic look back from the 1970s to the age of Edwardian elegance.

# ![image label](IMAGEFOLDER/pi-digits-appearing-800.png)

# It's easy to do this with tables. Like the circle packing, the code checks whether the coordinates of each table cell fall within a given perimeter, and changes the font accordingly.

# ### Day 7 Distinguished

# The excellent Colors.jl package has a function called `distinguishable_colors()` (which fortunately tab-completes). The help text says:

# > This uses a greedy brute-force approach to choose `n` colors that are maximally distinguishable. Given `seed` color(s), and a set of possible hue, chroma, and lightness values (in LCHab space), it repeatedly chooses the next color as the one that maximizes the minimum pairwise distance to any of the colors already in the palette.

# Much to do with color depends on the viewer's perception, but I think it works well here. It starts at the top left, and works from left to right. (That pesky decimal point defaults to using the previous color...) You can spot the Feynman point (`999999`) halfway down on the left (look for the six consecutive sandy brown squares), or the four purple sevens on the bottom row.

# ![image label](IMAGEFOLDER/pi-distinguishable_colors-800.png)

# I remembered to try to choose the color for the small labels (probably unreadable in the low-resolution PNG you see here) so that they're either light on dark, or dark on light.

#md ... r, g, b = color of square
#md gamma = 2.2
#md luminance = 0.2126 * r^gamma + 0.7152 * g^gamma + 0.0722 * b^gamma
#md (luminance > 0.5^gamma) ? sethue("black") : sethue("white")

# ### Day 8 Candy crush edition

# ![image label](IMAGEFOLDER/candy-crush.png)

# I must have seen an advert for CandyCrush yesterday, or perhaps all that talk of gamma and LCHab spaces caused a reaction, but this sugar rush of an image was the result. The SVG version looks tasty but is too big for this web page.

# ### Day 9 Like a circle in a spiral, a wheel within a wheel

# Arranging the sweets in a spiral looks tidy.

# ![image label](IMAGEFOLDER/pi-digits-in-spiral-balls-800.png)

# ### Day 10 π into circumference

# Luxor's `polysample()` function takes a polygon and samples it at regular intervals. This allows the following idea, where each point on a shape (here, the outline of the π character) is slowly moved to a matching location on the circular shape around the outside.

# ![image label](IMAGEFOLDER/pi-to-circle-800.png)

# For a point on the π border `p1`, and a matching point on the circumference polygon `p2`, the intermediate point is given by `between(p1, p2, n)`, where `n` is between 0 and 1.

# I like the almost 3D effect you get from this.

# ### Day 11 Charcoal

# Time for a charcoal sketch:

# ![image label](IMAGEFOLDER/pi-charcoal-1-800.png)

# The crinkly edges of the paper are made by the `polysample()` function on a rectangle then applying simplex-`noise()`-y nudges to the vertices. The paper is textured with `rule()`d lines, and there's some very low values for `setopacity()` smudges. Shifting the Bézier curve handles slightly for each iteration gives a brushy/sketchy feel. (It's fortunate I can copy and paste some of this code from drawings I've made before: I've learnt the hard way that it's better keep things than throw them away...)

# ### Day 12

# I ran out of time on this one, and there are still some problems with the text spacing. The idea is to have the infinite digits of π spiral into some fiery star with some space-y stuff. Probably not the sort of image I should be attempting at all with simple vector-based 2D graphics tools, but it feels like a challenge. Those wispy trails are the same as yesterday's brush strokes, but using custom `setdash()` dashing patterns.

# ![image label](IMAGEFOLDER/pi cosmic spiral-800.png)

# ### Day 13

# The idea here is to show which digit of π is the current leader, in terms of how many times that digit has appeared already. (Yes, a stupid idea, I know!) Then I couldn't decide on how many digits to show, so it's going to be an animated GIF showing the first 1000 digits. At the 200 digit mark poor old "7" is struggling at the back of the field, but the glory days are ahead - after 1000 digits, it's overtaken 0, 4, and 6.

# ![image label](IMAGEFOLDER/200-digits-of-pi-800.png)

# The animation turned into a video rather than a GIF, because I don't like the low resolution of GIFs today.

# And now of course I have to add a suitable audio soundtrack. Luckily I've recently been playing with George Datseris' [MIDI interface for Julia](https://github.com/JuliaMusic), so it was easy enough to make a musical version of the first 1000 digits of π, where the digits from 0 to 9 choose the appropriate note from a reasonably harmonious scale.

using MIDI

function savetrack(track, notes)
    file = MIDIFile()
    addnotes!(track, notes)
    addtrackname!(track, "a track")
    push!(file.tracks, track)
    writeMIDIFile("/tmp/sound-of-pi.mid", file)
end

scales = [46, 48, 51, 53, 55, 57, 58, 60, 62, 65, 67]

function generatetune!(notes)
    pos = 1
    dur = 80
    k = 1
    manypidigits = pidigits(1000)
    for i in manypidigits
        dur = k * 960
        pos += k * 960
        n = scales[i + 1]
        note = Note(n, 76, pos, dur)
        push!(notes, note)
    end
end

notes = Notes()
track = MIDITrack()
generatetune!(notes)
savetrack(track, notes)

# ![image label](IMAGEFOLDER/music-credits.png)

# This "sonification" (or "audification") is just for fun. For a more convincing critique of these sonifications than I can provide, watch the always entertaining [Tantacrul](https://www.youtube.com/watch?v=Ocq3NeudsVk)'s presentation on YouTube.

# And while you're on YouTube, the π video is on [my YouTube channel](https://www.youtube.com/channel/UCfd52kTA5JpzOEItSqXLQxg), and it's my entry for YouTube's Most Boring Video of 2019 competition, but I suspect it won't do very well—competition in this category is fierce, even if sometimes the contestants are unwilling participants.

# Happy π day!

# [2019-03-13]

# ![cormullion signing off](http://steampiano.net/cormullionknot.gif?piday){: .center-image}

using Literate                                                                 #src
# preprocess for notebooks                                                     #src
function setimagefolder(content)                                               #src
    content = replace(content, "IMAGEFOLDER" => "$IMAGEFOLDER")                #src
    return content                                                             #src
end                                                                            #src

# for Jupyter notebook, put images in subfolder                                #src
#IMAGEFOLDER = "images/piday"                                                   #src

#Literate.notebook("source/piday.jl", "notebooks", preprocess = setimagefolder) #src

# for Markdown/Jekyll notebook, put images in "/images"                        #src

IMAGEFOLDER = "/images/piday"                                                  #src

Literate.markdown("source/piday.jl", ".", name="_posts/2019-03-13-piday",      #src
 preprocess = setimagefolder,                                                  #src
 codefence = "{% highlight julia %}" => "{% endhighlight julia %}",            #src
 documenter=false)                                                             #src

#src


### A Pluto.jl notebook ###
# v0.17.4

using Markdown
using InteractiveUtils

# ╔═╡ 57ed2d88-970d-4b8d-9f36-7115f50622de
begin
	using LinearAlgebra # for the norm and dot function
	using ForwardDiff # for automatic differentiation
end

# ╔═╡ 3b36fb00-833f-11ec-0d4d-451fa6f0aaee
md"""
# Programming tapas (I)

These are a few, hopefully small, tasks to practice Julia.

- **Gradient decent**
- Array interface
"""

# ╔═╡ 0efbf532-66cb-41fd-98a5-6df89e364b84
md"""## Gradient decent with backtracking line search

Write a function `gradient_decent(f, x0, α, β)` which implements the gradient decent with backtracking line search.

You can follow the notation form this slides (or your own).

The pseudo algorithms are:
"""

# ╔═╡ 67ab9ac3-93e0-4d9a-84aa-131aaf543198
md"""
```julia
Gradient decent:
given a starting point x_0

x = x_0
repeat until stopping criterium
	Compute decent direction Δx = ∇f(x).
 	Line search: Choose a step size t > 0.
    Update: x = x + t * Δx

 	Stopping criterium: ‖ ∇f(x) ‖² < ε
end
```
"""

# ╔═╡ a5e2ee29-8de1-41be-8b05-8427b1a65bce
md"""
```julia
Backtracking line search:
given x, f, Δx, α, β

t = 1
repeat until f(x + t Δx) < f(x) + α t dot( ∇f(x), Δx )
 	Udate: t = β t
end

```

Source: [https://web.stanford.edu/class/ee364a/lectures/unconstrained.pdf](https://web.stanford.edu/class/ee364a/lectures/unconstrained.pdf)
"""

# ╔═╡ 304398ad-f680-4f8c-8a76-5768c0fbfa5e

# ╔═╡ 2c48c94e-7d31-450c-b3c7-0194894c38b7
function linesearch(f, Δx, x, α, β; t_min = 1e-5)
	t = 1
	fx = f(x)
	m = norm(Δx)
	while t > t_min
		if f(x .+ t*Δx) <= fx + α * t * m^2
			return t
		end
		t = t*β
		cond || return t
	end

	return NaN
end

# ╔═╡ ae9e20b2-4f88-4c28-8856-ce61368445a3
function gradientdecent(f, x0, α, β, ε; max_steps = 1e4)
	x = copy(x0)
	i = 0
	∇f = similar(x0)

	@assert β < 1
	@assert α < 0.5

	while i < max_steps
		ForwardDiff.gradient!(∇f, f, x)
		Δx = -∇f
		if norm(∇f)^2 < ε
			return x
		end

		t = linesearch(f, Δx, x, α, β)
		x .+= t * Δx

		i += 1
	end

	return NaN
end

# ╔═╡ e331926f-6e11-434c-a7d3-02d0e9f0f269
begin
	f(x) = exp(x[1]+3x[2]-0.1) + exp(x[1]-3x[2]-0.1) + exp(-x[1]-0.1)
	α = 0.25
	β = 0.5
	ε = 1e-8
	x0 = [0.0, 0.0]

	gradientdecent(f, x0, α, β, ε)
end

# ╔═╡ 7a5cfa1b-9b47-4741-8656-210a3515b6f3

# ╔═╡ 81361eb6-092c-4cd1-96a0-eb9e167dfd3f

# ╔═╡ b04a090b-e9d7-4032-b956-1238fac26f94
md"## Array interpolation"

# ╔═╡ 3413f532-50ec-47f5-9547-4e9474be174e
md"""
We want to take regular vectors, but have an automated way to compute in between values with the syntax
```julia
x = [5, 7, 5]
x[1.5] == 6.0  # linear interpolation between 5 and 7
```

Implement
`Base.getindex(x::Vector{Float64}, p::Float64) = `
accordingly.
"""

# ╔═╡ 641f85f8-368a-45eb-9912-284eca29d83b
x = [5.0, 7.0, 5.0]

# ╔═╡ 9f62d306-25f2-4b8f-a691-7a0fa3f4aa03
# x[1.5]

# ╔═╡ 61eaf1c1-f033-4e36-8171-19ff6621cc82

# ╔═╡ 84c50e3b-b836-477f-81bb-121a2e5d055f
md"
## Merge sort

Implement (any variant) of merge sort.

You can for example try this strategy
```
MergeSort(arr[], l,  r)
If r > l
     1. Find the middle point to divide the array into two halves:
             middle m = l+ (r-l)/2
     2. Call mergeSort for first half:
             Call mergeSort(arr, l, m)
     3. Call mergeSort for second half:
             Call mergeSort(arr, m+1, r)
     4. Merge the two halves sorted in step 2 and 3:
             Call merge(arr, l, m, r)
```
Source: https://www.geeksforgeeks.org/merge-sort/
"

# ╔═╡ 00000000-0000-0000-0000-000000000001
PLUTO_PROJECT_TOML_CONTENTS = """
[deps]
ForwardDiff = "f6369f11-7733-5829-9624-2563aa707210"
LinearAlgebra = "37e2e46d-f89d-539d-b4ee-838fcccc9c8e"

[compat]
ForwardDiff = "~0.10.25"
"""

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

julia_version = "1.7.1"
manifest_format = "2.0"

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

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

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

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

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

[[deps.CommonSubexpressions]]
deps = ["MacroTools", "Test"]
git-tree-sha1 = "7b8a93dba8af7e3b42fecabf646260105ac373f7"
uuid = "bbf7d656-a473-5ed7-a52c-81e309532950"
version = "0.3.0"

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

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

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

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

[[deps.DiffResults]]
deps = ["StaticArrays"]
git-tree-sha1 = "c18e98cba888c6c25d1c3b048e4b3380ca956805"
uuid = "163ba53b-c6d8-5494-b064-1a9d43ac40c5"
version = "1.0.3"

[[deps.DiffRules]]
deps = ["IrrationalConstants", "LogExpFunctions", "NaNMath", "Random", "SpecialFunctions"]
git-tree-sha1 = "84083a5136b6abf426174a58325ffd159dd6d94f"
uuid = "b552c78f-8df3-52c6-915a-8e097449b14b"
version = "1.9.1"

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

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

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

[[deps.ForwardDiff]]
deps = ["CommonSubexpressions", "DiffResults", "DiffRules", "LinearAlgebra", "LogExpFunctions", "NaNMath", "Preferences", "Printf", "Random", "SpecialFunctions", "StaticArrays"]
git-tree-sha1 = "1bd6fc0c344fc0cbee1f42f8d2e7ec8253dda2d2"
uuid = "f6369f11-7733-5829-9624-2563aa707210"
version = "0.10.25"

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

# ╔═╡ Cell order:
# ╟─3b36fb00-833f-11ec-0d4d-451fa6f0aaee
# ╟─0efbf532-66cb-41fd-98a5-6df89e364b84
# ╟─67ab9ac3-93e0-4d9a-84aa-131aaf543198
# ╟─a5e2ee29-8de1-41be-8b05-8427b1a65bce
# ╟─304398ad-f680-4f8c-8a76-5768c0fbfa5e
# ╠═57ed2d88-970d-4b8d-9f36-7115f50622de
# ╠═2c48c94e-7d31-450c-b3c7-0194894c38b7
# ╠═ae9e20b2-4f88-4c28-8856-ce61368445a3
# ╠═e331926f-6e11-434c-a7d3-02d0e9f0f269
# ╟─7a5cfa1b-9b47-4741-8656-210a3515b6f3
# ╟─81361eb6-092c-4cd1-96a0-eb9e167dfd3f
# ╟─b04a090b-e9d7-4032-b956-1238fac26f94
# ╟─3413f532-50ec-47f5-9547-4e9474be174e
# ╠═641f85f8-368a-45eb-9912-284eca29d83b
# ╠═9f62d306-25f2-4b8f-a691-7a0fa3f4aa03
# ╟─61eaf1c1-f033-4e36-8171-19ff6621cc82
# ╟─84c50e3b-b836-477f-81bb-121a2e5d055f
# ╟─00000000-0000-0000-0000-000000000001
# ╟─00000000-0000-0000-0000-000000000002


# f(x) = x^2 - 2x + 2

using LaTeXStrings, Measures
gr()

xf(x) = x^2 - 2x + 2

Plots.plot(xf,
    -3, 3,
    marker=:circle,
    c=:darkorange3,
    linewidth= 2,
    xlabel=L"n",
    ylabel=L"F_n",
    annotation=(2, 10, L"F_n = x^2 - 2x + 2"))


function GenEuropeanBinomialCall(K, T, S, sig, r, N)

        dt = T/N ## Time to Maturity/ Number of Steps
        nu = r - 0.5*sig^2
        dxu = sqrt(sig^2*dt + (nu*dt)^2)
        dxd = -dxu
        pu = 1/2 + 1/2*(nu*dt/dxu)
        pd = 1 - pu

        disc = exp(-r*dt)

        St = zeros(Float64, (N+1, N+1))

        # Initialising asset prices at maturity

        St[1 , N+1] = S / exp(N * dxd)
        for j in 2:(N+1)
                St[j, N+1] = St[j-1, N+1] / exp(dxu - dxd)
        end

        # create Array for options value tree

        C = zeros(Float64, (N+1, N+1))#linspace(1, N+1, N+1)

        # Initialise Option Values at maturity

        for j in 1:(N+1)
                C[j, N+1] = max(0.0, St[j, N+1] - K)
        end

        # Stepping back through the tree

        for i = linrange(N, 1, N)
                for j in 1:i
                      C[j, i] = disc * (pu * C[j, i+1] + pd * C[j+1, i+1])
                      #St[j,i] = St[j,i+1] / d
                      #P[j, i] = max(P[j, i], K - St[j,i])
                end
        end

C
end


# ==============================================================================
# Xavier Gandibleux - November 2021
#   Implemented in Julia 1.6

# ==============================================================================
# Parser of bi-01BKP instances generated by O. Perederieieva and X. Gandibleux
#   All coefficients P, W, and  ω are integer, all variables are binary

# ==============================================================================
# Datastructure of a multi-objective multi-dimensionnal KP with 0/1 variables
struct _MOMKP
    P  :: Matrix{Int} # profit of items for the objectives, k=1..p, j=1..n
    W  :: Matrix{Int} # weight of items for the constraints, i=1..m, j=1..n
    ω  :: Vector{Int} # capacity of knapsacks, i=1..m
end

# ==============================================================================
function readInstanceMOMKPformatPG(verbose::Bool, fname::String)

    f=open(fname)

    n = parse(Int, readline(f) )
    m = 2  # 2 constraints
    p = 2  # 2 objectives

    momkp = _MOMKP(zeros(Int,p,n),zeros(Int,m,n),zeros(Int,m))
    !verbose ? nothing : println("Instance of bi-01BKP : ",n," variables")

    momkp.ω[1] = parse(Int, readline(f) )
    momkp.ω[2] = parse(Int, readline(f) )
    !verbose ? nothing : println("ω[1]=",momkp.ω[1])
    !verbose ? nothing : println("ω[2]=",momkp.ω[2])

    # Extract the data of the MKP from the following lines ---------------------
    for j = 1:n
        momkp.P[1,j],
        momkp.P[2,j],
        momkp.W[1,j],
        momkp.W[2,j] = parse.(Int, split(readline(f)) )

        !verbose ? nothing :
          println("j=",j, "  |  ",
                  "p[1,",j,"]=",momkp.P[1,j], "   ",
                  "p[2,",j,"]=",momkp.P[2,j], "  |  ",
                  "w[1,",j,"]=",momkp.W[1,j], "   ",
                  "w[2,",j,"]=",momkp.W[2,j]
                 )
    end
    close(f)

    return momkp
end

# ==============================================================================

# Example on how to use it :
# fname="set1/ZL28.DAT"; verbose = true
# instancePG = readInstanceMOMKPformatPG(verbose,fname)


#=

  Assignment 1:
  https://edu.swi-prolog.org/mod/assign/view.php?id=242
  """
  http://cplint.eu/p/urns_and_balls.swinb

  Urns and balls

  Suppose you have two urns: urn1 contains 40 blue balls and 20 red balls and urn2 contains 25
  blue balls and 30 red balls.

  You throw an unbiased coin and, if it turns out head, you draw a ball from the first urn,
  it it turns out tails you draw a ball from the second urn.

  Write a program modeling this process and a query for answering the question
  "What is the probability of drawing a blue ball?"

  Write the program and the query in the cells below. Then save the notebook and submit the url.

  """
  The exact answer of drawing a blue call is
     0.5*40/60 + 0.5*25/55 = 0.56060606060606060606

  Summary Statistics
  parameters      mean       std   naive_se      mcse          ess      rhat   ess_per_sec
      Symbol   Float64   Float64    Float64   Float64      Float64   Float64       Float64

        coin    1.5019    0.5000     0.0025    0.0025   40549.0446    1.0000    28840.0032
        draw    1.4389    0.4963     0.0025    0.0023   40570.3731    1.0000    28855.1729

  Distributions of variable draw
  blue       =>   22442  (0.561050)
  red        =>   17558  (0.438950)

  Distributions of variable coin
  head       =>   20078  (0.501950)
  tail       =>   19922  (0.498050)

  Cf ~/cplint/course_urns_and_balls.pl
     ~/blog/urns_and_balls.blog
     ~/psi/urns_and_balls.blog
     ~/webppl/urns_and_balls.wppl

=#

using Turing, StatsPlots, DataFrames
using ReverseDiff, Zygote, Tracker
# Turing.setadbackend(:reversediff)
# Turing.setadbackend(:zygote)
# Turing.setadbackend(:tracker)
include("jl_utils.jl")

@model function urns_and_balls()
    tail = 1
    head = 2
    coin ~ Categorical(simplex([0.5,0.5]))

    blue = 1
    red = 2
    draw ~ coin == head ? Categorical(simplex([40,20])) : Categorical(simplex([25,30]))
end

model = urns_and_balls()

num_chns = 4
# chns = sample(model, Prior(), MCMCThreads(), 1000, num_chns)

# chns = sample(model, MH(), 40_000)
# chns = sample(model, MH(), MCMCThreads(), 40_000, num_chns)

# chns = sample(model, PG(20), MCMCThreads(), 10_000, num_chns)
# chns = sample(model, PG(20), 40_000)
# chns = sample(model, IS(), MCMCThreads(), 10_000, num_chns)
chns = sample(model, IS(), 40_000)

display(chns)

show_var_dist_pct(chns,:draw,["blue","red"])
show_var_dist_pct(chns,:coin,["tail","head"])


function compute(n::Int)::Int
    for c ∈ n ÷ 3 + 1:n ÷ 2
        s = c * c - n * n + 2n * c
        if s > 0
            t = trunc(Int, √s)
            if t * t == s
                b = (n - c + t) / 2
                a = n - c - b
                return a * b * c
            end
        end
    end
end


using LinearAlgebra
using LDLFactorizations
using SparseArrays
using BenchmarkTools

n = 3
A = zeros(3,3)
A[1,1] = 9.0

B = 9.0*Matrix(I,n,n)

@benchmark cholesky($B)

function get_svd_sqrt(A::AbstractMatrix)
    x = svd(A)
    return Diagonal(sqrt.(x.S))*x.V'
end

function get_eig_sqrt(A::AbstractMatrix)
    x = eigen(A)
    return Diagonal(sqrt.(x.values))*x.vectors'
end

@benchmark get_svd_sqrt($B)
@benchmark get_eig_sqrt($B)
@benchmark ldlt(sparse($B); shift = 0.0, check = true, perm=Int[])

cholesky(B).U
C = SparseMatrixCSC(B)
D = sparse(B)
E = sparse(A)

F = ldlt(sparse(B); shift = 0.0, check = true, perm=Int[])

Array(sparse(cholesky(C)))

:


using Test

include("../src/RSA.jl")

using .RSA

@testset "Secondary functions" begin

    @test RSA.exp!(2, 47) == 199807
    @test RSA.exp!(3, 51, 678) == 93

    @test RSA.inverse(2, 5) == 3
    @test RSA.inverse(17, 39) == 23
    @test_throws ErrorException RSA.inverse(2, 4)

    @test RSA.seive(1) == []
    @test RSA.seive(13) == [2, 3, 5, 7, 11, 13]

    @test RSA.phi!(10) == 4
    @test RSA.phi!(7) == 6
    @test RSA.phi!(33500000) == 13200000

    @test RSA.get_random_int(3) <= 7
    @test RSA.get_random_int(8) <= 255

    @test RSA.jacobi!(1001, 9907) == -1
    @test RSA.jacobi!(19, 45) == 1
    @test RSA.jacobi!(8,21) == -1
    @test RSA.jacobi!(3, 15) == 0
    @test_throws ErrorException RSA.jacobi!(13, 28)
    @test RSA.jacobi!(28, 13) == -1
end

@testset "Primality functions" begin

    @test RSA.miller_rabin(561) == false
    @test RSA.miller_rabin(29) == true
    @test RSA.miller_rabin(221) == false

    @test RSA.solovay_strassen(561) == false
    @test RSA.solovay_strassen(29) == true
    @test RSA.solovay_strassen(221) == false

    @test RSA.fermat_test(5) == true
    @test RSA.fermat_test(4) == false
    @test RSA.fermat_test(341) == false
end

@testset "Processing" begin

    a =""
    @test RSA.recover_string!(RSA.process_string!(a)) == a
    a ="123"
    @test RSA.recover_string!(RSA.process_string!(a)) == a
    a = "1234567890"
    @test RSA.recover_string!(RSA.process_string!(a)) == a
end

@testset "Encryption" begin
    B = RSASystem()
    a = "ABCDEFG"
    @test decrypt!(B, encrypt!(a, B.public_key)) == a
    a = "AB"
    @test decrypt!(B, encrypt!(a, B.public_key)) == a
    a = "ABABABABABABABABABABABABABABABAB123"
    @test decrypt!(B, encrypt!(a, B.public_key)) == a
end


@doc raw"""
    GibbsKernel(; lengthscale)

Gibbs Kernel with lengthscale function `lengthscale`.

The Gibbs kernel is a non-stationary generalisation of the squared exponential
kernel. The lengthscale parameter ``l`` becomes a function of
position ``l(x)``.

# Definition

For inputs ``x, x'``, the Gibbs kernel with lengthscale function ``l(\cdot)``
is defined as
```math
k(x, x'; l) = \sqrt{\left(\frac{2 l(x) l(x')}{l(x)^2 + l(x')^2}\right)}
\quad \exp{\left(-\frac{(x - x')^2}{l(x)^2 + l(x')^2}\right)}.
```

For a constant function ``l \equiv c``, one recovers the [`SqExponentialKernel`](@ref)
with lengthscale `c`.

# References

Mark N. Gibbs. "Bayesian Gaussian Processes for Regression and Classication." PhD thesis, 1997

Christopher J. Paciorek and Mark J. Schervish. "Nonstationary Covariance Functions
for Gaussian Process Regression". NeurIPS, 2003

Sami Remes, Markus Heinonen, Samuel Kaski. "Non-Stationary Spectral Kernels". arXiV:1705.08736, 2017

Sami Remes, Markus Heinonen, Samuel Kaski. "Neural Non-Stationary Spectral Kernel". arXiv:1811.10978, 2018
"""
struct GibbsKernel{T} <: Kernel
    lengthscale::T
end

GibbsKernel(; lengthscale) = GibbsKernel(lengthscale)

function (k::GibbsKernel)(x, y)
    lengthscale = k.lengthscale
    lx = lengthscale(x)
    ly = lengthscale(y)
    l = invsqrt2 * hypot(lx, ly)
    kernel = (sqrt(lx * ly) / l) * with_lengthscale(SqExponentialKernel(), l)
    return kernel(x, y)
end


export FiniteDiff

#TODO add boundary conditions

"""
`FiniteDiff([domainType=Float64::Type,] dim_in::Tuple, direction = 1)`

`FiniteDiff(x::AbstractArray, direction = 1)`

Creates a `LinearOperator` which, when multiplied with an array `x::AbstractArray{N}`, returns the discretized gradient over the specified `direction` obtained using forward finite differences.

```julia
julia> FiniteDiff(Float64,(3,))
δx  ℝ^3 -> ℝ^2

julia> FiniteDiff((3,4),2)
δy  ℝ^(3, 4) -> ℝ^(3, 3)

julia> all(FiniteDiff(ones(2,2,2,3),1)*ones(2,2,2,3) .== 0)
true

```

"""
struct FiniteDiff{T,N,D,C <: CartesianIndices{N}} <: LinearOperator
	dim_in::NTuple{N,Int}
    idx::C
	function FiniteDiff{T,N,D}(dim_in) where {T,N,D}
		D > N && error("direction is bigger the number of dimension $N")
        idx = CartesianIndices(([i == D ? (2:d) : (1:d) for (i,d) in enumerate(dim_in)]...,))
        new{T,N,D,typeof(idx)}(dim_in,idx)
	end
end

# Constructors
#default constructor
FiniteDiff(domainType::Type, dim_in::NTuple{N,Int}, dir::Int64 = 1) where {N} =
FiniteDiff{domainType,N,dir}(dim_in)

FiniteDiff(dim_in::NTuple{N,Int}, dir::Int64 = 1) where {N} =
FiniteDiff(Float64, dim_in, dir)

FiniteDiff(x::AbstractArray{T,N}, dir::Int64 = 1) where {T,N}  = FiniteDiff(eltype(x), size(x), dir)

# Mappings

@generated function mul!(y::AbstractArray{T,N},
                         L::FiniteDiff{T,N,D},
                         b::AbstractArray{T,N}) where {T,N,D}
	z = zeros(Int,N)
	z[D] = 1
	idx = CartesianIndex(z...)
	ex = quote
        for I in L.idx
			y[I-$idx] = b[I]-b[I-$idx]
		end
		return y
	end
end

@generated function mul!(y::AbstractArray{T,N},
                         L::AdjointOperator{FiniteDiff{T,N,D,C}},
                         b::AbstractArray{T,N}) where {T,N,D,C}
	z = zeros(Int,N)
	z[D] = 1
	idx = CartesianIndex(z...)
	ex = quote
		for I in CartesianIndices(size(y))
			y[I] =
			I[$D] == 1 ? -b[I]  :
			I[$D] == size(y,$D) ?   b[I-$idx]  : -b[I]+b[I-$idx]
		end
		return y
	end
end

# Properties

domainType(L::FiniteDiff{T, N}) where {T, N} = T
codomainType(L::FiniteDiff{T, N}) where {T, N} = T

function size(L::FiniteDiff{T,N,D}) where {T,N,D}
	dim_out = [L.dim_in...]
	dim_out[D] = dim_out[D]-1
	return ((dim_out...,), L.dim_in)
end

fun_name(L::FiniteDiff{T,N,1}) where  {T,N} = "δx"
fun_name(L::FiniteDiff{T,N,2}) where  {T,N} = "δy"
fun_name(L::FiniteDiff{T,N,3}) where  {T,N} = "δz"
fun_name(L::FiniteDiff{T,N,D}) where {T,N,D}  = "δx$D"

is_full_row_rank(L::FiniteDiff) = true


using JuAFEM
using Tensors

# Stiffness using normal matrices
function ke_element_mat!(Ke, X::Vector{Vec{dim, T}}, fe_values::CellScalarValues{dim}, Ee, B, DB, BDB) where {T, dim}
    n_basefuncs = getnbasefunctions(fe_values)
    @assert length(X) == n_basefuncs

    reinit!(fe_values, X)
    for q_point in 1:getnquadpoints(fe_values)
        for i in 1:n_basefuncs
            dNdx = shape_gradient(fe_values, q_point, i)[1]
            dNdy = shape_gradient(fe_values, q_point, i)[2]
            dNdz = shape_gradient(fe_values, q_point, i)[3]

            B[1, i * 3-2] = dNdx
            B[2, i * 3-1] = dNdy
            B[3, i * 3-0] = dNdz
            B[4, 3 * i-1] = dNdz
            B[4, 3 * i-0] = dNdy
            B[5, 3 * i-2] = dNdz
            B[5, 3 * i-0] = dNdx
            B[6, 3 * i-2] = dNdy
            B[6, 3 * i-1] = dNdx
        end

        A_mul_B!(DB, Ee, B)
        At_mul_B!(BDB, B, DB)
        scale!(BDB, getdetJdV(fe_values, q_point))
        for p in 1:size(Ke,1)
            for q in 1:size(Ke,2)
                Ke[p, q] += BDB[p, q]
            end
        end
    end

    return Ke
end;

# Stiffness using scalar values
function ke_element!(Ke, X::Vector{Vec{dim, T}}, fe_values::CellScalarValues{dim}, C) where {T,dim}
    n_basefuncs = getnbasefunctions(fe_values)
    @assert length(X) == n_basefuncs
    reinit!(fe_values, X)
    @inbounds for q_point in 1:getnquadpoints(fe_values)
        for a in 1:n_basefuncs
            for b in 1:n_basefuncs
                ∇ϕa = shape_gradient(fe_values, q_point, a)
                ∇ϕb = shape_gradient(fe_values, q_point, b)
                Ke_e = dotdot(∇ϕa, C, ∇ϕb) * getdetJdV(fe_values, q_point)
                for d1 in 1:dim, d2 in 1:dim
                    Ke[dim*(a-1) + d1, dim*(b-1) + d2] += Ke_e[d1,d2]
                end
            end
        end
    end
    return Ke
end;

# Stiffness using vector values
function ke_element2!(Ke, X::Vector{Vec{dim, T}}, fe_values::CellVectorValues{dim}, C) where {T,dim}
    n_basefuncs = getnbasefunctions(fe_values)
    @assert length(X) * dim == n_basefuncs
    reinit!(fe_values, X)
    ɛ = [zero(SymmetricTensor{2, dim, T}) for i in 1:n_basefuncs]
    @inbounds for q_point in 1:getnquadpoints(fe_values)
        for i in 1:n_basefuncs
            ɛ[i] = symmetric(shape_gradient(fe_values, q_point, i))
        end
        dΩ = getdetJdV(fe_values, q_point)
        for i in 1:n_basefuncs
            ɛC = ɛ[i] ⊡ C
            for j in 1:n_basefuncs
                Ke[i, j] += (ɛC ⊡ ɛ[j]) * dΩ
            end
        end
    end
    return Ke
end;

E = 200e9
ν = 0.3
λ = E*ν / ((1 + ν) * (1 - 2ν))
μ = E / (2(1 + ν))
δ(i,j) = i == j ? 1.0 : 0.0
g(i,j,k,l) = λ*δ(i,j)*δ(k,l) + μ*(δ(i,k)*δ(j,l) + δ(i,l)*δ(j,k))

C = SymmetricTensor{4, 3}(g)

M = λ/ν * (1 - ν)

Cmat = [ M      λ      λ    0.0    0.0   0.0;
         λ      M      λ    0.0    0.0   0.0;
         λ      λ      M    0.0    0.0   0.0;
        0.0    0.0    0.0    μ     0.0   0.0;
        0.0    0.0    0.0   0.0     μ    0.0;
        0.0    0.0    0.0   0.0    0.0    μ]

interpolation = Lagrange{3, RefCube, 1}()
quad_rule = QuadratureRule{3, RefCube}(1)
values = CellScalarValues(quad_rule, interpolation);
vector_values = CellVectorValues(quad_rule, interpolation);

# Generate some coordinates
x = [-1.0 -1.0 -1.0;
      1.0 -1.0 -1.0;
      1.0  1.0 -1.0;
     -1.0  1.0 -1.0;
     -1.0 -1.0  1.0;
      1.0 -1.0  1.0;
      1.0  1.0  1.0;
     -1.0  1.0  1.0;]
x = x .+ 0.05 * rand()
x_vec = reinterpret(Vec{3, Float64}, x, (8,));

n_basefunctions = getnbasefunctions(vector_values)
Ke = zeros(n_basefunctions, n_basefunctions)
Ke2 = copy(Ke)
Ke3 = copy(Ke)

B   =  zeros(6, n_basefunctions)
DB  =  zeros(6, n_basefunctions)
BDB =  zeros(n_basefunctions, n_basefunctions);

fill!(Ke, 0)
fill!(Ke2, 0)
fill!(Ke3, 0)
ke_element!(Ke2, x_vec, values, C)
ke_element2!(Ke3, x_vec, vector_values, C);
ke_element_mat!(Ke, x_vec, values, Cmat, B, DB, BDB);

using Test
@test norm(Ke - Ke2) / norm(Ke) < 1e-14
@test norm(Ke - Ke3) / norm(Ke) < 1e-14

println("Stiffness successful")

Ke


# Chebyshev regression: least-square fits of data
# to multidimensional Chebyshev polynomials.

function chebregression(x::AbstractVector{SVector{N,Td}}, y::AbstractVector{T},
                        lb::SVector{N,Td}, ub::SVector{N,Td}, order::NTuple{N,Int}) where {N,Td<:Real,T<:Union{SVector,Number}}
    length(x) == length(y) || throw(DimensionMismatch())
    length(x) ≥ prod(order .+ 1) || throw(ArgumentError("not enough data points $(length(x)) to fit to order $order"))

    # assemble rhs as matrix
    Y = Array{float(eltype(T))}(undef, length(y), length(first(y)))
    for j = 1:length(y)
        Y[j,:] .= y[j + (firstindex(y)-1)]
    end

    # assemble lhs matrix
    # (TODO: this algorithm is O(length(x) * length(c)²),
    #  but it should be possible to do it in linear time.
    #  However, the A \ Y step is also O(mn²), so this
    #  only affects the constant factor in the complexity.)
    c = ChebPoly{N,Td,Td}(zeros(Td, order .+ 1), lb, ub)
    A = Array{Td}(undef, length(x), length(c.coefs))
    for i = 1:length(c.coefs)
        c.coefs[i] = 1 # basis function
        for j = 1:length(x)
            A[j,i] = c(x[j + (firstindex(x)-1)])
        end
        c.coefs[i] = 0 # reset
    end

    # least-square solution
    C = A \ Y

    # rearrange C into a ChebPoly
    Tc = typeof(zero(T) * one(eltype(Y)))
    coefs = Array{Tc,N}(reshape(reinterpret(Tc, vec(transpose(C))), order .+ 1))
    return ChebPoly{N,Tc,Td}(coefs, lb, ub)
end

# convert arrays to vectors of svectors or scalars
to_svectors(x::AbstractVector{<:Number}) = x
to_svectors(x::AbstractVector{<:Number}, ::Val{1}) = SVector{1}.(x)
to_svectors(x::AbstractVector{<:SVector{N}}) where {N} = x
to_svectors(x::AbstractVector{<:SVector{N}}, ::Val{N}) where {N} = x
to_svectors(x::AbstractVector{<:AbstractVector{T}}, ::Val{N}=Val(length(first(x)))) where {T<:Number,N} =
    SVector{N,T}.(x)
to_svectors(x::AbstractMatrix{T}, ::Val{N}=Val(size(x,2))) where {T<:Number,N} =
    SVector{N,T}[row for row in eachrow(x)]

# normalize x and y arguments to vectors of svectors or scalars
chebregression(x::AbstractVecOrMat, y::AbstractVecOrMat, lb::AbstractVector, ub::AbstractVector, order::NTuple{N}) where {N} =
    chebregression(to_svectors(x, Val{N}()), to_svectors(y), SVector{N}(lb), SVector{N}(ub), order)

chebregression(x::AbstractVecOrMat, y::AbstractVecOrMat, order::NTuple{N}) where {N} =
    chebregression(to_svectors(x, Val{N}()), to_svectors(y), order)

# accept scalar bounds and order in 1d case
chebregression(x::AbstractVector{<:Real}, y::AbstractVecOrMat, lb::Real, ub::Real, order::Integer) =
    chebregression(x, y, SVector(lb), SVector(ub), (order,))
chebregression(x::AbstractVector{<:Real}, y::AbstractVecOrMat, order::Integer) =
    chebregression(x, y, minimum(x), maximum(x), order)

# construct lb and ub if omitted
chebregression(x::AbstractVector{<:SVector{N}}, y::AbstractVector, order::NTuple{N}) where {N} =
    chebregression(x, y, reduce((a,b) -> min.(a,b), x), reduce((a,b) -> max.(a,b), x), order)

# promote arguments to common types
function chebregression(x::AbstractVector{SVector{N,Tx}}, y::AbstractVector{Ty},
    lb::SVector{N,Tlb}, ub::SVector{N,Tub}, order::NTuple{N,<:Integer}) where {N,Tx<:Real,Tlb<:Real,Tub<:Real,Ty<:Union{SVector,Number}}
    Td = float(promote_type(Tx,Tub,Tlb))
    return chebregression(AbstractVector{SVector{N,Td}}(x), y, SVector{N,Td}(lb), SVector{N,Td}(ub), NTuple{N,Int}(order))
end

"""
    chebregression(x, y, [lb, ub,] order)

Return a Chebyshev polynomial (`ChebPoly`) constructed by
performing a least-square fit of Chebyshev polnomials of the
given `order`, where `x` are the coordinates of the data
points `y`.  `lb` and `ub` are the lower and upper bounds,
respectively, of the Chebyshev domain; these should normally
enclose all of the points in `x`, and default to the minimum
and maximum coordinates in `x` if they are omitted.

In the 1d case, `x` is an array of scalars, `lb < ub`
are scalars, and `order` is an integers.   In the `N`-dimensional
case, `order` is an `N`-tuple of integers (the order in each
dimension), `lb` and `ub` are `N`-component vectors, and
`x` is an array of `N`-component vectors (or a matrix with
`N` columns, interpreted as the vector components).

`y` can be a vector or numbers or a vector of vectors (for vector-
valued Chebyshev fits).  The latter case can also be input
as a matrix whose columns are the vector componnents.  `size(x,1)`
and `size(y,1)` must match, and must exceed `prod(order .+ 1)`.
"""
function chebregression end


export MvNormalModel, MvNormalData, MvNormalParams

"""
Model observations as drawn from a multivariate normal distribution. See
`MvNormalData` for summarizing data for estimation and likelihood calculations.
"""
struct MvNormalModel end

"""
    $SIGNATURES

Test if `matrix` is square and conformable with `vector`.
"""
function is_conformable_square(vector::AbstractVector, matrix::AbstractMatrix)
    k = length(vector)
    size(matrix) == (k, k)
end

struct MvNormalData{TW <: Real, Tm <: AbstractVector, TS <: AbstractMatrix}
    "sum of the weights (alternatively, total number of observations)"
    W::TW
    "(weighted) sample mean"
    m::Tm
    "(weighted) sample covariance matrix"
    S::TS
    @doc """
        MvNormalData(n, m, S)

    Multivariate normal model summary statistics with `n` observations, mean `m`
    and sample covariance `S`. Only saves the summary statistics.

    !!! usage

        Use `MvNormalData(X, [wv])` to construct from data.
    """ ->                      # FIXME workaround, remove -> in v0.7
    function MvNormalData(W::TW, m::Tm, S::TS) where {TW <: Real,
                                                      Tm <: AbstractVector,
                                                      TS <: AbstractMatrix}
        @argcheck is_conformable_square(m, S)
        new{TW, Tm, TS}(W, m, S)
    end
end

size(ss::MvNormalData) = ss.W, length(ss.m)

function show(io::IO, ss::MvNormalData)
    W, k = size(ss)
    println(io, "Summary statistics for multivariate normal, $(W) × $(k) samples")
    println(io, "sample mean: $(ss.m)")
    println(io, "sample covariance: $(ss.S)")
end

"""
    $SIGNATURES

Multivariate normal summary statistics from observations (each row of `X` is an
observation).
"""
function MvNormalData(X::AbstractMatrix)
    m, S = mean_and_cov(X, 1; corrected = false)
    MvNormalData(size(X, 1), vec(m), S)
end

"""
    $SIGNATURES

Multivariate normal summary statistics from observations (each row of `X` is an
observation), with weights.
"""
function MvNormalData(X::AbstractMatrix, wv::AbstractWeights)
    m, S = mean_and_cov(X, wv, 1; corrected = false)
    MvNormalData(sum(wv), vec(m), S)
end

struct MvNormalParams{Tμ, TΣ}
    "mean"
    μ::Tμ
    "variance"
    Σ::TΣ
    @doc """
        MvNormalParams(μ, Σ)

    Parameters for the multivariate normal model ``x ∼ MvNormal(μ, Σ)``.

    !!! usage

        Construct using [`MLE(::MvNormalModel, ::MvNormalData)`](@ref).
    """ ->                      # FIXME workaround, remove -> in v0.7
    function MvNormalParams(μ::Tμ, Σ::TΣ) where {Tμ, TΣ}
        @argcheck is_conformable_square(μ, Σ)
        new{Tμ, TΣ}(μ, Σ)
    end
end

MLE(::MvNormalModel, ss::MvNormalData) = MvNormalParams(ss.m, ss.S)

function loglikelihood(::MvNormalModel, ss::MvNormalData, params::MvNormalParams)
    @unpack W, m, S = ss
    @unpack μ, Σ = params
    K = length(m)
    @argcheck length(μ) == K
    C = cholfact(Σ)
    d = m-μ
    -W/2*(K*log(2*π) + logdet(C) + dot(vec(S) + vec(d * d'), vec(inv(Σ))))
end

vec_parameters(ϕ::MvNormalParams) = vec_parameters((ϕ.μ, ϕ.Σ))


"""
    project(u::AbstractVector, v::AbstractVector) -> AbstractVector

Project vector `u` onto vector `v`.

# Examples

```jldoctest
julia> project([1, 1], [1, 0])
2-element Base.Vector{Float64}:
 1.0
 0.0

julia> project([5, 5], [1, 0])
2-element Base.Vector{Float64}:
 5.0
 0.0

julia> project([5, -5], [0, 1])
2-element Base.Vector{Float64}:
 -0.0
 -5.0
```

"""
function project(u::AbstractVector, v::AbstractVector)
    return (u ⋅ v) / (v ⋅ v) * v
end

"""
    project(point::AbstractVector, line::AbstractLine) -> StaticArrays.SVector

Project a point onto a line.

# Examples

```jldoctest
julia> project([1, 1], Line([0, 0], [1, 0]))
2-element StaticArrays.SVector{2, Float64} with indices SOneTo(2):
 1.0
 0.0

julia> project([5, -1], Line([0, 0], [1, 0]))
2-element StaticArrays.SVector{2, Float64} with indices SOneTo(2):
 5.0
 0.0

julia> project([1, 0], Line([0, 0], [1, 1]))
2-element StaticArrays.SVector{2, Float64} with indices SOneTo(2):
 0.5
 0.5

julia> point = project([1, 0, 0], Line([0, 0, 0], [1, 1, 1]));

julia> round.(point, digits=3)
3-element StaticArrays.SVector{3, Float64} with indices SOneTo(3):
 0.333
 0.333
 0.333
```

"""
function project(point::AbstractVector, line::AbstractLine)

    # Vector from the point on the line to the point in space.
    vector_to_point = Vector(line.point, point)

    # Vector projected onto the line.
    vector_projected = project(vector_to_point, line.direction)

    return line.point + vector_projected
end

"""
    project(point::AbstractVector, plane::AbstractPlane) -> StaticArrays.SVector

Project a point onto a plane.

# Examples

```jldoctest
julia> project([0, 0, 5], Plane([0, 0, 0], [0, 0, 1]))
3-element StaticArrays.SVector{3, Float64} with indices SOneTo(3):
 0.0
 0.0
 0.0

julia> plane = Plane([1,2,3], [1, 3, -2]);

julia> point_projected = project([5, 1, 3], plane);

julia> round.(point_projected, digits=3)
3-element StaticArrays.SVector{3, Float64} with indices SOneTo(3):
 4.929
 0.786
 3.143
```

"""
function project(point::AbstractVector, plane::AbstractPlane)

    # Vector from the point in space to the point on the plane.
    vector_to_plane_point = Vector(point, plane.point)

    # Perpendicular vector from the point in space to the plane.
    vector_to_plane = project(vector_to_plane_point, plane.normal)

    return point + vector_to_plane
end


# The weight_space_approx function needed for ApproximateGPs.pathwise_sample
# Returns both the sampled basis and the distribution over w needed to approximate the GP
function gp_rff_approx(rng, kernel, input_dims, feature_dims)
    ϕ = sample_basis(rng, kernel, input_dims, feature_dims)
    p_w = MvNormal(Diagonal(Fill(1., feature_dims)))
    return ϕ, p_w
end

# Everything necessary to create a RFF approximation to a stationary kernel
# Currently ony supports SqExponentialKernel with variance and lengthscale
struct RFFBasis{Tinner, Touter, Tω, Tτ, Tsample}
    inner_weights::Tinner  # lengthscale
    outer_weights::Touter  # variance (scaled)
    ω::Tω  # Sampled frequencies;               size(ω): (input_dims, num_features)
    τ::Tτ  # Sampled phases;                    size(τ): (num_features,)
    sample_params::Tsample  # Returns a new sample of ω & τ
end

function (ϕ::RFFBasis)(x)
    # ϕ: R^{input_dims} -> R^{num_features}
    x_ = x ./ ϕ.inner_weights
    ωt_x = ϕ.ω'x_
    return ϕ.outer_weights * cos.(ωt_x .+ ϕ.τ)
end

function resample!(ϕ::RFFBasis)
    ω, τ = ϕ.sample_params()
    ϕ.ω .= ω
    ϕ.τ .= τ
end

# Currently need to pass `input_dims` explicitly - will eventually be in KernelFunctions
# https://github.com/JuliaGaussianProcesses/KernelFunctions.jl/issues/16
function sample_basis(rng, kernel, input_dims, num_features=100)
    inner, outer = spectral_weights(kernel)
    outer_scaled = outer * √(2/num_features)
    p_ω = spectral_distribution(kernel, input_dims)

    function sample_params()
        ω = rand(rng, p_ω, num_features)  #
        τ = rand(rng, Uniform(0, 2π), num_features)  #
        return ω, τ
    end

    return RFFBasis(inner, outer_scaled, sample_params()..., sample_params)
end

function spectral_distribution(::SqExponentialKernel, input_dims)
    return MvNormal(Diagonal(Fill(1., input_dims)))
end

spectral_distribution(k::ScaledKernel, args...) = spectral_distribution(k.kernel, args...)
spectral_distribution(k::TransformedKernel, args...) = spectral_distribution(k.kernel, args...)

spectral_distribution(k::Kernel) =  error("Spectral distribution not implemented for kernel:\n$k")

function spectral_weights(::SqExponentialKernel)
    return 1.0, 1.0
end

function spectral_weights(k::ScaledKernel)
    σ² = only(k.σ²)
    inner, outer = spectral_weights(k.kernel)
    return inner, outer * √σ²
end

function spectral_weights(k::TransformedKernel{<:Any, <:ScaleTransform})
    s = only(k.transform.s)
    inner, outer = spectral_weights(k.kernel)
    return inner / s, outer
end

spectral_weights(k::Kernel) = error("Spectral weights not implemented for kernel:\n$k")

# TODO:
# ARDTransform
# ProductKernel
# SumKernel
# MaternKernel


using StaticArrays, OrdinaryDiffEq, BenchmarkTools
tspan = (0.0,10.0)
println("Testing speed of solving ODEs\n")
println("---Using StaticArrays: f(t, u) = du::SVector")
#--------------------#
@inline eom(t, u) = SVector{5}(10.0(u[2]-u[1]), u[1]*(28.0-u[3]) - u[2],
u[1]*u[2] - (8/3)*u[3], 2.0u[4]*(1-u[4]), 0.15*u[4]*(1-u[5]))

# println("ODEProblem creation:")
# display(@benchmark ODEProblem(eom,SVector{3}(rand(3)),$tspan))
prob = ODEProblem(eom,SVector{5}(10rand(5)),tspan)
println("Solution (for total time 10)")
sol = solve(prob,Tsit5(),reltol=1e-8,abstol=1e-8)
display(@benchmark solve($prob,Tsit5(),reltol=1e-8,abstol=1e-8))
sleep(0.1)

println("\n---Using Base Arrays: f(t, u) = du::Vector")
#--------------------#
@inline eomB(t, u) = [10.0(u[2]-u[1]), u[1]*(28.0-u[3]) - u[2],
u[1]*u[2] - (8/3)*u[3], 2.0u[4]*(1-u[4]), 0.15*u[4]*(1-u[5])]

# println("ODEProblem creation:")
# display(@benchmark ODEProblem(eom,rand(3),$tspan))
prob = ODEProblem(eomB,10rand(5),tspan)
println("Solution (for total time 10)")
sol = solve(prob,Tsit5(),reltol=1e-8,abstol=1e-8)
display(@benchmark solve($prob,Tsit5(),reltol=1e-8,abstol=1e-8))
sleep(0.1)

println("\n---Using Base & in-place: f!(t, u, du)::Vector")

@inline function eom!(t, u, du)
    du[1] = 10.0(u[2]-u[1])
    du[2] = u[1]*(28.0-u[3]) - u[2]
    du[3] = u[1]*u[2] - (8/3)*u[3]
    du[4] = 2.0u[4]*(1-u[4])
    du[5] = 0.15*u[4]*(1-u[5])
end

# println("ODEProblem creation:")
# display(@benchmark ODEProblem(eom!,rand(3),$tspan))
prob = ODEProblem(eom!,10rand(5),tspan)
println("Solution (for total time 10)")
sol = solve(prob,Tsit5(),reltol=1e-8,abstol=1e-8)
display(@benchmark solve($prob,Tsit5(),reltol=1e-8,abstol=1e-8))
sleep(0.1)

# println("\n---Using MVector & in-place: f!(t, u, du)::MVector")
# @inline function eomM!(t, u::MVector, du::MVector)
#     du[1] = 10.0(u[2]-u[1])
#     du[2] = u[1]*(28.0-u[3]) - u[2]
#     du[3] = u[1]*u[2] - (8/3)*u[3]
# end
#
# probM = ODEProblem(eomM!,MVector{3}(10rand(3)),tspan)
# println("Solution (for total time 1)")
# sol = solve(probM,Tsit5(),reltol=1e-8,abstol=1e-8)
# display(@benchmark solve($probM,Tsit5(),reltol=1e-8,abstol=1e-8))
# # #this doesnt work currently


module Problems
export gen_matching_pennies, manh_dist, hide_and_seek, nemirovski1, nemirovski2, policeman_and_burglar_matrix

import LinearAlgebra
const LA = LinearAlgebra
using Distributions
using Random

"""
   Two test problems from Nemirovski et al. "Robust stochastic approximation approach to stochastic programming"
"""
function nemirovski1(n, α=1)
    A = zeros(n, n)
    for i in 1:n
        for j in 1:n
            A[i, j] = ((i + j - 1) / ( 2n - 1 ))^α
        end
    end
    return A
end

function nemirovski2(n, α=1)
    A = zeros(n, n)
    for i in 1:n
        for j in 1:n
            A[i, j] = ((abs(i - j) + 1) / ( 2n - 1 ))^α
        end
    end
    return A
end

"""
    From Juditski & Nemirovki tutorial. Problem Policemen and Burglar.
"""
function policeman_and_burglar_matrix(n, th=0.8; seed="false")
    if seed != "false"
        Random.seed!(parse(Int, seed))
    end
    w = abs.(randn(n))
    th = 0.8
    C = reshape(abs.([i - j for i in 1:n, j in 1:n]), (n, n))
    A = w .*(1 .- exp.(-th .* C))
    return A
end

function randunif(m, n; seed="false")
    if seed != "false"
        Random.seed!(parse(Int, seed))
    end

    A = rand(m, n)
    return A
end

"""
Problem 2.24
"""
function manh_dist(m=100, n=100)
    A = zeros(m, n)
    for i in 1:m
        for j in 1:n
            y = (i - 1) / (m - 1)
            x = (j - 1) / (n - 1)
            A[i,j] = abs(x - y)
        end
    end
    return A
end

"""
    'Hide and Seek' game
"""
function hide_and_seek(m, n, param=0.2; seed="false")
    if seed != "false"
        Random.seed!(parse(Int, seed))
    end
    A = 1.0 * reshape(rand(Bernoulli(param),  m * n), (m,n))
    return A
end

end


using StrainRecon
using Base.Test

 @testset "findw" begin

n    = 2
R     = StrainRecon.sdiag(5*ones(2))
M     = BitArray(eye(2))
Data  = ones(2)

paramGauss = getStrainReconParam(Data,n,R=R,pwr=2.0)
#paramLaplace = getStrainReconParam(Data,n,R=R,pwr=1.0)
#param = paramLaplace
for param=[paramGauss]
	@testset "findw pwr=$(param.pwr)" begin
		a     = findw(param,M)

		@test norm(a-[.5;.5])<1e-10

		param.d = ones(2)
		a    = findw(param,M)
		@test norm(a-[.5;.5])<1e-10

		param.d = [190;0]
		a    = findw(param,M)
		@test norm(a-[1;0])<1e-10

		param.d = [0;20]
		a    = findw(param,M)
		@test norm(a-[.5;.5])<1e-10

		M    = BitArray([1.0 0; 0 0])
		param.d = ones(2)
		a    = findw(param,M)
		@test norm(a-[1;0])<1e-10
	end
end

    n    = 2
    R     = StrainRecon.sdiag(5*ones(2))
    M     = BitArray([1 0; 1 0])
    Data  = ones(2)
    paramLaplace = getStrainReconParam(Data,n,R=R,pwr=1.0)

for param=[paramLaplace]
        @testset "findw pwr=$(param.pwr)" begin
            a     = findw(param,M)
            # println("a = $a")
            @test norm(a-[1;0])<1e-10

            param.d = zeros(2)
            a    = findw(param,M)
            # println("a = $a")
            @test norm(a-[0.5;0.5])<1e-10

            M    = BitArray([1.0 0; 0 0])
            param.d = ones(2)
            a    = findw(param,M)
            # println("a = $a")
            @test norm(a-[1;0])<1e-10

            m = 24
            for n = 2:5
                param.R = StrainRecon.sdiag(5*ones(m))
                param.n = n
                # srand(s)
                M = rand([0,1], m, n)
                atrue = randomw(n)
                param.d = M*atrue
                M = BitArray(M)
                a    = findw(param,M)
                # println("a = $a , atrue = $atrue")
                @test norm(a-atrue)<1e-10
            end

        end
    end
end


using Distributions, Statistics, Plots, Random; pyplot()
Random.seed!(0)

mu1, sig1, n1 = 0, 2, 8
mu2, sig2, n2 = 0, 30, 15
dist1 = Normal(mu1, sig1)
dist2 = Normal(mu2, sig2)

N = 10^6
tdArray = Array{Tuple{Float64,Float64}}(undef,N)

df(s1,s2,n1,n2) =
    (s1^2/n1 + s2^2/n2)^2 / ( (s1^2/n1)^2/(n1-1) + (s2^2/n2)^2/(n2-1) )

for i in 1:N
    x1Data = rand(dist1, n1)
    x2Data = rand(dist2, n2)
    x1Bar,x2Bar = mean(x1Data),mean(x2Data)
    s1,s2 = std(x1Data),std(x2Data)
    tStat = (x1Bar - x2Bar) / sqrt(s1^2/n1 + s2^2/n2)
    tdArray[i] = (tStat , df(s1,s2,n1,n2))
end
sort!(tdArray, by = first)

invVal(v,i) = quantile(TDist(v),i/(N+1))

xCoords  = Array{Float64}(undef,N)
yCoords1 = Array{Float64}(undef,N)
yCoords2 = Array{Float64}(undef,N)

for i in 1:N
    xCoords[i] = first(tdArray[i])
    yCoords1[i] = invVal(last(tdArray[i]), i)
    yCoords2[i] = invVal(n1+n2-2, i)
end

scatter(xCoords, yCoords1, c=:blue, label="Calculated v", msw=0)
scatter!(xCoords, yCoords2, c=:red, label="Fixed v", msw=0)
plot!([-10,10], [-10,10],
	 c=:black, lw=0.3, xlims=(-8,8), ylims=(-8,8), ratio=:equal, label="",
	 xlabel="Theoretical t-distribution quantiles",
	 ylabel="Simulated t-distribution quantiles", legend=:topleft)


## ---- Fit
"""
    fit(::Type{RationalFunction}, r::Polynomial, m, n; var=:x)

Fit a Pade approximant ([`pade_fit`](@ref)) to `r`.

Examples:

```jldoctext
julia> using Polynomials, PolynomialRatios

julia> x = variable()
Polynomial(x)

julia> ex = 1 + x + x^2/2 + x^3/6 + x^4/24 + x^5/120 # Taylor polynomial for e^x
Polynomial(1.0 + 1.0*x + 0.5*x^2 + 0.16666666666666666*x^3 + 0.041666666666666664*x^4 + 0.008333333333333333*x^5)

julia> maximum(abs, exp(x) - fit(RationalFunction, ex, 1,1)(x) for x ∈ 0:.05:0.5)
0.017945395966538547

julia> maximum(abs, exp(x) - fit(RationalFunction, ex, 1,2)(x) for x ∈ 0:.05:0.5)
0.0016624471707165078

julia> maximum(abs, exp(x) - fit(RationalFunction, ex, 2,1)(x) for x ∈ 0:.05:0.5)
0.001278729299871717

julia> maximum(abs, exp(x) - fit(RationalFunction, ex, 2,2)(x) for x ∈ 0:.05:0.5)
7.262205147950951e-5
```
"""
function Polynomials.fit(::Type{RationalFunction},r::Polynomial, m::Integer, n::Integer;var=:x)
    p,q = pade_fit(r, m,n, var=var)
    p // q
end

## https://mathworld.wolfram.com/PadeApproximant.html
"""
    pade_fit(r::Polynomial, m,n)

For a polynomial `r` of degree `d ≥ m + n`, find a rational function `p/q` with
`degree(p) ≤ m`, `degree(q) ≤ n` and `q*r - p = x^{m+n+1}*s(x)` for some polynomial `s`.

This implementation sets up a system of equations to identify `p` and `q`.
"""
function pade_fit(p::Polynomial{T}, m::Integer, n::Integer; var=:x) where {T}
    d = degree(p)
    @assert (0 <= m) && (1 <= n) && (m + n <= d)

    # could be much more perfomant
    c = convert(LaurentPolynomial, p) # for better indexing
    cs = [c[m+j-i] for j ∈ 1:n, i ∈ 0:n]

    qs′ = cs[:, 2:end] \ cs[:,1]
    qs = vcat(1, -qs′)

    cs = [c[0 + j - i] for j ∈ 0:m, i∈0:n]
    ps = cs * qs

    Polynomial(ps, var), Polynomial(qs,var)
end
export pade_fit


using Newton
using Test

f1(x)=x^3
f1_prime(x)=3*x^2

f2(x) = sin(x)
f2_prime(x) = cos(x)

f3(x) = log(x)
f3_prime(x) = 1//x

fs=[f1,f2,f3]
f_primes=[f1_prime,f2_prime,f3_prime]
f_nc(x) = 2+ x^2
f_nc_prime(x) = 2*x

@testset "Newton.jl" begin

    for (f,f_prime) in zip(fs,f_primes) # Several test for the root of a known function,given f and analytical f′
        @test newtonroot(f,x₀=1).x≈newtonroot(f,f_prime,x₀=1).x
    end

    for (f,f_prime) in zip(fs,f_primes)
        for i in 1:200:1000 # test to ensure that maxiter is working
            @test  newtonroot(f,f_prime,x₀=1, maxiter=i)===nothing || newtonroot(f,f_prime,x₀=1, maxiter=i).iteration <= i
        end
    end

    for (f,f_prime) in zip(fs,f_primes)
        for i in 1:200:1000 # test to ensure that maxiter is working
            @test  newtonroot(f,f_prime,x₀=1, maxiter=i)===nothing || newtonroot(f,f_prime,x₀=1, maxiter=i).iteration <= i
        end
    end

    @test newtonroot(f_nc,x₀=1) === nothing # Test of non-convergence
    @test newtonroot(f_nc,f_nc_prime,x₀=1)=== nothing # Test of non-convergence

    @test newtonroot(f1,f1_prime,x₀=BigFloat(1)).x |> typeof == BigFloat # BigFloat roots
    @test newtonroot(f1,x₀=BigFloat(1)).x |> typeof == BigFloat # BigFloat roots

    @test newtonroot(f1,x₀=1) == nothing ? true : newtonroot(f1,x₀=1).norm_dif <=1E-5
    @test newtonroot(f1,f1_prime, x₀=1) == nothing ? true : newtonroot(f1,x₀=1).norm_dif <=1E-5

end


function sample_interpolation(x,y,xmin,xmax,step)

  nx = length(x)
  n = round(Int64,(xmax-xmin)/step+1)
  xnew = Vector{Float64}(undef,n)
  ynew = Vector{Float64}(undef,n)

  for i in 1:n
   xnew[i] = xmin + (i-1)*step
   ix = findfirst( x -> x > xnew[i], x )
   if ix == 1
     ynew[i] = y[1]
   elseif ix == nothing
     ynew[i] = y[nx]
   else
     ynew[i] = y[ix-1] + (xnew[i]-x[ix-1])*((y[ix]-y[ix-1])/(x[ix]-x[ix-1]))
   end
  end

  return xnew, ynew

end


@doc raw"""
```
Z,p,q = retrieve_surface(algorithm::DiscreteShapeBound, img::AbstractArray, iterations::Int=2000)
```

Same as [`DiscreteShape`](@ref retrieve_surface(::DiscreteShape, ::AbstractArray, ::iterations))
except it has its initial conditions bound by the image as per bellow where
``E`` is the brightness of the image:
```math
\begin{gathered}
Z_{i,j}=\begin{cases}-100E_{i,j} &\text{if } E_{i,j}>0.75\\0 &\text{otherwise}\\
\end{cases}\\\\p,q=\nabla E
\end{gathered}
```
# Example
Compute the heightmap for a synthetic image generated by `generate_surface`.
```julia
using Images, Makie, ShapeFromShading

#generate synthetic image
img = generate_surface(SynthSphere(), 1, [0.2,0,0.9], radius = 5)

#calculate the heightmap (using 500 iterations)
Z,p,q = retrieve_surface(DiscreteShapeBound(), img, 500)

#normalize to maximum of 1 (not necessary but makes displaying easier)
Z = Z./maximum(Z)

#display using Makie (Note: Makie can often take several minutes first time)
r = 0.0:0.1:2
surface(r, r, Z)
```
# Reference
1. S. Elhabian, "Hands on Shape from Shading", Computer Vision and Image Processing, 2008.
"""
function retrieve_surface(algorithm::DiscreteShapeBound, img::AbstractArray, iterations::Int=2000; smoothness::Int=1000)
    ρ,I,σ,τ = estimate_img_properties(img)
    λ = smoothness
    return retrieve_surface(DiscreteShapeBound(), img, ρ, I, iterations, smoothness=λ)
end

function retrieve_surface(algorithm::DiscreteShapeBound, img::AbstractArray, albedo::Real, illumination_direction::Vector{T} where T <: Real, iterations::Int=2000; smoothness::Int=1000)
    ρ = albedo
    I = illumination_direction
    E = Array{Float64}(img)

    #initialize variables
    p = zeros(Complex{Float64},axes(E))
    q = zeros(Complex{Float64},axes(E))
    R = zeros(Complex{Float64},axes(E))
    Z = zeros(axes(E))

    #apply bounding conditions
    for i in CartesianIndices(E)
        if E[i] > 0.75
            Z[i] = -100*E[i]
        else
            Z[i] = 0.0
        end
    end
    q, p = Array{Complex{Float64}}.(imgradients(Z, KernelFactors.sobel, "replicate"))
    return solve_EulerLagrange(ρ, I, iterations, p, q, R, smoothness, E, Z)
end


### A Pluto.jl notebook ###
# v0.12.7

using Markdown
using InteractiveUtils

# ╔═╡ 6fd616f6-1ea1-11eb-3814-8bfb4a096c49
using Pkg, DrWatson

# ╔═╡ 6ffe2628-1ea1-11eb-24ea-57f985146a72
begin
	@quickactivate "StatisticsWithJuliaPlutoNotebooks"
	using Distributions, Random, StatsBase, DataFrames, Plots
	Random.seed!(1)
end;

# ╔═╡ ed174bc4-1ea0-11eb-1e2f-a32874cec549
md"## Listing 3.16"

# ╔═╡ 70179b62-1ea1-11eb-13ce-7fb4d54bad4b
function rouletteSpins(p)
    x = 0
    while true
        x += 1
        if rand() < p
            return x
        end
    end
end

# ╔═╡ beee83e0-2162-11eb-10f1-7f63ce3708d2
begin
	p, xGrid, N = 18/37, 1:7, 10^6
	mcEstimate = counts([rouletteSpins(p) for _ in 1:N],xGrid)/N

	gDist = Geometric(p)
	gPmf = [pdf(gDist,x-1) for x in xGrid]
end;

# ╔═╡ beeeb36a-2162-11eb-0698-5fc29070f3ee
begin
	plot(xGrid, mcEstimate, line=:stem, marker=:circle,
		c=:blue, ms=10, msw=0, lw=4, label="MC estimate")
	plot!( xGrid, gPmf, line=:stem, marker=:xcross,
		c=:red, ms=6, msw=0, lw=2, label="PMF",
		ylims=(0,0.5), xlabel="x", ylabel="Probability")
end

# ╔═╡ 70182cd8-1ea1-11eb-094d-8d0d49cf15f3
md"## End of listing 3.16"

# ╔═╡ Cell order:
# ╠═ed174bc4-1ea0-11eb-1e2f-a32874cec549
# ╠═6fd616f6-1ea1-11eb-3814-8bfb4a096c49
# ╠═6ffe2628-1ea1-11eb-24ea-57f985146a72
# ╠═70179b62-1ea1-11eb-13ce-7fb4d54bad4b
# ╠═beee83e0-2162-11eb-10f1-7f63ce3708d2
# ╠═beeeb36a-2162-11eb-0698-5fc29070f3ee
# ╟─70182cd8-1ea1-11eb-094d-8d0d49cf15f3


using Bilevel
using ForwardDiff
using Bilevel: svd, svd_finite
using LinearAlgebra
using BenchmarkTools

A = rand(3,3)
d = svd(A)
# d.S[3] = d.S[2]
A = d.U*Diagonal(d.S)*d.V'
z0 = A[:]
# z0 = rand(4)*200. .- 100.

function f(a)
    n = Int(sqrt(length(a)))
    A = reshape(a,n,n) .* 2.
    # if eltype(A) <: ForwardDiff.Dual
        # U,s,V = svd_finite(A)
    # else
        U,s,V = svd(A)
    # end
    return vcat(U[:],s[:],V[:])
end

# sol = f(z0)

# autodiff
J_auto = ForwardDiff.jacobian(f,z0)

# # # numerical
# ϵ = sqrt(eps(1.))
# J_num = zeros(size(J_auto))
# for i = 1:length(z0)
#     δ = zeros(length(z0))
#     δ[i] = ϵ
#     J_num[:,i] = (f(z0 + δ) .- sol)/ϵ
# end
#
# display(sol)
# println("")
# println("Auto")
# display(J_auto)
# println("")
# display(maximum(abs.(J_auto)))
# println("")
# println("")
# println("Finite diff")
# display(J_num)
# println("")
# display(maximum(abs.(J_num)))
# println("")
# println("")
#
# err = maximum(abs.(J_auto .- J_num))
# display(err)
# println("")
#
# err = sort(abs.(J_auto .- J_num)[:])
# display(err)
# println("")


using DataStructures: MutableBinaryMaxHeap, top_with_handle, delete!

function proj_simplex0(v::Array{T, 1}, s::T=one(T)) where {T<:Real}
    n = length(v)
    if sum(v) == s && all(v .≥ 0)
        w = v
    else
        u = sort(v, rev=true)
        cssv = cumsum(u)
        ρ = findall(>(0), u .* Array((1:n)) .> (cssv .- s))[end]
        θ = (cssv[ρ] - s) / (ρ)
        w = max.(v .- θ, zero(T))
    end
    return w
end

""" Algorithm from Held, M., Wolfe, P., Crowder, H.: 'Validation of
subgradient optimization'. The description is from the Condat L.:
'Fast Projection onto the Simplex and the l1 Ball'. (Algorithm 1)"""

function proj_simplex1(y::Array{T, 1}, a::T=one(T)) where {T<:Real}
    n = length(y)
    if sum(y) == a && all(y .≥ 0)
        x = y
    else
        let τ
            u = sort(y, rev=true)
            cumsum_u = zero(T)
            for k in 1:n
                if cumsum_u + u[k] < k * u[k] + a
                    cumsum_u += u[k]
                    τ = (cumsum_u - a) / k
                else
                    break
                end
            end
            x = max.(y .- τ, zero(T))
        end
    end
    return x
end

function proj_simplex12(y::Array{T, 1}, a::T=one(T)) where {T<:Real}
    n = length(y)
    if sum(y) == a && all(y .≥ 0)
        return y
    else
        τ = 0.0
        u = sort(y, rev=true)
        cumsum_u = zero(T)
        for k in 1:n
            if cumsum_u + u[k] < k * u[k] + a
                cumsum_u += u[k]
                τ = (cumsum_u - a) / k
            else
                break
            end
        end
        y = max.(y .- τ, zero(T))
    end
    return y
end

""" Algorithm from van den Berg, E., Friedlander, M.P.: 'Probing the
Pareto frontier for basis pursuit solution'. The description is from
Condat L: 'Fast Projection onto the Simplex and the l1
Ball'. (Algorithm 2)"""

function proj_simplex2(y::Array{T, 1}, a::T=one(T)) where {T<:Real}
    N = length(y)
    if sum(y) == a && all(y .≥ 0)
        x = y
    else
        τ = zero(T)
        v = MutableBinaryMaxHeap(y)
        cumsum_u = zero(T)
        for k in 1:N
            u = first(v)
            if cumsum_u + u < k * u + a
                cumsum_u += u
                i = top_with_handle(v)[2]
                delete!(v, i)
                τ = (cumsum_u - a) / k
            else
                break
            end
        end
        x = max.(y .- τ, zero(T))
    end
    return x
end

function proj_simplex22(y::Array{Float64, 1}, a=1.0)
    N = length(y)
    if sum(y) == a && all(y .≥ 0)
        x = y
    else
        τ = 0.
        v = MutableBinaryMaxHeap(y)
        cumsum_u = 0.
        for k in 1:N
            u = first(v)
            if cumsum_u + u < k * u + a
                cumsum_u += u
                i = top_with_handle(v)[2]
                delete!(v, i)
                τ = (cumsum_u - a) / k
            else
                break
            end
        end
        x = max.(y .- τ, 0.)
    end
    return x
end

"""
From Condat “Fast projection onto the simplex and the l_1 ball”. In: Mathematical Programming 158.1
(2016), pp. 575–585
"""
function proj_simplex_condat(y::Array{Float64, 1}, a=Float64(1.0))
    N = length(y)
    v = [y[1]]
    v_tilde = Float64[]
    ρ = y[1] - a
    for n in 2:N
        yn = y[n]
        if yn > ρ
            ρ += (yn - ρ) / (length(v) + 1)
            if ρ > yn - a
                append!(v, y[n])
            else
                append!(v_tilde, v)
                v = [yn]
                ρ = yn - a
            end
        end
    end

    if !isempty(v_tilde)
        for yi in v_tilde
            if yi > ρ
                append!(v, yi)
                ρ += (yi - ρ) / length(v)
            end
        end
    end
    # if during the loop rho is increases at least once, then the flag is true. Otherwise we stop
    flag = true
    while flag
        flag = false
        for (i, yi) in enumerate(v)
            if yi ≤ ρ
                deleteat!(v, i)
                ρ += (ρ - yi) / length(v)
                flag = true
            end
        end
    end
    τ = ρ
    x = max.(y .- τ, zero(y))
    return x
end

function proj_simplex3(v, z=1.)
    n = length(v)
    U = Array((1:n))
    s = 0
    ρ = 0
    while length(U) > 0
        G = []
        L = []
        k = U[rand(1:length(U))]
        ds = v[k]
        for j in U
            if v[j] >= v[k]
                if j != k
                    ds += v[j]
                    append!(G, j)
                end
            elseif v[j] < v[k]
                append!(L, j)
            end
        end
        drho = length(G) + 1
        if s + ds - (ρ + drho) * v[k] < z
            s += ds
            ρ += drho
            U = L
        else
            U = G
        end
    end
    theta = (s - z) / ρ
    return max.(v .- theta, 0.)

end

function proj_simplex4(v, z=1., τ=1e-7, max_iter=1000)
    lower = 0
    upper = maximum(v)
    current = Inf
    w = zeros(n)
    for it in 1:max_iter
        if abs(current) / z < τ && current < 0.
            break
        end
        theta = (upper + lower) / 2.0
        w = max.(v .- theta, 0.)
        current = sum(w) - z
        if current <= 0.
            upper = theta
        else
            lower = theta
        end
    end
    return w
end

function softmax(x::Array{T, 1}) where {T<:Real}
    # compare with NNlib implementation
    res = exp.(x .- maximum(x))
    res ./= sum(res)
    return res
end


using Base.Test
using MathProgBase
using MathProgBase.SolverInterface
using Xpress

function quadprogtest(solver=MathProgBase.defaultQPsolver)
    println("Testing quadprog with solver ", string(typeof(solver)))

    sol = quadprog([0., 0., 0.],[2. 1. 0.; 1. 2. 1.; 0. 1. 2.],[1. 2. 3.; 1. 1. 0.],'>',[4., 1.],-Inf,Inf,solver)

    @test sol.status == :Optimal
    @test_approx_eq_eps sol.objval 130/70 1e-5
    @test_approx_eq_eps norm(sol.sol[1:3] - [0.5714285714285715,0.4285714285714285,0.8571428571428572]) 0.0 1e-4
    println("prs 1")
    let
    m = LinearQuadraticModel(solver)
    loadproblem!(m, [1. 2. 3.; 1. 1. 0.],[-Inf,-Inf,-Inf],[Inf,Inf,Inf],[0.,0.,0.],[4., 1.],[Inf,Inf], :Min)

    setquadobj!(m,diagm([10.0,10.0,10.0]))
    rows = [1, 2, 2, 2, 3, 3, 3]
    cols = [1, 1, 1, 2, 2, 3, 3]
    vals = Float64[2, 0.5, 0.5, 2, 1, 1, 1]
    setquadobj!(m,rows,cols,vals)
    optimize!(m)
    stat = status(m)
    @test stat == :Optimal
    @test_approx_eq_eps getobjval(m) 130/70 1e-5
    @test_approx_eq_eps norm(getsolution(m) - [0.5714285714285715,0.4285714285714285,0.8571428571428572]) 0.0 1e-4
    end
    println("prb 2")

    let
    m = LinearQuadraticModel(solver)
    loadproblem!(m, [-1. 1.; 1. 1.], [0.,0.], [Inf,Inf], [1.,1.], [0.,0.], [Inf,Inf], :Max)
    addquadconstr!(m, [2], [1.], [1], [1], [1.], '<', 2)
    optimize!(m)
    stat = status(m)
    @test stat == :Optimal
    @test_approx_eq_eps getobjval(m) 2.25 1e-4
    @test_approx_eq_eps norm(getsolution(m) - [0.5,1.75]) 0.0 1e-3
    end
    println("Done")
end
quadprogtest(XpressSolver(OPTIMALITYTOL = 1e-8))


import Polynomials
using Polynomials

#
# given the coefficients of a polynomial p(x) = p(n)*x^n + + p(1)*x + p(0)
#
# determne a scaling factor for x and a linear factor t that the transformed
# polynomial q(z) = t * p(s*z) has coefficient p(n) = 1 and all |p(j)| <= 1
#
# Invariant:
# if q, shift, smax, u = polyscale(p) then q = polytransform(p, shift, smax) * u
# up to numerical precision

FloatOrComplex = Union{AbstractFloat, Complex, Complex{Float32}, Complex{BigFloat}}

function polyscale(P::Poly{T}) where {T<:FloatOrComplex}
	  n = length(P.a) -1
    shift = - P.a[n] / n
    P = polytransform(P, shift)
	  p = P.a
    p[n] = 0
    pni = 1 / p[n+1]
    smaxlog = maximum(log(abs(p[1:n] * pni)) ./ (n+1 - (1:n)))
    smax = exp(smaxlog)
    u = pni / exp(smaxlog * n)
    P = polytransform(Poly(p), 0.0, smax) * u
    P.a[n+1] = T(1)
    P, shift, smax, u
end

"""
given a monic polynomial (x^n + ...) determine a scaling factor s that the transformed
polynomial q(z) = p(s*z) is half of the time < 1, rest >= 1. Ignore zero coefficients.
"""
function polyscale2(P::Poly{T}) where T<:FloatOrComplex

  p = abs.(P.a)
  n = length(p) - 1
  pni = T(1) / p[n+1]
  scale!(p, pni)
  p = view(p,1:n)
  bigg = log(realmax(T))
  tiny = log(realmin(T))
  clip(x::T) = x == -Inf ? -x : x

  llim = exp(maximum(((log.(p) - bigg) ./ (n:-1:1))))
  ulim = exp(minimum(clip, ((log.(p) - tiny) ./ (n:-1:1))))
  med = exp(sort(log.(p) ./ (n:-1:1))[n÷2+1])

  med, llim, ulim

end

"""

Find scaling factor for a polynomial p_1 + p_2*x^1 + ... + p_(n+1)*x^n,
which has the minimal possible quotient of coefficients.
Coefficients which are zero ignored.
"""
function polyscale_mini_quotient(P::Poly{T}) where T<:FloatOrComplex
  p = abs.(P.a)
  n = length(p) - 1
  pni = log(abs(p[n+1]))

  plog = log.(p[1:n]) - pni

  f = Array{real(T)}(2n)
  copy!(f, plog)
  copy!(f, n+1, -f, 1, n)
  g = Array{real(T)}(2n)
  g =
  copy!(g, -(n:-1:1))
  copy!(g, n+1, -g, 1, n)

  xa, xb, val = minimize(f, g)
  exp(xa), exp(xb), exp(val)
end

"""

  `minimize(f::Array, g::Array of same size) -> xa, xb, vmin`

Solve the following special linear optimization problem:
Define `f(x) = max{ f1[k] + g1[k] * x / k = 1..n1}`.
Minimize `f(x)` for real x.
The function f is convex as maximum of linear (convex) functions is convex.
If f is bounded below, it has minmum value `vmin` and a solution interval `[xa, xb]`
with `f(x) = vmin for all x in [xa, xb]`.
If f is not bounded below, return `vmin = -Inf` and `xa = xb = ±Inf`.
"""
function minimize(f::AbstractVector{T}, g::AbstractVector{T}) where T<:Real

  n::Int = length(f)
  n == length(g) || error(ArgumentError("lengths of f1 and g1 are different"))

  Z = zero(T)
  inf = typemax(T)

  # remove unwanted infinities
  f, g = unzip(Iterators.filter(x -> x[1] !=  inf, zip(f, g)))::NTuple{2,AbstractArray{T}}
  n = length(f)

  if n == 0
    return -inf, inf, -inf
  elseif n == 1
    if g[1] == Z
      return -inf, inf, f[1]
    elseif g[1] < Z
      return inf, inf, -inf
    else
      return -inf, -inf, -inf
    end
  end

  eva(x::T)::Tuple{T,T,Int} = maximum(1:n) do k; ((f[k] + g[k] * x), g[k], k) end

  function nextto(x::T)::Tuple{T, T, T}
    v, gk, k = eva(x)
    cross(j::Int) = -(f[j] - f[k]) / (g[j] - gk)

    if isnan(v)
      xa = xb = x
    elseif gk > Z
      xa = xb = maximum(filter(y->y<x, cross.(filter(j->j!=k, 1:n))))
    elseif gk < Z
      xa = xb = minimum(filter(y->y>x, cross.(filter(j->j!=k, 1:n))))
    elseif gk == Z
      xa = maximum(filter(y->y<x, cross.(filter(j->j!=k, 1:n))))
      xb = minimum(filter(y->y>x, cross.(filter(j->j!=k, 1:n))))
    else
      xa, xb = inf, -inf
    end
    xa, xb, gk
  end

  # find extreme slopes
  gmax::T, fmax::T, kmax::Int = maximum(1:n) do k; (g[k], f[k], k) end
  gmin::T, fmin::T, kmin::Int = minimum(1:n) do k; (g[k], f[k], k) end

  xmax::T = maximum(filter(x->!isnan(x), -(f-f[kmax]) ./ (g - gmax)))
  xmin::T = minimum(filter(x->!isnan(x), -(f-f[kmin]) ./ (g - gmin)))
  xa, xb = xmax, xmin

  while xmin < xa || xb < xmax
    xa, xb, gab = nextto((xmin + xmax) / 2)
    gs::Int = cmp(gab, Z)
    if gs >= 0
      xmax = xb
    end
    if gs <= 0
      xmin = xa
    end
  end
  xa, xb, eva((xa + xb)/ 2)...
end

maximum1(f::Function, v0, itr) = mapreduce(f, Base.scalarmax, v0, itr)
maximum1(v0, itr) = mapreduce(identity, Base.scalarmax, v0, itr)
maximum1(f::Function, itr) = mapreduce(f, Base.scalarmax, lowerbound(f, itr), itr)
maximum1(itr) = mapreduce(identity, Base.scalarmax, lowerbound(itr), itr)
lowerbound(itr) = lowerbound(eltype(itr))
lowerbound(f::Function, itr) = lowerbound(promote_type(Base.return_types(f, (eltype(itr),))))
lowerbound(T::Type) = error("no lower bound for type $T")
lowerbound(::Type{T}) where T<:Union{AbstractFloat,Unsigned} = typemin(T)
lowerbound(S::Type{Rational{T}}) where T<:Integer = typemin(S)
lowerbound(::Type{String}) = ""

#Base.mr_empty(::typeof(identity),::typeof(Base.scalarmax),::Type{T}) where T<:Real = typemin(T)
#Base.mr_empty(::typeof(identity),::typeof(Base.scalarmin),::Type{T}) where T<:Real = typemax(T)

"""
  `unzip(itr)`

The iterable object itr must deliver N-tuples.
Convert an array of N-tuples into N-tuple of arrays.
"""
function unzip(aot)
  tuple(map(x->Array(collect(x)), zip(aot...))...)
end


# * Linear interpolation

# https://github.com/JuliaLang/julia/pull/18777
lerp(a::T,b::T,t) where T = T(fma(t, b, fma(-t, a, a)))
lerp(a::R,b::R,t::C) where {R<:Real,C<:Complex} = lerp(a,b,real(t)) + im*lerp(a,b,imag(t))
lerp(a::C,b::C,t::R) where {R<:Real,C<:Complex} = lerp(real(a),real(b),t) + im*lerp(imag(a),imag(b),(t))

# * Gaußian quadrature

"""
    change_interval!(xs, ws, x, w[, a=0, b=1, γ=1])

Transform the Gaußian quadrature roots `x` and weights `w` on the
elementary interval `[-1,1]` to the interval `[γ*a,γ*b]` and store the
result in `xs` and `ws`, respectively. `γ` is an optional root of
unity, used to complex-rotate the roots (but not the weights).
"""
function change_interval!(xs::AbstractVector{T}, ws, x, w,
                          a=zero(T), b=one(T), γ=one(T)) where T
    xs .= lerp.(γ*a, γ*b, (x .+ 1)/2)
    ws .= (b-a)*w/2
    xs,ws
end

change_interval(x::AbstractVector{T}, w::AbstractVector{T},
                a=zero(T), b=one(T), γ::U=one(T)) where {T,U} =
    change_interval!(similar(x,U), similar(w), x, w, a, b)

# * Gauß–Legendre quadrature

"""
    num_quadrature_points(k, k′)

The number of quadrature points needed to exactly compute the matrix
elements of an operator of polynomial order `k′` with respect to a
basis of order `k`.
"""
function num_quadrature_points(k, k′)
    N2 = 2*(k-1) + k′
    N2>>1 + N2&1
end

"""
    lgwt(t, N) -> (x,w)

Generate the `N` Gauß–Legendre quadrature roots `x` and associated
weights `w`, with respect to the B-spline basis generated by the knot
set `t`.

# Examples

```jldoctest
julia> CompactBases.lgwt(LinearKnotSet(2, 0, 1, 3), 2)
([0.0704416, 0.262892, 0.403775, 0.596225, 0.737108, 0.929558], [0.166667, 0.166667, 0.166667, 0.166667, 0.166667, 0.166667])

julia> CompactBases.lgwt(ExpKnotSet(2, -4, 2, 7), 2)
([2.11325e-5, 7.88675e-5, 0.000290192, 0.000809808, 0.00290192, 0.00809808, 0.0290192, 0.0809808, 0.290192, 0.809808, 2.90192, 8.09808, 29.0192, 80.9808], [5.0e-5, 5.0e-5, 0.00045, 0.00045, 0.0045, 0.0045, 0.045, 0.045, 0.45, 0.45, 4.5, 4.5, 45.0, 45.0])
```
"""
function lgwt(t::AbstractKnotSet{k,ml,mr,T}, N) where {k,ml,mr,T}
    2N-1 ≥ 2(k-1) || @warn "N = $N quadrature point$(N > 1 ? "s" : "") not enough to calculate overlaps between polynomials of order k = $k"
    x, w = gausslegendre(N)

    nei = nonempty_intervals(t)
    ni = length(nei)
    xo = zeros(T, ni*length(x))
    wo = zeros(T, ni*length(x))

    for (i,j) in enumerate(nei)
        sel = (i-1)*N+1 : i*N
        change_interval!(view(xo, sel), view(wo, sel),
                         x, w,
                         t[j], t[j+1])
    end

    xo,wo
end

# * Gauß–Lobatto

function element_grid(order, a::T, b::T, c::T=zero(T), eiϕ=one(T)) where T
    x,w = gausslobatto(order)
    xs,ws = change_interval(x, w, a-c, b-c, eiϕ)
    c .+ xs, ws
end


@testset "Map, reduce, mapreduce, broadcast" begin
    @testset "map and map!" begin
        v1 = @SVector [2,4,6,8]
        v2 = @SVector [4,3,2,1]
        mv = MVector{4, Int}()

        normal_v1 = [2,4,6,8]
        normal_v2 = [4,3,2,1]

        @test map(-, v1) === @SVector [-2, -4, -6, -8]
        @test map(+, v1, v2) === @SVector [6, 7, 8, 9]
        @test map(+, normal_v1, v2) === @SVector [6, 7, 8, 9]
        @test map(+, v1, normal_v2) === @SVector [6, 7, 8, 9]

        map!(+, mv, v1, v2)
        @test mv == @MVector [6, 7, 8, 9]
    end

    @testset "reduce" begin
        v1 = @SVector [2,4,6,8]
        @test reduce(+, v1) === 20
        @test reduce(+, 0, v1) === 20
    end

    @testset "mapreduce" begin
        v1 = @SVector [2,4,6,8]
        v2 = @SVector [4,3,2,1]
        @test mapreduce(-, +, v1) === -20
        @test mapreduce(-, +, 0, v1) === -20
        @test mapreduce(*, +, v1, v2) === 40
        @test mapreduce(*, +, 0, v1, v2) === 40
    end

    @testset "broadcast and broadcast!" begin
        c = 2
        v1 = @SVector [2,4,6,8]
        v2 = @SVector [4,3,2,1]
        mv = MVector{4, Int}()
        mm = MMatrix{4, 2, Int}()
        M = @SMatrix [1 2; 3 4; 5 6; 7 8]

        @test broadcast(-, v1) === map(-, v1)

        @test broadcast(+, v1, c) === @SVector [4, 6, 8, 10]
        @test broadcast(+, v1, v2) === map(+, v1, v2)
        @test broadcast(+, v1, M) === @SMatrix [3 4; 7 8; 11 12; 15 16]

        broadcast!(-, mv, v1)
        @test mv == @MVector [-2, -4, -6, -8]

        broadcast!(+, mv, v1, v2)
        @test mv == @MVector [6, 7, 8, 9]

        broadcast!(+, mm, v1, M)
        @test mm == @MMatrix [3 4; 7 8; 11 12; 15 16]
    end
end


"""
```julia
towel(u0 = [0.085, -0.121, 0.075])
```
```math
\\begin{aligned}
x_{n+1} &= a x_n (1-x_n) -0.05 (y_n +0.35) (1-2z_n) \\\\
y_{n+1} &= 0.1 \\left( \\left( y_n +0.35 \\right)\\left( 1+2z_n\\right) -1 \\right)
\\left( 1 -1.9 x_n \\right) \\\\
z_{n+1} &= 3.78 z_n (1-z_n) + b y_n
\\end{aligned}
```
The folded-towel map is a hyperchaotic mapping due to Rössler [1]. It is famous
for being a mapping that has the smallest possible dimensions necessary for hyperchaos,
having two positive and one negative Lyapunov exponent.
The name comes from the fact that when plotted looks like a folded towel, in every
projection.

Default values are the ones used in the original paper.

[1] : O. E. Rössler, Phys. Lett. **71A**, pp 155 (1979)
"""
function towel(u0=[0.085, -0.121, 0.075])
    return DDS(eom_towel, u0, nothing, jacob_towel)
end# should result in lyapunovs: [0.432207,0.378834,-3.74638]
function eom_towel(x, p, n)
    @inbounds x1, x2, x3 = x[1], x[2], x[3]
    SVector( 3.8*x1*(1-x1) - 0.05*(x2+0.35)*(1-2*x3),
    0.1*( (x2+0.35)*(1-2*x3) - 1 )*(1 - 1.9*x1),
    3.78*x3*(1-x3)+0.2*x2 )
end
function jacob_towel(x, p, n)
    @SMatrix [3.8*(1 - 2x[1]) -0.05*(1-2x[3]) 0.1*(x[2] + 0.35);
    -0.19((x[2] + 0.35)*(1-2x[3]) - 1)  0.1*(1-2x[3])*(1-1.9x[1])  -0.2*(x[2] + 0.35)*(1-1.9x[1]);
    0.0  0.2  3.78(1-2x[3]) ]
end

function eom_towel_iip(dx, x, p, n)
    @inbounds begin
        x1, x2, x3 = x[1], x[2], x[3]
        dx[1] = 3.8*x1*(1-x1) - 0.05*(x2+0.35)*(1-2*x3)
        dx[2] = 0.1*( (x2+0.35)*(1-2*x3) - 1 )*(1 - 1.9*x1)
        dx[3] = 3.78*x3*(1-x3)+0.2*x2
    end
end
function jacob_towel_iip(J, x, p, n)
    @inbounds begin
        J[1,1] = 3.8*(1 - 2x[1])
        J[2,1] = -0.19((x[2] + 0.35)*(1-2x[3]) - 1)
        J[3,1] = 0.0
        J[1,2] = -0.05*(1-2x[3])
        J[2,2] =  0.1*(1-2x[3])*(1-1.9x[1])
        J[3,2] = 0.2
        J[1,3] = 0.1*(x[2] + 0.35)
        J[2,3] = -0.2*(x[2] + 0.35)*(1-1.9x[1])
        J[3,3] = 3.78(1-2x[3])
    end
end

"""
```julia
standardmap(u0=0.001rand(2); k = 0.971635)
```
```math
\\begin{aligned}
\\theta_{n+1} &= \\theta_n + p_{n+1} \\\\
p_{n+1} &= p_n + k\\sin(\\theta_n)
\\end{aligned}
```
The standard map (also known as Chirikov standard map) is a two dimensional,
area-preserving chaotic mapping due to Chirikov [1]. It is one of the most studied
chaotic systems and by far the most studied Hamiltonian (area-preserving) mapping.

The map corresponds to the  Poincaré's surface of section of the kicked rotor system.
Changing the non-linearity parameter `k` transitions the system from completely periodic
motion, to quasi-periodic, to local chaos (mixed phase-space) and finally to global
chaos.

The default parameter `k` is the critical parameter where the golden-ratio torus is
destroyed, as was calculated by Greene [2]. The e.o.m. considers the angle variable
`θ` to be the first, and the angular momentum `p` to be the second, while
both variables
are always taken modulo 2π (the mapping is on the [0,2π)² torus).

The parameter container has the parameters in the same order as stated in this
function's documentation string.

[1] : B. V. Chirikov, Preprint N. **267**, Institute of
Nuclear Physics, Novosibirsk (1969)

[2] : J. M. Greene, J. Math. Phys. **20**, pp 1183 (1979)
"""
function standardmap(u0=0.001rand(2); k = 0.971635)
    return DDS(standardmap_eom, u0, [k], standardmap_jacob)
end
@inbounds function standardmap_eom(x, par, n)
    theta = x[1]; p = x[2]
    p += par[1]*sin(theta)
    theta += p
    while theta >= twopi; theta -= twopi; end
    while theta < 0; theta += twopi; end
    while p >= twopi; p -= twopi; end
    while p < 0; p += twopi; end
    return SVector(theta, p)
end
@inbounds standardmap_jacob(x, p, n) =
@SMatrix [1 + p[1]*cos(x[1])    1;
          p[1]*cos(x[1])        1]

"""
```julia
coupledstandardmaps(M::Int, u0 = 0.001rand(2M); ks = ones(M), Γ = 1.0)
```
```math
\\begin{aligned}
\\theta_{i}' &= \\theta_i + p_{i}' \\\\
p_{i}' &= p_i + k_i\\sin(\\theta_i) - \\Gamma \\left[
\\sin(\\theta_{i+1} - \\theta_{i}) + \\sin(\\theta_{i-1} - \\theta_{i})
\\right]
\\end{aligned}
```
A discrete system of `M` nonlinearly coupled standard maps, first
introduced in [1] to study diffusion and chaos thresholds.
The *total* dimension of the system
is `2M`. The maps are coupled through `Γ`
and the `i`-th map has a nonlinear parameter `ks[i]`.

The first `M` entries of the state are the angles, the last `M` are the momenta.

[1] : H. Kantz & P. Grassberger, J. Phys. A **21**, pp 127–133 (1988)
"""
function coupledstandardmaps end
using SparseArrays
function coupledstandardmaps(M::Int, u0 = 0.001rand(2M);
    ks = ones(M), Γ = 1.0)

    SV = SVector{M, Int}
    idxs = SV(1:M...) # indexes of thetas
    idxsm1 = SV(circshift(idxs, +1)...)  #indexes of thetas - 1
    idxsp1 = SV(circshift(idxs, -1)...)  #indexes of thetas + 1

    csm = CoupledStandardMaps{M}(idxs, idxsm1, idxsp1)
    J = zeros(eltype(u0), 2M, 2M)
    # Set ∂/∂p entries (they are eye(M,M))
    # And they dont change they are constants
    for i in idxs
        J[i, i+M] = 1
        J[i+M, i+M] = 1
    end
    sparseJ = sparse(J)
    p = (ks, Γ)
    csm(sparseJ, u0, p, 0)
    return DDS(csm, u0, p, csm, sparseJ)
end
struct CoupledStandardMaps{N}
    idxs::SVector{N, Int}
    idxsm1::SVector{N, Int}
    idxsp1::SVector{N, Int}
end
function (f::CoupledStandardMaps{N})(xnew::AbstractVector, x, p, n) where {N}
    ks, Γ = p
    @inbounds for i in f.idxs

        xnew[i+N] = mod2pi(
            x[i+N] + ks[i]*sin(x[i]) -
            Γ*(sin(x[f.idxsp1[i]] - x[i]) + sin(x[f.idxsm1[i]] - x[i]))
        )

        xnew[i] = mod2pi(x[i] + xnew[i+N])
    end
    return nothing
end
function (f::CoupledStandardMaps{M})(
    J::AbstractMatrix, x, p, n) where {M}

    ks, Γ = p
    # x[i] ≡ θᵢ
    # x[[idxsp1[i]]] ≡ θᵢ+₁
    # x[[idxsm1[i]]] ≡ θᵢ-₁
    @inbounds for i in f.idxs
        cosθ = cos(x[i])
        cosθp= cos(x[f.idxsp1[i]] - x[i])
        cosθm= cos(x[f.idxsm1[i]] - x[i])
        J[i+M, i] = ks[i]*cosθ + Γ*(cosθp + cosθm)
        J[i+M, f.idxsm1[i]] = - Γ*cosθm
        J[i+M, f.idxsp1[i]] = - Γ*cosθp
        J[i, i] = 1 + J[i+M, i]
        J[i, f.idxsm1[i]] = J[i+M, f.idxsm1[i]]
        J[i, f.idxsp1[i]] = J[i+M, f.idxsp1[i]]
    end
    return J
end

"""
```julia
henon(u0=zeros(2); a = 1.4, b = 0.3)
```
```math
\\begin{aligned}
x_{n+1} &= 1 - ax^2_n+y_n \\\\
y_{n+1} & = bx_n
\\end{aligned}
```
The Hénon map is a two-dimensional mapping due to Hénon [1] that can display a strange
attractor (at the default parameters). In addition, it also displays many other aspects
of chaos, like period doubling or intermittency, for other parameters.

According to the author, it is a system displaying all the properties of the
Lorentz system (1963) while being as simple as possible.
Default values are the ones used in the original paper.

The parameter container has the parameters in the same order as stated in this
function's documentation string.

[1] : M. Hénon, Commun.Math. Phys. **50**, pp 69 (1976)
"""
function henon(u0=zeros(2); a = 1.4, b = 0.3)
    return DDS(hoop, u0, [a,b], hoop_jac)
end # should give lyapunov exponents [0.4189, -1.6229]
hoop(x, p, n) = SVector{2}(1.0 - p[1]*x[1]^2 + x[2], p[2]*x[1])
hoop_jac(x, p, n) = @SMatrix [-2*p[1]*x[1] 1.0; p[2] 0.0]

function henon_iip(u0=zeros(2); a = 1.4, b = 0.3)
    return DDS(hiip, u0, [a, b], hiip_jac)
end
function hiip(dx, x, p, n)
    dx[1] = 1.0 - p[1]*x[1]^2 + x[2]
    dx[2] = p[2]*x[1]
    return
end
function hiip_jac(J, x, p, n)
    J[1,1] = -2*p[1]*x[1]
    J[1,2] = 1.0
    J[2,1] = p[2]
    J[2,2] = 0.0
    return
end

"""
```julia
logistic(x0 = rand(); r = 4.0)
```
```math
x_{n+1} = rx_n(1-x_n)
```
The logistic map is an one dimensional unimodal mapping due to May [1] and is used by
many as the archetypal example of how chaos can arise from very simple equations.

Originally intentend to be a discretized model of polulation dynamics, it is now famous
for its bifurcation diagram, an immensly complex graph that that was shown
be universal by Feigenbaum [2].

The parameter container has the parameters in the same order as stated in this
function's documentation string.

[1] : R. M. May, Nature **261**, pp 459 (1976)

[2] : M. J. Feigenbaum, J. Stat. Phys. **19**, pp 25 (1978)
"""
function logistic(x0=rand(); r = 4.0)
    return DDS(logistic_eom, x0, [r], logistic_jacob)
end
logistic_eom(x, p, n) = p[1]*x*(1-x)
logistic_jacob(x, p, n) = p[1]*(1-2x)

"""
    pomaeu_manneville(u0 = 0.2; z = 2.5)
The Pomeau-Manneville map is a one dimensional discrete map which is
characteristic for displaying intermittency [1]. Specifically, for
z > 2 the average time between chaotic bursts diverges, while
for z > 2.5, the map iterates are long range correlated [2].

Notice that here we are providing the "symmetric" version:
```math
x_{n+1} = \\begin{cases}
-4x_n + 3, & \\quad x_n \\in (0.5, 1] \\\\
x_n(1 + |2x_n|^{z-1}), & \\quad |x_n| \\le 0.5 \\\\
-4x_n - 3, & \\quad x_n \\in [-1, 0.5)
\\end{cases}
```

[1] : Manneville & Pomeau, Comm. Math. Phys. **74** (1980)

[2] : Meyer et al., New. J. Phys **20** (2019)
"""
function pomeau_manneville(u0 = 0.2, z = 2.5)
    return DDS(pm_eom, u0, [z], pm_jac)
end
function pm_eom(x, p, n)
    if x < -0.5
        -4x - 3
    elseif -0.5 ≤ x ≤ 0.5
        @inbounds x*(1 + abs(2x)^(p[1]-1))
    else
        -4x + 3
    end
end
function pm_jac(x, p, n)
    if x < -0.5
        -4.0
    elseif -0.5 ≤ x ≤ 0.5
        @inbounds z = p[1]
        0.5(x^2 * 2^z * (z-1)*abs(x)^(z-3) + 2^z * abs(x)^(z-1) + 2)
    else
        -4.0
    end
end


immutable Uniform <: ContinuousUnivariateDistribution
    a::Float64
    b::Float64
    function Uniform(a::Real, b::Real)
	    if a < b
	    	new(float64(a), float64(b))
	    else
	    	error("a < b required for range [a, b]")
	    end
	end
end

Uniform() = Uniform(0.0, 1.0)

@_jl_dist_2p Uniform unif

entropy(d::Uniform) = log(d.b - d.a + 1.0)

insupport(d::Uniform, x::Number) = isreal(x) && d.a <= x <= d.b

kurtosis(d::Uniform) = -6.0 / 5.0

mean(d::Uniform) = (d.a + d.b) / 2.0

median(d::Uniform) = (d.a + d.b) / 2.0

function mgf(d::Uniform, t::Real)
	a, b = d.a, d.b
	return (exp(t * b) - exp(t * a)) / (t * (b - a))
end

function cf(d::Uniform, t::Real)
	a, b = d.a, d.b
	return (exp(im * t * b) - exp(im * t * a)) / (im * t * (b - a))
end

modes(d::Uniform) = error("The uniform distribution has no modes")

rand(d::Uniform) = d.a + (d.b - d.a) * rand()

skewness(d::Uniform) = 0.0

function var(d::Uniform)
	w = d.b - d.a
	return w * w / 12.0
end


function sieve(x::Int)
    sieve = fill(true, x)
    for i in 2:isqrt(x)
        if sieve[i]
            j = 2i
            while j <= x
                sieve[j] = false
                j += i
            end
        end
    end
    primes = findall(sieve)
    popfirst!(primes)
    return primes
end