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

using Markdown
using InteractiveUtils

# ╔═╡ e6106427-6fb1-4a3f-a556-dc60d190ca20
# hideall

using Genie,Genie.Router;

# ╔═╡ 95cc40c3-7b57-4b54-b80b-0b57fa0e347f
# hideall

using Genie.Requests  # Genie and Genie.Router already imported above

# ╔═╡ 43a9d450-3269-11ec-2353-1b13386fbc8d
md"""
# Advanced routing techniques

Genie's router can be considered the brain of the app, matching web requests to functions, extracting and setting up the request's variables and the execution environment, and invoking the response methods. Such power is accompanied by a powerful set of features for defining routes. Let's dive into these.

"""

# ╔═╡ 3427c72a-a73c-4cb9-a0f1-4ae6671394a3
md"""

## Static routing

Starting with the simplest case, we can register "plain" routes by using the `route` method. The method takes as its required arguments the URI pattern and the function that should be invoked in order to provide the response. The router supports two ways of registering routes, either `route(pattern::String, f::Function)` or `route(f::Function, pattern::String)`. The first syntax is for passing function references -- while the second is for defining inline function.
"""

# ╔═╡ 9bb74978-18b7-42f3-9fd7-b25c1ce676de
md"""

### Example

The following snippet defines a function `greet` which returns the "Welcome to Genie!" string. We use the function as our route handler, by passing it as the second argument to the `route` method.
"""

# ╔═╡ e00bd027-f54e-46c8-acb9-b031b3f06f1b
md"""

```julia
julia> using Genie, Genie.Router

julia> greet() = "Welcome to Genie!"

julia> route("/greet", greet)          # [GET] /greet => greet

julia> up() # start the server
```
"""

# ╔═╡ 452263da-3412-4356-b336-9161d22f8ac3
# hideall

greet() = "Welcome to Genie!";

# ╔═╡ c50433e6-1bb1-434f-9924-3446ecaa0a95
# hideall

route("/greet", greet)

# ╔═╡ 7cc2446e-3e17-4b23-b2aa-c3d754889550
# hideall

up(); # start the server

# ╔═╡ 6746b8e6-8ae7-4cae-84a6-13bff5b17863
# hideall

down(); # stop the server

# ╔═╡ 2234a9ac-150b-41dd-aefc-d46d96f69761
md"""
If you use your browser to navigate to <http://127.0.0.1:8000/greet> you'll see the code in action.

However, defining a dedicated handler function might be overkill for simple cases like this. As such, Genie allows registering in-line handlers:
"""

# ╔═╡ 95de3f62-6428-4c17-aafd-173892eafa9e
md"""

```julia
route("/bye") do
	"Good bye!"
end
```
"""

# ╔═╡ 02234d5a-0cb9-4687-9a2b-af1036b8a126
# hideall

route("/bye") do
	"Good bye!"
end # # [GET] /bye => getfield(Main, Symbol("##3#4"))()

# ╔═╡ d4e34ce0-f2f8-4f1d-b06c-fb5ecb5d84c4
# hideall

up();

# ╔═╡ dc0e4134-d611-48ab-a344-42b7b4ce7e54
# hideall

down();

# ╔═╡ 5dcd29af-b256-42c1-b4c0-13c1b003b142
md"""
You can just navigate to <http://127.0.0.1:8000/bye> -- the route is instantly available in the app.
"""

# ╔═╡ dd3692e0-bf7b-4174-8619-011b732f4700
md"""
---
**HEADS UP**

The routes are added in the order in which they are defined but are matched from newest to oldest. This means that you can define a new route to overwrite a previously defined one.

Unlike Julia's multiple dispatch, Genie's router won't match the most specific rule, but the first matching one. So if, for example, you register a route to match `/*`, it will handle all the requests, even if you have previously defined more specific routes. As a side-note, you can use this technique to temporarily divert all users to a maintenance page.

---
"""

# ╔═╡ fb90d39c-03fb-453f-bc71-cefde15b7189
md"""
## Dynamic routing (using route parameters)

Static routing works great for fixed URLs. But what if we have dynamic URLs, where the components map to information in the backend (like database IDs) and vary with each request? For example, how would we handle a URL like "/customers/57943/orders/458230", where 57943 is the customer id and 458230 is the order id.

Such situations are handled through dynamic routing or route parameters. For the previous example, "/customers/57943/orders/458230", we can define a dynamic route as "/customers/:customer_id/orders/:order_id". Upon matching the request, the Router will unpack the values and expose them in the `params` collection.

### Example
"""

# ╔═╡ 4bdafae5-426b-4735-b0ba-57ad82d8a8cb
md"""

```julia

using Genie, Genie.Router, Genie.Requests

route("/customers/:customer_id/orders/:order_id") do
  "You asked for the order $(payload(:order_id)) for customer $(payload(:customer_id))"
end

up()
```
"""

# ╔═╡ 2345f769-cf5f-421d-9237-a86d79f470a8
# hideall

route("/customers/:customer_id/orders/:order_id") do
	"You asked for the order $(payload(:order_id)) for customer $(payload(:customer_id))"
end;

# ╔═╡ 98a2f767-3605-420e-89af-5b1d974465e0
md"""
you can type something like this in browser to verify

http://127.0.0.1:8000/customers/34234234234/orders/34burger_discount
"""

# ╔═╡ 586e9672-5878-457f-aad7-24ac0173b550
# hideall

up();

# ╔═╡ 8af441f4-d446-4881-af93-b38ea3390916
# hideall

down();

# ╔═╡ cda56305-7c53-4a05-8dd8-56d4edf06369
md"""

## Routing methods (`GET`, `POST`, `PUT`, `PATCH`, `DELETE`, `OPTIONS`)

By default, routes handle `GET` requests, since these are the most common. In order to define routes for handling other types of request methods, we need to pass the `method` keyword argument, indicating the HTTP method. Genie's Router supports `GET`, `POST`, `PUT`, `PATCH`, `DELETE`, `OPTIONS` methods.

The router defines and exports constants for each of these as `Router.GET`, `Router.POST`, `Router.PUT`, `Router.PATCH`, `Router.DELETE`, and `Router.OPTIONS`.

"""

# ╔═╡ 1d6a2d14-5f98-4503-b756-6128b62b1bea
md"""

### Example

We can setup the following `PATCH` route:
"""

# ╔═╡ 8b243b7f-5a3d-4809-9dce-e21b0e16d5e7
md"""

```julia
using Genie, Genie.Router, Genie.Requests

route("/patch_stuff", method = PATCH) do
  "Stuff to patch"
end

up()
```
"""

# ╔═╡ 03e98ab3-712b-49e5-9c28-381df88e7eb1
# hideall

route("/patch_stuff", method= PATCH) do
	"Stuff to patch"
end;

# ╔═╡ af21fd1b-b06a-4193-95e1-9a97c7fe9f7a
# hideall

up();

# ╔═╡ 14eb943e-b7e1-41b8-84d1-7cbf0b47b22c
md"""
And we can test it using `HTTP` package:

```julia
julia> using HTTP

julia> HTTP.request("PATCH", "http://127.0.0.1:8000/patch_stuff").body |> String
2019-08-19 14:23:46:INFO:Main: /patch_stuff 200
```

"""

# ╔═╡ 1e388f30-bd1d-4cab-96ce-98c98e2f0c21
# hideall

#using HTTP;

# ╔═╡ daaac609-bd82-42e9-bd15-998fef170746
# hideall

#HTTP.request("PATCH", "http://127.0.0.1:8000/patch_stuff").body |> String

# ╔═╡ 9eee3b60-65df-4312-bfbf-402f5efdbb43
# hideall

down();

# ╔═╡ 330f8fa5-6b91-488b-960d-58591c593c79
md"""
By sending a request with the `PATCH` method, our route is triggered. Consequently, we access the response body and convert it to a string, which is "Stuff to patch", corresponding to our response.

### Named routes

Genie allows tagging routes with names. This is a very powerful feature, to be used in conjunction with the `Router.tolink` method, for dynamically generating URLs towards the routes. The advantage of this technique is that if we refer the route by name and generate the links dynamically using `tolink`, as long as the name of the route stays the same, if we change the route pattern, all the URLs will automatically match the new route definiton.

In order to name a route we need to use the `named` keyword argument, which expects a `Symbol`.

### Example
"""

# ╔═╡ baf44a2a-bbc3-463a-ac8e-9879b1e6e843
md"""

```julia
using Genie, Genie.Router, Genie.Requests

route("/customers/:customer_id/orders/:order_id", named = :get_customer_order) do
  "Looking up order $(payload(:order_id)) for customer  $(payload(:customer_id))"
end
```
"""

# ╔═╡ 6e38af88-fe97-42e6-a821-9f7ace5938bf
# hideall

route("/customers/:customer_id/orders/:order_id", named = :get_customer_order) do
	"Looking up order $(payload(:order_id)) for customer $(payload(:customer_id))"
end; #  [GET] /customers/:customer_id/orders/:order_id => getfield(Main, Symbol("##5#6"))()

# ╔═╡ 8347e271-01ad-4bb6-aee3-048884250296
md"""
We can check the status of our route with:
"""

# ╔═╡ 319ec0d0-c372-4494-b14f-82c6b0ec1521
md"""

```julia
julia> Genie.Router.routes()
```
"""

# ╔═╡ 13816d4c-5bcc-4b6b-b185-736ad311af8f
# hideall

Genie.Router.routes()

# ╔═╡ 3c95a47d-0847-4cae-b9e0-20461a90d41f
md"""

---
**HEADS UP**

For consistency, Genie names all the routes. However, the auto-generated name is state dependent. So, if you change the route, it's possible that the name will change as well. Thus, it's best to explicitly name the routes if you plan on referencing them throughout the app.

---

We can confirm this by adding an anonymous route:

"""

# ╔═╡ daf764d0-7b41-46e6-a2e7-df7692246c40
# hideall

route("/foo") do
	"foo"
end;

# ╔═╡ 4de5ee94-67c5-45ad-8faf-2eee9085d214
md"""

```julia
route("/foo") do
  "foo"
end  #  [GET] /foo => getfield(Main, Symbol("##7#8"))()

julia> Genie.Router.routes()
OrderedCollections.OrderedDict{Symbol,Genie.Router.Route} with 2 entries:
  :get_customer_order => [GET] /customers/:customer_id/orders/:order_id => getfield(Main, Symbol("##5#6"))()
  :get_foo            => [GET] /foo => getfield(Main, Symbol("##7#8"))()
```
"""

# ╔═╡ 06615877-44a6-4dfc-87ec-dba8b9b50923
md"""
The new route has been automatically named `get_foo`, based on the method and URI pattern.

### Links to routes

We can use the name of the route to link back to it through the `linkto` method.

### Example

Let's start with the previously defined two routes:

```julia
julia> Genie.Router.routes()
OrderedCollections.OrderedDict{Symbol,Genie.Router.Route} with 2 entries:
  :get_customer_order => [GET] /customers/:customer_id/orders/:order_id => getfield(Main, Symbol("##5#6"))()
  :get_foo            => [GET] /foo => getfield(Main, Symbol("##7#8"))()
```
"""

# ╔═╡ 05db4144-2b1c-4262-8dc8-d4dd5cfb786f
md"""
Static routes such as `:get_foo` are straightforward to target:
"""

# ╔═╡ 5b711e10-58bc-4e52-bf69-c3bb71816ac6
md"""

```julia
julia> linkto(:get_foo)
```
"""

# ╔═╡ ea097103-c7ef-4873-991c-1f2747c1e388
# hideall

linkto(:get_foo)

# ╔═╡ 0c2548e6-c289-4d77-8ad3-d655a6af6197
md"""
For dynamic routes, it's a bit more involved as we need to supply the values for each of the parameters, as keyword arguments:
"""

# ╔═╡ 654595db-c64d-4ecd-b035-c4019a18aa05
md"""

```julia
julia> linkto(:get_customer_order, customer_id = 1234, order_id = 5678)
```
"""

# ╔═╡ f2630c63-e33c-4395-8670-fe7c053f3951
# hideall

linkto(:get_customer_order, customer_id = 1234, order_id = 5678)

# ╔═╡ 8a3d030f-d89d-4e95-97c0-f2ae895f68a4
md"""
The `linkto` should be used in conjunction with the HTML code for generating links, ie:

```
<a href="$(linkto(:get_foo))">Foo</a>
```
"""

# ╔═╡ 41d29f5a-e3f2-4b38-898d-4bc9efa27c92
md"""

## Listing routes

At any time we can check which routes are registered with `Router.routes`:
"""

# ╔═╡ 6928b47a-2d53-4a48-b330-10b4cd078247
md"""

```julia
julia> Genie.Router.routes();
```
"""

# ╔═╡ b5f1cfa6-a60c-4b2d-a8c7-73d14bff5873
# hideall

routes()

# ╔═╡ ff337fb2-ce52-4e4e-ac81-eef3fe964ce5
md"""

### The `Route` type

The routes are represented internally by the `Route` type which has 4 fields:

* `method::String` - for storing the method of the route (`GET`, `POST`, etc)
* `path::String` - represents the URI pattern to be matched against
* `action::Function` - the route handler to be executed when the route is matched
* `name::Union{Symbol,Nothing}` - the name of the route

## Removing routes

We can delete routes from the stack by calling the `delete!` method and passing the collection of routes and the name of the route to be removed. The method returns the collection of (remaining) routes

### Example
"""

# ╔═╡ b5b552b4-6b2a-419e-8276-8335b678871d
md"""
Listing all routes:
"""

# ╔═╡ 7e3a3909-d2a7-456b-955e-7545e6f7db4c
md"""

```julia
julia> routes()
```

"""

# ╔═╡ 694be0b4-1c62-4386-913b-f64f7aa1911c
# hideall

routes()

# ╔═╡ 160f18a8-6819-4e02-a238-4a3c257d55d4
md"""
Deleting route:

```julia
julia> Router.delete!(:get_foo)
OrderedCollections.OrderedDict{Symbol,Genie.Router.Route} with 1 entry:
  :get_customer_order => [GET] /customers/:customer_id/orders/:order_id => getfield(Main, Symbol("##3#4"))()
```
"""

# ╔═╡ ee82e180-62cd-4c06-b415-7be50677d654
md"""
## Matching routes by type of arguments

By default route parameters are parsed into the `payload` collection as `SubString{String}`:

"""

# ╔═╡ 36040f4c-74b1-4c4d-a115-88e2ec573a03
md"""

```julia
using Genie, Genie.Router, Genie.Requests

route("/customers/new/:customer_id/orders/:order_id") do
	"Order ID has type $(payload(:order_id) |> typeof) // Customer ID has type $(payload(:customer_id) |> typeof)"
end
```
"""

# ╔═╡ 97dc0184-090b-458d-b2e9-ee6750779a50
# hideall

route("/customers/new/:customer_id/orders/:order_id") do
	"Order ID has type $(payload(:order_id) |> typeof) // Customer ID has type $(payload(:customer_id) |> typeof)"
end;

# ╔═╡ f5b121a4-46e2-48c7-9c86-47acaafb2c5b
md"""
This will output `Order ID has type SubString{String} // Customer ID has type SubString{String}`

However, for such a case, we'd very much prefer to receive our data as `Int` to avoid an explicit conversion -- _and_ to match only numbers. Genie supports such a workflow by allowing type annotations to route parameters:

"""

# ╔═╡ 380649f1-e194-4c3b-8e9c-5643b58a2300
route("/customers/newtwo/:customer_id::Int/orders/:order_id::Int", named= :get_customer_order) do
	"Order ID has type $(payload(:order_id) |> typeof) // Customer ID has type $(payload(:customer_id) |> typeof)"
end;

# ╔═╡ 64ef981f-befe-4f5d-bd89-e95a95c3a8e7
md"""

```julia
julia> HTTP.request("GET", "http://127.0.0.1:8000/customers/newtwo/34234/orders/24")
```
"""

# ╔═╡ 96f18f32-9b5f-42e4-8d65-652754da73d3
md"""Notice how we've added type annotations to `:customer_id` and `:order_id` in the form `:customer_id::Int` and `:order_id::Int`.

"""

# ╔═╡ a4db6b7b-1cec-4096-a74a-08f177e77880
md"""
However, attempting to access the URL `http://127.0.0.1:8000/customers/10/orders/20` will fail:

```julia
Failed to match URI params between Int64::DataType and 10::SubString{String}
MethodError(convert, (Int64, "10"), 0x00000000000063fe)
/customers/10/orders/20 404
```

As you can see, Genie attempts to convert the types from the default `SubString{String}` to `Int` – but doesn't know how. It fails, can't find other matching routes and returns a `404 Not Found` response.

"""

# ╔═╡ 8111a773-6568-47e1-90c6-c63a3f193921
md"""
### Type conversion in routes

The error is easy to address though: we need to provide a type converter from `SubString{String}` to `Int`.

```julia
Base.convert(::Type{Int}, v::SubString{String}) = parse(Int, v)
```

Once we register the converter in `Base`, our request will be correctly handled, resulting in `Order ID has type Int64 // Customer ID has type Int64`
"""

# ╔═╡ 2d995a3b-9739-427b-9280-0f7ea6c3a352
md"""
## Matching individual URI segments

Besides matching the full route, Genie also allows matching individual URI segments. That is, enforcing that the various route parameters obey a certain pattern. In order to introduce constraints for route parameters we append `#pattern` at the end of the route parameter.

### Example

For instance, let's assume that we want to implement a localized website where we have a URL structure like: `mywebsite.com/en`, `mywebsite.com/es`, `mywebsite.com/in` and `mywebsite.com/de`. We can define a dynamic route and extract the locale variable to serve localized content:

```julia
route(":locale", TranslationsController.index)
```

This will work very well, matching requests and passing the locale into our code within the `payload(:locale)` variable. However, it will also be too greedy, virtually matching all the requests, including things like static files (ie `mywebsite.com/favicon.ico`). We can constrain what the `:locale` variable can match, by appending the pattern (a regex pattern):

```julia
route(":locale#(en|es|de)", TranslationsController.index)
```

---
**HEADS UP**

Keep in mind not to duplicate application logic. For instance, if you have an array of supported locales, you can use that to dynamically generate the pattern -- routes can be fully dynamically generated!

```julia
const LOCALE = ":locale#($(join(TranslationsController.AVAILABLE_LOCALES, '|')))"

route("/$LOCALE", TranslationsController.index, named = :get_index)
```

---

## The `params` collection

It's good to know that the router bundles all the parameters of the current request into the `params` collection (a `Dict{Symbol,Any}`). This contains valuable information, such as route parameters, query params, POST payload, the original HTTP.Request and HTTP.Response objects, etc. In general it's recommended not to access the `params` collection directly but through the utility methods defined by `Genie.Requests` and `Genie.Responses` -- but knowing about `params` might come in handy for advanced users.
"""

# ╔═╡ ec5f638b-8877-470d-9368-d5a13da98d38
# hideall

down();

# ╔═╡ 00000000-0000-0000-0000-000000000001
PLUTO_PROJECT_TOML_CONTENTS = """
[deps]
Genie = "c43c736e-a2d1-11e8-161f-af95117fbd1e"

[compat]
Genie = "~4.9.1"
"""

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

[[ArgParse]]
deps = ["Logging", "TextWrap"]
git-tree-sha1 = "3102bce13da501c9104df33549f511cd25264d7d"
uuid = "c7e460c6-2fb9-53a9-8c5b-16f535851c63"
version = "1.1.4"

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

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

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

[[CSTParser]]
deps = ["Tokenize"]
git-tree-sha1 = "f9a6389348207faf5e5c62cbc7e89d19688d338a"
uuid = "00ebfdb7-1f24-5e51-bd34-a7502290713f"
version = "3.3.0"

[[CodeTracking]]
deps = ["InteractiveUtils", "UUIDs"]
git-tree-sha1 = "9aa8a5ebb6b5bf469a7e0e2b5202cf6f8c291104"
uuid = "da1fd8a2-8d9e-5ec2-8556-3022fb5608a2"
version = "1.0.6"

[[CommonMark]]
deps = ["Crayons", "JSON", "URIs"]
git-tree-sha1 = "4aff51293dbdbd268df314827b7f409ea57f5b70"
uuid = "a80b9123-70ca-4bc0-993e-6e3bcb318db6"
version = "0.8.5"

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

[[Crayons]]
git-tree-sha1 = "249fe38abf76d48563e2f4556bebd215aa317e15"
uuid = "a8cc5b0e-0ffa-5ad4-8c14-923d3ee1735f"
version = "4.1.1"

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

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

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

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

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

[[EzXML]]
deps = ["Printf", "XML2_jll"]
git-tree-sha1 = "0fa3b52a04a4e210aeb1626def9c90df3ae65268"
uuid = "8f5d6c58-4d21-5cfd-889c-e3ad7ee6a615"
version = "1.1.0"

[[FilePathsBase]]
deps = ["Compat", "Dates", "Mmap", "Printf", "Test", "UUIDs"]
git-tree-sha1 = "04d13bfa8ef11720c24e4d840c0033d145537df7"
uuid = "48062228-2e41-5def-b9a4-89aafe57970f"
version = "0.9.17"

[[FileWatching]]
uuid = "7b1f6079-737a-58dc-b8bc-7a2ca5c1b5ee"

[[GMP_jll]]
deps = ["Artifacts", "Libdl"]
uuid = "781609d7-10c4-51f6-84f2-b8444358ff6d"

[[Genie]]
deps = ["ArgParse", "Dates", "Distributed", "EzXML", "FilePathsBase", "HTTP", "HttpCommon", "Inflector", "JSON3", "JuliaFormatter", "Logging", "Markdown", "MbedTLS", "Millboard", "Nettle", "OrderedCollections", "Pkg", "REPL", "Random", "Reexport", "Revise", "SHA", "Serialization", "Sockets", "UUIDs", "Unicode", "VersionCheck", "YAML"]
git-tree-sha1 = "d0362686961375e910e437c76ff4ed6f31b54fef"
uuid = "c43c736e-a2d1-11e8-161f-af95117fbd1e"
version = "4.9.1"

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

[[HttpCommon]]
deps = ["Dates", "Nullables", "Test", "URIParser"]
git-tree-sha1 = "46313284237aa6ca67a6bce6d6fbd323d19cff59"
uuid = "77172c1b-203f-54ac-aa54-3f1198fe9f90"
version = "0.5.0"

[[Inflector]]
deps = ["Unicode"]
git-tree-sha1 = "8555b54ddf27806b070ce1d1cf623e1feb13750c"
uuid = "6d011eab-0732-4556-8808-e463c76bf3b6"
version = "1.0.1"

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

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

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

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

[[JSON3]]
deps = ["Dates", "Mmap", "Parsers", "StructTypes", "UUIDs"]
git-tree-sha1 = "7d58534ffb62cd947950b3aa9b993e63307a6125"
uuid = "0f8b85d8-7281-11e9-16c2-39a750bddbf1"
version = "1.9.2"

[[JuliaFormatter]]
deps = ["CSTParser", "CommonMark", "DataStructures", "Pkg", "Tokenize"]
git-tree-sha1 = "da0c8830cebe2337093bb46fc117498517a9df80"
uuid = "98e50ef6-434e-11e9-1051-2b60c6c9e899"
version = "0.21.2"

[[JuliaInterpreter]]
deps = ["CodeTracking", "InteractiveUtils", "Random", "UUIDs"]
git-tree-sha1 = "6ca01d8e5bc75d178e8ac2d1f741d02946dc1853"
uuid = "aa1ae85d-cabe-5617-a682-6adf51b2e16a"
version = "0.9.2"

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

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

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

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

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

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

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

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

[[LoweredCodeUtils]]
deps = ["JuliaInterpreter"]
git-tree-sha1 = "f46e8f4e38882b32dcc11c8d31c131d556063f39"
uuid = "6f1432cf-f94c-5a45-995e-cdbf5db27b0b"
version = "2.2.0"

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

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

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

[[Millboard]]
git-tree-sha1 = "ea6a5b7e56e76d8051023faaa11d91d1d881dac3"
uuid = "39ec1447-df44-5f4c-beaa-866f30b4d3b2"
version = "0.2.5"

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

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

[[Nettle]]
deps = ["Libdl", "Nettle_jll"]
git-tree-sha1 = "a68340b9edfd98d0ed96aee8137cb716ea3b6dea"
uuid = "49dea1ee-f6fa-5aa6-9a11-8816cee7d4b9"
version = "0.5.1"

[[Nettle_jll]]
deps = ["Artifacts", "GMP_jll", "JLLWrappers", "Libdl", "Pkg"]
git-tree-sha1 = "eca63e3847dad608cfa6a3329b95ef674c7160b4"
uuid = "4c82536e-c426-54e4-b420-14f461c4ed8b"
version = "3.7.2+0"

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

[[Nullables]]
git-tree-sha1 = "8f87854cc8f3685a60689d8edecaa29d2251979b"
uuid = "4d1e1d77-625e-5b40-9113-a560ec7a8ecd"
version = "1.0.0"

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

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

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

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

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

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

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

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

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

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

[[Revise]]
deps = ["CodeTracking", "Distributed", "FileWatching", "JuliaInterpreter", "LibGit2", "LoweredCodeUtils", "OrderedCollections", "Pkg", "REPL", "Requires", "UUIDs", "Unicode"]
git-tree-sha1 = "2f9d4d6679b5f0394c52731db3794166f49d5131"
uuid = "295af30f-e4ad-537b-8983-00126c2a3abe"
version = "3.3.1"

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

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

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

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

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

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

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

[[StringEncodings]]
deps = ["Libiconv_jll"]
git-tree-sha1 = "50ccd5ddb00d19392577902f0079267a72c5ab04"
uuid = "69024149-9ee7-55f6-a4c4-859efe599b68"
version = "0.3.5"

[[StructTypes]]
deps = ["Dates", "UUIDs"]
git-tree-sha1 = "d24a825a95a6d98c385001212dc9020d609f2d4f"
uuid = "856f2bd8-1eba-4b0a-8007-ebc267875bd4"
version = "1.8.1"

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

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

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

[[TextWrap]]
git-tree-sha1 = "9250ef9b01b66667380cf3275b3f7488d0e25faf"
uuid = "b718987f-49a8-5099-9789-dcd902bef87d"
version = "1.0.1"

[[Tokenize]]
git-tree-sha1 = "0952c9cee34988092d73a5708780b3917166a0dd"
uuid = "0796e94c-ce3b-5d07-9a54-7f471281c624"
version = "0.5.21"

[[URIParser]]
deps = ["Unicode"]
git-tree-sha1 = "53a9f49546b8d2dd2e688d216421d050c9a31d0d"
uuid = "30578b45-9adc-5946-b283-645ec420af67"
version = "0.4.1"

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

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

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

[[UrlDownload]]
deps = ["HTTP", "ProgressMeter"]
git-tree-sha1 = "05f86730c7a53c9da603bd506a4fc9ad0851171c"
uuid = "856ac37a-3032-4c1c-9122-f86d88358c8b"
version = "1.0.0"

[[VersionCheck]]
deps = ["Dates", "JSON3", "Logging", "Pkg", "Random", "Scratch", "UrlDownload"]
git-tree-sha1 = "89ef2431dd59344ebaf052d0737205854ded0c62"
uuid = "a637dc6b-bca1-447e-a4fa-35264c9d0580"
version = "0.2.0"

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

[[YAML]]
deps = ["Base64", "Dates", "Printf", "StringEncodings"]
git-tree-sha1 = "3c6e8b9f5cdaaa21340f841653942e1a6b6561e5"
uuid = "ddb6d928-2868-570f-bddf-ab3f9cf99eb6"
version = "0.4.7"

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

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

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

# ╔═╡ Cell order:
# ╟─43a9d450-3269-11ec-2353-1b13386fbc8d
# ╟─3427c72a-a73c-4cb9-a0f1-4ae6671394a3
# ╟─9bb74978-18b7-42f3-9fd7-b25c1ce676de
# ╟─e00bd027-f54e-46c8-acb9-b031b3f06f1b
# ╠═e6106427-6fb1-4a3f-a556-dc60d190ca20
# ╠═452263da-3412-4356-b336-9161d22f8ac3
# ╠═c50433e6-1bb1-434f-9924-3446ecaa0a95
# ╠═7cc2446e-3e17-4b23-b2aa-c3d754889550
# ╠═6746b8e6-8ae7-4cae-84a6-13bff5b17863
# ╟─2234a9ac-150b-41dd-aefc-d46d96f69761
# ╟─95de3f62-6428-4c17-aafd-173892eafa9e
# ╠═02234d5a-0cb9-4687-9a2b-af1036b8a126
# ╠═d4e34ce0-f2f8-4f1d-b06c-fb5ecb5d84c4
# ╠═dc0e4134-d611-48ab-a344-42b7b4ce7e54
# ╟─5dcd29af-b256-42c1-b4c0-13c1b003b142
# ╟─dd3692e0-bf7b-4174-8619-011b732f4700
# ╟─fb90d39c-03fb-453f-bc71-cefde15b7189
# ╟─4bdafae5-426b-4735-b0ba-57ad82d8a8cb
# ╠═95cc40c3-7b57-4b54-b80b-0b57fa0e347f
# ╠═2345f769-cf5f-421d-9237-a86d79f470a8
# ╟─98a2f767-3605-420e-89af-5b1d974465e0
# ╠═586e9672-5878-457f-aad7-24ac0173b550
# ╠═8af441f4-d446-4881-af93-b38ea3390916
# ╟─cda56305-7c53-4a05-8dd8-56d4edf06369
# ╟─1d6a2d14-5f98-4503-b756-6128b62b1bea
# ╟─8b243b7f-5a3d-4809-9dce-e21b0e16d5e7
# ╠═03e98ab3-712b-49e5-9c28-381df88e7eb1
# ╠═af21fd1b-b06a-4193-95e1-9a97c7fe9f7a
# ╟─14eb943e-b7e1-41b8-84d1-7cbf0b47b22c
# ╠═1e388f30-bd1d-4cab-96ce-98c98e2f0c21
# ╠═daaac609-bd82-42e9-bd15-998fef170746
# ╠═9eee3b60-65df-4312-bfbf-402f5efdbb43
# ╟─330f8fa5-6b91-488b-960d-58591c593c79
# ╟─baf44a2a-bbc3-463a-ac8e-9879b1e6e843
# ╠═6e38af88-fe97-42e6-a821-9f7ace5938bf
# ╟─8347e271-01ad-4bb6-aee3-048884250296
# ╟─319ec0d0-c372-4494-b14f-82c6b0ec1521
# ╠═13816d4c-5bcc-4b6b-b185-736ad311af8f
# ╟─3c95a47d-0847-4cae-b9e0-20461a90d41f
# ╠═daf764d0-7b41-46e6-a2e7-df7692246c40
# ╟─4de5ee94-67c5-45ad-8faf-2eee9085d214
# ╟─06615877-44a6-4dfc-87ec-dba8b9b50923
# ╟─05db4144-2b1c-4262-8dc8-d4dd5cfb786f
# ╟─5b711e10-58bc-4e52-bf69-c3bb71816ac6
# ╠═ea097103-c7ef-4873-991c-1f2747c1e388
# ╟─0c2548e6-c289-4d77-8ad3-d655a6af6197
# ╟─654595db-c64d-4ecd-b035-c4019a18aa05
# ╠═f2630c63-e33c-4395-8670-fe7c053f3951
# ╟─8a3d030f-d89d-4e95-97c0-f2ae895f68a4
# ╟─41d29f5a-e3f2-4b38-898d-4bc9efa27c92
# ╟─6928b47a-2d53-4a48-b330-10b4cd078247
# ╠═b5f1cfa6-a60c-4b2d-a8c7-73d14bff5873
# ╟─ff337fb2-ce52-4e4e-ac81-eef3fe964ce5
# ╟─b5b552b4-6b2a-419e-8276-8335b678871d
# ╟─7e3a3909-d2a7-456b-955e-7545e6f7db4c
# ╠═694be0b4-1c62-4386-913b-f64f7aa1911c
# ╟─160f18a8-6819-4e02-a238-4a3c257d55d4
# ╟─ee82e180-62cd-4c06-b415-7be50677d654
# ╟─36040f4c-74b1-4c4d-a115-88e2ec573a03
# ╠═97dc0184-090b-458d-b2e9-ee6750779a50
# ╟─f5b121a4-46e2-48c7-9c86-47acaafb2c5b
# ╠═380649f1-e194-4c3b-8e9c-5643b58a2300
# ╟─64ef981f-befe-4f5d-bd89-e95a95c3a8e7
# ╟─96f18f32-9b5f-42e4-8d65-652754da73d3
# ╟─a4db6b7b-1cec-4096-a74a-08f177e77880
# ╟─8111a773-6568-47e1-90c6-c63a3f193921
# ╟─2d995a3b-9739-427b-9280-0f7ea6c3a352
# ╠═ec5f638b-8877-470d-9368-d5a13da98d38
# ╟─00000000-0000-0000-0000-000000000001
# ╟─00000000-0000-0000-0000-000000000002


text:
"""
    scc(di_ei, di_ej, scc_init::SCCInit)

Compute to which strongly connected component each vertex belongs.

`di_ei` and `di_ej` describe the edges such that the k-th component of di_ei and k-th component
of `di_ej` form an edge i.e `di_ei = [1, 1, 2]` and `di_ej = [3, 4, 3]` =>
  those three edges  (1, 3), (1, 4), (2, 3)

`di_ei` must be sorted asc to make this function work.

The last argument is a `SCCInit` struct which itself provides the memory for the functionality.
This speeds up the process as no new memory needs to be allocated.
"""
function scc(di_ei, di_ej, scc_init::SCCInit)
    len = length(di_ei)
    index_ei = scc_init.index_ei
    n = length(index_ei) - 1
    # compute the starting index for each vertex
    # bascially knowing where to start/end when looking for neighbors
    last = di_ei[1]
    prev_last = 1
    c = 2
    last_i = 0
    @inbounds for i in 2:len
        di_ei[i] == 0 && break
        if di_ei[i] > last
            j = last
            index_ei[(last + 1):di_ei[i]] .= c
            last = di_ei[i]
        end
        c += 1
        last_i = i
    end
    index_ei[(di_ei[last_i] + 1):end] .= c

    index_ei[1] = 1

    id = 0
    sccCount = 0

    ids = scc_init.ids
    low = scc_init.low
    on_stack = scc_init.on_stack
    group_id = scc_init.group_id

    ids .= -1
    low .= 0
    on_stack .= false
    stack = Int[]
    group_id .= 0
    c_group_id = 1

    # start dfs from each vertex if unconnected graph
    @inbounds for s in 1:n
        ids[s] != -1 && continue # if visited already continue
        dfs_work = Vector{Tuple{Int,Int}}()
        # the 0 in dfs_work represents whether it's the first time calling that vertex
        push!(dfs_work, (s, 0))
        dfs_stack = Int[]

        while !isempty(dfs_work)
            at, i = pop!(dfs_work)
            if i == 0
                on_stack[at] = true
                id += 1
                ids[at] = id
                low[at] = id
                push!(dfs_stack, at)
            end
            recurse = false
            # only works because `di_ei` is sorted
            for j in (index_ei[at] + i):(index_ei[at + 1] - 1)
                to = di_ej[j]
                # println("$to is successor of $at")
                if ids[to] == -1
                    push!(dfs_work, (at, j - index_ei[at] + 1))
                    push!(dfs_work, (to, 0))
                    recurse = true
                    break
                elseif on_stack[to]
                    low[at] = min(low[at], ids[to])
                end
            end
            recurse && continue
            # if at is the representative of the group
            if ids[at] == low[at]
                # take from stack as long as the representative doesn't appear
                # and put all of them in the same scc
                while true
                    w = pop!(dfs_stack)
                    on_stack[w] = false
                    group_id[w] = c_group_id
                    w == at && break
                end
                c_group_id += 1
            end
            if !isempty(dfs_work)
                w = at
                at, _ = dfs_work[end]
                low[at] = min(low[at], low[w])
            end
        end
    end
    return group_id
end

ext: jl


text:
# Use baremodule to shave off a few KB from the serialized `.ji` file
baremodule Raylib_jll
using Base
using Base: UUID
import JLLWrappers

JLLWrappers.@generate_main_file_header("Raylib")
JLLWrappers.@generate_main_file("Raylib", UUID("f9cae4b5-603c-5a13-b73f-1dfe71482639"))
end  # module Raylib_jll


text:
#!/usr/bin/env julia

using Luxor

using Test

using Random
Random.seed!(42)

function make_some_holes(fname)
    Drawing(1200, 1200, fname)
    origin()
    background("black")
    pagetiles = Tiler(1200, 1200, 17, 19, margin=50)
    for (pos, n) in pagetiles
      randomhue()
      rad = pagetiles.tilewidth
      ngon(pos, rad/2, rand(5:12), 0, :path)
      newsubpath()
      ngon(pos, rand(1:rad/3), rand(5:12), 0, :path, reversepath=true)
      fillstroke()
    end
    @test finish() == true
end

fname = "holes.pdf"
make_some_holes(fname)
println("...finished test: output in $(fname)")


text:
nt = 1000
seed = 99
m = ACQR_simula(nt,seed)
y = m["y"]
nx = 4

#######################
print("Testing ACQR_em_s_run: ")
mest = ACQR_em_s_run(y,nx,max_iter=10,txo=false)
if sum(mest["A"].^2) > 1e-6
    println("OK")
end


text:
struct PMA <: ParametrisedConvexApproximator
    n::Int
    m::Int
    i_max::Int
    NN::Flux.Chain
    # NN1::Flux.Chain
    # NN2::Flux.Chain
end

"""
Basic constructor PMA based on Flux.Chain.
"""
function PMA(n::Int, m::Int, i_max::Int, h_array::Vector{Int}, act)
    node_array = [n, h_array..., i_max*(m+1)]
    PMA(n, m, i_max, construct_layer_array(node_array, act))
end

"""
    (nn::PMA)(x, u)

Infer using two input arguments `x` and `u`.
# Notes
size(x) = (n, d)
size(u) = (m, d)
# Types
x and u should be arrays.
For example, u = [1] for the one-element case.
"""
function (nn::PMA)(x::AbstractArray, u::AbstractArray)
    is_vector = length(size(x)) == 1
    @assert is_vector == (length(size(u)) == 1)
    x = is_vector ? reshape(x, :, 1) : x
    u = is_vector ? reshape(u, :, 1) : u
    @assert size(x)[2] == size(u)[2]
    tmp = affine_map(nn, x, u)
    _res = maximum(tmp, dims=1)
    res = is_vector ? reshape(_res, 1) : _res
end

"""
When receiving Convex.AbstractExpr input
"""
function (nn::PMA)(x::AbstractArray, u::Convex.AbstractExpr)
    tmp = affine_map(nn, x, u)
    res = [maximum(tmp)]
end

Flux.params(approximator::PMA) = Flux.params(approximator.NN)


text:
################################################################################
# Iterative Best Response Example
################################################################################
# using Algames
using Plots
using StaticArrays
using LinearAlgebra

function ibr_experiment(p::Int)
	T = Float64

	# Define the dynamics of the system
	d = 2
	model = DoubleIntegratorGame(p=p, d=d) # game with 3 players with unicycle dynamics
	n = model.n
	m = model.m

	# Define the horizon of the problem
	N = 20 # N time steps
	dt = 0.1 # each step lasts 0.1 second
	probsize = ProblemSize(N,model) # Structure holding the relevant sizes of the problem

	# Define the objective of each player
	# We use a LQR cost
	Q = [Diagonal(10*ones(SVector{model.ni[i],T})) for i=1:p] # Quadratic state cost
	R = [Diagonal(0.01*ones(SVector{model.mi[i],T})) for i=1:p] # Quadratic control cost
	# Desrired state
	xf = [SVector{model.ni[1],T}([ 2, 0.2*i,0,0]) for i = 0:p-1]
	# Desired control
	uf = [zeros(SVector{model.mi[i],T}) for i=1:p]
	# Objectives of the game
	game_obj = GameObjective(Q,R,xf,uf,N,model)
	radius = 1.0*ones(p)
	μ = 15.0*ones(p)
	add_collision_cost!(game_obj, radius, μ)

	# Define the constraints that each player must respect
	game_con = GameConstraintValues(probsize)

	# Define the initial state of the system
	x0 = [[ 0.0,  0.2*i,  0.0,  0.0] for i=0:p-1]
	x0 = SVector{model.n,T}(reshape(vcat([x0[i]' for i=1:p]...), model.n))

	# Define the Options of the solver
	opts_alg = Options(Δ_min=1e-9, inner_iter=2, inner_print=false, ϵ_opt=1e-9)
	opts_ibr = Options(dual_reset=false, outer_iter=1, inner_iter=1, Δ_min=1e-5, inner_print=false, ϵ_opt=1e-9)
	ibr_opts = IBROptions(Δ_min=1e-9, ibr_iter=100, ordering=[1,2,3,4,5,6,7,8,9,10])

	# Define the game problem
	prob0_alg = GameProblem(N,dt,x0,model,opts_alg,game_obj,game_con)
	prob0_ibr = GameProblem(N,dt,x0,model,opts_ibr,game_obj,game_con)
	prob_alg = deepcopy(prob0_alg)
	prob_ibr = deepcopy(prob0_ibr)

	# Solve the problem
	@elapsed newton_solve!(prob_alg)
	@elapsed ibr_newton_solve!(prob_ibr, ibr_opts=ibr_opts)

	return prob_alg, prob_ibr
end

function display_result(prob::GameProblem; sample::Int=1)
	L = length(prob.stats.res[1:sample:end])
	for t = 1:L
		t_elap = cumsum(prob.stats.t_elap)[1:sample:end][t]
		res = prob.stats.res[1:sample:end][t]
		println("($t_elap, $res)")
	end
	return nothing
end

# Visualize the Results
# Run it TWICE to avoid including compile times.
plt = plot(layout = (2, 2), xlabel="time (s)", ylabel="residual")
for (i,p) in enumerate([2,4,7,10])
# for (i,p) in enumerate([2,4])
	prob_alg, prob_ibr = ibr_experiment(p)
	plot!(plt[i], cumsum(prob_alg.stats.t_elap), log.(10, prob_alg.stats.res),
		linewidth=3.0, color=:blue, label="Algames")
	plot!(plt[i], cumsum(prob_ibr.stats.t_elap), log.(10, prob_ibr.stats.res),
		linewidth=3.0, color=:orange, label="IBR")

	# plot!(plt, cumsum(prob_alg.stats.t_elap), log.(10, [v.max for v in prob_alg.stats.dyn_vio]))
	# plot!(plt, cumsum(prob_ibr.stats.t_elap), log.(10, [v.max for v in prob_ibr.stats.dyn_vio]))
end
display(plt)

# Script for exporting results to tikz
display_result(prob_alg)
display_result(prob_ibr, sample=10)


text:
# TODO: GitHub's list users only uses `since`.

export @paginate

"""
    @paginate fun(args...; kwargs...) page=1 per_page=100 -> Paginator

Create an iterator that paginates the results of repeatedly calling `fun(args...; kwargs...)`.
The first argument of `fun` must be a [`Forge`](@ref) and it must return a `Tuple{Vector{T}, HTTP.Response}`.

## Keywords
- `page::Int=1`: Starting page.
- `per_page::Int=100`: Number of entries per page.
"""
macro paginate(ex::Expr, kws::Expr...)
    (ex.args[2] isa Expr && ex.args[2].head === :parameters) ||
        insert!(ex.args, 2, Expr(:parameters))
    append!(ex.args[2].args, map(kw -> Expr(:kw, kw.args...), kws))
    insert!(ex.args, 3, ex.args[1])
    isempty(ex.args[2].args) && deleteat!(ex.args, 2)
    ex.args[1] = :(GitForge.paginate)
    esc(ex)
end

mutable struct Paginator{T, F<:Function}
    f::F
    xs::Vector{T}
    page::Int
    next::Dict{String, String}
    last::Int
    resp::HTTP.Response

    Paginator{T}(f::F) where {T, F <: Function} = new{T, F}(f, T[], 1, Dict(), typemax(Int))
end

function Paginator{T}(
    f::Function, args::Tuple, kwargs::Pairs, page::Int, per_page::Int,
) where T
    pages = Dict("page" => page, "per_page" => per_page)
    query = merge(get(kwargs, :query, Dict()), pages)
    return Paginator{T}(q -> f(args...; kwargs..., query=merge(query, q)))
end

function paginate(
    fun::Function, f::Forge, args...;
    page::Int=1, per_page::Int=100, kwargs...,
)
    V = into(f, fun)
    V <: Vector || throw(ArgumentError("Function must return Vector{T}"))
    return Paginator{eltype(V)}(fun, (f, args...), kwargs, page, per_page)
end

function Base.iterate(p::Paginator, state=nothing)
    # The state is nothing when we are starting a new page.
    it = state === nothing ? iterate(p.xs) : iterate(p.xs, state)
    if it === nothing
        # This page is finished, so either fetch a new page or quit.
        p.page > p.last && return nothing
        nextpage!(p)
        return iterate(p)
    else
        # More results in the page.
        x, state = it
        return (x, p.resp), state
    end
end

Base.collect(p::Paginator{T}) where T = foldl((acc, (x, _)) -> push!(acc, x), p; init=T[])

function nextpage!(p::Paginator)
    xs, p.resp = p.f(p.next)
    empty!(p.xs)
    append!(p.xs, xs)
    p.page += 1
    next, last = parserels(HTTP.header(p.resp, "Link"))
    next === nothing || (p.next = next)
    # If we didn't get any result for the next page, assume this is the last one.
    p.last = last === nothing ? p.page - 1 : last
    return p
end

function unescape(url::AStr)
    ms = eachmatch(r"[\?&](.*?)=(.*?)(?:&|$)", url; overlap=true)
    return Dict(m[1] => m[2] for m in ms)
end

function parserels(link::AStr)
    nextm = match(r"(<.*?)>; rel=\"next\"", link)
    lastm = match(r"(<.*?)>; rel=\"last\"", link)
    nextd = nextm === nothing ? nothing : unescape(nextm[1])
    lastd = lastm === nothing ? nothing : unescape(lastm[1])
    last = if lastd == nothing
        nothing
    else
        l = get(lastd, "page", nothing)
        l === nothing ? nothing : parse(Int, l)
    end
    return nextd, last
end


text:
# functions related to noncentral chi-square distribution

# R implementations
using .RFunctions:
    nchisqpdf,
    nchisqlogpdf,
    nchisqcdf,
    nchisqccdf,
    nchisqlogcdf,
    nchisqlogccdf,
    nchisqinvcdf,
    nchisqinvccdf,
    nchisqinvlogcdf,
    nchisqinvlogccdf


text:
function ∂getindex end
function ∂mul end


text:
## Low level gemm! call with pointers
## Borrowed from Knet.jl, adapted for compile-time constants

using LinearAlgebra
using LinearAlgebra.BLAS: libblas, BlasInt, @blasfunc

"""
    gemm!()

Low-level gemm!() call with pointers, borrowed from Knet.jl

Calculates `C = alpha*op(A)*op(B) + beta*C`, where:
  - `transA` and `transB` set `op(X)` to be either `identity()` or `transpose()`
  - alpha and beta are scalars
  - op(A) is an (M, K) matrix
  - op(B) is a (K, N) matrix
  - C is an (M, N) matrix.
"""
gemm!

# These are the datatypes we have fast GEMM for
gemm_datatype_mappings = (
    (:dgemm_, Float64),
    (:sgemm_, Float32),
    (:zgemm_, ComplexF64),
    (:cgemm_, ComplexF32),
)
for (gemm, elt) in gemm_datatype_mappings
    @eval begin
        @inline function gemm!(transA::Val, transB::Val,
                               M::Int, N::Int, K::Int,
                               alpha::$(elt), A::Ptr{$elt}, B::Ptr{$elt},
                               beta::$(elt), C::Ptr{$elt})
            # Convert our compile-time transpose marker to a char for BLAS
            convtrans(V::Val{false}) = 'N'
            convtrans(V::Val{true})  = 'T'

            if transA == Val(false)
                lda = M
            else
                lda = K
            end
            if transB == Val(false)
                ldb = K
            else
                ldb = N
            end
            ldc = M
            ccall((@blasfunc($(gemm)), libblas), Nothing,
                  (Ref{UInt8}, Ref{UInt8}, Ref{BlasInt}, Ref{BlasInt},
                   Ref{BlasInt}, Ref{$elt}, Ptr{$elt}, Ref{BlasInt},
                   Ptr{$elt}, Ref{BlasInt}, Ref{$elt}, Ptr{$elt},
                   Ref{BlasInt}),
                  convtrans(transA), convtrans(transB), M, N, K,
                  alpha, A, lda, B, ldb, beta, C, ldc)
        end
    end
end

for (gemm, elt) in gemm_datatype_mappings
    @eval begin
        @inline function batched_gemm!(transA::AbstractChar,
                               transB::AbstractChar,
                               alpha::($elt),
                               A::AbstractArray{$elt, 3},
                               B::AbstractArray{$elt, 3},
                               beta::($elt),
                               C::AbstractArray{$elt, 3})
            @assert !Base.has_offset_axes(A, B, C)
            @assert size(A, 3) == 1 || size(A, 3) == size(C, 3) "batch size mismatch: A != C"
            @assert size(B, 3) == 1 || size(B, 3) == size(C, 3) "batch size mismatch: B != C"

            m = size(A, transA == 'N' ? 1 : 2)
            ka = size(A, transA == 'N' ? 2 : 1)
            kb = size(B, transB == 'N' ? 1 : 2)
            n = size(B, transB == 'N' ? 2 : 1)
            if ka != kb || m != size(C,1) || n != size(C,2)
                throw(DimensionMismatch("A1 has size ($m,$ka), B1 has size ($kb,$n), C1 has size $(size(C)[1:2])"))
            end
            LinearAlgebra.BLAS.chkstride1(A)
            LinearAlgebra.BLAS.chkstride1(B)
            LinearAlgebra.BLAS.chkstride1(C)

            ptrA = Base.unsafe_convert(Ptr{$elt}, A)
            ptrB = Base.unsafe_convert(Ptr{$elt}, B)
            ptrC = Base.unsafe_convert(Ptr{$elt}, C)

            strA = size(A, 3) == 1 ? 0 : Base.stride(A, 3)
            strB = size(B, 3) == 1 ? 0 : Base.stride(B, 3)
            strC = Base.stride(C, 3)

            n_threads = min(Threads.nthreads(), 1 + max(length(A), length(B)) ÷ 8000)
            # In some tests, size (20,20,20) is worth splitting between two threads,
            # as is size (32,32,8).

            # TODO fix multithreading problems
            # if n_threads > 1
                # Threads.@sync for ks in Iterators.partition(1:size(C, 3), cld(size(C, 3), n_threads))
                    # Threads.@spawn for k in ks

            #             ptrAk = ptrA + (k-1) * strA * sizeof($elt)
            #             ptrBk = ptrB + (k-1) * strB * sizeof($elt)
            #             ptrCk = ptrC + (k-1) * strC * sizeof($elt)

            #             ccall((@blasfunc($(gemm)), libblas), Nothing,
            #                   (Ref{UInt8}, Ref{UInt8}, Ref{BlasInt}, Ref{BlasInt},
            #                    Ref{BlasInt}, Ref{$elt}, Ptr{$elt}, Ref{BlasInt},
            #                    Ptr{$elt}, Ref{BlasInt}, Ref{$elt}, Ptr{$elt},
            #                    Ref{BlasInt}),
            #                   transA, transB, m, n,
            #                   ka, alpha, ptrAk, max(1,Base.stride(A,2)),
            #                   ptrBk, max(1,Base.stride(B,2)), beta, ptrCk,
            #                   max(1,Base.stride(C,2)))
            #         end
            #     end
            # else # small problem, no threads

                for k in 1:size(C, 3)
                    # Identical loop body

                    ptrAk = ptrA + (k-1) * strA * sizeof($elt)
                    ptrBk = ptrB + (k-1) * strB * sizeof($elt)
                    ptrCk = ptrC + (k-1) * strC * sizeof($elt)

                    ccall((@blasfunc($(gemm)), libblas), Nothing,
                          (Ref{UInt8}, Ref{UInt8}, Ref{BlasInt}, Ref{BlasInt},
                           Ref{BlasInt}, Ref{$elt}, Ptr{$elt}, Ref{BlasInt},
                           Ptr{$elt}, Ref{BlasInt}, Ref{$elt}, Ptr{$elt},
                           Ref{BlasInt}),
                          transA, transB, m, n,
                          ka, alpha, ptrAk, max(1,Base.stride(A,2)),
                          ptrBk, max(1,Base.stride(B,2)), beta, ptrCk,
                          max(1,Base.stride(C,2)))
                end

            # end

            C
        end
    end
end


text:
# Dates and Times for Julia
#
# The types and functions in this class store and manipulate representations of dates and times
# Instances are stored internally as a Julian Date (jd), which is a representation of dates counting linearly
# in days since midnight localtime on January 1, 4713 BCE. The original Julian Day Number, counting in days
# since noon GMT, is refered to as the astronomical julian date (ajd) within this code base.

# Only minimal timezone support exists. DateTime objects keep track of timezones supplied, and use timezones
# in difference calculations. However, no timezone conversion functionality is provided. DST is also not
# considered by this code. Both these items are cosidered to be the responsibility of the calling code.
module SimpleDate

using Base
import Base.+, Base.-, Base.<, Base.>, Base.==, Base.<=, Base.>=, Base.*, Base.show, Base.string, Base.isequal, Base.hash

export MONTHS, SHORT_MONTHS, DAY_OF_WEEK, SHORT_DAY_OF_WEEK,
	DateTime,Date,
	date, datetime, yday, mday, wday, current_time_millis, current_time_micros, hour, month, now, civil, leap_year, +, -, *

MONTHS = ["January" , "February", "March", "April", "May", "June", "July", "August", "Septempber", "October", "November", "December"]
SHORT_MONTHS = ["Jan" , "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
DAY_OF_WEEK = ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"]
SHORT_DAY_OF_WEEK = ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"]

_UNIXEPOCH = 2440588 # JD of midnight localtime 1/1/70

type DateTime{T<:Real}
	jd::T #Julian Date
	off::Int8 #TZ offset in number of 15 minutes intervals

	function DateTime{T<:Real}(jd::T)
		new(jd, 0)
	end

	function DateTime{T<:Real}(jd::T, off::Int8)
		new(jd, off)
	end

	function DateTime{T<:Real}(jd::T, off::Float64) #Offset in hour
		new(jd, int8(ifloor(off * 4)) )
	end
end

typealias Date DateTime{Int}

#creation functions.
function date(y::Int,m::Int,d::Int)
	jd::Int = valid_date(y, m, d)
	if jd==-1
		throw ("Invalid date: $y-$m-$d")
	else
		DateTime{Int}(jd,0.0)
	end
end

function datetime(y::Integer, m::Integer, d::Integer, hh::Integer, mm::Integer, ss::Integer, frac::FloatingPoint, off::FloatingPoint) #Offset in hours
	jd = valid_date(int(y), int(m), int(d))
	if jd==-1
		throw ("Invalid date: $y-$m-$d")
	else
		jd=jd+_time_to_day_frac(hh,mm,ss) + frac/86400
		DateTime{Float64}(jd, off)
	end
end

#Create a datetime upto seconds accuracy with local timezone
function datetime(y::Integer, m::Integer, d::Integer, hh::Integer, mm::Integer, ss::Integer)
	datetime(y,m,d,hh,mm,ss,0.0, TZ_OFFSET)
end

function string{T<:FloatingPoint}(dt::DateTime{T})
	(y,m,d, hh, mm, ss, fr) = civil(dt)
	hoff = dt.off/4
	"$(d) $(SHORT_MONTHS[m]) $(y) $(hh):$(mm):$(ss).$(string(round(fr*100)/100)[3:end]) ($(dec(int((floor(hoff)*100) + (hoff - floor(hoff))*60), 4))) "
end

function string{T<:Integer}(dt::DateTime{T})
	(y,m,d) = civil(dt)
	"$(d) $(SHORT_MONTHS[m]) $(y)"
end

show(io, d::DateTime) = print(io, string(d))

(-){T<:Integer,S<:Integer} (x::DateTime{T}, y::DateTime{S}) = convert(promote_type(T,S), x.jd - y.jd )
(-){T<:Real,S<:Real} (x::DateTime{T}, y::DateTime{S}) = convert(promote_type(T,S), x.jd - y.jd - ((x.off - y.off) / 96) )
(-){T<:Real, S<:Real} (x::DateTime{T}, y::S) = DateTime{promote_type(T,S)}(x.jd - y, x.off)
(+){T<:Real,S<:Real} (x::DateTime{T}, y::S) = DateTime{promote_type(T,S)}(x.jd + y, x.off)
(+){T<:Real,S<:Real} (x::S, y::DateTime{T}) = DateTime{promote_type(T,S)}(y.jd + x, y.off)

<=(x::DateTime, y::DateTime) = x-y <= 0
>=(x::DateTime, y::DateTime) = x-y >= 0
<(x::DateTime, y::DateTime) = x-y < 0
>(x::DateTime, y::DateTime) = x-y > 0

hash(d::DateTime) = bitmix(hash(d.jd), hash(d.off))
isequal(x::DateTime, y::DateTime) = isequal(x.jd, y.jd) && isequal(x.off, y.off)

function current_time_millis()
    return int(floor(time()*10^3))
end

function current_time_micros()
    return int(floor(time()*10^6))
end

function now()
	t = ccall(:clock_now, Float64, ())  #Seconds since unix epoch
	tm = Array(Uint32, 14)
    ccall(:localtime_r, Ptr{Void}, (Ptr{Int}, Ptr{Uint32}), &int(t), tm)
	datetime( int(tm[6]) + 1900,  	#int tm_year
			int(tm[5]) + 1 ,		#int tm_mon
			int(tm[4]) ,			#int tm_mday
			int(tm[3]) ,			#int tm_hour
			int(tm[2]) ,			#int tm_min
			int(tm[1]) , 			#int tm_sec
			(t-floor(t)),
			tm[11] / 3600,			#long tm_gmtoff
			#cstring(convert(Ptr{Uint8}, ((uint64(0)|tm[14]) << 32 ) | tm[13])) #char *tm_zone
		)
end

function _default_zone()
	t = ccall(:clock_now, Float64, ())  #Seconds since unix epoch
	tm = Array(Uint32, 14)
    ccall(:localtime_r, Ptr{Void}, (Ptr{Int}, Ptr{Uint32}), &(int(t)), tm)
    zone = bytestring(convert(Ptr{Uint8}, ((uint64(0)|tm[14]) << 32 ) | tm[13])) #char *tm_zone
    off = tm[11] / 3600
    return (off,zone)
end

#Day of the week for any day, 1=Sunday, 7=Saturday
function wday(d::DateTime)
	((ifloor(d.jd) + 1) % 7) +1
end

common_year_yday_offset = [
    0,
    0 + 31,
    0 + 31 + 28,
    0 + 31 + 28 + 31,
    0 + 31 + 28 + 31 + 30,
    0 + 31 + 28 + 31 + 30 + 31,
    0 + 31 + 28 + 31 + 30 + 31 + 30,
    0 + 31 + 28 + 31 + 30 + 31 + 30 + 31,
    0 + 31 + 28 + 31 + 30 + 31 + 30 + 31 + 31,
    0 + 31 + 28 + 31 + 30 + 31 + 30 + 31 + 31 + 30,
    0 + 31 + 28 + 31 + 30 + 31 + 30 + 31 + 31 + 30 + 31,
    0 + 31 + 28 + 31 + 30 + 31 + 30 + 31 + 31 + 30 + 31 + 30 ]

leap_year_yday_offset = [
    0,
    0 + 31,
    0 + 31 + 29,
    0 + 31 + 29 + 31,
    0 + 31 + 29 + 31 + 30,
    0 + 31 + 29 + 31 + 30 + 31,
    0 + 31 + 29 + 31 + 30 + 31 + 30,
    0 + 31 + 29 + 31 + 30 + 31 + 30 + 31,
    0 + 31 + 29 + 31 + 30 + 31 + 30 + 31 + 31,
    0 + 31 + 29 + 31 + 30 + 31 + 30 + 31 + 31 + 30,
    0 + 31 + 29 + 31 + 30 + 31 + 30 + 31 + 31 + 30 + 31,
    0 + 31 + 29 + 31 + 30 + 31 + 30 + 31 + 31 + 30 + 31 + 30 ]

#Day of the year for any date 1=1JanYY, 365/366=31DecYY
function yday(dt::DateTime)
	y,m,d = _jd_to_date(ifloor(dt.jd))
	tm_year = y - 1900 ;
    tm_year_mod400::Integer = tm_year % 400;
    tm_yday = d;

    if (leap_year(tm_year_mod400 + 1900))
		tm_yday = tm_yday + leap_year_yday_offset[m];
    else
		tm_yday = tm_yday + common_year_yday_offset[m];
	end
end

function leap_year(y::Integer)
	((y % 4 == 0) && (y % 100 != 0)) || (y % 400 == 0)
end

function leap_year(d::Date)
	leap_year(d.year)
end

function is_julian(jd::Integer)
	jd < 2299161 # Date of Gregorian Calendar Reform, ITALY; 1582-10-15
end

function civil{T<:FloatingPoint}(dt::DateTime{T})
	return tuple(_jd_to_date(ifloor(dt.jd))..., _day_frac_to_time(dt.jd-ifloor(dt.jd))...)
end

function civil{T<:Integer}(dt::DateTime{T})
	return _jd_to_date(dt.jd)
end

hour{T<:FloatingPoint}(dt::DateTime{T}) = civil(dt)[4]
minute{T<:FloatingPoint}(dt::DateTime{T}) = civil(dt)[5]
second{T<:FloatingPoint}(dt::DateTime{T}) = civil(dt)[6]
mday(dt::DateTime) = civil(dt)[3]
month(dt::DateTime) = civil(dt)[2]
year(dt::DateTime) = civil(dt)[1]

#convert a date to a Julian Day Number
function _date_to_jd (y,m,d)
	if m <= 2
      y -= 1
      m += 12
    end
    a = int(floor(y / 100.0))
    b = 2 - a + int(floor(a / 4.0))
    jd = int(floor(365.25 * (y + 4716))) + int(floor(30.6001 * (m + 1))) + d + b - 1524
    if is_julian(jd)
    	jd -= b
    end
    return jd
end

function _jd_to_date (jd::Integer)
	if is_julian(jd)
		a=jd
	else
		x = int(floor((jd - 1867216.25) / 36524.25))
	    a = jd + 1 + x - int(floor(x / 4.0))
	end
    b = a + 1524
    c = ifloor((b - 122.1) / 365.25)
    d = ifloor(365.25 * c)
    e = ifloor((b - d) / 30.6001)
    dom = b - d - ifloor(30.6001 * e)
    if e <= 13
      m = e - 1
      y = c - 4716
    else
      m = e - 13
      y = c - 4715
    end

    return y, m , dom
end

function _day_frac_to_time{T}(fr::T)
    h,   fr = divmod(fr, 1//24)
    min, fr = divmod(fr, 1//1440)
    s,   fr = divmod(fr, 1//86400)
    return int(h), int(min), int(s), convert (T, fr*86400)
 end

function _time_to_day_frac(hh::Integer, mm::Integer, ss::Integer )
	 hh/24 + mm/1440 + ss/86400
end

function valid_date(y,m,d)
	jd=_date_to_jd(y,m,d)
	if (y, m, d) == _jd_to_date(jd)
		return jd
	else
		return -1
	end
end

function _ajd_to_jd(ajd::Real, off::FloatingPoint)
	ajd + off + 0.5
end

function _jd_to_ajd(jd::Real, off::FloatingPoint)
	jd -off - 0.5
end

function unixtime(dt::DateTime)
	(dt.jd-_UNIXEPOCH)*86400
end

TZ = 0
TZ_OFFSET = ""
function init()
	d=now();
	(off, zone) = _default_zone()
	global  TZ = zone
	global  TZ_OFFSET = off
end

divmod(x, y) = (div(x,y) , mod(x, y))

init()

const days = 1
const hours = days / 24
const minutes = hours / 60
const seconds = minutes / 60

abstract Period

type Month <: Period; num::Integer; end

const months = Month(1);
(*)(x::Integer, y::Month) = Month(x);

end #module


text:
immutable VorbisInfo
    version::Cint
    channels::Cint
    rate::Clong

    bitrate_upper::Clong
    bitrate_nominal::Clong
    bitrate_lower::Clong
    bitrate_window::Clong

    codec_setup::Ptr{Void}
end

VorbisInfo() = VorbisInfo(0,0,0,0,0,0,0,C_NULL)

immutable VorbisDSPState
    analysisp::Cint
    vorbis_info::VorbisInfo

    pcm::Ptr{Cfloat}
    pcmret::Ptr{Cfloat}
    pcm_storage::Cint
    pcm_current::Cint
    pcm_returned::Cint

    preextrapolate::Cint
    eofflag::Cint

    lW::Clong
    W::Clong
    nW::Clong
    ceneterW::Clong

    granulepos::Int64
    sequence::Int64

    glue_bits::Int64
    time_bits::Int64
    floor_bits::Int64
    res_bits::Int64

    backend_state::Ptr{Void}
end

immutable AllocChain
    ptr::Ptr{Void}
    next::Ptr{AllocChain}
end

immutable VorbisBlock
    pcm::Ptr{Ptr{Cfloat}}
    opb::Ogg.PackBuffer

    lW::Clong
    W::Clong
    nW::Clong
    pcmend::Cint
    mode::Cint

    eofflag::Cint
    granulepos::Int64
    sequence::Int64
    vd::Ptr{VorbisDSPState}

    localstore::Ptr{Void}
    localtop::Clong
    localalloc::Clong
    totaluse::Clong
    reap::Ptr{AllocChain}

    glue_bits::Int64
    time_bits::Int64
    floor_bits::Int64
    res_bits::Int64

    internal::Ptr{Void}
end

immutable VorbisComment
    user_comments::Ptr{Ptr{Cuchar}}
    comment_lengths::Ptr{Cint}
    comments::Cint
    vendor::Ptr{Cuchar}
end


text:
using Oceananigans.Architectures: device_event
using Oceananigans.Fields: location
using Oceananigans.TimeSteppers: ab2_step_field!
using Oceananigans.TurbulenceClosures: implicit_step!

using KernelAbstractions: NoneEvent

import Oceananigans.TimeSteppers: ab2_step!

#####
##### Step everything
#####

function ab2_step!(model::HydrostaticFreeSurfaceModel, Δt, χ)

    workgroup, worksize = work_layout(model.grid, :xyz)

    explicit_velocity_step_events = ab2_step_velocities!(model.velocities, model, Δt, χ)
    explicit_tracer_step_events = ab2_step_tracers!(model.tracers, model, Δt, χ)
    free_surface_event = ab2_step_free_surface!(model.free_surface, model, Δt, χ,
                                                MultiEvent(Tuple(explicit_velocity_step_events)))

    prognostic_field_events = MultiEvent(tuple(free_surface_event,
                                               explicit_velocity_step_events...,
                                               explicit_tracer_step_events...))

    wait(device(model.architecture), prognostic_field_events)

    return nothing
end

#####
##### Step velocities
#####

function ab2_step_velocities!(velocities, model, Δt, χ)

    barrier = device_event(model)

    # Launch velocity update kernels
    explicit_velocity_step_events = []

    for name in (:u, :v)
        Gⁿ = model.timestepper.Gⁿ[name]
        G⁻ = model.timestepper.G⁻[name]
        velocity_field = model.velocities[name]

        event = launch!(model.architecture, model.grid, :xyz,
                        ab2_step_field!, velocity_field, Δt, χ, Gⁿ, G⁻,
                        dependencies = device_event(model))

        push!(explicit_velocity_step_events, event)
    end

    for (i, name) in enumerate((:u, :v))
        velocity_field = model.velocities[name]

        # TODO: let next implicit solve depend on previous solve + explicit velocity step
        # Need to distinguish between solver events and tendency calculation events.
        # Note that BatchedTridiagonalSolver has a hard `wait`; this must be solved first.
        implicit_step!(velocity_field,
                       model.timestepper.implicit_solver,
                       model.clock,
                       Δt,
                       model.closure,
                       nothing,
                       model.diffusivities,
                       model.velocities,
                       model.tracers,
                       model.buoyancy,
                       dependencies = explicit_velocity_step_events[i])
    end

    return explicit_velocity_step_events
end

#####
##### Step velocities
#####

const EmptyNamedTuple = NamedTuple{(),Tuple{}}

ab2_step_tracers!(::EmptyNamedTuple, model, Δt, χ) = [NoneEvent()]

function ab2_step_tracers!(tracers, model, Δt, χ)

    # Tracer update kernels
    explicit_tracer_step_events = []

    for (tracer_index, tracer_name) in enumerate(propertynames(tracers))
        Gⁿ = model.timestepper.Gⁿ[tracer_name]
        G⁻ = model.timestepper.G⁻[tracer_name]
        tracer_field = tracers[tracer_name]

        event = launch!(model.architecture, model.grid, :xyz,
                        ab2_step_field!, tracer_field, Δt, χ, Gⁿ, G⁻,
                        dependencies = device_event(model))

        push!(explicit_tracer_step_events, event)
    end

    for (tracer_index, tracer_name) in enumerate(propertynames(tracers))
        tracer_field = tracers[tracer_name]

        implicit_step!(tracer_field,
                       model.timestepper.implicit_solver,
                       model.clock,
                       Δt,
                       model.closure,
                       tracer_index,
                       model.diffusivities,
                       model.velocities,
                       model.tracers,
                       model.buoyancy,
                       dependencies = explicit_tracer_step_events[tracer_index])
    end

    return explicit_tracer_step_events
end


text:
using FunctionBarrier
using Test

@testset "FunctionBarrier.jl" begin
    a, b = 1, 2
    local c, d
    @barrier begin
        c = a + b
        d = c + 1
        c, d
    end
    @test c == a + b
    @test d == c + 1
    @test (@barrier a * b) == a * b
    x = [1, 2, 4]
    @test (@barrier x[end]) == x[end]
    @test (@barrier x[1]) == x[1]
end


text:
# function importicd10who(fname)
#   icdcodes = CSV.File(fname, delim=';',
#   header=["level", "terminal", "terminaltype", "chapter", "firstblockcode", "nodagger", "noasterisk", "nopunct", "description1", "description2", "description3", "what", "mort1", "mort2", "mort3", "mort4", "morb"])
# end


text:
using .Model

basic_protocol = JSON.parsefile("../data/com_protocol_basic.json")

const APP_JSON_FILE = "../data/app.json"

# -_---_-_---_-_---_-_---_-_---_-_---_-_---_-_---_-_---_-_---_-_---_
# NOTE: These run functions are called from handlers.jl
# -_---_-_---_-_---_-_---_-_---_-_---_-_---_-_---_-_---_-_---_-_---_

# Launched from handle_client_to_server() when a message arrives
# from client app.
function run(chan::Channel{String}, simulation::String)
    model = Model.ModelData()

    Model.load!(model, "../data/app.json")
    Model.load_sim!(model)

    # Async version to used when truly simulating.
    @async begin
        trace = try
            cell = build_simulation!(model)

            simulate(chan, model, cell)

            data = basic_protocol

            # Populate
            data["From"] = "Simulation"
            data["To"] = "Client"
            data["Type"] = "Response"
            data["Data"] = "Simulation Complete"

            put!(chan, JSON.json(data))

            nothing
        catch ex
            println("### Exception ###: ", ex)
            stacktrace(catch_backtrace())
        end

        if trace ≠ nothing
            println(trace)
            println("############################################")
        end
    end
end

cell = nothing

# WARNING! Use only for development. The simulation will not run async
# hence the client won't be notified of span completions during simulation.
function run_debug(chan::Channel{String}, simulation::String)
    println("#####################################")
    println("#### RUNNING DEBUG VARIANT")
    println("#####################################")
    model = Model.ModelData()

    Model.load!(model, APP_JSON_FILE)
    Model.load_sim!(model)

    # The samples collected during the simulation.
    samples = Model.Samples()

    # config streams so build can bind to them.
    streams = Streams()

    # ~--~--~--~--~--~--~--~--~--~--~--~--~--~--~--~--~--~--~--~--
    # Setup and configure the collections that hold sampling data
    # captured during simulation.
    # ~--~--~--~--~--~--~--~--~--~--~--~--~--~--~--~--~--~--~--~--
    config_streams!(streams)

    println("~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-")
    duration = Model.total_simulation_time(model)
    println("Simulation duration: ", duration)

    # How many synapses afferent on the dendrite.
    synapses = Model.synapses(model)
    println("Synapses: ", synapses)

    # Span time is how many samples to capture before saving to disk.
    span_time = Model.span_time(model)
    println("Span time: ", span_time)

    # Poi streams have a firing rate property
    firing_rate = Model.firing_rate(model)
    println("firing_rate: ", firing_rate)
    println("~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-")

    # We "set" the samples for the first span.
    Model.config_samples!(samples, synapses, span_time)

    config_poi_streams!(streams, synapses, firing_rate)

    config_stimulus_streams!(streams, model)

    # "cell" is also used during re-run
    global cell = build_simulation!(model, samples, streams)

    simulate(chan, model, samples, streams, cell)

    @async begin
        data = basic_protocol

        # Populate
        data["From"] = "Simulation"
        data["To"] = "Client"
        data["Type"] = "Response"
        data["Data"] = "Simulation Complete"

        put!(chan, JSON.json(data))
    end
end

# This is for RE running a simulation without creating every
# component from scratch.
function re_run_debug(chan::Channel{String}, simulation::String)
    println("#####################################")
    println("#### RE - RUNNING DEBUG VARIANT")
    println("#####################################")

    model = Model.ModelData()

    Model.load!(model, APP_JSON_FILE)
    Model.load_sim!(model)

    # The samples collected during the simulation.
    samples = Model.Samples()

    simulate(chan, model, samples, cell)

    @async begin
        data = basic_protocol

        # Populate
        data["From"] = "Simulation"
        data["To"] = "Client"
        data["Type"] = "Response"
        data["Data"] = "Simulation Complete"

        put!(chan, JSON.json(data))
    end
end


text:
module SMTPClient

using Distributed
using LibCURL

import Base: convert
import Sockets: send

export SendOptions, SendResponse, send

include("utils.jl")
include("types.jl")
include("cbs.jl")  # callbacks
include("mail.jl")

##############################
# Module init/cleanup
##############################

function __init__()
  curl_global_init(CURL_GLOBAL_ALL)

  global c_curl_write_cb =
    @cfunction(curl_write_cb, Csize_t, (Ptr{Cchar}, Csize_t, Csize_t, Ptr{Cvoid}))
  global c_curl_read_cb =
    @cfunction(curl_read_cb,  Csize_t, (Ptr{Cchar}, Csize_t, Csize_t, Ptr{Cvoid}))

  atexit() do
    curl_global_cleanup()
  end
end

end  # module SMTPClient


text:
using Plots

"""
  i: index of the wave,
  fileextra: additional text in the output-filename (default "")
  folder: name of the folder where the data files are located (default "output")
  output: boolean to decide if the plot should be saved in the outputfile (default true)
"""
function CreatePlot(i::Int64, fileextra = "", filefolder = "output", output::Bool = true)
  stephan = readdlm("/mnt/data/compass/2008/integrals_stefan/h$(i).n.out")
  misha = readdlm("$(filefolder)/h$(i).txt")
  stephan[2:202,2] ./= (stephan[2:202,1])
  m3π = collect(0.5:0.01:2.5)
  let p = sum(stephan[2:202,2])
    plot(stephan[2:202,1],stephan[2:202,2]./p,label="stephan", title=stephan[1,1]*" "*stephan[1,2]*" "*stephan[1,3]*" "*stephan[1,4])
  end
  let p = sum(misha[:,2])
    plot!(m3π, misha[:,2]./p,label="misha")
  end
  if output
    savefig("misha_vs_stephan$(fileextra)$(i).pdf")
  end
  plot!()
end

for i in [2]
  CreatePlot(i, "", "output_new", false)
end

readdlm("output_new/h2.txt")
function CreatePlotInterference(i_misha::Int64, j_misha::Int64, i_florian::Int64, j_florian::Int64, fileextra = "", filefolder = "output", output::Bool = true)
  mishai = readdlm("$(filefolder)/h$(i_misha).txt")
  mishaj = readdlm("$(filefolder)/h$(j_misha).txt")
  mishaij = readdlm("$(filefolder)/h$(i_misha)_$(j_misha).txt")

  mishaij[:,2:3] ./= sqrt.(mishai[:,2].*mishaj[:,2])
  plot( mishaij[:,1], mishaij[:,2], label="re(misha)", frame=:origin)
  plot!(mishaij[:,1], mishaij[:,3], label="im(misha)")

  florian = readdlm("diag.txt")
  florianij = readdlm("offdiag_$(i_florian)_$(j_florian).txt")
  florianij[:,2:3] = florianij[:,2:3]./sqrt.(florian[i_florian+1,2:201].*florian[j_florian+1,2:201])
  plot!(florianij[:,1], florianij[:,2], label="re(florian)", title="Interference:  $(florian[i_florian+1,1])...$(florian[j_florian+1,1])")
  plot!(florianij[:,1], florianij[:,3], label="im(florian)")
  # if output
  #   savefig("interference_$(fileextra)$(i_misha)_$(j_misha).pdf")
  # end
  plot!()
end

CreatePlotInterference(2,11,13,17,"test", "output_new")


text:
# make some Julia names compatible with our naming conventions
@alias is_trivial istrivial
@alias is_hermitian ishermitian

# for backwards compatibility
@alias hasalgebra has_algebra
@alias hasembedding has_embedding
@alias hasimage has_image
@alias hasmatrix_action has_matrix_action
@alias hasroot has_root

@alias is2_normal_difficult is_2_normal_difficult
@alias isGLZ_conjugate is_GLZ_conjugate
@alias isabelian is_abelian
@alias isabelian2 is_abelian2
@alias isabsolute is_absolute
@alias isabsolutely_primitive is_absolutely_primitive
@alias isalmost_maximally_ramified is_almost_maximally_ramified
@alias isautomorphisms_known is_automorphisms_known
@alias isbass is_bass
@alias isbijective is_bijective
@alias iscached is_cached
@alias iscanonical is_canonical
@alias iscentral is_central
@alias ischain_complex is_chain_complex
@alias ischaracteristic is_characteristic
@alias iscm is_cm
@alias iscm_field is_cm_field
@alias iscm_field_easy is_cm_field_easy
@alias iscm_field_known is_cm_field_known
@alias iscochain_complex is_cochain_complex
@alias iscommutative is_commutative
@alias iscommutative_known is_commutative_known
@alias iscomplex is_complex
@alias iscomplex_conjugation is_complex_conjugation
@alias isconductor is_conductor
@alias isconjugate is_conjugate
@alias isconsistent is_consistent
@alias iscontained is_contained
@alias iscoprime is_coprime
@alias iscyclic is_cyclic
@alias iscyclotomic_type is_cyclotomic_type
@alias isdefining_polynomial_nice is_defining_polynomial_nice
@alias isdefinite is_definite
@alias isdegenerate is_degenerate
@alias isdiagonal is_diagonal
@alias isdiagonalisable is_diagonalisable
@alias isdiscriminant is_discriminant
@alias isdivisible is_divisible
@alias isdivisible2 is_divisible2
@alias isdivisible_mod_ideal is_divisible_mod_ideal
@alias isdyadic is_dyadic
@alias iseichler is_eichler
@alias iseisenstein_polynomial is_eisenstein_polynomial
@alias iseq is_eq
@alias isequation_order is_equation_order
@alias isequivalent is_equivalent
@alias isequivalent_with_isometry is_equivalent_with_isometry
@alias isfinite_gen is_finite_gen
@alias isfinite_snf is_finite_snf
@alias isfixed_point_free is_fixed_point_free
@alias isfree is_free
@alias isfree_a4_fabi is_free_a4_fabi
@alias isfree_a5_fabi is_free_a5_fabi
@alias isfree_resolution is_free_resolution
@alias isfree_s4_fabi is_free_s4_fabi
@alias isfrom_db is_from_db
@alias isfull_lattice is_full_lattice
@alias isfundamental_discriminant is_fundamental_discriminant
@alias isgenus is_genus
@alias isgood_bong is_good_bong
@alias isgorenstein is_gorenstein
@alias isidentity is_identity
@alias isin is_in
@alias isindefinite is_indefinite
@alias isindependent is_independent
@alias isindex_divisor is_index_divisor
@alias isinduced is_induced
@alias isinert is_inert
@alias isinfinite is_infinite
@alias isinjective is_injective
@alias isintegral is_integral
@alias isinvolution is_involution
@alias isirreducible_easy is_irreducible_easy
@alias isirreducible_known is_irreducible_known
@alias isisometric is_isometric
@alias isisometric_with_isometry is_isometric_with_isometry
@alias isisotropic is_isotropic
@alias iskummer_extension is_kummer_extension
@alias isleaf is_leaf
@alias isleft_ideal is_left_ideal
@alias islessorequal is_lessorequal
@alias islinearly_disjoint is_linearly_disjoint
@alias islll_reduced is_lll_reduced
@alias islocal_norm is_local_norm
@alias islocal_square is_local_square
@alias islocally_equivalent is_locally_equivalent
@alias islocally_free is_locally_free
@alias islocally_hyperbolic is_locally_hyperbolic
@alias islocally_isometric is_locally_isometric
@alias islocally_isometric_kirschmer is_locally_isometric_kirschmer
@alias islocally_isomorphic is_locally_isomorphic
@alias islocally_isomorphic_with_isomophism is_locally_isomorphic_with_isomophism
@alias islocally_represented_by is_locally_represented_by
@alias islower_triangular is_lower_triangular
@alias ismaximal is_maximal
@alias ismaximal_integral is_maximal_integral
@alias ismaximal_known is_maximal_known
@alias ismaximal_known_and_maximal is_maximal_known_and_maximal
@alias ismaximal_order_known is_maximal_order_known
@alias ismodular is_modular
@alias isnegative_definite is_negative_definite
@alias isnilpotent is_nilpotent
@alias isnorm is_norm
@alias isnorm_divisible is_norm_divisible
@alias isnorm_divisible_pp is_norm_divisible_pp
@alias isnorm_fac_elem is_norm_fac_elem
@alias isnormal is_normal
@alias isnormal_basis_generator is_normal_basis_generator
@alias isnormal_difficult is_normal_difficult
@alias isnormal_easy is_normal_easy
@alias isnormalized is_normalized
@alias isone_sided is_one_sided
@alias ispairwise_coprime is_pairwise_coprime
@alias ispositive_definite is_positive_definite
@alias ispositive_entry is_positive_entry
@alias ispower_trager is_power_trager
@alias ispower_unram is_power_unram
@alias isprimary is_primary
@alias isprime_known is_prime_known
@alias isprime_nice is_prime_nice
@alias isprime_power is_prime_power
@alias isprimitive is_primitive
@alias isprimitive_over is_primitive_over
@alias isprimitive_root is_primitive_root
@alias isprincipal is_principal
@alias isprincipal_fac_elem is_principal_fac_elem
@alias isprincipal_maximal is_principal_maximal
@alias isprincipal_maximal_fac_elem is_principal_maximal_fac_elem
@alias isprincipal_non_maximal is_principal_non_maximal
@alias ispseudo_hnf is_pseudo_hnf
@alias isquadratic is_quadratic
@alias isquadratic_type is_quadratic_type
@alias isquaternion_algebra is_quaternion_algebra
@alias isradical_extension is_radical_extension
@alias isramified is_ramified
@alias isrationally_equivalent is_rationally_equivalent
@alias isrationally_isometric is_rationally_isometric
@alias isreduced is_reduced
@alias isreducible is_reducible
@alias isregular is_regular
@alias isregular_at is_regular_at
@alias isrepresented_by is_represented_by
@alias isright_ideal is_right_ideal
@alias issimilar is_similar
@alias issimple is_simple
@alias issimple_known is_simple_known
@alias issimultaneous_diagonalizable is_simultaneous_diagonalizable
@alias issmooth is_smooth
@alias issmooth! is_smooth!
@alias issplit is_split
@alias isstable is_stable
@alias issubfield is_subfield
@alias issubfield_normal is_subfield_normal
@alias issubgroup is_subgroup
@alias issublattice is_sublattice
@alias issublattice_with_relations is_sublattice_with_relations
@alias issurjective is_surjective
@alias istamely_ramified is_tamely_ramified
@alias istorsion is_torsion
@alias istorsion_point is_torsion_point
@alias istorsion_unit is_torsion_unit
@alias istorsion_unit_group_known is_torsion_unit_group_known
@alias istotally_complex is_totally_complex
@alias istotally_positive is_totally_positive
@alias istotally_real is_totally_real
@alias istriangular is_triangular
@alias isweakly_ramified is_weakly_ramified
@alias iszero_entry is_zero_entry
@alias iszero_mod_hnf! is_zero_mod_hnf!
@alias iszerodivisor is_zerodivisor


text:
export Record

struct Record
    chrom::String
    first::Int
    last::Int
    value::Real
end

function Base.:(==)(a::Record, b::Record)
    return a.chrom  == b.chrom &&
           a.first == b.first &&
           a.last == b.last &&
           a.value == b.value
end

function Base.isless(a::Record, b::Record, chrom_isless::Function=isless) :: Bool
    if a.chrom != b.chrom
        return chrom_isless(a.chrom, b.chrom) :: Bool
    elseif a.first != b.first
        return a.first < b.first
    elseif a.last != b.last
        return a.last < b.last
    elseif a.value != b.value
        return a.value < b.value
    else
        return false
    end
end

function Record(data::Vector{String})
    return convert(Record, data)
end

function Base.convert(::Type{Record}, data::Vector{String}) :: Record
    chrom, first, last, value = _convertCells(data)
    return Record(chrom, first, last, value)
end

function Base.convert(::Type{Vector{String}}, record::Record) :: Vector{String}
    return String[record.chrom, string(record.first), string(record.last), string(record.value)]
end

function Record(data::String)
    return convert(Record, data)
end

function Base.convert(::Type{Record}, str::AbstractString)
    data = _splitLine(str)
    return convert(Record, data)
end

function chrom(record::Record)::String
    return record.chrom
end

function Base.first(record::Record)::Int
    return record.first
end

function Base.last(record::Record)::Int
    return record.last
end

function value(record::Record)::Real
    return record.value
end

## Internal helper functions.
function _splitLine(line::String) ::Vector{String}
    cells::Vector{String} = filter!(!isempty, split(line, r"\s"))
end

function _convertCells(cells::Vector{String})
    length(cells) == 4 || error("Poor formatting:", cells)
    return cells[1], parse(Int, cells[2]), parse(Int, cells[3]), parse(Float64, cells[4]) #TODO: parse cell 4 as a generic Real.
end


text:
export AbstractPolynomial

# *internal* means to pass variable symbol to constructor through 2nd position and keep type stability
struct Var{T} end
Var(x::Symbol) = Var{x}()
Symbol(::Var{T}) where {T} = T

const SymbolLike = Union{AbstractString,Char,Symbol, Var{T} where T}
Base.Symbol(::Val{T}) where {T} = Symbol(T)

"""
    AbstractPolynomial{T,X}

An abstract type for various polynomials.

A polynomial type holds an indeterminate `X`; coefficients of type `T`, stored in some container type; and an implicit basis, such as the standard basis.

# Properties
- `coeffs` - The coefficients of the polynomial

# The type `T`

`T` need not be `T <: Number`, at the moment it is not restricted

Some `T`s will not be successful

* scalar mult: `c::Number * p::Polynomial` should be defined
* scalar mult: `c::T * p::Polynomial{T}` An  ambiguity when `T <: AbstractPolynomial`
* scalar mult: `p::Polynomial{T} * c::T` need not commute

* scalar add/sub: `p::Polynomial{T} + q::Polynomial{T}` should be defined
* scalar sub: `p::Polynomial{T} - p::Polynomial{T}`  generally needs `zeros(T,1)` defined for `zero(Polynomial{T})`

* poly mult: `p::Polynomial{T} * q::Polynomial{T}` Needs "`T * T`" defined (e.g. `Base.promote_op(*, Vector{Int}, Vector{Int}))` needs to be something.)
* poly powers: `p::Polynomial{T}^2` needs "`T^2`" defined

* implicit promotion: `p::Polynomial{T} + c::Number`  needs `convert(T, c)` defined
* implicit promotion: `p::Polynomial{T} + c::T`  ambiguity if `T <: AbstractPolynomial`

* evaluation: `p::Polynomial{T}(s::Number)`
* evaluation `p::Polynomial{T}(c::T)`   needs `T*T` defined

* derivatives: `derivative(p::Polynomial{T})`
* integrals: `integrate(p::Polynomial{T})`

"""
abstract type AbstractPolynomial{T,X} end

## -----
# We want  ⟒(P{α…,T,X}) = P{α…}; this default
# works for most cases
⟒(P::Type{<:AbstractPolynomial}) = constructorof(P)

# convert `as` into polynomial of type P based on instance, inheriting variable
# (and for LaurentPolynomial the offset)
_convert(p::P, as) where {T,X,P <: AbstractPolynomial{T,X}} = ⟒(P)(as, Var(X))

"""
    Polynomials.@register(name)

Given a polynomial with `name`, creates some common convenience constructors and conversions to minimize code required for implementation of a new polynomial type.

# Example
```julia
struct MyPolynomial{T,X} <: AbstractPolynomial{T,X} end

Polynomials.@register MyPolynomial
```

# Implementations
This will implement simple self-conversions like `convert(::Type{MyPoly}, p::MyPoly) = p` and creates two promote rules. The first allows promotion between two types (e.g. `promote(Polynomial, ChebyshevT)`) and the second allows promotion between parametrized types (e.g. `promote(Polynomial{T}, Polynomial{S})`).

For constructors, it implements the shortcut for `MyPoly(...) = MyPoly{T}(...)`, singleton constructor `MyPoly(x::Number, ...)`,  conversion constructor `MyPoly{T}(n::S, ...)`, and `variable` alternative  `MyPoly(var=:x)`.
"""
macro register(name)
    poly = esc(name)
    quote
        Base.convert(::Type{P}, p::P) where {P<:$poly} = p
        function Base.convert(P::Type{<:$poly}, p::$poly{T,X}) where {T,X}
            isconstant(p) && return constructorof(P){eltype(P),indeterminate(P)}(constantterm(p))
            constructorof(P){eltype(P), indeterminate(P,p)}(_coeffs(p))
        end
        Base.promote(p::P, q::Q) where {X, T, P <:$poly{T,X}, Q <: $poly{T,X}} = p,q
        Base.promote_rule(::Type{<:$poly{T,X}}, ::Type{<:$poly{S,X}}) where {T,S,X} =  $poly{promote_type(T, S),X}
        Base.promote_rule(::Type{<:$poly{T,X}}, ::Type{S}) where {T,S<:Number,X} =
            $poly{promote_type(T, S),X}

        $poly(coeffs::AbstractVector{T}, var::SymbolLike=Var(:x)) where {T} =
            $poly{T, Symbol(var)}(coeffs)
        $poly{T}(x::AbstractVector{S}, var::SymbolLike=Var(:x)) where {T,S} =
            $poly{T,Symbol(var)}(T.(x))

        function $poly{T}(coeffs::G, var::SymbolLike=Var(x)) where {T,G}
            !Base.isiterable(G) && throw(ArgumentError("coeffs is not iterable"))
            cs = collect(T, coeffs)
            $poly{T, Symbol(var)}(cs)
        end
        function $poly(coeffs::G, var::SymbolLike=Var(:x)) where {G}
            !Base.isiterable(G) && throw(ArgumentError("coeffs is not iterable"))
            cs = collect(coeffs)
            $poly{eltype(cs), Symbol(var)}(cs)
        end

        $poly{T,X}(c::AbstractPolynomial{S,Y}) where {T,X,S,Y} = convert($poly{T,X}, c)
        $poly{T}(c::AbstractPolynomial{S,Y}) where {T,S,Y} = convert($poly{T}, c)
        $poly(c::AbstractPolynomial{S,Y}) where {S,Y} = convert($poly, c)

        $poly{T,X}(n::S) where {T, X, S<:Number} =
            T(n) *  one($poly{T, X})
        $poly{T}(n::S, var::SymbolLike = Var(:x)) where {T, S<:Number} =
            T(n) *  one($poly{T, Symbol(var)})
        $poly(n::S, var::SymbolLike = Var(:x))  where {S  <: Number} = n * one($poly{S, Symbol(var)})

        $poly{T}(var::SymbolLike=Var(:x)) where {T} = variable($poly{T, Symbol(var)})
        $poly(var::SymbolLike=Var(:x)) = variable($poly, Symbol(var))

        (p::$poly)(x) = evalpoly(x, p)
    end
end

macro registerN(name, params...)
    poly = esc(name)
    αs = tuple(esc.(params)...)
    quote
        Base.convert(::Type{P}, q::Q) where {$(αs...),T, P<:$poly{$(αs...),T}, Q <: $poly{$(αs...),T}} = q
        Base.convert(::Type{$poly{$(αs...)}}, q::Q) where {$(αs...),T, Q <: $poly{$(αs...),T}} = q
        Base.promote(p::P, q::Q) where {$(αs...),T, X, P <:$poly{$(αs...),T,X}, Q <: $poly{$(αs...),T,X}} = p,q
        Base.promote_rule(::Type{<:$poly{$(αs...),T,X}}, ::Type{<:$poly{$(αs...),S,X}}) where {$(αs...),T,S,X} =
            $poly{$(αs...),promote_type(T, S),X}
        Base.promote_rule(::Type{<:$poly{$(αs...),T,X}}, ::Type{S}) where {$(αs...),T,X,S<:Number} =
            $poly{$(αs...),promote_type(T,S),X}

        function $poly{$(αs...),T}(x::AbstractVector{S}, var::SymbolLike = Var(:x)) where {$(αs...),T,S}
            $poly{$(αs...),T,Symbol(var)}(T.(x))
        end
        $poly{$(αs...)}(coeffs::AbstractVector{T}, var::SymbolLike=Var(:x)) where {$(αs...),T} =
            $poly{$(αs...),T,Symbol(var)}(coeffs)
        $poly{$(αs...),T,X}(c::AbstractPolynomial{S,Y}) where {$(αs...),T,X,S,Y} = convert($poly{$(αs...),T,X}, c)
        $poly{$(αs...),T}(c::AbstractPolynomial{S,Y}) where {$(αs...),T,S,Y} = convert($poly{$(αs...),T}, c)
        $poly{$(αs...),}(c::AbstractPolynomial{S,Y}) where {$(αs...),S,Y} = convert($poly{$(αs...),}, c)

        $poly{$(αs...),T,X}(n::Number) where {$(αs...),T,X} = T(n)*one($poly{$(αs...),T,X})
        $poly{$(αs...),T}(n::Number, var::SymbolLike = Var(:x)) where {$(αs...),T} = T(n)*one($poly{$(αs...),T,Symbol(var)})
        $poly{$(αs...)}(n::S, var::SymbolLike = Var(:x)) where {$(αs...), S<:Number} =
            n*one($poly{$(αs...),S,Symbol(var)})
        $poly{$(αs...),T}(var::SymbolLike=Var(:x)) where {$(αs...), T} =
            variable($poly{$(αs...),T,Symbol(var)})
        $poly{$(αs...)}(var::SymbolLike=Var(:x)) where {$(αs...)} = variable($poly{$(αs...)},Symbol(var))
        (p::$poly)(x) = evalpoly(x, p)
    end
end


text:
using Test
using LogicCircuits
using ProbabilisticCircuits
using DataFrames: DataFrame

@testset "structured probabilistic circuit nodes" begin

    vtree = PlainVtree(10, :balanced)
    f = fully_factorized_circuit(StructProbCircuit, vtree)
    @test f isa StructProbCircuit
    @test num_nodes(f) == 20+10+9*2+1
    @test num_edges(f) == 20+18+9+1
    @test length(mul_nodes(f)) == 9
    @test length(sum_nodes(f)) == 10+9+1
    @test num_parameters_node(f) == 1

    @test respects_vtree(f)
    @test respects_vtree(f, PlainVtree(10, :balanced))
    @test !respects_vtree(f, PlainVtree(5, :balanced))
    @test !respects_vtree(f, PlainVtree(10, :rightlinear))
    @test !respects_vtree(f, PlainVtree(10, :leftlinear))

    @test variable(left_most_descendent(f)) == Var(1)
    @test variable(right_most_descendent(f)) == Var(10)
    @test ispositive(left_most_descendent(f))
    @test isnegative(right_most_descendent(f))

    @test literal((StructProbCircuit,vtree)(Lit(-5))) == Lit(-5)

    @test_throws Exception multiply(StructProbCircuit[])
    @test_throws Exception summate(StructProbCircuit[])

    @test isdecomposable(f)

    @test variables(f) == BitSet(1:10)
    @test num_variables(f) == 10
    @test issmooth(f)

    input = DataFrame(BitArray([1 0 1 0 1 0 1 0 1 0;
                1 1 1 1 1 1 1 1 1 1;
                0 0 0 0 0 0 0 0 0 0;
                0 1 1 0 1 0 0 1 0 1]), :auto)
    @test satisfies(f,input) == BitVector([1,1,1,1])

    plainf = PlainLogicCircuit(f)
    foreach(plainf) do n
        @test n isa PlainLogicCircuit
    end
    @test plainf !== f
    @test num_edges(plainf) == num_edges(f)
    @test num_nodes(plainf) == num_nodes(f)
    @test length(and_nodes(plainf)) == 9
    @test length(or_nodes(plainf)) == 10+9+1
    @test model_count(plainf) == BigInt(2)^10
    @test isempty(intersect(linearize(f),linearize(plainf)))

    ref = StructProbCircuit(vtree,plainf)
    foreach(ref) do n
        @test n isa StructProbCircuit
    end
    @test plainf !== ref
    @test f !== ref
    @test f.vtree === ref.vtree
    @test num_edges(ref) == num_edges(f)
    @test num_nodes(ref) == num_nodes(f)
    @test num_parameters_node(ref) == num_parameters_node(f)
    @test length(and_nodes(ref)) == 9
    @test length(or_nodes(ref)) == 10+9+1
    @test model_count(ref) == BigInt(2)^10
    @test isempty(intersect(linearize(f),linearize(ref)))

    ref = StructProbCircuit(vtree,f)
    foreach(ref) do n
        @test n isa StructProbCircuit
    end
    @test plainf !== ref
    @test f !== ref
    @test f.vtree === ref.vtree
    @test num_edges(ref) == num_edges(f)
    @test num_nodes(ref) == num_nodes(f)
    @test num_parameters_node(ref) == num_parameters_node(f)
    @test length(and_nodes(ref)) == 9
    @test length(or_nodes(ref)) == 10+9+1
    @test model_count(ref) == BigInt(2)^10
    @test isempty(intersect(linearize(f),linearize(ref)))

    mgr = SddMgr(7, :balanced)
    v = Dict([(i => compile(mgr, Lit(i))) for i=1:7])
    c = (v[1] | !v[2] | v[3]) &
        (v[2] | !v[7] | v[6]) &
        (v[3] | !v[4] | v[5]) &
        (v[1] | !v[4] | v[6])

    c2 = StructLogicCircuit(mgr, c)
    c2 = propagate_constants(c2; remove_unary=true)

    c3 = StructProbCircuit(mgr, c2)
    foreach(c3) do n
      @test n isa StructProbCircuit
    end
    @test num_edges(c3) == 69
    @test num_variables(c3) == 7

    # compilation tests
    v = Vtree(Var(1))
    @test_throws Exception compile(StructProbCircuit, v, true)
    lit1 = compile(StructProbCircuit, v, Lit(1))
    litn1 = compile(StructProbCircuit, v, Lit(-1))
    r = lit1 * 0.3 + 0.7 * litn1
    @test r isa StructSumNode
    @test all(children(r) .== [lit1, litn1])
    @test r.vtree === lit1.vtree
    @test all(r.log_probs .≈ log.([0.3, 0.7]))
    r1 = compile(r, r)
    @test r1 !== r
    @test r1.vtree === r.vtree
    @test num_edges(r1) == num_edges(r)
    @test num_nodes(r1) == num_nodes(r)
    @test num_parameters_node(r1) == num_parameters_node(r)
    @test isempty(intersect(linearize(r1),linearize(r)))

    # Convert tests
    function apply_test_cnf(n_vars::Int, cnf_path::String)
        for i in 1:10
            vtree = Vtree(n_vars, :random)
            sdd = compile(SddMgr(vtree), zoo_cnf(cnf_path))
            @test num_variables(sdd) == n_vars
            psdd = compile(StructProbCircuit, sdd)
            @test num_variables(psdd) == n_vars
            @test issmooth(psdd)
            @test isdecomposable(psdd)
            @test isdeterministic(psdd)
            @test respects_vtree(psdd, vtree)
            data = DataFrame(convert(BitMatrix, rand(Bool, 100, n_vars)), :auto)
            @test all(sdd(data) .== (EVI(psdd, data) .!=  -Inf))
        end
        nothing
    end
    apply_test_cnf(17, "easy/C17_mince.cnf")
    apply_test_cnf(14, "easy/majority_mince.cnf")
    apply_test_cnf(21, "easy/b1_mince.cnf")
    apply_test_cnf(20, "easy/cm152a_mince.cnf")
end


text:
using Pkg; Pkg.activate("."); Pkg.instantiate()
using Revise


text:
"""
    center(f=mean, x, y=f(skipmissing(x)))

Center an array `x` about a scalar `y`.

See also [`center!`](@ref)
"""
center(x) = center(mean, x)
center(f::Function, x) = center(x, f(skipmissing(x)))
center(x, y) = x .- y

"""
    center!(f=mean, x, y=f(skipmissing(x)))

Center an array `x` in place about a scalar `y`.

See also [`center`](@ref)
"""
center!(x) = center!(mean, x)
center!(f::Function, x) = center!(x, f(skipmissing(x)))

function center!(x, y)
    try
        y = convert(eltype(x), y)
        # verify that we also don't hit problems during substraction
        # I can't think of any common number type that isn't closed under
        # subtraction, but maybe somebody has created a PositiveInt type
        convert(eltype(x), first(x) - y)
    catch e
        if e isa InexactError
            throw(ArgumentError("Subtracting the center $(y) changes the eltype of " *
                                "the array. Promote to $(typeof(first(x) - y)) first."))
        else
            rethrow(e)
        end
    end
    x .-= y
    return x
end

"""
    struct Center

Represents a centering scheme, akin to `StatsModels.AbstractContrasts`.  Pass as
value in `Dict` as hints to `schema` (or as `contrasts` kwarg for `fit`).

## Examples

Can specify center value to use:

```
julia> schema((x=collect(1:10), ), Dict(:x => Center(5)))
StatsModels.Schema with 1 entry:
  x => center(x, 5)
```

You can use a function to compute the center value:

julia> schema((x=collect(1:10), ), Dict(:x => Center(median)))
StatsModels.Schema with 1 entry:
  x => x(centered: 5.5)

Or [`center`](@ref) will be automatically computed if omitted:

```
julia> schema((x=collect(1:10), ), Dict(:x => Center()))
StatsModels.Schema with 1 entry:
  x => center(x, 5.5)
```
"""
struct Center
    center::Any
end

Center() = Center(nothing)
Center(xs::AbstractArray, c::Center) = Center(_standard(xs, c.center))

"""
    struct CenteredTerm{T,C} <: AbstractTerm

A lazily centered term.  A wrapper around an `T<:AbstractTerm` which will
produce centered values with `modelcols` by subtracting `center` from each
element generated by the wrapped term with `modelcols`.

## Fields

- `term::T`: The wrapped term.
- `center::C`: The center value subtracted from the resulting `modelcols`.

## Examples

Directly construct with given center:

```
julia> d = (x=collect(1:10), );

julia> t = concrete_term(term(:x), d)
x(continuous)

julia> tc = CenteredTerm(t, 5)
x(centered: 5)

julia> hcat(modelcols(t + tc, d)...)
10×2 Matrix{Int64}:
  1  -4
  2  -3
  3  -2
  4  -1
  5   0
  6   1
  7   2
  8   3
  9   4
 10   5
```

Construct with lazy centering via [`Center`](@ref)

```
julia> tc = concrete_term(term(:x), d, Center())
center(x, 5.5)

julia> hcat(modelcols(t + tc, d)...)
10×2 Matrix{Float64}:
  1.0  -4.5
  2.0  -3.5
  3.0  -2.5
  4.0  -1.5
  5.0  -0.5
  6.0   0.5
  7.0   1.5
  8.0   2.5
  9.0   3.5
 10.0   4.5
```

Or similarly via schema hints:

```
julia> sch = schema(d, Dict(:x => Center()))
StatsModels.Schema with 1 entry:
  x => center(x, 5.5)
```

"""
struct CenteredTerm{T,C} <: AbstractTerm
    term::T
    center::C
end

function StatsModels.concrete_term(t::Term, xs::AbstractArray, c::Center)
    return center(StatsModels.concrete_term(t, xs, nothing), Center(xs, c))
end

# run-time constructors:
center(t::ContinuousTerm, c::Center) = CenteredTerm(t, something(c.center, t.mean))
center(t::ContinuousTerm, c) = CenteredTerm(t, c)
center(t::ContinuousTerm) = CenteredTerm(t, t.mean)
function center(t::AbstractTerm)
    throw(ArgumentError("can only compute center for ContinuousTerm; must provide center value via center(t, c)"))
end

function center(t::AbstractTerm, c::Center)
    c.center !== nothing ||
        throw(ArgumentError("can only compute center for ContinuousTerm; must provide center via center(t, c)"))
    return CenteredTerm(t, c.center)
end

StatsModels.modelcols(t::CenteredTerm, d::NamedTuple) = modelcols(t.term, d) .- t.center
function StatsBase.coefnames(t::CenteredTerm)
    if StatsModels.width(t.term) == 1
        return "$(coefnames(t.term))(centered: $(_round(t.center)))"
    elseif length(t.center) > 1
        return string.(vec(coefnames(t.term)), "(centered: ", _round.(vec(t.center)), ")")
    else
        return string.(coefnames(t.term), "(centered: ", _round.(t.center), ")")
    end
end
# coef table: "x(centered: 5.5)"
Base.show(io::IO, t::CenteredTerm) = print(io, "$(t.term)(centered: $(_round(t.center)))")
# regular show: "x(centered: 5.5)", used in displaying schema dicts
function Base.show(io::IO, ::MIME"text/plain", t::CenteredTerm)
    return print(io, "$(t.term)(centered: $(_round(t.center)))")
end
# long show: "x(centered: 5.5)"

# statsmodels glue code:
StatsModels.width(t::CenteredTerm) = StatsModels.width(t.term)
# don't generate schema entries for terms which are already centered
StatsModels.needs_schema(::CenteredTerm) = false
StatsModels.termsyms(t::CenteredTerm) = StatsModels.termsyms(t.term)
StatsModels.termvars(t::CenteredTerm) = StatsModels.termvars(t.term)