text:
using DifferentialEquations
using Optim

function init_guess(M, X, Y)
    #=
    Gives a reasonable guess for Pc, Tc, R, and L for a star of mass M
    =#
    yhalf = [Rsun * 10 ^ 0.062544715341505616, Lsun * 10 ^ -0.36630780815983022, 10 ^ 19.700356657315954, 10 ^ 7.2863105891013289]
    y2 = [Rsun * 10 ^ 0.36072469919806871, Lsun * 10 ^ 1.3021335874028637, 10 ^ 17.186879800538229, 10 ^ 7.3557279873577759]
    y5 = [Rsun * 10 ^ 0.59322648100945885, Lsun * 10 ^ 2.8743140385067893, 10 ^ 16.752833342638592, 10 ^ 7.4547058342916337]
    y4 = [Rsun * 10 ^ 0.52962615387538248, Lsun * 10 ^ 2.50275963393719, 10 ^ 16.855407680647915, 10 ^ 7.429616124439673]
    y10 = [Rsun * 10 ^ 0.68191099104456998, Lsun * 10 ^ 3.8447263619458569, 10 ^ 16.517451455410988, 10 ^ 7.5012272142698366]
    if M == Msun
        return [Rsun, Lsun, 2.4e17, 1.6e7]
    elseif M == 0.5 * Msun
        @info("init_guess: from MESA-Web")
        return yhalf
    elseif M == 2 * Msun
        @info("init_guess: from MESA-Web")
        return y2
    elseif (M >= 4 * Msun) & (M <= 5 * Msun)
        @info("init_guess: from MESA-Web")
        dm = M - 4 * Msun
        dydm = (y5 - y4) / Msun
        return y4 + dydm * dm
    elseif M == 10 * Msun
        @info("init_guess: from MESA-Web")
        return y10
    end
    mu = mu_from_composition(X, Y)

    # mass-radius relation
    if M / Msun > 1.3 # then CNO burning dominates
        z1 = 0.8
        z2 = 0.64
    else # then pp chain dominates
        z1 = 0.5
        z2 = 0.1
    end # if
    R = Rsun * (M / Msun) ^ z1 * (mu / 0.61) ^ z2
    # mass-luminosity relation
    L = Lsun * (M / Msun) ^ 3 * (mu / 0.61) ^ 4
    # Pc, Tc guesses, essentially dimensional analysis with fudges
    Pc = 2 * G * M ^ 2 / (pi * R ^ 4)
    Tc = 8 * 0.02 * mu * m_H * G * M / (k_B * R)
    return [R, L, Pc, Tc]
end # init_guess

function load1(m, Pc, Tc, X, Y)
    #=
    Subroutine for shootf.
    Loads the (r, l, P, T) center (m = 0) values for starting the outward
    integration.
    =#
    mu = mu_from_composition(X, Y)
    # m = 0.01 * Msun # needs tuning
    rhoc = rho_ideal(Pc, Tc, mu)
    rc = (3 * m / (4 * pi * rhoc)) ^ (1 / 3)
    lc = dldm(m, 0, 0, 0, Tc, rhoc, X, Y) * m
    # @debug("load1: center values = ", [rc, lc, Pc, Tc])
    return [rc, lc, Pc, Tc]
end # load1

function load2(M, Rs, Ls, X, Y; max_iterations=1000)
    #=
    Subroutine for shootf.
    Loads the (r, l, P, T) surface (m = M) values for starting the inward
    integration.
    =#
    mu = mu_from_composition(X, Y)
    converged = false
    rho_guess = 1e-8 # need a less arbitrary guess?
    Ts = try
        T_surface(Rs, Ls)
    catch
        @warn("load2: Rs = ", Rs)
        @warn("load2: Ls = ", Ls)
    end
    Ps = 0
    count = 1
    while ~converged & (count < 100)
        kappa = opacity(logkappa_spl, rho_guess, Ts)
        Ps = P_surface(M, Rs, kappa)
        rho = rho_ideal(Ps, Ts, mu)
        relerr = (rho - rho_guess) / rho
        if abs(relerr) < 1e-6 # need less arbitrary convergence condition
            converged = true
        else
            rho_guess = rho_guess + relerr * rho / 6
            count += 1
        end # if
    end # while
    if count >= max_iterations
        @warn("load2: Exceeded ", max_iterations, " iterations!")
    end # if
    # @debug("load2: surface values = ", [Rs, Ls, Ps, Ts])
    return [Rs, Ls, Ps, Ts]
end # load2

function profiles(m, M, Rs, Ls, Pc, Tc, X, Y, mf)
    #=
    Integrates outward from m to mf and inward from M to mf to construct
    r, l, P, and T profiles from the given initial conditions.
    =#
    yc = load1(m, Pc, Tc, X, Y)
    prob = ODEProblem(deriv!,yc,(0, mf), (X, Y))
    sol_out = solve(prob, Tsit5());
    sol_out_v = hcat(sol_out.u...)'

    ys = load2(M, Rs, Ls, X, Y)
    prob = ODEProblem(deriv!,ys,(M, mf), [X, Y])
    sol_in = solve(prob, Tsit5());
    sol_in_v = hcat(sol_in.u...)'
    return sol_out.t, sol_in.t, sol_out_v, sol_in_v
end

function profiles(star::Star, Rs, Ls, Pc, Tc; n=1000)
    return profiles(star.m, star.M, Rs, Ls, Pc, Tc, star.X, star.Y, star.mf)
end

function deriv!(du,u,p,m)
    r, l, P, T = u
    X, Y = p

    mu = mu_from_composition(X, Y)
    rho = rho_ideal(P, T, mu)

    kappa = opacity(logkappa_spl, rho, T)

    du[1] = drdm(m, r, l, P, T, rho, mu)
    du[2] = dldm(m, r, l, P, T, rho, X, Y)
    du[3] = dPdm(m, r, l, P, T)
    du[4] = dTdm(m, r, l, P, T, mu, kappa)
end

function score(m, M, Rs, Ls, Pc, Tc, X, Y, mf)
    #=
    Function to zero.
    =#
    ss = try
        sol_out_t, sol_in_t, sol_out_v, sol_in_v = profiles(m, M, Rs, Ls, Pc, Tc, X, Y, mf)
        (sol_out_v[end, :] - sol_in_v[end, :]) ./ sol_in_v[end, :]
    catch
        [NaN]
    end

    return (sum(ss.^2))
end # score

function shootf(m, M, X, Y; fixed_point=0.8)
    #=
    Shooting to a fixed point
    =#
    y0 = init_guess(M, X, Y)
    mf = fixed_point * M
    f(y) = score(m, M, y[1], y[2], y[3], y[4], X, Y, mf)

    res = optimize(f, y0,
                g_tol = 1e-15,
                iterations = 10000,
                allow_f_increases=true,
                store_trace = false,
                show_trace = false
    )
    return res
end # shootf

function shootf(star::Star)
    return shootf(star.m, star.M, star.X, star.Y)
end

function shootf!(star::Star)
    res = shootf(star)
    bc = Optim.minimizer(res)
    sol_out_t, sol_in_t, sol_out_v, sol_in_v = profiles(star, bc...)
    star.grid = vcat(sol_out_t, reverse(sol_in_t))
    star.r = vcat(sol_out_v[:,1], reverse(sol_in_v[:,1]))
    star.l = vcat(sol_out_v[:,2], reverse(sol_in_v[:,2]))
    star.P = vcat(sol_out_v[:,3], reverse(sol_in_v[:,3]))
    star.T = vcat(sol_out_v[:,4], reverse(sol_in_v[:,4]));
    return res
end


text:
function tree_write(file, DT::DecisionTree, edges::Array{String, 1}, i; return_i = false)
    tree = DT.tree
    s = []
    push!(s, (nothing, tree))
    while length(s) != 0
        old_name, v = pop!(s)
        name = "node"*string(i)
        println(file, name*" [")
        st = ""
        if v["left"] != nothing
            st = "X["*string(v["feature_id"])*"]<"*string(v["threshold"])*"\n"
        end
        samples = sum(v["class_count"])
        st *= "samples="*string(samples)*"\n"
        st *= "value="*string(v["class_count"])
        println(file, "label = \""*st*"\"")
        println(file, "];")
        if old_name != nothing
            if old_name != "node1"
                push!(edges, old_name*"->"*name*";")
            elseif name == "node2"
                push!(edges, "node1->node2[label = \"True\"];")
            else
                push!(edges, "node1->"*name*"[label = \"False\"];")
            end
        end
        i += 1
        if v["left"] != nothing
            push!(s, (name, v["right"]))
            push!(s, (name, v["left"]))
        end
        if return_i
            return i
        end
    end
end

"""
    MV(path, forest; rounded=false, bg="#ffffff", fc="#000000", nc="#ffffff", label="Tree", fs="18")
Make DicisionTree and RandomForest Visual(make a dot file, see also [Graphviz](https://graphviz.org/)). The arguments are as follows:
- `path` : The full path of the dot file. The suffix must be `.dot`.
- `forest` : The model.
- `rounded` : If `rounded` is `true`, the nodes will be rounded.
- `bg` : Background color, type of this must be `String`.
- `fc` : Font color, type of this must be `String`.
- `nc` : Node color, type of this must be `String`.
- `label` : The label of the graph.
- `fs` : Font size, type of this must be `String`.

# Example
```repl
julia> MV("/home/ubuntu/test.dot", model, rounded = true)
```
"""
function MV(path, tree::DecisionTree; rounded = false, bg = "#ffffff", fc = "#000000", nc="#ffffff", label = "Tree", fs = "18") # Model Visualization
    if path[length(path) - 3 : length(path)] != ".dot"
        throw(ArgumentError("The file path you passed is not prefixed with'dot'."))
    end
    open(path, "w") do file
        println(file, "digraph Tree_model{")
        println(file, "graph[")
        println(file, "label = \""*label*"\",")
        println(file, "bgcolor = \""*bg*"\",")
        println(file, "fontcolor = \""*fc*"\",")
        println(file, "fontsize = \""*fs*"\",")
        println(file, "style = \"filled\",")
        println(file, "margin = 0.2\n];")
        println(file, "node[")
        println(file, "shape = box,")
        if rounded
            println(file, "style = \"rounded, filled\",")
        end
        println(file, "fillcolor = \""*nc*"\"\n];")
        i = 1
        edges = String[]
        tree_write(file, tree, edges, i)
        for edge in edges
            println(file, edge)
        end
        print(file, "}")
    end
end

function MV(paths::Vector{String}, forest::RandomForest{N}; rounded = false, bg = "#ffffff", fc = "#000000", label = "Tree", fs = "18", nc="#ffffff") where {N}# Model Visualization
    if length(paths) != N
        throw(ArgumentError("the number of trees in RandomForest and length of `paths` must match!"))
    end
    for i in 1 : N
        MV(paths[i], forest.forest[i], rounded=rounded, bg=bg, fc=fc, label=label, fs=fs, nc=nc)
    end
end

ext: jl


text:
#-------------------------------------------------------------------
#* EMSO Model Library (EML) Copyright (C) 2004 - 2007 ALSOC.
#*
#* This LIBRARY is free software; you can distribute it and/or modify
#* it under the therms of the ALSOC FREE LICENSE as available at
#* http://www.enq.ufrgs.br/alsoc.
#*
#* EMSO Copyright (C) 2004 - 2007 ALSOC, original code
#* from http://www.rps.eng.br Copyright (C) 2002-2004.
#* All rights reserved.
#*
#* EMSO is distributed under the therms of the ALSOC LICENSE as
#* available at http://www.enq.ufrgs.br/alsoc.
#*--------------------------------------------------------------------
#* Author: Gerson Balbueno Bicca
#* $Id$
#*--------------------------------------------------------------------
type Tube_Pdrop
	Tube_Pdrop()=begin
		new(
			press_delta(Dict{Symbol,Any}(
				:Brief=>"Tube Pressure Drop due to friction",
				:Symbol=>"\\Delta P_{tube}",
				:Default=>0.01,
				:Lower=>1E-10,
				:DisplayUnit=>"kPa"
			)),
			press_delta(Dict{Symbol,Any}(
				:Brief=>"Inlet Nozzle Pressure Drop",
				:Symbol=>"\\Delta P_{Nozzle\\_In}",
				:Default=>0.01,
				:Lower=>0,
				:DisplayUnit=>"kPa"
			)),
			press_delta(Dict{Symbol,Any}(
				:Brief=>"Outlet Nozzle Pressure Drop",
				:Symbol=>"\\Delta P_{Nozzle\\_Out}",
				:Default=>0.01,
				:Lower=>0,
				:DisplayUnit=>"kPa"
			)),
			press_delta(Dict{Symbol,Any}(
				:Brief=>"Total Pressure Drop",
				:Symbol=>"\\Delta P_{total}",
				:Default=>0.01,
				:Lower=>1E-10,
				:DisplayUnit=>"kPa"
			)),
			velocity(Dict{Symbol,Any}(
				:Brief=>"Inlet Nozzle Velocity",
				:Symbol=>"V_{Nozzle\\_In}",
				:Default=>1,
				:Upper=>1e5,
				:Lower=>0
			)),
			velocity(Dict{Symbol,Any}(
				:Brief=>"Outlet Nozzle Velocity",
				:Symbol=>"V_{Nozzle\\_Out}",
				:Default=>1,
				:Upper=>1E5,
				:Lower=>0
			)),
			fricfactor(Dict{Symbol,Any}(
				:Brief=>"Friction Factor",
				:Symbol=>"f_i",
				:Default=>0.05,
				:Lower=>1e-10,
				:Upper=>2000
			)),
			[
				:(Total = TubeFriction + InletNozzle + OutletNozzle),
			],
			[
				"Total Pressure Drop",
			],
			[:TubeFriction,:InletNozzle,:OutletNozzle,:Total,:Vnozzle_in,:Vnozzle_out,:FricFactor,]
		)
	end
	TubeFriction::press_delta
	InletNozzle::press_delta
	OutletNozzle::press_delta
	Total::press_delta
	Vnozzle_in::velocity
	Vnozzle_out::velocity
	FricFactor::fricfactor
	equations::Array{Expr,1}
	equationNames::Array{String,1}
	variables::Array{Symbol,1}
	attributes::Dict{Symbol,Any}
end
export Tube_Pdrop
function setEquationFlow(in::Tube_Pdrop)
	addEquation(1)
end
function atributes(in::Tube_Pdrop,_::Dict{Symbol,Any})
	fields::Dict{Symbol,Any}=Dict{Symbol,Any}()
	fields[:Pallete]=false
	fields[:Brief]="Pressure drop and velocities in the tube side section of a shell and tube heat exchanger."
	drive!(fields,_)
	return fields
end
Tube_Pdrop(_::Dict{Symbol,Any})=begin
	newModel=Tube_Pdrop()
	newModel.attributes=atributes(newModel,_)
	newModel
end


text:
module OpenSMILES
    using SimpleWeightedGraphs, LightGraphs
    import Base: ==
    include("Elements.jl")
    export bondoperators, specialoperators, operators, bonds, valence,
            aliphatics, aromatics, bracket, bracket_aromatic, isoperator,
            isbondoperator, isspecialoperator, ispm, FindNumerics, FindPMs,
            Element, GraphElement, abbreviation, implicitH, explicitH, H,
            charge, isotope, ==, instantiate_hydrogens

    include("ReadFns.jl")
    export ReadNextElement, ReadNextNumeric, ReadNextCharge

    include("Brackets.jl")
    export BracketState, BracketParseException, ParseBracket

    include("SMILESParser.jl")
    export SMILESParseException, ParseSMILES

    include("Utils.jl")
    export WeightedToSimple, countitems

    include("deprecations.jl")
end


text:
#  Copyright 2016, Iain Dunning, Joey Huchette, Miles Lubin, and contributors
#  This Source Code Form is subject to the terms of the Mozilla Public
#  License, v. 2.0. If a copy of the MPL was not distributed with this
#  file, You can obtain one at http://mozilla.org/MPL/2.0/.
#############################################################################
# JuMP
# An algebraic modelling langauge for Julia
# See http://github.com/JuliaOpt/JuMP.jl
#############################################################################
# Hock-Schittkowski Nonlinear Test Suite  -  HS112
# This file is JuMP implementation of the model described in
#  W. Hock, K. Schittkowski, Test Examples for Nonlinear Programming
#  Codes, Lecture Notes in Economics and Mathematical Systems,
#  Springer, No, 187, 1981
# More information, including original model description, at
# http://www.ai7.uni-bayreuth.de/downloads.htm
#
# This problem has an objective with the logarithm of a fraction where both
# the nominator and denominator have variables in them. Constraints linear.
#############################################################################

using JuMP
using Base.Test

let

c = [-6.089, -17.164, -34.054, -5.914, -24.721, -14.986, -24.100, -10.708, -26.662, -22.179]

m = Model()
@variable(m, x[1:10] >= 1e-6, start = 0.1)

@NLobjective(m, Min, sum{x[j]*(c[j] + log(x[j]/sum{x[k],k=1:10})), j=1:10})

@NLconstraint(m, x[1] + 2*x[2] + 2*x[3] + x[6] + x[10] == 2)
@NLconstraint(m, x[4] + 2*x[5] + x[6] + x[7] == 1)
@NLconstraint(m, x[3] + x[7] + x[8] + 2*x[9] + x[10] == 1)

solve(m)

@test_approx_eq_eps getobjectivevalue(m) -47.76109026 1e-5

end


text:
function unassigned_test_functional()
    test_delivery_unassigned_functional()
end

function test_delivery_unassigned_functional()
    employees = Dict() # no employees :-)
    ok, weekly_schedule = schedule(employees, test_delivery_unassigned_env)
    @test ok
end

max_stars_repo_name: shdann/autoshift

max_issues_repo_name: shdann/autoshift

max_forks_repo_name: shdann/autoshift


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

using Markdown

# ╔═╡ 148d5d63-2af4-42dc-82b7-66048377a773
md"""
# Introduction to metaprogramming: "Code that creates code"
"""

# ╔═╡ a5fa90ce-e4bc-490a-b86a-9a1acefc3b9c
md"""
Julia has strong **metaprogramming** capabilities. What does this mean?

> **meta**: something on a higher level

**metaprogramming** = "higher-level programming"

i.e. writing code (a program) to manipulate not data, but code (that itself manipulates data)

"""

# ╔═╡ 054538b8-6e2a-475c-96bd-b1a063b5e454
md"""
## Motivating example: Interact.jl
"""

# ╔═╡ 819b2c2a-40f8-4a9c-be30-b8bd54efb53e
md"""
#### Exercise 1

1. Install the `Interact.jl` package.

2. Run the following code
"""

# ╔═╡ f4531836-3fde-40df-8b96-1587cc4d2037
for i in 1:10
    j = i^2
    println("The square of $i is $(j)")
end

# ╔═╡ c9260621-2249-4a37-9bc8-e67967c5ccae
using Interact

@manipulate for i in 1:10
    j = i^2
    "The square of $i is $(j)"
end

# ╔═╡ 1e9b883b-0799-46fd-ba28-0652fcdc89d5

# ╔═╡ fdffa5b5-0ec9-4f0a-b170-f958e5709495
md"""
You should see a slider appear with the label `i`. When you manipulate the slider, the caption should show the value of `i` and its square, and should update when you move the slider.
"""

# ╔═╡ 4a82f384-7872-4730-9daf-5c5d9a148b9b
md"""
What happened here? A `for` loop iterates over its values. Somehow the `@manipulate` command, which is a **macro**, took the object "code for the `for` loop" and replaced it with "code for a slider with the same range"; in other words, `@manipulate` operated on code to produce some different code that did something useful -- it took in a program and replaced it by a different program.
"""

# ╔═╡ 7f2ee2d3-050c-47b1-ab77-51858651660f
md"""
We can see the result of the `@manipulate` by "expanding" the effect of the macro using `@macroexpand`. To make it easier to read, we will suppress some line information using the `MacroTools` package:
"""

# ╔═╡ 3b19a1ce-4950-46bc-aac3-56f9dd335563
code = @macroexpand @manipulate for i in 1:10
    j = i^2
    "The square of $i is $j"
end;

using MacroTools
MacroTools.striplines(code)

# ╔═╡ 5c2243f3-2fbb-4c1b-a51b-62516eb618c9
md"""
We see that the `for` loop has been replaced by code for manipulating a widget. The information about the variable name `i`,  the range `1:10` and and the code inside the `for` loop have been preserved, but they have been embedded in a certain way into a new *piece of code*.
"""

# ╔═╡ 5f53981e-b61e-4b23-8c1a-e2b24da67d1c
md"""
In order to carry out a *code transformation* like this, Julia allows us to *manipulate Julia code from within Julia*: we need to be able to get inside a piece of Julia code and modify it, *before* that code reaches the Julia compiler.

Finally we need to see how to wrap the result up into a macro.
"""

# ╔═╡ ae487af5-0f68-44c7-8693-ed499de7b0b5
md"""
## Expressions
"""

# ╔═╡ 522811ea-32aa-4501-843f-20e0c7f1cc69
i

# ╔═╡ d2c93c05-c245-4bd5-b655-12a39128f915
j = i^2

# ╔═╡ e342b2b1-f75e-49e9-968d-00feb6d77939
md"""
Let's start with just the part `j = i^2`. If we type this code into a *fresh* Julia session, we get the following error:
```
julia> j  = i^2
ERROR: UndefVarError: i not defined
Stacktrace:
 [1] top-level scope at none:0
```
since Julia is trying to *evaluate* the code using the values for the variables `i` and `j`, which are not defined.

[If instead we type this after running the above `@manipulate` command, `i` is interpreted as a slider and we get a different error.]
"""

# ╔═╡ ecb42fef-b833-467b-b4cf-339c61224997
md"""
For metaprogramming purposes, we do not wish to *evaluate* the code; instead, we just want to treat the code as unevaluated symbolic expressions, which will gain meaning only later. Julia allows us to construct unevaluated pieces of code as follows:
"""

# ╔═╡ b27af88b-130f-4874-8265-f675e993ef63
quote
    j = i^2
end

# ╔═╡ 87942a87-aa67-4c3a-94e2-5573256168c8
md"""
or with the following shorthand syntax:
"""

# ╔═╡ 90dd1959-b677-4e8e-8969-9ad38bf4c268
:(j = i^2)

# ╔═╡ cec66c72-a710-4772-81e5-cb80e38f7360
md"""
#### Exercise 2

1. Define a variable `code` to be `:(j = i^2)`.

2. What type is the object `code`? Note that `code` is just a normal Julia variable, of a particular special type.

3. Use the `dump` function to see what there is inside `code`.
Remembering that `code` is just a particular kind of Julia object, use the Julia to play around interactively, seeing how you can extract pieces of the `code` object.

4. How is the operation `i^2` represented? What kind of object is that subpiece?

5. Copy `code` into a variable `code2`. *Modify* this to replace the power `2` with a power `3`. Make sure that the original `code` variable is *not* also modified.

6. Copy `code2` to a variable `code3`. Replace `i` with `i + 1` in `code3`.

7. Define a variable `i` with the value `4`. *Evaluate* the different `code` expressions using the `eval` function and check the *value* of the variable `j`.

"""

# ╔═╡ 5ce56321-b125-4fc9-a67e-af3bd8ff0b52
code = Meta.parse("j = i^2")

# ╔═╡ 70007757-cef8-4a61-a22c-97557c10cde7
typeof(code)

# ╔═╡ a75957ce-df3c-4d06-ab37-24c28223c70c
dump(code)

# ╔═╡ a396823a-255c-474c-95c4-740dc32c918a
code.head

# ╔═╡ cdb6135b-ab18-47cc-bb22-548a2c3933d7
typeof(ans)

# ╔═╡ 80c70710-7393-4744-bf04-c2733ef29555
:+

# ╔═╡ 09e9f3e0-627a-46de-8245-7b318853a14d
typeof(ans)

# ╔═╡ afc9966e-1a44-43f3-9679-93d99b3457e1
+

# ╔═╡ 392fdf9e-b09e-468f-b2b0-da74853329d2
code.args

# ╔═╡ 906ba4d2-cc31-481c-920a-db27d7bea71d
typeof(code.args)

# ╔═╡ 9150e575-ec71-4c2f-9464-818b9dac368d
code.args[1]

# ╔═╡ aebe63a2-6546-4855-8956-b0f2a1f6b699
typeof(code.args[1])

# ╔═╡ f916093d-c950-45c2-9a07-14a8b0c9d961
code.args[2]

# ╔═╡ ecf7d993-f186-4c7b-90cd-ebbf391362b6
typeof(code.args[2])

# ╔═╡ 4176cf1d-2d9f-4054-bedb-48aa561b5612
code.args[2].head

# ╔═╡ d5d7cd00-403d-410a-93be-3f4b7bbfa7a6
code.args[2].args

# ╔═╡ 58ba1f17-87dd-4395-a77e-e733a94b82ff
code.args[2].args[3]

# ╔═╡ 72357f57-94ec-4807-b923-e315691bf892
code.args[2].args[3] = 3

# ╔═╡ adf9f854-1252-4a9d-8bdd-4d2d637d63c8
code

# ╔═╡ 0440a738-05d6-4011-8d9c-3c4440fa50d8
code.args[2].args[2] = :(i + 1)

# ╔═╡ 0df91dca-4baf-4d38-a4b0-ff77e2c8fbb0
code

# ╔═╡ 6ccc334b-6512-4f01-a73a-4b94c4469e30
dump(ans)

# ╔═╡ c3251450-8347-4a4f-af52-adadf8fa17fa
md"""
We have just taken a (very simple) program, represented by `code`, and produced two new programs, `code2` and `code3`, which we ran using `eval`. This is the basis of all metaprogramming: taking in a piece of code, and modifying it to produce a new piece of code.
"""

# ╔═╡ 9ab8441f-7dc9-4a8d-a522-29e668ae123a

# ╔═╡ 8e9507d6-4a5d-4885-80e3-31d3603e17da
md"""
## Walking a syntax tree
"""

# ╔═╡ d7e95362-16f1-445a-874b-bd321bcaac45
md"""
In the previous exercise, we modified a single `i` to `i + 1`. But in general we might have a more complicated expression like `i^2 + (i * (i - 3))` and we may wish to modify *all* of the `i`s in the expression to `(i+1)`s or to `k`s to produce the new expression. The problem is that they may be buried arbitrarily deeply. We thus need to find a way of walking through the whole expression to examine each subpiece of it.
"""

# ╔═╡ ebda6433-c48f-4fee-bf8b-e9e774ae300a
code = :( i^2 + (i * (i - 3)) )

# ╔═╡ fbfe77dc-0f53-4014-a77f-e1604e613bfd
dump(code)

# ╔═╡ 66385c35-dcf1-44c2-85fe-203761bb3ea2
md"""
#### Exercise 3

1. Write a function `walk!` that takes an expression object and replaces **all** of the `:x`s by `:z`s.

    Hint: This function should be *recursive*: at some point it will need to call itself if it finds that a piece of the expression is itself an `Expr`.

2. Make this function into a more general pattern matcher that looks for a given sub-expression and replaces it by another.
"""

# ╔═╡ 4c8bde82-c96c-43a9-a006-6956208bba7f
3 isa Expr

# ╔═╡ 6fa34d03-ecb9-49cf-bb19-b37fdf5684b0
3 isa Symbol

# ╔═╡ dfb63c97-48e1-412e-a49c-915a183ffa08
3 isa Number

# ╔═╡ 1f2ed81a-31ec-42e6-8cf4-e7213d708b69
:x isa Expr

# ╔═╡ c9ffc07e-f6ac-4b4d-8c3b-3c05b352b9bf
:x isa Symbol

# ╔═╡ aed407de-294e-4041-9403-da51f1a0ec18
:(x + 1) isa Expr

# ╔═╡ 3cd54d83-85e0-4bc9-8bdc-8cf62ac4c307
function walk!(ex::Expr)
    for arg in ex.args
        @show arg
        if arg == :x
            arg = :z
        end

        if arg isa Expr
            walk!(arg)
        end
    end
end

# ╔═╡ d79bf8c7-262b-4f1f-b031-78c8a1f9cdad
ex = :(x*x + x)

# ╔═╡ d6a523e3-e6b1-4c00-9ba2-b24680af1ac0
walk!(ex)

# ╔═╡ 752265e0-4302-4958-8876-f4107bcf2100
ex

# ╔═╡ 70dc8adb-ab2f-4944-923f-924db65f7079
function walk!(ex::Expr)

    args = ex.args

    for i in 1:length(args)

        if args[i] == :x
            args[i] = :z
        end

        if args[i] isa Expr
            walk!(args[i])
        end
    end

    return ex
end

# ╔═╡ dcbba42d-ccb4-4b6f-8b64-2c4e2cd3570e
walk!(ex)

# ╔═╡ 35ffc623-d9ab-4bda-917a-96fb9ac9d569
ex

# ╔═╡ 5eb80f60-928d-4369-9f39-c2c22d7a62c6

# ╔═╡ 7dcd3307-8fe5-44f6-8175-641173eb40ae
md"""
#### Exercise 4
Julia by default uses standard 64-bit (or 32-bit) integers, which leads to surprising overflow behaviour, e.g.
"""

# ╔═╡ 4b68fe1c-12d8-4741-a81e-ed3e82e36cda
2^32 * 2^31

# ╔═╡ e2df3d00-c59b-4985-976d-96cc5aa7590c
md"""
No warning is given that there was an overflow in this calculation.

However, in `Base` there are *checked* operations, such as `checked_mul`, which do throw an exception on overflow:
"""

# ╔═╡ fb785eed-cca1-49a9-b7eb-83f2780683c2
Base.checked_mul(2^60, 2^60)

# ╔═╡ 9e96a2aa-dffe-4933-b8c6-0c18ac674ee0
md"""
1. Write a function `make_checked` that replaces standard functions (`-`, `+`, `*`, `/`) in an expression by their corresponding checked counterparts.
"""

# ╔═╡ 49cbc031-d11e-4bd1-9749-e46064a51279
code

# ╔═╡ 7bd84e40-b5d1-4a26-baa1-2bc58eb320d8
ex = :(x + x * x)

# ╔═╡ a58e651c-3d98-43d0-9b7e-05afd43b0995
eval(ex)

# ╔═╡ 7383fb43-ae28-4781-aca7-70bd9cb3fe59
x = 3

# ╔═╡ b9a90b60-c11f-49c5-a766-57c9cd610f35
eval(ex)

# ╔═╡ 296048c5-8895-4d86-8d80-70c34f79214b
walk!(ex)

# ╔═╡ f3658d71-2c0d-43c1-ac3c-fd86070ef59f
ex

# ╔═╡ ea14f593-fcaa-481d-954e-134ab0d66106
eval(z)

# ╔═╡ 213524c6-163e-44a7-b302-2777dbeaac18
z = 5

# ╔═╡ 04688e15-47d9-43f6-84d2-703f4fd446ab
eval(z)

# ╔═╡ 778dab36-8746-4e96-9b1e-b7dd6b3c295e
md"""
## Generating repetitive code
"""

# ╔═╡ 5cd6868c-1d49-4346-9790-941a5186b160
md"""
One common application of basic metaprogramming in Julia is generating repetitive code. For example, there are situations in which it's useful to wrap on object of one type into a user-defined type in order to modify its behaviour in some way.

e.g. Let's define a wrapper type `MyFloat` of `Float64`:
"""

# ╔═╡ 64a34288-39e3-49f1-9d02-1c31c2821a98
struct MyFloat
    x::Float64
end

# ╔═╡ e8b2d0ee-26dd-4c8c-a43b-21889598d4aa
md"""
We can generate objects of this type:
"""

# ╔═╡ ada46c0e-1ff5-43c9-91f8-4590c61013c6
a = MyFloat(3)
b = MyFloat(4)

# ╔═╡ 54117856-f2f8-4c31-9387-86bdbc6cb92a
md"""
But arithmetic operations are not defined:
"""

# ╔═╡ 3438cb89-4318-42f3-bda2-864e64947550
a + b

# ╔═╡ f708517a-49a4-4457-979a-2dcc4ddfbe4b
md"""
We can define them in the natural way:
"""

# ╔═╡ 7ed8f940-133f-4d7a-b405-209b014b711f
import Base: +, -, *, /

+(a::MyFloat, b::MyFloat) = a.x + b.x
-(a::MyFloat, b::MyFloat) = a.x - b.x

# ╔═╡ a6140e94-4125-4a94-8ac1-4637f7552f6f
md"""
But this will quickly get dull, and we could easily make a mistake.
As usual, whenever we are repeating something more than twice, we should try to automate it.

We have some code of the form

    op(a::MyFloat, b::MyFloat) = op(a.x, b.x)

where `op` denotes the operator. Julia allows us to do this almost literally; we just need to substitute in the *value* of the *variable* `op`!
"""

# ╔═╡ 28915673-0d98-4c01-a456-f7699491d466
md"""
#### Exercise 5

1. Let `op` be the symbol `:+`, which is an unevaluated version of the `+` operator.

2. Let `code` be the expression corresponding to `op(a::MyFloat, b::MyFloat) = op(a.x, b.x)`.

3. Substitute the *value* of `op` by replacing `op` by `$(op)`. Check that `code` contains the correct result.

4. Evaluate the code in order to generate the new method. Check that the method works for objects of type `MyFloat`.

5. We can replace the two steps "define `code`" and "evaluate `code`" by one step, `@eval` of the expression that defines `code`.

6. Write a loop over the operations `+`, `-`, `*` and `/` to define them all for our wrapper type.
"""

# ╔═╡ e921585b-6995-4daf-be67-9ace3f344a07
md"""
Finally we need to evaluate the code. The combination of `eval` and `:(...)` that we used above can be abbreviated to `@eval`:
"""

# ╔═╡ 5f5f3202-1257-4bf3-8f7d-cf811b181f9c
md"""
## Macros
"""

# ╔═╡ 8bc47e2d-bd01-4d6e-a623-3564156471c8
md"""
Finally let's return to macros. Recall that macros begin with `@` and behave like "super-functions", which take in a piece of code and replace it with another piece of code.
In fact, the effect of a macro call will be to insert the new piece of code in place of the old code, which is consequently compiled by the Julia compiler.

Note that the user *does not need to explicitly pass an `Expr`ession object*; Julia turns the code that follows the macro call into an expression.
"""

# ╔═╡ 6db221b4-bc11-4920-99bd-2f9ca7eec9f0
md"""
To see this, let's define the simplest macro:
"""

# ╔═╡ c46ea253-0daf-4f0a-a1db-bf878b177beb
macro simple(expr)
    @show expr, typeof(expr)
    nothing   # return nothing for the moment
end

# ╔═╡ 2ea1d8eb-41e5-4f47-8a5d-6445506ff0fb
md"""
and run it with the following simple code:
"""

# ╔═╡ f83d327c-d1b1-4a05-9af0-49049864f90d
result = @simple yy = xx^2

# ╔═╡ b686cba3-8e28-4043-a200-9da1e08db156
result

# ╔═╡ 7aaeb0a2-9cb8-4d7e-802b-06aa72abd81f
macro walk!(expr)
    @show expr
    result = walk!(expr)
    @show result
    return result
end

# ╔═╡ 63e2d127-4178-4ebb-9c8c-c8f247505ced
@walk! x + x

# ╔═╡ 308ab0a8-a29c-45e4-a9c9-12156a6f53bc
@macroexpand @walk! x + x

# ╔═╡ 19187987-1d3c-4ba2-b3a1-87b4bf1d3649
md"""
We see that the Julia code that follows the macro call is passed to the macro, *already having been parsed into an `Expr` object*.
"""

# ╔═╡ 01efdcad-c996-4577-8722-56e81f100ab5
md"""
#### Exercise 6
"""

# ╔═╡ 9e5096f5-061e-4365-bc7c-81c509c2354d
md"""
1. Define a macro `@simple2` that returns the expression that was passed to it.

2. What happens when you call `@simple2 yy = xx^2`?

3. Define a variable `xx` with the value `3`. Does the macro work now?

4. Does the variable `xx` now exist?

5. To see what's happening, use `@macroexpand`.
"""

# ╔═╡ 51f3ef0d-8f8b-43ea-ac8c-7e41efbbce4c
macro simple2(expr)
    return expr
end

# ╔═╡ 27583f6a-6666-4582-a545-f27348e3d2cb
@simple2 yy = xx^2

# ╔═╡ 5251e228-0a44-4447-9992-2296ab89e58d
xx = 10

# ╔═╡ ba7e5c53-f6e4-4178-9155-8e2c777345ec
@simple2 yy = xx^2

# ╔═╡ 40e5af42-fc50-4987-948e-28729441da83
yy

# ╔═╡ d7a0bf86-9571-43eb-bab1-39ab842ce814
@macroexpand @simple2 yy = xx^2

# ╔═╡ 491ed759-d61e-4b7c-865f-79e3183c5cf0
md"""
You should find that the variable `yy` does *not* now exist, even though it seems like it should, since the code `yy = xx^2` was evaluated. However, macros by default do not "touch" variables in the context from where they are called, since this may have unintended consequences. We refer to macro **hygiene** (they do not "infect" code where they are not welcome).

Nonetheless, often we may *wish* them to modify variables in the context from which they are called, in which case we can "escape" from this hygiene using `esc`:
"""

# ╔═╡ 8e7f6916-94c1-4a66-9b37-9c71bc3e4859
op = :+

# ╔═╡ 719ab988-4cd1-4356-ac31-996e36cf5efa
code = :($op(x, y))

# ╔═╡ 42e5f45a-b8fe-45e1-b43e-2ede631a18cc

# ╔═╡ 769024b2-5631-4e17-84f1-dca6187329db
macro simple3(expr)
    return :($(esc(expr)))
end

# ╔═╡ f823d1ae-eaf0-4333-9692-a00febd96c36
@simple3 yy = xx^2

# ╔═╡ e7ad8810-5610-448e-b6f5-7e65fc31b68f
yy

# ╔═╡ 67d660a9-cd5f-448c-978e-78d9f1028ae5
@macroexpand @simple3 yy = xx^2

# ╔═╡ b8e79fd4-8d7d-4183-afe6-941fa7ebc03e
md"""
Note that once again the macro must return an *expression*.
"""

# ╔═╡ 18ab2d1e-0fec-42ae-96b4-50853a0fb6c7
md"""
#### Exercise 7

1. Check that `@simple3` does create a variable `yy`.
"""

# ╔═╡ 3b62459d-584a-4107-b9c8-78e0c18be1d4

# ╔═╡ a1e1182b-63d5-4e3a-ab53-785edeff34ef
md"""
When writing macros, it is common to treat the macro as simply a wrapper around a function that does the hard work of transforming one `Expr` into another`:
"""

# ╔═╡ ecb6004a-19bd-4898-bc81-dc163a261341
md"""
#### Exercise 8

1. Write a macro `@walk!` that uses the function `walk!` defined above to replace terms in an expression. Apply it to `yy = xx^2`, replacing `xx` by `xx + 1`.

2. Write a macro `@checked` that replaces all arithmetic operations with checked operations.
"""

# ╔═╡ Cell order:
# ╟─148d5d63-2af4-42dc-82b7-66048377a773
# ╟─a5fa90ce-e4bc-490a-b86a-9a1acefc3b9c
# ╟─054538b8-6e2a-475c-96bd-b1a063b5e454
# ╟─819b2c2a-40f8-4a9c-be30-b8bd54efb53e
# ╠═f4531836-3fde-40df-8b96-1587cc4d2037
# ╠═c9260621-2249-4a37-9bc8-e67967c5ccae
# ╠═1e9b883b-0799-46fd-ba28-0652fcdc89d5
# ╟─fdffa5b5-0ec9-4f0a-b170-f958e5709495
# ╟─4a82f384-7872-4730-9daf-5c5d9a148b9b
# ╟─7f2ee2d3-050c-47b1-ab77-51858651660f
# ╠═3b19a1ce-4950-46bc-aac3-56f9dd335563
# ╟─5c2243f3-2fbb-4c1b-a51b-62516eb618c9
# ╟─5f53981e-b61e-4b23-8c1a-e2b24da67d1c
# ╟─ae487af5-0f68-44c7-8693-ed499de7b0b5
# ╠═522811ea-32aa-4501-843f-20e0c7f1cc69
# ╠═d2c93c05-c245-4bd5-b655-12a39128f915
# ╟─e342b2b1-f75e-49e9-968d-00feb6d77939
# ╟─ecb42fef-b833-467b-b4cf-339c61224997
# ╠═b27af88b-130f-4874-8265-f675e993ef63
# ╟─87942a87-aa67-4c3a-94e2-5573256168c8
# ╠═90dd1959-b677-4e8e-8969-9ad38bf4c268
# ╟─cec66c72-a710-4772-81e5-cb80e38f7360
# ╠═5ce56321-b125-4fc9-a67e-af3bd8ff0b52
# ╠═70007757-cef8-4a61-a22c-97557c10cde7
# ╠═a75957ce-df3c-4d06-ab37-24c28223c70c
# ╠═a396823a-255c-474c-95c4-740dc32c918a
# ╠═cdb6135b-ab18-47cc-bb22-548a2c3933d7
# ╠═80c70710-7393-4744-bf04-c2733ef29555
# ╠═09e9f3e0-627a-46de-8245-7b318853a14d
# ╠═afc9966e-1a44-43f3-9679-93d99b3457e1
# ╠═392fdf9e-b09e-468f-b2b0-da74853329d2
# ╠═906ba4d2-cc31-481c-920a-db27d7bea71d
# ╠═9150e575-ec71-4c2f-9464-818b9dac368d
# ╠═aebe63a2-6546-4855-8956-b0f2a1f6b699
# ╠═f916093d-c950-45c2-9a07-14a8b0c9d961
# ╠═ecf7d993-f186-4c7b-90cd-ebbf391362b6
# ╠═4176cf1d-2d9f-4054-bedb-48aa561b5612
# ╠═d5d7cd00-403d-410a-93be-3f4b7bbfa7a6
# ╠═58ba1f17-87dd-4395-a77e-e733a94b82ff
# ╠═72357f57-94ec-4807-b923-e315691bf892
# ╠═adf9f854-1252-4a9d-8bdd-4d2d637d63c8
# ╠═0440a738-05d6-4011-8d9c-3c4440fa50d8
# ╠═0df91dca-4baf-4d38-a4b0-ff77e2c8fbb0
# ╠═6ccc334b-6512-4f01-a73a-4b94c4469e30
# ╟─c3251450-8347-4a4f-af52-adadf8fa17fa
# ╠═9ab8441f-7dc9-4a8d-a522-29e668ae123a
# ╟─8e9507d6-4a5d-4885-80e3-31d3603e17da
# ╟─d7e95362-16f1-445a-874b-bd321bcaac45
# ╠═ebda6433-c48f-4fee-bf8b-e9e774ae300a
# ╠═fbfe77dc-0f53-4014-a77f-e1604e613bfd
# ╟─66385c35-dcf1-44c2-85fe-203761bb3ea2
# ╠═4c8bde82-c96c-43a9-a006-6956208bba7f
# ╠═6fa34d03-ecb9-49cf-bb19-b37fdf5684b0
# ╠═dfb63c97-48e1-412e-a49c-915a183ffa08
# ╠═1f2ed81a-31ec-42e6-8cf4-e7213d708b69
# ╠═c9ffc07e-f6ac-4b4d-8c3b-3c05b352b9bf
# ╠═aed407de-294e-4041-9403-da51f1a0ec18
# ╠═3cd54d83-85e0-4bc9-8bdc-8cf62ac4c307
# ╠═d79bf8c7-262b-4f1f-b031-78c8a1f9cdad
# ╠═d6a523e3-e6b1-4c00-9ba2-b24680af1ac0
# ╠═752265e0-4302-4958-8876-f4107bcf2100
# ╠═70dc8adb-ab2f-4944-923f-924db65f7079
# ╠═dcbba42d-ccb4-4b6f-8b64-2c4e2cd3570e
# ╠═35ffc623-d9ab-4bda-917a-96fb9ac9d569
# ╠═5eb80f60-928d-4369-9f39-c2c22d7a62c6
# ╟─7dcd3307-8fe5-44f6-8175-641173eb40ae
# ╠═4b68fe1c-12d8-4741-a81e-ed3e82e36cda
# ╟─e2df3d00-c59b-4985-976d-96cc5aa7590c
# ╠═fb785eed-cca1-49a9-b7eb-83f2780683c2
# ╟─9e96a2aa-dffe-4933-b8c6-0c18ac674ee0
# ╠═49cbc031-d11e-4bd1-9749-e46064a51279
# ╠═7bd84e40-b5d1-4a26-baa1-2bc58eb320d8
# ╠═a58e651c-3d98-43d0-9b7e-05afd43b0995
# ╠═7383fb43-ae28-4781-aca7-70bd9cb3fe59
# ╠═b9a90b60-c11f-49c5-a766-57c9cd610f35
# ╠═296048c5-8895-4d86-8d80-70c34f79214b
# ╠═f3658d71-2c0d-43c1-ac3c-fd86070ef59f
# ╠═ea14f593-fcaa-481d-954e-134ab0d66106
# ╠═213524c6-163e-44a7-b302-2777dbeaac18
# ╠═04688e15-47d9-43f6-84d2-703f4fd446ab
# ╟─778dab36-8746-4e96-9b1e-b7dd6b3c295e
# ╟─5cd6868c-1d49-4346-9790-941a5186b160
# ╠═64a34288-39e3-49f1-9d02-1c31c2821a98
# ╟─e8b2d0ee-26dd-4c8c-a43b-21889598d4aa
# ╠═ada46c0e-1ff5-43c9-91f8-4590c61013c6
# ╟─54117856-f2f8-4c31-9387-86bdbc6cb92a
# ╠═3438cb89-4318-42f3-bda2-864e64947550
# ╟─f708517a-49a4-4457-979a-2dcc4ddfbe4b
# ╠═7ed8f940-133f-4d7a-b405-209b014b711f
# ╟─a6140e94-4125-4a94-8ac1-4637f7552f6f
# ╟─28915673-0d98-4c01-a456-f7699491d466
# ╟─e921585b-6995-4daf-be67-9ace3f344a07
# ╟─5f5f3202-1257-4bf3-8f7d-cf811b181f9c
# ╟─8bc47e2d-bd01-4d6e-a623-3564156471c8
# ╟─6db221b4-bc11-4920-99bd-2f9ca7eec9f0
# ╠═c46ea253-0daf-4f0a-a1db-bf878b177beb
# ╟─2ea1d8eb-41e5-4f47-8a5d-6445506ff0fb
# ╠═f83d327c-d1b1-4a05-9af0-49049864f90d
# ╠═b686cba3-8e28-4043-a200-9da1e08db156
# ╠═7aaeb0a2-9cb8-4d7e-802b-06aa72abd81f
# ╠═63e2d127-4178-4ebb-9c8c-c8f247505ced
# ╠═308ab0a8-a29c-45e4-a9c9-12156a6f53bc
# ╟─19187987-1d3c-4ba2-b3a1-87b4bf1d3649
# ╟─01efdcad-c996-4577-8722-56e81f100ab5
# ╟─9e5096f5-061e-4365-bc7c-81c509c2354d
# ╠═51f3ef0d-8f8b-43ea-ac8c-7e41efbbce4c
# ╠═27583f6a-6666-4582-a545-f27348e3d2cb
# ╠═5251e228-0a44-4447-9992-2296ab89e58d
# ╠═ba7e5c53-f6e4-4178-9155-8e2c777345ec
# ╠═40e5af42-fc50-4987-948e-28729441da83
# ╠═d7a0bf86-9571-43eb-bab1-39ab842ce814
# ╟─491ed759-d61e-4b7c-865f-79e3183c5cf0
# ╠═8e7f6916-94c1-4a66-9b37-9c71bc3e4859
# ╠═719ab988-4cd1-4356-ac31-996e36cf5efa
# ╠═42e5f45a-b8fe-45e1-b43e-2ede631a18cc
# ╠═769024b2-5631-4e17-84f1-dca6187329db
# ╠═f823d1ae-eaf0-4333-9692-a00febd96c36
# ╠═e7ad8810-5610-448e-b6f5-7e65fc31b68f
# ╠═67d660a9-cd5f-448c-978e-78d9f1028ae5
# ╟─b8e79fd4-8d7d-4183-afe6-941fa7ebc03e
# ╟─18ab2d1e-0fec-42ae-96b4-50853a0fb6c7
# ╠═3b62459d-584a-4107-b9c8-78e0c18be1d4
# ╟─a1e1182b-63d5-4e3a-ab53-785edeff34ef
# ╟─ecb6004a-19bd-4898-bc81-dc163a261341

max_stars_repo_path: 4. Metaprogramming.jl

max_issues_repo_path: 4. Metaprogramming.jl

max_forks_repo_path: 4. Metaprogramming.jl


text:
@with_kw mutable struct BoxParameters{T <: AbstractFloat} <: AbstractPolicyParameters
    l::Vector{T}
    u::Vector{T}
end

struct Box <: AbstractProx
    params::BoxParameters{Float64}

    function (::Type{Box})(; kwargs...)
        return new(BoxParameters(; kwargs...))
    end
end

function prox!(box::Box,step::Real,xprev::AbstractVector,gcurr::AbstractVector,xcurr::AbstractVector)
    @unpack l, u = box.params
    temp = xprev - step*gcurr
    xcurr .= min.(max.(temp, l), u)
end


text:
#==============================================================================#
# AWSAPI.jl
#
# Amazon Web Service API Generation
#
# This module use service definitions from AWSMetadata.jl to generate Julia
# API bindings and documentation.
#
# Copyright OC Technology Pty Ltd 2017 - All rights reserved
#==============================================================================#

module AWSAPI

include("AWSMetadata.jl")
include("HTML2MD.jl")

using .AWSMetadata
using .HTML2MD
using OrderedCollections

sdk_module_name(service_name) = "AWSSDK.$(service_name)"

"""
    uncamel(s)

Transform CamelCase to lowercase_with_underscores.
"""
uncamel(s) = lowercase(replace(s, r"([a-z])([A-Z])" => s"\1_\2"))

"""
    orjoin(words)

Join `words` with commas and "or". e.g. "one, two or three".
"""
orjoin(words) = join(words, ", ", " or ")

function is_simple_post_service(service)
    return (service["metadata"]["protocol"] in ["json", "query", "ec2"]
        &&  service["metadata"]["endpointPrefix"] != "importexport")
end

is_rest_service(service) = occursin(r"^rest", service["metadata"]["protocol"])

function member_name(service, name, info)

    if haskey(info, "locationName")
        name = info["locationName"]
    else
        shape = service["shapes"][info["shape"]]
        if get(shape, "flattened", false)
            if shape["type"] == "list"
                name = shape["member"]["shape"]
                name = get(shape["member"], "locationName", name)
            end
        end
    end

    if service["metadata"]["signingName"] == "ec2"
        name = string(uppercase(name[1]), name[2:end])
    end

    if get(info, "location", "") in ["header", "headers"]
        name = "*header:* $name"
    end

    return name
end

function service_args(service, name)

    shape = service["shapes"][name]
    @assert shape["type"] == "structure"

    m = filter((n, i)::Pair -> (n in get(shape, "required", [])), shape["members"])

    args = join(["$(member_name(service, name, info))="
                 for (name, info) in m], ", ")
    if length(m) < length(shape["members"])
        if args != ""
            args *= ", "
        end
        args *= "<keyword arguments>"
    end

    return args
end

function service_shape_doc(service, name, pad="", stack=[])

    shape = service["shapes"][name]
    t = shape["type"]

    # No infinite recursion...
    if name in stack
        println("Interrupting recursion to $name from: $(join(stack, " -> "))")
        return t
    end

    push!(stack, name) ; try

    @assert pad != "" || t == "structure"

    padmore = pad * "    "

    if t == "structure"

        if pad == ""

            result = ""

            for (m, i) in shape["members"]
                n = member_name(service, m, i)
                s = service_shape_doc(service, i["shape"], padmore, stack)
                d = html2md(get(i, "documentation", ""))
                r = haskey(shape, "required") && m in shape["required"]
                n = "$n = "
                if !occursin("\n", s)
                    n = "$n$s"
                    s = ""
                else
                    brief = join(map(strip, split(s, "\n")[[1,end]]), " ... ")
                    s = "```\n $n$s\n```"
                    n = "$n$brief"
                end

                result *= "## `$n`$(r ? " -- *Required*" : "")\n$d\n$s\n\n"
            end

            return result
        end

        members = []
        for (m, i) in shape["members"]
            n = member_name(service, m, i)
            s = service_shape_doc(service, i["shape"], padmore, stack)
            r = haskey(shape, "required") && m in shape["required"]
            push!(members, "\"$n\" => $(r ? "<required>" : "") $s")
        end
        if length(members) == 1
            return "[$(members[1])]"
        else
            return "[\n$padmore$(join(members, ",\n$padmore"))\n$pad]"
        end
    end

    if t == "list"
        s = service_shape_doc(service, shape["member"]["shape"], pad, stack)
        return  "[$s, ...]"
    end

    if t == "map"
        return "::Dict{String,String}"
    end

    if t == "string"
        if haskey(shape, "enum")
            return orjoin(["\"$v\"" for v in shape["enum"]])
        else
            return "::String"
        end
    end

    if t in ["integer", "long"]
        return "::Int"
    end

    if t == "boolean"
        return "::Bool"
    end

    return t

    finally pop!(stack) end
end

function pretty(d::AbstractDict, dict = "Dict()", pad="")
    dict[1:end-1] * "\n" *
    join([string(pad, "    \"", k, "\" => ", pretty(v, dict, pad * "    "))
          for (k, v) in d], ",\n") *
    "\n$pad" * dict[end:end]
end

function pretty(v::Vector, dict, pad)
    "[\n" *
    join([string(pad, "    ", pretty(i, dict, pad * "    ")) for i in v], ",\n") *
    "\n$pad]"
end

function pretty(s::String, args...)
    s = replace(s, "\$" => "\\\$")

    # Work around for https://github.com/JuliaLang/julia/pull/22800
    s = replace(s, r"([^\\])\\([^ntrebfva\\'\"$`0-9])" => s"\1\\\\\2")

    return "\"$s\""
end

pretty(n, args...) = string(n)

function service_operation(service, operation, info)

    @assert !haskey(info, "input") || isa(info["input"], OrderedDict)
    @assert !haskey(info, "output") || isa(info["output"], OrderedDict)

    name = uncamel(operation)
    input = ""
    inputdoc = ""
    if haskey(info, "input")
        input = service_args(service, info["input"]["shape"])
        inputdoc = string("\n\n# Arguments\n\n",
                          service_shape_doc(service, info["input"]["shape"]))
    end

    output = ""
    if haskey(info, "output")
        output = "\n\n# Returns\n\n`$(info["output"]["shape"])`"
    end

    errors = ""
    if haskey(info, "errors")
        errors = orjoin(["`$(e["shape"])`" for e in (info["errors"])])
        errors = "\n\n# Exceptions\n\n$errors."
    end

    api_ref = service_api_reference_url(service, operation)

    request = service_request_function_name(service)
    if is_simple_post_service(service)
        @assert info["http"]["method"] == "POST"
        @assert info["http"]["requestUri"] == "/"
        method = ""
        resource = ""
    else
        method = " \"$(info["http"]["method"])\","
        resource = " \"$(info["http"]["requestUri"])\","
    end

    example = ""
    if haskey(service, "examples") && haskey(service["examples"], operation)

        for eg in service["examples"][operation]
            example *= """

            # Example: $(eg["title"])

            $(get(eg,"description", ""))
            """

            if haskey(eg, "input")
                example *= """

                Input:
                ```
                $(pretty(eg["input"], "[]"))
                ```
                """
            end
            if haskey(eg, "output")
                example *= """

                Output:
                ```
                $(pretty(eg["output"]))
                ```
                """
            end
        end
    end

    operation_name = operation
    operation = is_rest_service(service) ? "" : " \"$operation\","
    resource = replace(resource, "\$" => "\\\$")

    if haskey(info, "input")
        sig1 = "$name([::AWSConfig], arguments::Dict)"
        sig2 = "$name([::AWSConfig]; $input)"
        sig3 = "$request([::AWSConfig],$method$resource$operation arguments::Dict)"
        sig4 = "$request([::AWSConfig],$method$resource$operation $input)"
    else
        sig1 = "$name([::AWSConfig])"
        sig2 = ""
        sig3 = "$request([::AWSConfig],$method$resource$operation)"
        sig4 = ""
    end

    @assert !occursin(r"[{][^{}]+[}]", resource) || is_rest_service(service)

    m = service["metadata"]["juliaModule"]

    """
    \"\"\"
        using AWSSDK.$m.$name
        $sig1
        $sig2

        using AWSCore.Services.$request
        $sig3
        $sig4

    # $operation_name Operation

    $(html2md(get(info, "documentation", "")))$inputdoc$output$errors
    $example
    See also: [AWS API Documentation]($api_ref)
    \"\"\"
    @inline $name(aws::AWSConfig=default_aws_config(); args...) = $name(aws, args)

    @inline $name(aws::AWSConfig, args) = AWSCore.Services.$request(aws,$method$resource$operation args)

    @inline $name(args) = $name(default_aws_config(), args)

    """
end

function service_request_function_name(service)

    join(split(service["metadata"]["uid"], ['.', '-'])[1:end-3], "_")
end

function service_request_function(service)

    meta = service["metadata"]

    protocol = replace(meta["protocol"], "-" => "_")
    if protocol == "ec2"
        protocol = "query"
    end
    name = service_request_function_name(service)

    args = ["service      = \"$(meta["signingName"])\"",
            "version      = \"$(meta["apiVersion"])\""]

    if meta["endpointPrefix"] != meta["signingName"]
        push!(args,
            "endpoint     = \"$(meta["endpointPrefix"])\"")
    end

    if is_simple_post_service(service)
        verb = ""
        resource = ""
    else
        push!(args,
            "verb         = verb",
            "resource     = resource")
        verb = "verb, "
        resource = "resource, "
    end

    if meta["protocol"] == "json"
        json_version = get(meta, "jsonVersion", "1.0")
        if json_version == "1"
            json_version = "1.0"
        end
        push!(args,
            "json_version = \"$json_version\"",
            "target       = \"$(get(meta, "targetPrefix", ""))\"")
    end

    if !is_rest_service(service)
        push!(args,
            "operation    = operation")
        operation = "operation, "
    else
        operation = ""
    end
    push!(args,
            "args         = args")

"""
function $name(aws::AWSConfig, $(verb)$(resource)$(operation)args=[])

    AWSCore.service_$protocol(
        aws;
        $(join(args, ",\n        ")))
end

$name($(verb)$(resource)$(operation)args=[]) =
    $name(default_aws_config(), $(verb)$(resource)$(operation)args)

$name(a...; b...) = $name(a..., b)
"""
end

function service_interface(service)

    meta = service["metadata"]
    m = meta["juliaModule"]

    string(
"""
#==============================================================================#
# $m.jl
#
# This file is generated from:
# $(meta["sourceURL"])
#==============================================================================#

__precompile__()

module $m

using AWSCore

""",

    (service_operation(service, o, i) for (o, i) in service["operations"])...,

"""

end # module $m

#==============================================================================#
# End of file
#==============================================================================#
"""
    )
end

function service_api_reference_url(service, operation)

    uid = service["metadata"]["uid"]
    "https://docs.aws.amazon.com/goto/WebAPI/$uid/$operation"
end

function service_documentation(service)

    meta = service["metadata"]
    m = meta["juliaModule"]

    """
    # AWSSDK.$m

    $(html2md(get(service, "documentation", "")))

    This document is generated from
    [$(meta["sourceFile"])]($(meta["sourceURL"])).
    See [JuliaCloud/AWSCore.jl](https://github.com/JuliaCloud/AWSCore.jl).

    ```@index
    Pages = ["AWSSDK.$m.md"]
    ```

    ```@autodocs
    Modules = [AWSSDK.$m]
    ```
    """

end

function service_generate(name, definition)

    meta = definition["metadata"]
    println(meta["serviceFullName"])
    meta["juliaModule"] = name

    sdk_dir = joinpath(@__DIR__, "..", "..", "AWSSDK")
    src_path = joinpath(sdk_dir, "src", "$name.jl")
    mkpath(dirname(src_path))
    write(src_path, service_interface(definition))

    doc_src = joinpath(@__DIR__, "..", "..", "AWSCoreDoc", "src", "AWSSDK.$name.md")
    mkpath(dirname(doc_src))
    write(doc_src, service_documentation(definition))
end

function generate_doc(services)

    doc = """
        using Documenter
        using AWSCore
        using AWSS3
        using AWSSES
        using AWSSQS
        using AWSSNS
    """

    for s in services
        doc *= "using $(sdk_module_name(s))\n"
    end

    names = map(sdk_module_name, collect(services))

    doc *= """
    makedocs(modules = [AWSCore, AWSS3, AWSSES, AWSSQS, AWSSNS,
                        $(join(names, ","))],
             format = :html,
             sitename = "AWSCore.jl",
             pages = ["AWSCore.jl" => "index.md",
                      "AWSS3.jl" => "AWSS3.md",
                      "AWSSQS.jl" => "AWSSQS.md",
                      "AWSSES.jl" => "AWSSES.md",
                      "AWSSNS.jl" => "AWSSNS.md",
                      $(join(map(m->"\"$m.jl\" => \"$m.md\"", names), ","))
             ])
    """

    return (doc)
end

function generate_all()

    services = keys(AWSMetadata.service_list())

    request_functions = []
    sdk = []

    for name in services
        service = AWSMetadata.service_list()[name]
        definition = AWSMetadata.service_definition(service)
        service_generate(name, definition)
        push!(request_functions, service_request_function(definition))
        push!(sdk, "include(\"$name.jl\")")
    end

    write(joinpath(@__DIR__, "Services.jl"),
"""
#==============================================================================#
# Services.jl
#
# This file is generated by AWSAPI.jl from service decriptions at:
# https://github.com/aws/aws-sdk-js/tree/master/apis
#==============================================================================#

module Services

using ..AWSCore

$(join(request_functions, "\n"))

end # module Services

#==============================================================================#
# End of file
#==============================================================================#
""")

    write(joinpath(@__DIR__, "..", "..", "AWSCoreDoc", "make.jl"),
          generate_doc(services))

    sdk_dir = joinpath(@__DIR__, "..", "..", "AWSSDK")
    src_path = joinpath(sdk_dir, "src", "AWSSDK.jl")
    mkpath(dirname(src_path))

    write(src_path,
"""
#==============================================================================#
# AWSSDK.jl
#
# This file is generated by AWSAPI.jl from service decriptions at:
# https://github.com/aws/aws-sdk-js/tree/master/apis
#==============================================================================#

module AWSSDK

$(join(sdk, "\n"))

end # module AWSSDK

#==============================================================================#
# End of file
#==============================================================================#
""")

end

end # module AWSAPI

#==============================================================================#
# End of file.
#==============================================================================#


text:
# (7) Labor market specific industry =======================================================================
nvcasino08 = get_data(api_fred, "NVLEIH"; units="pc1", observation_start = "2008-06-01", observation_end = "2011-03-01")
nvcasino20 = get_data(api_fred, "NVLEIH"; units="pc1", observation_start = "2020-02-01")

txoil08 = get_data(api_fred, "SMU48000001021000001SA"; units="pc1", observation_start = "2008-06-01", observation_end = "2011-03-01")
txoil20 = get_data(api_fred, "SMU48000001021000001SA"; units="pc1", observation_start = "2020-02-01")

# Date index, just need one, same data source.
len08 = length(nvcasino08.data[:, :value])
len20 = length(nvcasino20.data[:, :value])

plot_nvcasino08 = layer(nvcasino08.data, x = 1:len08, y = :value, Geom.line,
                        Theme(default_color=colorant"red", line_style = [:dash])
                    );
plot_nvcasino20 = layer(nvcasino20.data, x = 1:len20, y = :value, Geom.line,
                        Theme(default_color=colorant"red", line_style = [:solid])
                    );
plot_txoil08 = layer(txoil08.data, x = 1:len08, y = :value, Geom.line,
                    Theme(default_color=colorant"deepskyblue", line_style = [:dash])
                );
plot_txoil20 = layer(txoil20.data, x = 1:len20, y = :value, Geom.line,
                    Theme(default_color=colorant"deepskyblue", line_style = [:solid])
                );
plt_emplystate = plot(plot_nvcasino08, plot_nvcasino20, plot_txoil08, plot_txoil20,
        Guide.manual_color_key("Employment by state: industry",
                                ["Nevada: Leisure and hospitality",
                                 "Texas: Oil and gas",
                                 ],
                                ["red", "deepskyblue"],
                                pos = [0.13w,0.38h]
                               ),
         Guide.xlabel("Month(s) since the start of the recession"), Guide.ylabel("Year over year growth, percent"),
         Coord.cartesian(xmin=1, xmax=len08),
         Guide.annotation(compose(context(), Compose.text(22, 16, "Dash - 2008 Recession"))),
         Guide.annotation(compose(context(), Compose.text(9, -30, "Solid - 2020 Pandemic")))
    )
pngout = SVG("plt_emplystate.svg", 5inch, 4inch)
draw(pngout, plt_emplystate)


text:
#
# Superblock
#

const SUPERBLOCK_SIGNATURE = htol(0x0a1a0a0d46444889) # UInt8[0o211, 'H', 'D', 'F', '\r', '\n', 0o032, '\n']

# https://www.hdfgroup.org/HDF5/doc/H5.format.html#FileMetaData
# Superblock (Version 2)
struct Superblock
    file_consistency_flags::UInt8
    base_address::Int64
    superblock_extension_address::RelOffset
    end_of_file_address::Int64
    root_group_object_header_address::RelOffset
end

jlsizeof(::Union{Type{Superblock},Superblock}) =
    12+jlsizeof(RelOffset)*4+4

function jlread(io::IO, ::Type{Superblock})
    cio = begin_checksum_read(io)

    # Signature
    signature = jlread(cio, UInt64)
    signature == SUPERBLOCK_SIGNATURE || throw(InvalidDataException())

    # Version
    version = jlread(cio, UInt8)
    version == 2 || throw(UnsupportedVersionException())

    # Size of offsets and size of lengths
    size_of_offsets = jlread(cio, UInt8)
    size_of_lengths = jlread(cio, UInt8)
    (size_of_offsets == 8 && size_of_lengths == 8) || throw(UnsupportedFeatureException())

    # File consistency flags
    file_consistency_flags = jlread(cio, UInt8)

    # Addresses
    base_address = jlread(cio, Int64)
    superblock_extension_address = jlread(cio, RelOffset)
    end_of_file_address = jlread(cio, Int64)
    root_group_object_header_address = jlread(cio, RelOffset)

    # Checksum
    cs = end_checksum(cio)
    jlread(io, UInt32) == cs || throw(InvalidDataException())

    Superblock(file_consistency_flags, base_address, superblock_extension_address,
                end_of_file_address, root_group_object_header_address)
end

function jlwrite(io::IO, s::Superblock)
    cio = begin_checksum_write(io, 8+4+4*jlsizeof(RelOffset))
    jlwrite(cio, SUPERBLOCK_SIGNATURE::UInt64)    # Signature
    jlwrite(cio, UInt8(2))                        # Version
    jlwrite(cio, UInt8(8))                        # Size of offsets
    jlwrite(cio, UInt8(8))                        # Size of lengths
    jlwrite(cio, s.file_consistency_flags::UInt8)
    jlwrite(cio, s.base_address::Int64)
    jlwrite(cio, s.superblock_extension_address::RelOffset)
    jlwrite(cio, s.end_of_file_address::Int64)
    jlwrite(cio, s.root_group_object_header_address::RelOffset)
    jlwrite(io, end_checksum(cio))
end


text:
using Base.Test
using Musical.Notations # A C E
using Musical.Chords # Am Chord Major notes root minorthird third fifth

# Am = Chord(A, Minor)
@test Am isa Chord
@test notes(Am) == (A, C, E)
@test root(Am) == A
@test minorthird(Am) == C
@test fifth(Am) == E
# @test pitches(Am) == (A, B, C, D, E, F, G)

CM = Chord(C, Major)
@test notes(CM) == (C, E, G)
@test root(CM) == C
@test third(CM) == E
@test fifth(CM) == G


text:
using DelimitedFiles, Dates

function hfun_btable(params)
    path = params[1]
    csvcontent,headers   = readdlm(path, ',', String, header=true)
    nrows, ncols = size(csvcontent)

    # sort the table so earlier deadlines are first
    sorting = sortperm(Date.(csvcontent[:,3],"m/d/yyyy"))
    csvcontent = csvcontent[sorting,:]

    io = IOBuffer()
    # write headers
    println(io,"
")
    println(io,"
","$nrows conferences","
")
    println(io,"
")
    println(io,"
")
    for i in 1:ncols-1
        println(io,"",headers[i],"	")
    end
    println(io,"
")
    println(io,"
")

    # writing content
    println(io,"
")
    for i in 1:nrows
        println(io, "
")

        println(io,"","",csvcontent[i,1],"","	")
        for j in 2:ncols-1
            println(io,"",csvcontent[i,j],"	")
        end
        println(io,"
")
    end
    println(io,"
")
    return String(take!(io))
end

max_stars_repo_name: tbeason/financeconferences

max_issues_repo_name: tbeason/financeconferences

max_forks_repo_name: tbeason/financeconferences


text:
using Bonsai: CancelToken, register!
using AbstractTrees
using Sockets: InetAddr, TCPServer

export App

function path_params(stream)
    stream.context[:params]
end

Base.@kwdef mutable struct App
    id::Int = rand(Int)
    docs::Union{String,Nothing} = nothing
    cancel_token::CancelToken = CancelToken()
    inet_addr::Union{InetAddr,Nothing} = nothing
    server::Union{TCPServer,Nothing} = nothing
    paths::Node = Node("*")
    paths_docs::Dict{Symbol, Dict{String, String}} = Dict(
        :get => Dict(),
        :post => Dict(),
        :put => Dict(),
        :trace => Dict(),
        :delete => Dict(),
        :options => Dict(),
        :patch => Dict(),
        :summary => Dict(),
    )
    middleware::Node = Node("*")
end

function Base.show(io::IO, e::NoHandler)
    print(
        io,
        "Target - $(e.stream.message.target)\n",
        "Method - $(e.stream.message.method)"
    )
end

function ws_upgrade(http::HTTP.Stream; binary=false)

    @info "upgrading"
    @info http

    # check_upgrade(http)
    if !hasheader(http, "Sec-WebSocket-Version", "13")
        throw(WebSocketError(0, "Expected \"Sec-WebSocket-Version: 13\"!\n" *
                                "$(http.message)"))
    end

    setstatus(http, 101)
    setheader(http, "Upgrade" => "websocket")
    setheader(http, "Connection" => "Upgrade")
    key = header(http, "Sec-WebSocket-Key")
    setheader(http, "Sec-WebSocket-Accept" => accept_hash(key))

    startwrite(http)

    io = http.stream
    req = http.message
    ws = WebSocket(io; binary=binary, server=true, request=req)
    return ws
end

function is_ws(stream)

end

function (app::App)(stream)
    request::Request = stream.message

    if !hasheader(stream, "Sec-WebSocket-Version", "13")
        request.body = Base.read(stream)
        closeread(stream)
    end

    try
        handler, middleware::Array{Any} = match(app, stream)

        if isnothing(middleware) || ismissing(middleware)
            middleware = []
        end

        if ismissing(handler) || isnothing(handler)
            push!(middleware, (stream, next) -> throw(NoHandler(stream)))
        else
            push!(middleware, (stream, next) -> handler(stream))
        end
        combine_middleware(middleware)(stream)

    catch e
        if e isa NoHandler
            @warn e
        else
            @error "error" type = typeof(e) e = e
        end
    finally
        request.response.request = request
        startwrite(stream)
        Base.write(stream, request.response.body)
    end
end

function middleware(app::App)
    leaves = Leaf[]
    for n in PostOrderDFS(app.middleware)
        if !isempty(n.methods)
            push!(leaves, n.methods...)
        end
    end
    return leaves
end

function handlers(app::App)
    leaves = Leaf[]
    for n in PostOrderDFS(app.paths)
        if !isempty(n.methods)
            push!(leaves, n.methods...)
        end
    end
    return leaves
end

function create_handler(app, method)

    return function (fn, path)
        handler = wrap_handler(fn)
        node = handler isa Middleware ? app.middleware : app.paths
        register!(
            node,
            method,
            path,
            handler
        )
        # We need to return this hanlder for the open api doc functionality
        # to work
        handler
    end
end

function Base.getproperty(app::App, s::Symbol)
    if s == :get
        return create_handler(app, GET)
    elseif s == :post
        return create_handler(app, POST)
    elseif s == :put
        return create_handler(app, POST)
    elseif s == :trace
        return create_handler(app, TRACE)
    elseif s == :delete
        return create_handler(app, DELETE)
    elseif s == :options
        return create_handler(app, OPTIONS)
    elseif s == :connect
        return create_handler(app, CONNECT)
    elseif s == :patch
        return create_handler(app, PATCH)
    elseif s == :summary
        return (x) -> nothing
    elseif s == :all
        return create_handler(app, ALL)
    else
        return Base.getfield(app, s)
    end
end


text:
using FromR
using Test

@testset "FromR" begin
    for (input, expected) in [
        ("1 + 1", :(1 + 1)),
        ("x <- c(1, 2, 3)", :(x = [1., 2, 3])),
        ("x = c(1, 2, 3)", :(x = [1., 2, 3])),
        # ("function(x){x + 2}", :(x -> x + 2))
    ]
        @test transpile(Expr, input) == expected
    end
end


text:
using Gadgetron
using Test
using AcuteML
import EzXML

example_header = """<?xml version="1.0" encoding="utf-8"?>
<ismrmrdHeader xmlns="http://www.ismrm.org/ISMRMRD" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.ismrm.org/ISMRMRD ismrmrd.xsd">
   <experimentalConditions>
      <H1resonanceFrequency_Hz>32130323</H1resonanceFrequency_Hz>
   </experimentalConditions>
   <encoding>
      <encodedSpace>
         <matrixSize>
            <x>64</x>
            <y>64</y>
            <z>1</z>
         </matrixSize>
         <fieldOfView_mm>
            <x>300</x>
            <y>300</y>
            <z>40</z>
         </fieldOfView_mm>
      </encodedSpace>
      <reconSpace>
         <matrixSize>
            <x>64</x>
            <y>64</y>
            <z>1</z>
         </matrixSize>
         <fieldOfView_mm>
            <x>300</x>
            <y>300</y>
            <z>40</z>
         </fieldOfView_mm>
      </reconSpace>
      <trajectory>radial</trajectory>
      <encodingLimits>
      </encodingLimits>
   </encoding>
</ismrmrdHeader>
"""

function print_to_string(object)
   io = IOBuffer()
   print(io,object)
   seekstart(io)
   return String(take!(io))
end

@testset "HeaderSerialization" begin

   header = MRD.MRDHeader(example_header)

    header_xml = EzXML.parsexml(string(header.aml))
    reference_xml = EzXML.parsexml(example_header)

   @test print_to_string(header_xml) == print_to_string(reference_xml)

end


text:
using LazySets.Arrays: SingleEntryVector

@testset "LGG09 algorithm: template construction" begin
    # case where the template directions is as single vector
    dirs = fill(0.0, 5); dirs[1] = 1.0 # e1
    alg = LGG09(δ=0.01, template=dirs)
    alg.template == CustomDirections([dirs])

    # case where the template directions is a vector of vectors
    dirs = [SingleEntryVector(1, 5, 1.0), SingleEntryVector(1, 5, -1.0)]
    alg = LGG09(δ=0.01, template=dirs)
    alg.template == CustomDirections(dirs)

    # alias dirs
    dirs = [SingleEntryVector(1, 5, 1.0), SingleEntryVector(1, 5, -1.0)]
    alg = LGG09(δ=0.01, dirs=dirs)
    alg.template == CustomDirections(dirs)

    # specification of vars (requires n)
    @test_throws ArgumentError LGG09(δ=0.01, vars=(1))
    alg = LGG09(δ=0.01, vars=(1), n=5)
    @test collect(alg.template) == dirs

    alg = LGG09(δ=0.01, vars=(1, 3), n=5)
    dirs_1_3 = [SingleEntryVector(1, 5, 1.0), SingleEntryVector(3, 5, 1.0),
                SingleEntryVector(1, 5, -1.0), SingleEntryVector(3, 5, -1.0)]
    @test collect(alg.template) == dirs_1_3

    # alias using dim
    alg2 = LGG09(δ=0.01, vars=(1, 3), dim=5)
    @test collect(alg2.template) == dirs_1_3
end

@testset "LGG09 algorithm: 1d" begin
    # one-dimensional
    prob, dt = exponential_1d()
    box1d = CustomDirections([[1.0], [-1.0]])
    sol = solve(prob, tspan=dt, LGG09(δ=0.01, template=box1d))
    @test isa(sol.alg, LGG09)
    @test setrep(sol) <: HPolyhedron
    @test setrep(sol) == HPolyhedron{Float64, Vector{Float64}}
    @test dim(sol) == 1
end

@testset "LGG09 algorithm: 5d" begin
    # higher-dimensional
    prob, dt = linear5D()
    box5d = BoxDirections{Float64, Vector{Float64}}(5)
    sol = solve(prob, tspan=dt, LGG09(δ=0.01, template=box5d))
    @test setrep(sol) == HPolyhedron{Float64, Vector{Float64}}
    @test dim(sol) == 5
end


text:
```Write functions that add, subtract, and multiply two numbers in their
 digit-list representation (and return a new digit list). If you’re ambitious you
 can implement Karatsuba multiplication. Try different bases. What is the best
 base if you care about speed? If you couldn’t completely solve the prime number
exercise above due to the lack of large numbers in your language, you can
 now use your own library for this task.```

list1 = [1,2,3,4,5]
list2 = [6,7,8,9,10]

function a_s_m(x::Vector{Int64}, y::Vector{Int64})
      a = x .+ y
      s = x .- y
      m = x .* y
      return a,s,m

end

a_s_m(list1, list2)

max_stars_repo_name: atott/Simple-Programming-Problems-Julia

max_issues_repo_name: atott/Simple-Programming-Problems-Julia

max_forks_repo_name: atott/Simple-Programming-Problems-Julia


text:
@testset "Estimate" begin
    @testset "Normal" begin
        ω = [0.1, 0.1]
        A = [0.5 0; 0 0.5]
        B = [0.5 0; 0 0.5]
        simulation = simulate_GAS_1_1(Normal, 0.0, ω, A, B, 1)
        @testset "Estimation by passing number of initial_points" begin
            # LBFGS
            gas = Model(1, 1, Normal, 0.0)
            fit!(gas, simulation; verbose = 2, opt_method = ScoreDrivenModels.LBFGS(gas, 3))
            test_coefficients_GAS_1_1(gas, ω, A, B)
            # NelderMead
            gas = Model(1, 1, Normal, 0.0)
            fit!(gas, simulation; verbose = 2, opt_method = ScoreDrivenModels.NelderMead(gas, 3))
            test_coefficients_GAS_1_1(gas, ω, A, B)
            # IPNewton
            gas = Model(1, 1, Normal, 0.0)
            fit!(gas, simulation; verbose = 2, opt_method = ScoreDrivenModels.IPNewton(gas, 3))
            test_coefficients_GAS_1_1(gas, ω, A, B)
        end
        @testset "Estimation by passing initial_points" begin
            # LBFGS
            gas = Model(1, 1, Normal, 0.0)
            initial_points = [[0.1, 0.1, 0.5, 0.5, 0.5, 0.5]]
            fit!(gas, simulation; verbose = 1, opt_method = ScoreDrivenModels.LBFGS(gas, initial_points))
            test_coefficients_GAS_1_1(gas, ω, A, B)
            # NelderMead
            gas = Model(1, 1, Normal, 0.0)
            initial_points = [[0.1, 0.1, 0.5, 0.5, 0.5, 0.5]]
            fit!(gas, simulation; verbose = 1, opt_method = ScoreDrivenModels.NelderMead(gas, initial_points))
            test_coefficients_GAS_1_1(gas, ω, A, B)
            # IPNewton
            gas = Model(1, 1, Normal, 0.0)
            initial_points = [[0.1, 0.1, 0.5, 0.5, 0.5, 0.5]]
            fit!(gas, simulation; verbose = 1, opt_method = ScoreDrivenModels.IPNewton(gas, initial_points))
            test_coefficients_GAS_1_1(gas, ω, A, B)
        end
    end

    @testset "Lognormal" begin
        @testset "Scaling = 0.0" begin
            ω = [0.1, 0.1]
            A = [0.5 0; 0 0.5]
            B = [0.5 0; 0 0.5]
            simulation = simulate_GAS_1_1(LogNormal, 0.0, ω, A, B, 13)
            gas = Model(1, 1, LogNormal, 0.0)
            fit!(gas, simulation; verbose = 1, opt_method = ScoreDrivenModels.LBFGS(gas, 3))
            test_coefficients_GAS_1_1(gas, ω, A, B)
        end
        @testset "Scaling = 0.5" begin
            ω = [0.1, 0.1]
            A = [0.5 0; 0 0.5]
            B = [0.5 0; 0 0.5]
            simulation = simulate_GAS_1_1(LogNormal, 0.5, ω, A, B, 13)
            gas = Model(1, 1, LogNormal, 0.5)
            fit!(gas, simulation; verbose = 1, opt_method = ScoreDrivenModels.LBFGS(gas, 3))
            test_coefficients_GAS_1_1(gas, ω, A, B)
        end
        @testset "Scaling = 1.0" begin
            ω = [0.5, 0.5]
            A = [0.1 0; 0 0.1]
            B = [0.1 0; 0 0.1]
            simulation = simulate_GAS_1_1(LogNormal, 1.0, ω, A, B, 3)
            gas = Model(1, 1, LogNormal, 1.0)
            fit!(gas, simulation; verbose = 1, opt_method = ScoreDrivenModels.LBFGS(gas, 3))
            test_coefficients_GAS_1_1(gas, ω, A, B)
        end
        @testset "Model([1, 12], [1, 12])" begin
            simulation = simulate_GAS_1_12(LogNormal, 0.0, 123)
            gas = Model([1, 12], [1, 12], LogNormal, 0.0)
            fit!(gas, simulation; verbose = 1, opt_method = ScoreDrivenModels.LBFGS(gas, 3))
            test_coefficients_GAS_1_12(gas)
        end
    end
    @testset "GARCH(1, 1)" begin
        # Compare results with ARCHModels
        y = readdlm("$(@__DIR__)/data/BG96.csv")[:]

        # Redefine links
        function ScoreDrivenModels.link!(param_tilde::Matrix{T}, ::Type{Normal}, param::Matrix{T}, t::Int) where T
            param_tilde[t, 1] = link(IdentityLink, param[t, 1])
            param_tilde[t, 2] = link(IdentityLink, param[t, 2])
            return
        end
        function ScoreDrivenModels.unlink!(param::Matrix{T}, ::Type{Normal}, param_tilde::Matrix{T}, t::Int) where T
            param[t, 1] = unlink(IdentityLink, param_tilde[t, 1])
            param[t, 2] = unlink(IdentityLink, param_tilde[t, 2])
            return
        end
        function ScoreDrivenModels.jacobian_link!(aux::AuxiliaryLinAlg{T}, ::Type{Normal}, param::Matrix{T}, t::Int) where T
            aux.jac[1] = jacobian_link(IdentityLink, param[t, 1])
            aux.jac[2] = jacobian_link(IdentityLink, param[t, 2])
            return
        end
        test_GARCH_1_1(y, 1, IPNewton)
        # Back to default links
        function ScoreDrivenModels.link!(param_tilde::Matrix{T}, ::Type{Normal}, param::Matrix{T}, t::Int) where T
            param_tilde[t, 1] = link(IdentityLink, param[t, 1])
            param_tilde[t, 2] = link(LogLink, param[t, 2], zero(T))
            return
        end
        function ScoreDrivenModels.unlink!(param::Matrix{T}, ::Type{Normal}, param_tilde::Matrix{T}, t::Int) where T
            param[t, 1] = unlink(IdentityLink, param_tilde[t, 1])
            param[t, 2] = unlink(LogLink, param_tilde[t, 2], zero(T))
            return
        end
        function ScoreDrivenModels.jacobian_link!(aux::AuxiliaryLinAlg{T}, ::Type{Normal}, param::Matrix{T}, t::Int) where T
            aux.jac[1] = jacobian_link(IdentityLink, param[t, 1])
            aux.jac[2] = jacobian_link(LogLink, param[t, 2], zero(T))
            return
        end
    end
end


text:
# Example 3.2 in Book: Lenhart S, Workman J T. Optimal control applied to biological models[M]. Chapman and Hall/CRC, 2007.

using InfiniteOpt, Ipopt, Plots

# Parmeters
# Time span
t0 = 0
tf = 1
# Initial values
x1 = 1

# Model
model = InfiniteModel(Ipopt.Optimizer)

# infinite_parameter
@infinite_parameter(model, t ∈ [t0, tf], num_supports = 101)

# variable
@variable(model, x ≥ 0, Infinite(t))

@variable(model, u, Infinite(t), start = 0)

# objective
@objective(model, Min, x(1)^2 + ∫(u^2, t))

# constraint
@constraint(model, x(0) == x1)
@constraint(model, x_constr, ∂(x, t) == x - u)

# Optimization

print(model)

optimize!(model)

# Visulization
ts = value(t)
u_value = value(u)
x_value = value(x)

display(plot(ts, u_value))
display(plot(ts, x_value))

using DiffEqFlux, DifferentialEquations
const solveeq = DifferentialEquations.solve

tspan = (t0, tf)
ann = FastChain(FastDense(1, 64, tanh), FastDense(64, 1))
θ = initial_params(ann)
function sir_nn!(du, u, p, t)
    du[1] = u[1] - ann([t], p)[1]
    du[2] = (ann([t], p)[1])^2
end
u0 = [x1, 0]
ts = Float32.(collect(0.0:0.01:tspan[2]))
prob = ODEProblem(sir_nn!, u0, tspan, θ)
function predict_adjoint(θ)
    Array(solveeq(prob, Vern9(), p=θ, saveat=ts, sensealg=InterpolatingAdjoint(autojacvec=ReverseDiffVJP(true))))
end
function loss_adjoint(θ)
    x = predict_adjoint(θ)
    x[1, end]^2 + x[2, end]
end
l = loss_adjoint(θ)

const losses = []
callback(θ, l, pred) = begin
    push!(losses, l)
    if length(losses) % 50 == 0
        println(losses[end])
        p = plot(Array(solveeq(remake(prob, p=θ), Tsit5(), saveat=ts), lw=3)[1, :])
        #plot!(p, ts, [first(ann([t], θ)) for t in ts], label = "u(t)", lw = 3)
        display(p)
    end
    false
end
cb = function (θ, l)
    println(l)
    p = plot(solveeq(remake(prob, p=θ), Tsit5(), saveat=ts), lw=3)
    #plot!(p, ts, [first(ann([t], θ)) for t in ts], label = "u(t)", lw = 3)
    display(p)
    return false
end
#Display the ODE with the current parameter values.
cb(θ, l)
loss1 = loss_adjoint(θ)
res1 = DiffEqFlux.sciml_train(loss_adjoint, θ, ADAM(0.05), cb=cb, maxiters=100)
@time res2 = DiffEqFlux.sciml_train(loss_adjoint, res1.u,
    BFGS(initial_stepnorm=0.01), cb=cb, maxiters=100,
    allow_f_increases=false)

using DataFrames
using CSV
using LaTeXStrings
p_value = res2.u
results_saving_opt = DataFrame()
results_saving_opt.t = ts
results_saving_opt.I = x_value
results_saving_opt.u = u_value
results_saving_opt.I_nn = Array(solveeq(remake(prob, p=p_value), Tsit5(), saveat=ts))[1, :]
results_saving_opt.u_nn = [first(ann([t], p_value)) for t in ts]
CSV.write("results_saving_Example3_2_mayer.csv", results_saving_opt)

plot(results_saving_opt.t, results_saving_opt.I, lw=2, label="State I(t) by direct collection")
plot!(results_saving_opt.t, results_saving_opt.I_nn, lw=2, label="State I(t) by deep learning")
g(x) = exp(-x) / (1 + exp(-2)) + exp(x - 2) / (1 + exp(-2))
plot!(results_saving_opt.t[2:end-1], g.(results_saving_opt.t[2:end-1]), foreground_color_legend=nothing, label=latexstring("Analytic solution \$ I(t) =\\exp(-x) / (1 + \\exp(-2)) + \\exp(x - 2) / (1 + \\exp(-2))\$"))
xlabel!("t")
ylabel!("I(t)")
savefig("Example3_2_I_mayer.png")

plot(results_saving_opt.t[2:end-1], results_saving_opt.u[2:end-1], lw=2, label="Control u(t) by direct collection")
plot!(results_saving_opt.t[2:end-1], results_saving_opt.u_nn[2:end-1], lw=2, label="Control u(t) by deep learning")
f(x) = 2 * exp(-x) / (1 + exp(-2))
plot!(results_saving_opt.t[2:end-1], f.(results_saving_opt.t[2:end-1]), foreground_color_legend=nothing, label=latexstring("Analytic solution \$ u(t) =2*\\exp(-x)/(1+\\exp(-2))\$"))
xlabel!("t")
ylabel!("u(t)")
savefig("Example3_2_control_mayer.png")

#
opt_loss = objective_value(model)
# opt_loss = 1.75
opt_loss_nn = loss_adjoint(p_value)


text:
module YaoZX

using YaoBlocks
using ZXCalculus
import ZXCalculus: push_gate!

export push_gate!, decompose_zx
export ZXDiagram
export toqbir

include("push_gate.jl")
include("toqbir.jl")

end


text:
using SuperEnum

@se Colors begin
    RED => "red"
    GREEN => "green"
    BLUE => "blue"

end

function show()
    color_map = Dict(Colors.RED => "1", Colors.GREEN => "2", Colors.BLUE => "3")
    a = Colors.GREEN
    if a == Colors.GREEN
        println(join(["green"], " "))
    else

        println(join(["Not green"], " "))
    end
    println(join([length(color_map)], " "))
end

function main()
    show()
end

main()


text:
# This file was generated by the Julia Swagger Code Generator
# Do not modify this file directly. Modify the swagger specification instead.

mutable struct ContentSummary <: SwaggerModel
    directoryCount::Any # spec type: Union{ Nothing, Int64 } # spec name: directoryCount
    fileCount::Any # spec type: Union{ Nothing, Int64 } # spec name: fileCount
    length::Any # spec type: Union{ Nothing, Int64 } # spec name: length
    spaceConsumed::Any # spec type: Union{ Nothing, Int64 } # spec name: spaceConsumed

    function ContentSummary(;directoryCount=nothing, fileCount=nothing, length=nothing, spaceConsumed=nothing)
        o = new()
        validate_property(ContentSummary, Symbol("directoryCount"), directoryCount)
        setfield!(o, Symbol("directoryCount"), directoryCount)
        validate_property(ContentSummary, Symbol("fileCount"), fileCount)
        setfield!(o, Symbol("fileCount"), fileCount)
        validate_property(ContentSummary, Symbol("length"), length)
        setfield!(o, Symbol("length"), length)
        validate_property(ContentSummary, Symbol("spaceConsumed"), spaceConsumed)
        setfield!(o, Symbol("spaceConsumed"), spaceConsumed)
        o
    end
end # type ContentSummary

const _property_map_ContentSummary = Dict{Symbol,Symbol}(Symbol("directoryCount")=>Symbol("directoryCount"), Symbol("fileCount")=>Symbol("fileCount"), Symbol("length")=>Symbol("length"), Symbol("spaceConsumed")=>Symbol("spaceConsumed"))
const _property_types_ContentSummary = Dict{Symbol,String}(Symbol("directoryCount")=>"Int64", Symbol("fileCount")=>"Int64", Symbol("length")=>"Int64", Symbol("spaceConsumed")=>"Int64")
Base.propertynames(::Type{ ContentSummary }) = collect(keys(_property_map_ContentSummary))
Swagger.property_type(::Type{ ContentSummary }, name::Symbol) = Union{Nothing,eval(Meta.parse(_property_types_ContentSummary[name]))}
Swagger.field_name(::Type{ ContentSummary }, property_name::Symbol) =  _property_map_ContentSummary[property_name]

function check_required(o::ContentSummary)
    true
end

function validate_property(::Type{ ContentSummary }, name::Symbol, val)
end


text:
# Autogenerated wrapper script for extxyz_jll for x86_64-unknown-freebsd
export libextxyz

using PCRE2_jll
using libcleri_jll
JLLWrappers.@generate_wrapper_header("extxyz")
JLLWrappers.@declare_library_product(libextxyz, "libextxyz.so")
function __init__()
    JLLWrappers.@generate_init_header(PCRE2_jll, libcleri_jll)
    JLLWrappers.@init_library_product(
        libextxyz,
        "lib/libextxyz.so",
        RTLD_LAZY | RTLD_DEEPBIND,
    )

    JLLWrappers.@generate_init_footer()
end  # __init__()


text:
function Experiment(
    ::Val{:JuliaRL},
    ::Val{:TabularCFR},
    ::Val{:OpenSpiel},
    game;
    n_iter = 300,
    seed = 123,
)
    env = OpenSpielEnv(game)
    rng = StableRNG(seed)
    π = TabularCFRPolicy(; rng = rng)

    description = """
        # Play `$game` in OpenSpiel with TabularCFRPolicy
        """
    Experiment(π, env, StopAfterStep(300), EmptyHook(), description)
end


text:
abstract type AbstractTableauSplitting{T <: Real} <: AbstractTableau{T} end

function get_splitting_coefficients(r, a::Vector{T}, b::Vector{T}) where {T}
    @assert length(a) == length(b)

    s = length(a)
    f = zeros(Int, 2r*s)
    c = zeros(T,   2r*s)

    for i in 1:s
        for j in 1:r
            f[(2i-2)*r+j] = j
            c[(2i-2)*r+j] = a[i]
        end
        for j in 1:r
            f[(2i-1)*r+j] = r-j+1
            c[(2i-1)*r+j] = b[i]
        end
    end

    return f, c
end

@doc raw"""
Tableau for general splitting methods for vector fields with two components A and B.

Integrator:
```math
\varphi_{\tau} = \varphi_{b_s \tau}^{B} \circ \varphi_{a_s \tau}^{A} \circ \dotsc \circ \varphi_{b_1 \tau}^{B} \circ \varphi_{a_1 \tau}^{A}
```
"""
struct TableauSplitting{T} <: AbstractTableauSplitting{T}
    @HeaderTableau

    a::Vector{T}
    b::Vector{T}

    function TableauSplitting{T}(name, o, s, a, b) where {T}
        @assert s == length(a) == length(b)
        new(name, o, s, a, b)
    end
end

function TableauSplitting(name, o, a::Vector{T}, b::Vector{T}) where {T}
    TableauSplitting{T}(name, o, length(a), a, b)
end

function get_splitting_coefficients(nequs, tableau::TableauSplitting{T}) where {T}
    @assert nequs == 2
    @assert length(tableau.a) == length(tableau.b)

    s = length(tableau.a)
    f = zeros(Int, 2s)
    c = zeros(T,   2s)

    for i in eachindex(tableau.a, tableau.b)
        f[2*(i-1)+1] = 1
        c[2*(i-1)+1] = tableau.a[i]
        f[2*(i-1)+2] = 2
        c[2*(i-1)+2] = tableau.b[i]
    end

    # select all entries with non-vanishing coefficients
    y = c .!= 0

    return f[y], c[y]
end

@doc raw"""
Tableau for non-symmetric splitting methods.
    See McLachlan, Quispel, 2003, Equ. (4.10).
    The methods A and B are the composition of all vector fields in the SODE
    and its adjoint, respectively.

Basic method: Lie composition
```math
\begin{aligned}
\varphi_{\tau}^{A} &= \varphi_{\tau}^{v_1} \circ \varphi_{\tau}^{v_2} \circ \dotsc \circ \varphi_{\tau}^{v_{r-1}} \circ \varphi_{\tau}^{v_r} \\
\varphi_{\tau}^{B} &= \varphi_{\tau}^{v_r} \circ \varphi_{\tau}^{v_{r-1}} \circ \dotsc \circ \varphi_{\tau}^{v_2} \circ \varphi_{\tau}^{v_1}
\end{aligned}
```

Integrator:
```math
\varphi_{\tau}^{NS} = \varphi_{b_s \tau}^{B} \circ \varphi_{a_s \tau}^{A} \circ \dotsc \circ \varphi_{b_1 \tau}^{B} \circ \varphi_{a_1 \tau}^{A}
```
"""
struct TableauSplittingNS{T} <: AbstractTableauSplitting{T}
    @HeaderTableau

    a::Vector{T}
    b::Vector{T}

    function TableauSplittingNS{T}(name, o, s, a, b) where {T}
        @assert s == length(a) == length(b)
        new(name, o, s, a, b)
    end
end

function TableauSplittingNS(name, o, a::Vector{T}, b::Vector{T}) where {T}
    TableauSplittingNS{T}(name, o, length(a), a, b)
end

function get_splitting_coefficients(nequs, tableau::TableauSplittingNS)
    # R = length(equation.v)
    # S = tableau.s
    #
    # f = zeros(Int, 2R*S)
    # c = zeros(TT,  2R*S)
    #
    # for i in 1:S
    #     for j in 1:R
    #         f[(2i-2)*R+j] = j
    #         c[(2i-2)*R+j] = tableau.a[i]
    #     end
    #     for j in 1:R
    #         f[(2i-1)*R+j] = R-j+1
    #         c[(2i-1)*R+j] = tableau.b[i]
    #     end
    # end

    get_splitting_coefficients(nequs, tableau.a, tableau.b)
end

@doc raw"""
Tableau for symmetric splitting methods with general stages.
    See McLachlan, Quispel, 2003, Equ. (4.11).

Basic method: Lie composition
```math
\begin{aligned}
\varphi_{\tau}^{A} &= \varphi_{\tau}^{v_1} \circ \varphi_{\tau}^{v_2} \circ \dotsc \circ \varphi_{\tau}^{v_{r-1}} \circ \varphi_{\tau}^{v_r} \\
\varphi_{\tau}^{B} &= \varphi_{\tau}^{v_r} \circ \varphi_{\tau}^{v_{r-1}} \circ \dotsc \circ \varphi_{\tau}^{v_2} \circ \varphi_{\tau}^{v_1}
\end{aligned}
```

Integrator:
```math
\varphi_{\tau}^{GS} = \varphi_{a_1 \tau}^{A} \circ \varphi_{b_1 \tau}^{B} \circ \dotsc \circ \varphi_{b_1 \tau}^{B} \circ \varphi_{a_1 \tau}^{A}
```
"""
struct TableauSplittingGS{T} <: AbstractTableauSplitting{T}
    @HeaderTableau

    a::Vector{T}
    b::Vector{T}

    function TableauSplittingGS{T}(name, o, s, a, b) where {T}
        @assert s == length(a) == length(b)
        new(name, o, s, a, b)
    end
end

function TableauSplittingGS(name, o, a::Vector{T}, b::Vector{T}) where {T}
    TableauSplittingGS{T}(name, o, length(a), a, b)
end

function get_splitting_coefficients(nequs, tableau::TableauSplittingGS)
    # R = nequs
    # S = tableau.s
    #
    # f = zeros(Int, 2R*S)
    # c = zeros(TT,  2R*S)
    #
    # for i in 1:S
    #     for j in 1:R
    #         f[(2i-2)*R+j] = j
    #         c[(2i-2)*R+j] = tableau.a[i]
    #     end
    #     for j in R:-1:1
    #         f[(2i-1)*R+j] = R-j+1
    #         c[(2i-1)*R+j] = tableau.b[i]
    #     end
    # end

    f, c = get_splitting_coefficients(nequs, tableau.a, tableau.b)
    vcat(f, f[end:-1:1]), vcat(c, c[end:-1:1])
end

@doc raw"""
Tableau for symmetric splitting methods with symmetric stages.
    See McLachlan, Quispel, 2003, Equ. (4.6).

Basic method: symmetric Strang composition
```math
\varphi_{\tau}^{A} = \varphi_{\tau/2}^{v_1} \circ \varphi_{\tau/2}^{v_2} \circ \dotsc \circ \varphi_{\tau/2}^{v_{r-1}} \circ \varphi_{\tau/2}^{v_r} \circ \varphi_{\tau/2}^{v_r} \circ \varphi_{\tau/2}^{v_{r-1}} \circ \dotsc \circ \varphi_{\tau/2}^{v_2} \circ \varphi_{\tau/2}^{v_1}
```

Integrator:
```math
\varphi_{\tau}^{SS} = \varphi_{a_1 \tau}^{A} \circ \varphi_{a_2 \tau}^{A} \circ \dotsc \circ \varphi_{a_s \tau}^{A} \circ \dotsc \circ \varphi_{a_2 \tau}^{A} \circ \varphi_{a_1 \tau}^{A}
```
"""
struct TableauSplittingSS{T} <: AbstractTableauSplitting{T}
    @HeaderTableau

    a::Vector{T}

    function TableauSplittingSS{T}(name, o, s, a) where {T}
        @assert s == length(a)
        new(name, o, s, a)
    end
end

function TableauSplittingSS(name, o, a::Vector{T}) where {T}
    TableauSplittingSS{T}(name, o, length(a), a)
end

function get_splitting_coefficients(nequs, tableau::TableauSplittingSS{T}) where {T}
    r = nequs
    a = vcat(tableau.a, tableau.a[end-1:-1:1]) ./ 2
    s = length(a)

    f = zeros(Int, 2r*s)
    c = zeros(T,   2r*s)

    for i in 1:s
        for j in 1:r
            f[(2i-2)*r+j] = j
            c[(2i-2)*r+j] = a[i]
            f[(2i-1)*r+j] = r-j+1
            c[(2i-1)*r+j] = a[i]
        end
    end

    f, c
end