text:
@testset "solve" begin

    @testset "total degree (simple)" begin
        @var x y
        affine_square = System([
            2.3 * x^2 + 1.2 * y^2 + 3x - 2y + 3,
            2.3 * x^2 + 1.2 * y^2 + 5x + 2y - 5,
        ])
        @test count(is_success, track.(total_degree(affine_square)...)) == 2

        @var x y z
        proj_square = System([
            2.3 * x^2 + 1.2 * y^2 + 3x * z - 2y * z + 3 * z^2,
            2.3 * x^2 + 1.2 * y^2 + 5x * z + 2y * z - 5 * z^2,
        ])
        @test count(is_success, track.(total_degree(proj_square)...)) == 4

        @var x y
        affine_overdetermined = System([
            (x^2 + y^2 + x * y - 3) * (x + 3),
            (x^2 + y^2 + x * y - 3) * (y - x + 2),
            2x + 5y - 3,
        ])
        @test count(is_success, track.(total_degree(affine_overdetermined)...)) == 2

        @var x y
        affine_overdetermined_reordering = System([
            (x^2 + y^2 + x * y - 3) * (x + 3),
            2x + 5y - 3,
            (x^2 + y^2 + x * y - 3) * (y^2 - x + 2),
        ])
        tracker, starts = total_degree(affine_overdetermined_reordering)
        @test length(starts) == 4 * 3
        @test count(is_success, track.(tracker, starts)) == 2

        @var x y z
        proj_overdetermined = System([
            (x^2 + y^2 + x * y - 3 * z^2) * (x + 3z),
            (x^2 + y^2 + x * y - 3 * z^2) * (y - x + 2z),
            2x + 5y - 3z,
        ])
        @test count(is_success, track.(total_degree(proj_overdetermined)...)) == 2

        @var x y
        proj_overdetermined_reordering = System([
            (x^2 + y^2 + x * y - 3 * z^2) * (x + 3z),
            2x + 5y - 3z,
            (x^2 + y^2 + x * y - 3 * z^2) * (y^2 - x * z + 2 * z^2),
        ])
        tracker, starts = total_degree(proj_overdetermined_reordering)
        @test length(starts) == 4 * 3
        @test count(is_success, track.(tracker, starts)) == 2

        @var x y
        affine_underdetermined = System([2.3 * x^2 + 1.2 * y^2 + 3x - 2y + 3])
        @test_throws HC2.FiniteException total_degree(affine_underdetermined)

        @var x y z
        proj_underdetermined = System([2.3 * x^2 + 1.2 * y^2 + 3x * z])
        @test_throws HC2.FiniteException total_degree(proj_underdetermined)
    end

    @testset "total degree (variable groups)" begin
        @var x y v w
        affine_sqr = System([x * y - 2, x^2 - 4], variable_groups = [[x], [y]])
        tracker, starts = total_degree(affine_sqr)
        @test length(collect(starts)) == 2
        @test count(is_success, track.(tracker, starts)) == 2

        @var x y v w
        proj_sqr =
            System([x * y - 2v * w, x^2 - 4 * v^2], variable_groups = [[x, v], [y, w]])
        tracker, starts = total_degree(proj_sqr)
        @test length(collect(starts)) == 2
        @test count(is_success, track.(tracker, starts)) == 2

        @var x y v w
        affine_overdetermined = System(
            [(x^2 - 4) * (x * y - 2), x * y - 2, x^2 - 4],
            variable_groups = [[x], [y]],
        )
        tracker, starts = total_degree(affine_overdetermined)
        @test count(is_success, track.(tracker, starts)) == 2
        @var x y v w
        proj_overdetermined = System(
            [(x^2 - 4 * v^2) * (x * y - v * w), x * y - v * w, x^2 - v^2],
            variable_groups = [[x, v], [y, w]],
        )
        tracker, starts = total_degree(proj_overdetermined)
        @test count(is_success, track.(tracker, starts)) == 2
    end

    @testset "polyhedral" begin
        @var x y
        affine_square = System([
            2.3 * x^2 + 1.2 * y^2 + 3x - 2y + 3,
            2.3 * x^2 + 1.2 * y^2 + 5x + 2y - 5,
        ])
        @test count(is_success, track.(polyhedral(affine_square)...)) == 2

        @var x y z
        proj_square = System([
            2.3 * x^2 + 1.2 * y^2 + 3x * z - 2y * z + 3 * z^2,
            2.3 * x^2 + 1.2 * y^2 + 5x * z + 2y * z - 5 * z^2,
        ])
        @test count(is_success, track.(polyhedral(proj_square)...)) == 4

        @var x y
        affine_overdetermined = System([
            (x^2 + y^2 + x * y - 3) * (x + 3),
            (x^2 + y^2 + x * y - 3) * (y - x + 2),
            2x + 5y - 3,
        ])
        @test count(is_success, track.(polyhedral(affine_overdetermined)...)) == 2

        @var x y z
        proj_overdetermined = System([
            (x^2 + y^2 + x * y - 3 * z^2) * (x + 3z),
            (x^2 + y^2 + x * y - 3 * z^2) * (y - x + 2z),
            2x + 5y - 3z,
        ])
        @test count(is_success, track.(polyhedral(proj_overdetermined)...)) == 2

        @var x y
        affine_underdetermined = System([2.3 * x^2 + 1.2 * y^2 + 3x - 2y + 3])
        @test_throws HC2.FiniteException polyhedral(affine_underdetermined)

        @var x y z
        proj_underdetermined = System([2.3 * x^2 + 1.2 * y^2 + 3x * z])
        @test_throws HC2.FiniteException polyhedral(proj_underdetermined)
    end

    @testset "overdetermined" begin
        @testset "3 by 5 minors" begin
            res = track.(total_degree(minors())...)
            @test count(is_success, res) == 80
        end
    end

    @testset "Result" begin
        d = 2
        @var x y a[1:6]
        F = System(
            [
                (a[1] * x^d + a[2] * y) * (a[3] * x + a[4] * y) + 1,
                (a[1] * x^d + a[2] * y) * (a[5] * x + a[6] * y) + 1,
            ];
            parameters = a,
        )
        res = solve(F; target_parameters = [0.257, -0.139, -1.73, -0.199, 1.79, -1.32])

        @test startswith(sprint(show, res), "Result with 3 solutions")
        @test seed(res) isa UInt32
        test_treeviews(res)

        seeded_res = solve(
            F;
            target_parameters = [0.257, -0.139, -1.73, -0.199, 1.79, -1.32],
            seed = seed(res),
        )
        @test seed(seeded_res) == seed(res)
        test_treeviews(res)

        @test length(path_results(res)) == ntracked(res) == 7
        @test length(results(res)) == nresults(res) == 3
        @test length(solutions(res)) == 3
        @test length(findall(is_success, res)) == 3
        @test real_solutions(res) isa Vector{Vector{Float64}}
        @test length(real_solutions(res)) == nreal(res) == 1
        @test length(nonsingular(res)) == nnonsingular(res) == 3
        @test isempty(singular(res))
        @test nsingular(res) == 0
        @test length(at_infinity(res)) == nat_infinity(res) == 4
        @test isempty(failed(res))
        @test nfailed(res) == 0
        @test nexcess_solutions(res) == 0
        @test !isempty(sprint(show, statistics(res)))

        # singular result
        @var x y
        g = System([(29 / 16) * x^3 - 2 * x * y, x^2 - y])
        res = solve(g; start_system = :total_degree)
        @test startswith(sprint(show, res), "Result with 3 solutions")
        @test seed(res) isa UInt32
        test_treeviews(res)
        @test !isempty(sprint(show, statistics(res)))
        @test !isempty(sprint(show, res))
    end

    @testset "composition" begin
        @var a b c x y z u v
        e = System([u + 1, v - 2])
        f = System([a * b - 2, a * c - 1])
        g = System([x + y, y + 3, x + 2])

        res = solve(e ∘ f ∘ g; start_system = :total_degree)
        @test nsolutions(res) == 2

        res = solve(e ∘ f ∘ g; start_system = :polyhedral)
        @test nsolutions(res) == 2
    end

    @testset "paths to track" begin
        @var x y
        f = System([2y + 3 * y^2 - x * y^3, x + 4 * x^2 - 2 * x^3 * y])

        @test paths_to_track(f; start_system = :total_degree) == 16
        @test paths_to_track(f; start_system = :polyhedral) == 8
        @test paths_to_track(f; start_system = :polyhedral, only_non_zero = true) == 3
        @test paths_to_track(f) == 8
        @test_deprecated bezout_number(f) == 16
        @test mixed_volume(f) == 3
    end

    @testset "solve (parameter homotopy)" begin
        # affine
        @var x a y b
        F = System([x^2 - a, x * y - a + b], [x, y], [a, b])
        s = [1, 1]
        res = solve(F, [s]; start_parameters = [1, 0], target_parameters = [2, 4])
        @test nsolutions(res) == 1
        res = solve(
            ModelKitSystem(F),
            [s];
            start_parameters = [1, 0],
            target_parameters = [2, 4],
            threading = false,
        )
        @test nsolutions(res) == 1

        @var x a y b
        F = System([x^2 - a], [x, y], [a, b])
        s = [1, 1]
        @test_throws FiniteException(1) solve(
            F,
            [s];
            start_parameters = [1, 0],
            target_parameters = [2, 4],
        )

        # proj
        @var x a y b z
        F_proj = System([x^2 - a * z^2, x * y + (b - a) * z^2], [x, y, z], [a, b])
        s = [1, 1, 1]
        res = solve(F_proj, [s]; start_parameters = [1, 0], target_parameters = [2, 4])
        @test nsolutions(res) == 1
        res = solve(ModelKitSystem(F_proj), [s]; p₁ = [1, 0], p₀ = [2, 4])
        @test nsolutions(res) == 1

        F_proj_err = System([x * y + (b - a) * z^2], [x, y, z], [a, b])
        @test_throws FiniteException solve(F_proj_err, [s]; p₁ = [1, 0], p₀ = [2, 4])

        # multi-proj
        @var x y v w a b
        F_multi_proj = System(
            [x * y - a * v * w, x^2 - b * v^2],
            parameters = [a, b],
            variable_groups = [[x, v], [y, w]],
        )
        S = [
            [
                -1.808683149843597 + 0.2761582523875564im,
                -0.9043415749217985 + 0.1380791261937782im,
                -0.0422893850686111 - 0.7152002569359284im,
                -0.0422893850686111 - 0.7152002569359283im,
            ],
            [
                -0.36370464807054353 + 0.6777414371333245im,
                0.18185232403527177 - 0.33887071856666223im,
                -0.3348980281838583 - 0.7759382220656511im,
                0.3348980281838583 + 0.7759382220656511im,
            ],
        ]
        res = solve(F_multi_proj, S; start_parameters = [2, 4], target_parameters = [3, 5])
        @test nsolutions(res) == 2
        res = solve(ModelKitSystem(F_multi_proj), S; p₁ = [2, 4], p₀ = [3, 5])
        @test nsolutions(res) == 2

        F_multi_proj_err = System(
            [x * y - a * v * w],
            parameters = [a, b],
            variable_groups = [[x, v], [y, w]],
        )
        @test_throws FiniteException(1) solve(F_multi_proj_err, S; p₁ = [2, 4], p₀ = [3, 5])
    end

    @testset "solve (Homotopy)" begin
        @var x a y b
        F = System([x^2 - a, x * y - a + b], [x, y], [a, b])
        s = [1, 1]
        H = ParameterHomotopy(F, [1, 0], [2, 4])
        res = solve(H, [s])
        @test nsolutions(res) == 1
    end

    @testset "solve (start target)" begin
        @var x a y b
        f = System([x^2 - a, x * y - a + b], parameters = [a, b])
        s = [1, 1]
        res = solve(f, f, [s]; start_parameters = [1, 0], target_parameters = [2, 4])
        @test nsolutions(res) == 1

        G = FixedParameterSystem(f, [1, 0])
        F = FixedParameterSystem(f, [2, 4])
        res = solve(G, F, [s];)
        @test nsolutions(res) == 1
    end

    @testset "solve (Vector{Expression})" begin
        @var x a y b
        F = [x^2 - a, x * y - a + b]
        s = [1, 1]
        res = solve(
            F,
            [s];
            parameters = [a, b],
            start_parameters = [1, 0],
            target_parameters = [2, 4],
        )
        @test nsolutions(res) == 1
    end

    @testset "solve (DynamicPolynomials)" begin
        @polyvar x y
        # define the polynomials
        f₁ = (x^4 + y^4 - 1) * (x^2 + y^2 - 2) + x^5 * y
        f₂ = x^2 + 2x * y^2 - 2 * y^2 - 1 / 2
        result = solve([f₁, f₂])
        @test nsolutions(result) == 18

        @polyvar x a y b
        F = [x^2 - a, x * y - a + b]
        s = [1, 1]
        res = solve(
            F,
            [s];
            parameters = [a, b],
            start_parameters = [1, 0],
            target_parameters = [2, 4],
        )
        @test nsolutions(res) == 1
    end

    @testset "change parameters" begin
        @var x a y b
        F = System([x^2 - a, x * y - a + b]; parameters = [a, b])
        s = [1.0, 1.0 + 0im]
        S = solver(F, generic_parameters = [2.2, 3.2])
        start_parameters!(S, [1, 0])
        target_parameters!(S, [2, 4])
        @test is_success(track(S, s))
    end

    @testset "solve (threading)" begin
        res = solve(cyclic(7), threading = true, show_progress = false)
        @test nsolutions(res) == 924
    end

    @testset "stop early callback" begin
        @var x
        first_result = nothing
        results = solve(
            [(x - 3) * (x + 6) * (x + 2)];
            stop_early_cb = r -> begin
                first_result = r
                true
            end,
            threading = false,
            show_progress = false,
            start_system = :total_degree,
        )
        @test length(results) == 1
        @test first(results) === first_result

        nresults = 0
        result = solve(
            [(x - 3) * (x + 6) * (x + 2)],
            stop_early_cb = r -> (nresults += 1) == 2,
            start_system = :total_degree,
            show_progress = false,
            threading = false,
        )
        @test length(result) == 2

        # threading
        @var x y z
        first_result = nothing
        # this has 5^3 = 125 solutions, so we should definitely stop early if we
        # have less than 64 threads
        results = solve(
            [
                (x - 3) * (x + 6) * (x + 2) * (x - 2) * (x + 2.5),
                (y + 2) * (y - 2) * (y + 3) * (y + 5) * (y - 1),
                (z + 2) * (z - 2) * (z + 3) * (z + 5) * (z - 2.1),
            ];
            stop_early_cb = r -> begin
                first_result = r
                true
            end,
            threading = true,
            show_progress = false,
            start_system = :total_degree,
        )
        @test length(results) < 125
    end

    @testset "Many parameters solver" begin
        # Setup
        @var x y
        f = x^2 + y^2 - 1

        @var a b c
        l = a * x + b * y + c
        F = [f, l]

        # Compute start solutions S₀ for given start parameters p₀
        p₀ = randn(ComplexF64, 3)
        S₀ = solutions(solve(subs(F, [a, b, c] => p₀)))
        # The parameters we are intersted in
        params = [rand(3) for i = 1:100]

        result1 = solve(
            F,
            S₀,
            ;
            start_parameters = p₀,
            target_parameters = params,
            parameters = [a, b, c],
            threading = true,
        )
        @test typeof(result1) == Vector{Tuple{Result,Vector{Float64}}}
        result1 = solve(
            F,
            S₀,
            ;
            start_parameters = p₀,
            target_parameters = params,
            parameters = [a, b, c],
            show_progress = false,
            threading = false,
        )
        @test typeof(result1) == Vector{Tuple{Result,Vector{Float64}}}

        # Only keep real solutions
        result2 = solve(
            F,
            S₀,
            ;
            start_parameters = p₀,
            target_parameters = params,
            parameters = [a, b, c],
            transform_result = (r, p) -> real_solutions(r),
            threading = true,
        )
        @test typeof(result2) == Vector{Vector{Vector{Float64}}}
        @test !isempty(result2)

        # Now instead of an Array{Array{Array{Float64,1},1},1} we want to have an
        # Array{Array{Float64,1},1}
        result3 = solve(
            F,
            S₀,
            ;
            start_parameters = p₀,
            target_parameters = params,
            parameters = [a, b, c],
            transform_result = (r, p) -> real_solutions(r),
            flatten = true,
            threading = false,
        )
        @test typeof(result3) == Vector{Vector{Float64}}
        @test !isempty(result3)

        # The passed `params` do not directly need to be the target parameters.
        # Instead they can be some more concrete informations (e.g. an index)
        # and we can them by using the `transform_parameters` method
        result4 = solve(
            F,
            S₀,
            ;
            start_parameters = p₀,
            target_parameters = 1:100,
            parameters = [a, b, c],
            transform_result = (r, p) -> (real_solutions(r), p),
            transform_parameters = _ -> rand(3),
        )
        @test typeof(result4) == Vector{Tuple{Vector{Vector{Float64}},Int64}}
    end
end


text:
abstract type ZoteroObject end
abstract type Document <: ZoteroObject end
doc_color = crayon"blue"
pdf_color = crayon"red"
col_color = crayon"green"
reset_color = Crayon(reset=true)

rand_key() = join(rand(vcat('A':'Z', '0':'9'), 8))

@with_kw mutable struct Library
    name::String = ""
    # Links
    alternate::Dict{String, Any} = Dict{String, Any}("href" => "", "type" => "text/html")
    id::Int = 0
    type::String = ""
end

function Library(dict::Dict{String, Any})
    alternate = dict["links"]["alternate"]
    delete!(dict, "links")
    dict["alternate"] = alternate
    Library(;Dict(Symbol(key)=>value for (key, value) in dict)...)
end

@with_kw mutable struct ParentDoc <: Document
    key::String = rand_key()
    library::Library
    # Meta Info
    meta::Dict{String, Any} = Dict{String, Any}()
    # Links
    links::Dict{String, Any} = Dict{String, Any}()
    # Data
    data::Dict{String, Any} = Dict{String, Any}()
    version::Int = 0
    attachments::Vector{Document} = Document[]
end

@with_kw mutable struct Attachment <: Document
    key::String = rand_key()
    library::Library
    # Meta Info
    meta::Dict{String, Any} = Dict{String, Any}()
    # Links
    links::Dict{String, Any} = Dict{String, Any}()
    # Data
    data::Dict{String, Any} = Dict{String, Any}()
    version::Int = 0
    attachments::Vector{Document} = Document[]
end

@with_kw mutable struct PDF <: Document
    key::String = rand_key()
    library::Library
    # Meta Info
    meta::Dict{String, Any} = Dict{String, Any}()
    # Links
    links::Dict{String, Any} = Dict{String, Any}()
    # Data
    data::Dict{String, Any} = Dict{String, Any}()
    version::Int = 0
    attachments::Vector{Document} = Document[]
end

@with_kw mutable struct URL <: Document
    key::String = rand_key()
    library::Library
    # Meta Info
    meta::Dict{String, Any} = Dict{String, Any}()
    # Links
    links::Dict{String, Any} = Dict{String, Any}()
    # Data
    data::Dict{String, Any} = Dict{String, Any}()
    version::Int = 0
    attachments::Vector{Document} = Document[]
end

@with_kw mutable struct Presentation <: Document
    key::String = rand_key()
    library::Library
    # Meta Info
    meta::Dict{String, Any} = Dict{String, Any}()
    # Links
    links::Dict{String, Any} = Dict{String, Any}()
    # Data
    data::Dict{String, Any} = Dict{String, Any}()
    version::Int = 0
    attachments::Vector{Document} = Document[]
end

@with_kw mutable struct Note <: Document
    key::String = rand_key()
    library::Library
    # Meta Info
    meta::Dict{String, Any} = Dict{String, Any}()
    # Links
    links::Dict{String, Any} = Dict{String, Any}()
    # Data
    data::Dict{String, Any} = Dict{String, Any}()
    version::Int = 0
    attachments::Vector{Document} = Document[]
end

function dict_to_doc(dict::Dict{String, Any})
    dict["library"] = Library(dict["library"])
    dict = Dict(Symbol(key)=>value for (key, value) in dict) # Convert in symbol convention
    if !haskey(dict[:data], "parentItem")
        return ParentDoc(;dict...)
    end
    if dict[:data]["itemType"] == "attachment"
        if dict[:data]["contentType"] == "application/pdf"
            return PDF(;dict...)
        elseif dict[:data]["contentType"] == "text/html"
            return URL(;dict...)
        else
            return Attachment(;dict...)
        end
    elseif dict[:data]["itemType"] == "note"
        return Note(;dict...)
    # elseif dict[:data]["ItemType"] == "presentation"
    #     return Presentation(;dict...)
    end
end
@with_kw mutable struct Collection <: ZoteroObject
    key::String = rand_key()
    library::Library
    # Meta Info
    numCollections::Int = 0
    numItems::Int = 0
    # Links
    self::Dict{String, Any} = Dict{String, Any}("href" => "", "type" => "application/json")
    alternate::Dict{String, Any} = Dict{String, Any}("href" => "", "type" => "text/html")
    up::Dict{String, Any} = Dict{String, Any}("href" => "", "type" => "application/json")
    # Data
    name::String = ""
    parentCollection::String = ""
    relations::Dict = Dict{String, Any}()
    version::Int = 0
    # Contained objects
    cols::Vector{Collection} = Collection[]
    docs::Vector{Document} = Document[]
end

function Collection(dict::Dict{String, Any})
    dict["data"]["parentCollection"] isa Bool ? dict["data"]["parentCollection"] = "" : nothing
    merged_dicts = merge(
        Dict("key" => dict["key"], "library" => Library(dict["library"])),
        dict["meta"],
        dict["links"],
        dict["data"],
        )
    Collection(;Dict(Symbol(key)=>value for (key, value) in merged_dicts)...)
end

Base.getindex(c::Collection, i::Int) = getindex(c.docs, i)
Base.iterate(c::Collection, state) = iterate(c.docs, state)
Base.iterate(c::Collection) = iterate(c.docs)
Base.length(c::Collection) = length(c.docs)

title(c::Collection) = c.name
title(d::ParentDoc) = d.data["title"]

AbstractTrees.children(::Document) = ()
AbstractTrees.children(c::Collection) = vcat(c.cols, c.docs)

ispdf(d::Document) = endswith(d.creatorSummary, ".pdf")

AbstractTrees.printnode(io::IO, d::ParentDoc) = print(io, doc_color, d.data["title"], reset_color)
AbstractTrees.printnode(io::IO, c::Collection) = print(io, col_color, c.name, reset_color)

function get_library(client::ZoteroClient; kwargs...)
    root = Collection(name="root", key="", library=Library())
    dicts = request_json(client, "GET", "collections"; kwargs...)
    @showprogress for dict in dicts
        col = Collection(dict)
        if col.parentCollection == ""
            update_col!(client, col)
            push!(root.cols, col)
        end
    end
    root.numCollections = length(root.cols)
    return root
end

function update_col!(client::ZoteroClient, col::Collection)
    # First add all items
    collect_items!(client, col, refresh=true)
    # Then add all subcollections
    dicts = request_json(client, "GET", joinpath("collections", col.key, "collections"))
    for dict in dicts
        new_col = Collection(dict)
        update_col!(client, new_col)
        push!(col.cols, new_col)
    end
    return col
end

function organize(docs::AbstractVector)
    topdocs = Document[]
    for doc in docs
        if doc isa ParentDoc
            organize!(doc, docs)
            push!(topdocs, doc)
        end
    end
    return topdocs
end

function organize!(topdoc::ParentDoc, docs::AbstractVector)
    for doc in docs
        if haskey(doc.data, "parentItem") && doc.data["parentItem"] == topdoc.key
            push!(topdoc.attachments, doc)
        end
    end
end

function obj_to_dict(doc::Document)
    dict = type2dict(doc)
    return Dict(string(key)=>value for (key, value) in dict)
end

function obj_to_dict(col::Collection)
    dict = type2dict(col)
    delete!(dict, :objects)
    return Dict(string(key)=>value for (key, value) in dict)
end

function find_col(f, client::ZoteroClient, col::Collection=get_library(client))
    if f(col)
        return col
    end
    for c in col.cols
        res = find_col(f, client, c)
        isnothing(res) ? nothing : return res
    end
    return nothing
end

ext: jl


text:
# Function that executes code and
# returns the result
execute_code(code) = begin
    try
        parse("begin\n" * code * "\nend") |> eval
    catch ex
        sprint() do io
            showerror(io, ex)
            println(io)
            Base.show_backtrace(io, catch_backtrace())
        end
    end
end

## View

# Code mirror to input some code
showinput(code;kwargs...) = begin
    s = sampler()

    intent(s,
        codemirror(code; kwargs...) |>
        watch!(s, :code) |>
        keypress("ctrl+enter") |>
        trigger!(s, :submit))
end

getcode(x) = x[:code]
code_io(code, code_input; kwargs...) = begin
    intent(getcode, showinput(code; kwargs...)) >>> code_input
end

# Output area
showoutput(code) = begin
    obj = try
        execute_code(code)
    catch ex
        sprint() do io
            bt = catch_backtrace()
            showerror(io, ex)
            println(io)
            Base.show_backtrace(io, bt)
        end
    end
    try
        convert(Tile, obj)
    catch codemirror(string(obj), readonly=true, linenumbers=false)
    end
end

# REPL
newrepl() = vbox(empty)
function append_execution(repl, code)
    cell_sig = Signal(code)
    println(cell_sig)
    vbox(
        vcat(
            repl.tiles.tiles,
            vbox(
                code_io(code, cell_sig, linenumbers=false),
                map(showoutput, cell_sig)
            )
        )
    )
end


text:
using Base: AbstractFloat
########################################################################################################
## Details
# This file will create basic type structures for the UQ inference problems.
#
#########################################################################################################


text:
using Literate, Documenter, Plugins

# Generating to docs/src, was unable to load pages from a different directory
Literate.markdown("docs/examples/gettingstarted.jl", "docs/src/"; documenter = true)

makedocs(;
    modules=[Plugins],
    format=Documenter.HTML(
        assets = ["assets/favicon.ico"]
    ),
    pages=[
        "index.md",
        "tutorial.md",
        "features.md",
        "guide.md",
        "repo.md",
        "reference.md",
    ],
    repo="https://github.com/tisztamo/Plugins.jl/blob/{commit}{path}#L{line}",
    sitename="Plugins.jl",
    authors="Krisztián Schaffer",
    assets=String[],
)

deploydocs(;
    repo="github.com/tisztamo/Plugins.jl",
    devbranch = "main"
)


text:
using Unitful
using DataStructures

t = range(0.0, stop=10.0, length=100)

result = OrderedDict{String,Any}()

result["time"] = t*u"s"
result["phi"]  = sin.(t)u"rad"
result["phi2"] = 0.5 * sin.(t)u"rad"
result["w"]    = cos.(t)u"rad/s"
result["w2"]   = 0.6 * cos.(t)u"rad/s"

plot(result, ["phi2", ("w",), ("phi", "phi2", "w", "w2")], heading="Vector of plots")

println("\n... result info:")
printResultInfo(result)


text:
function ElementWise(fun::MemoryWrapper)
    return LayerGenerator(
        function(pb, sett, input_shape)
                f = fun(sett)
                LayerDesign(Sig(:ElementWise), input_shape, input_shape, (0), nothing,
                    LayerInitializer(
                        function (ins, outs, cons)
                            return function ()
                                broadcast!(f, outs, ins)
                            end
                        end))
        end)
end

export ElementWiseLayer
ElementWiseLayer(fun::Function) = ElementWise(fun)


text:
# --------------------------------------------------------------------------
# ACE1.jl: Julia implementation of the Atomic Cluster Expansion
# Copyright (c) 2019 Christoph Ortner <christophortner0@gmail.com>
# Licensed under ASL - see ASL.md for terms and conditions.
# --------------------------------------------------------------------------

@testset "RPIBasis"  begin

#---

using ACE1
using Random, Printf, Test, LinearAlgebra, JuLIP, JuLIP.Testing
using JuLIP: evaluate, evaluate_d, evaluate_ed
using JuLIP.MLIPs: combine

#---

@info("Basic test of RPIBasis construction and evaluation")
maxdeg = 15
N = 3
r0 = 1.0
rcut = 3.0
trans = PolyTransform(1, r0)
Pr = transformed_jacobi(maxdeg, trans, rcut; pcut = 2)
D = SparsePSHDegree()
P1 = BasicPSH1pBasis(Pr; species = :X, D = D)

#---

pibasis = PIBasis(P1, N, D, maxdeg)
rpibasis = RPIBasis(P1, N, D, maxdeg)

#---
@info("Basis construction and evaluation checks")
@info("check single species")
Nat = 15
Rs, Zs, z0 = rand_nhd(Nat, Pr, :X)
B = evaluate(rpibasis, Rs, Zs, z0)
println(@test(length(rpibasis) == length(B)))
dB = evaluate_d(rpibasis, Rs, Zs, z0)
println(@test(size(dB) == (length(rpibasis), length(Rs))))
B_, dB_ = evaluate_ed(rpibasis, Rs, Zs, z0)
println(@test (B_ ≈ B) && (dB_ ≈ dB))

#---
@info("check multi-species")
maxdeg = 5
Pr = transformed_jacobi(maxdeg, trans, rcut; pcut = 2)
species = [:C, :O, :H]
P1 = ACE1.BasicPSH1pBasis(Pr; species = species, D = D)
basis = ACE1.RPIBasis(P1, N, D, maxdeg)
Rs, Zs, z0 = ACE1.rand_nhd(Nat, Pr, species)
B = evaluate(basis, Rs, Zs, z0)
println(@test(length(basis) == length(B)))
dB = evaluate_d(basis, Rs, Zs, z0)
println(@test(size(dB) == (length(basis), length(Rs))))
B_, dB_ = evaluate_ed(basis, Rs, Zs, z0)
println(@test (B_ ≈ B) && (dB_ ≈ dB))

#---

degrees = [ 12, 10, 8, 8, 8, 8 ]

@info("Check a few basis properties ")
# for species in (:X, :Si) # , [:C, :O, :H])
for species in (:X, :Si, [:C, :O, :H]), N = 1:length(degrees)
   local Rs, Zs, z0, B, dB, basis, D, P1, Nat
   Nat = 15
   D = SparsePSHDegree()
   P1 = ACE1.BasicPSH1pBasis(Pr; species = species)
   basis = ACE1.RPIBasis(P1, N, D, degrees[N])
   @info("species = $species; N = $N; deg = $(degrees[N]); len = $(length(basis))")
   @info("   check (de-)serialization")
   println(@test(all(JuLIP.Testing.test_fio(basis))))
   @info("   isometry and permutation invariance")
   for ntest = 1:30
      Rs, Zs, z0 = ACE1.rand_nhd(Nat, Pr, species)
      Rsp, Zsp = ACE1.rand_sym(Rs, Zs)
      print_tf(@test(evaluate(basis, Rs, Zs, z0) ≈
                     evaluate(basis, Rsp, Zsp, z0)))
   end
   println()
   @info("   check derivatives")
   for ntest = 1:30
      Rs, Zs, z0 = ACE1.rand_nhd(Nat, Pr, species)
      B = evaluate(basis, Rs, Zs, z0)
      dB = evaluate_d(basis, Rs, Zs, z0)
      Us = [ rand(eltype(Rs)) .- 0.5 for _=1:length(Rs) ]
      dB_dUs = transpose.(dB) * Us
      errs = []
      for p = 2:12
         h = 0.1^p
         B_h = evaluate(basis, Rs + h * Us, Zs, z0)
         dB_h = (B_h - B) / h
         # @show norm(dAA_h - dAA_dUs, Inf)
         push!(errs, norm(dB_h - dB_dUs, Inf))
      end
      success = (/(extrema(errs)...) < 1e-3) || (minimum(errs) < 1e-10)
      print_tf(@test success)
   end
   println()
   @info("   check combine")
   coeffs = randcoeffs(basis)
   V = combine(basis, coeffs)
   Vst = standardevaluator(V)
   for ntest = 1:30
      Rs, Zs, z0 = ACE1.rand_nhd(Nat, Pr, species)
      v = evaluate(V, Rs, Zs, z0)
      vst = evaluate(Vst, Rs, Zs, z0)
      cdotB = dot(coeffs, evaluate(basis, Rs, Zs, z0))
      print_tf(@test v ≈ cdotB ≈ vst)
   end
   println()
   @info("   check graph evaluator")
   basisst = standardevaluator(basis)
   for ntest = 1:30
      env = ACE1.rand_nhd(Nat, Pr, species)
      print_tf(@test evaluate(basisst, env...) ≈ evaluate(basis, env...))
      print_tf(@test evaluate_d(basisst, env...) ≈ evaluate_d(basis, env...))
   end
   println()
end

#---

end


text:
"""
    astar(g::AbstractGraph{U},
          src::W;
          goal::Union{W,Nothing}=nothing,
          distmx::AbstractMatrix{T}=weights(g),
          restrictions::Union{AbstractDict{V,Vector{MutableLinkedList{V}}},Nothing}=nothing,
          heuristic::Function=h(u, v) =  0.0,
          )::Vector{U} where {T <: Real,U <: Integer,V <: Integer,W <: Integer}

A* shortest path algorithm taken and adapted from `LightGraphs.jl`. This version improves runtime
speed, memory usage, has a more flexible heruistic function, and accounts for OpenStreetMap
turn restrictions.

# Arguments
- `g::AbstractGraph{U}`: LightGraphs abstract graph object.
- `src::W`: Source vertex.
- `goal::Union{W,Nothing}=nothing`: Optional target vertex as a break condition.
- `distmx::AbstractMatrix{T}=weights(g)`: Optional weight matrix.
- `restrictions::Union{AbstractDict{V,Vector{MutableLinkedList{V}}},Nothing}=nothing`: Optional
dictionary of restrictions, keyed by vertex, each restriction is a linked list of vertices,
any path containing the entire list will have an infinite cost assigned to it.
- `heuristic::Function=h(u, v) =  0.0`: Heuristic cost function, takes a source and target vertex, default is 0.

# Return
- `Vector{U}`: Array parent veritces from which the shortest path can be extracted.
"""
function astar(g::AbstractGraph{U},
               src::W;
               goal::Union{W,Nothing}=nothing,
               distmx::AbstractMatrix{T}=weights(g),
               restrictions::Union{AbstractDict{V,Vector{MutableLinkedList{V}}},Nothing}=nothing,
               heuristic::Function=h(u, v) =  0.0,
               )::Vector{U} where {T <: Real,U <: Integer,V <: Integer,W <: Integer}
    nvg = nv(g)

    # Preallocate
    visited = zeros(Bool, nvg)
    H = PriorityQueue{U,T}()
    dists = fill(typemax(T), nvg)
    parents = zeros(U, nvg)

    # Initialize src
    H[src] = zero(T)
    dists[src] = zero(T)
    parents[src] = 0

    while !isempty(H)
        u = dequeue!(H)
        d = dists[u]
        visited[u] = true

        for v in outneighbors(g, u)
            visited[v] && continue

            dist_u_v = distmx[u, v]

            if restrictions !== nothing && haskey(restrictions, u)
                for ll in restrictions[u]
                    if is_restricted(ll, u, v, parents)
                        dist_u_v = typemax(T) # Means path from u -> v is restricted, given parents
                        break
                    end
                end
            end

            alt = d + dist_u_v

            if alt < dists[v]
                H[v] = alt + heuristic(v, goal)
                dists[v] = alt
                parents[v] = u
                if goal == v && goal == peek(H).first
                    # If v is the goal (destination) and the goal is the first node in the
                    # priotity queue, we have found the optimal path so return parents
                    return parents
                end
            end
        end
    end

    return parents
end

"""
Dijkstra shortest path algorithm, same as A* but without a heuristic cost function.
"""
function dijkstra(g::AbstractGraph{U},
                  src::W;
                  goal::Union{W,Nothing}=nothing,
                  distmx::AbstractMatrix{T}=weights(g),
                  restrictions::Union{AbstractDict{V,Vector{MutableLinkedList{V}}},Nothing}=nothing
                  )::Vector{U} where {T <: Real,U <: Integer,V <: Integer,W <: Integer}
    return astar(g, src, goal=goal, distmx=distmx, restrictions=restrictions)
end

"""
    is_restricted(restriction_ll::MutableLinkedList{V}, u::U, v::U, parents::Vector{U})::Bool where {U <: Integer,V <: Integer}

Returns true if path between u and v is restricted, given parents.

# Arguments
- `restriction_ll::MutableLinkedList{V}`: Linked list holding vertices in order of v -> parents.
- `u::U`: Current vertex visiting.
- `v::U`: Current neighbour vertex.
- `parents::Vector{U}`: Vector mapping of shortest path parents.

# Return
- `Bool`: Returns true if path between u and v is restricted.
"""
function is_restricted(restriction_ll::MutableLinkedList{V}, u::U, v::U, parents::Vector{U})::Bool where {U <: Integer,V <: Integer}
    current = restriction_ll.node.next

    if v != current.data
        return false
    end

    checked = 1 # already checked v

    while checked < restriction_ll.len
        current = current.next

        if u == current.data
            u = parents[u]
        else
            return false
        end

        checked += 1
    end

    return true
end


text:
using OmegaGrav
using Dierckx
using Tables, CSV
# %% Compute the matter power spectrum using CLASS
include("compute_pk_class.jl")
# %% Compute the Compton-y-weighted halo bias, `by`, at seven redshifts
# Reference: Equation (B1) of Chiang, Makiya, Ménard & Komatsu, arXiv:2006.14650
redshift = [0, 0.3, 0.5, 0.7, 1, 1.3, 1.5]
nred = length(redshift)
byt10MF, byt08MF = zeros(nred), zeros(nred)
for ired = 1:nred
   z = redshift[ired]
   # Define functions to return linear baryon+CDM power spectrum
   # Note: The CLASS code takes wavenumbers in units of 1/Mpc (no h) and
   # return power spectra in units of Mpc^3 (no 1/h^3).
   pkcb_class(kovh) = cosmo.pk_cb_lin(kovh * h0, z) * h0^3
   # Spline interpolate in log(k)
   lnk = log(1e-4):0.05:log(100)
   pkcb = Spline1D(lnk, pkcb_class.(exp.(lnk)))
   # %% Compute the Compton-y-weighted halo bias, `by`, for two halo multiplicity functions.
   byt08MF[ired] = by(x -> pkcb(log(x)), z, Ωm, Ωcb, αp = 0.12)
   byt10MF[ired] = by(x -> pkcb(log(x)), z, Ωm, Ωcb, t10MF = true) # not specifying `αp` sets `αp = 0.12`
end

#%% Save table data to by.csv
t = Tables.table([redshift byt08MF byt10MF])
header = ["z", "by(Tinker08)", "by(Tinker10)"]
CSV.write("by.csv", t, header = header)

# %% Clean CLASS (the equivalent of the struct_free() in the `main`
# of CLASS. This step is primordial when running in a loop over different
# cosmologies, as you will saturate your memory very fast if you ommit
# it.
cosmo.struct_cleanup()
# If you want to change completely the cosmology, you should also
# clean the arguments, otherwise, if you are simply running on a loop
# of different values for the same parameters, this step is not needed
cosmo.empty()


text: abstract type MagneticEquilibrium end


text:
# used for single-line strings
type StringDiff{T1, T2} <: DeepDiff
    before::T1
    after::T2
    diff::VectorDiff
end

# used for multi-line strings
type StringLineDiff{T1, T2} <: DeepDiff
    before::T1
    after::T2
    diff::VectorDiff
end

function deepdiff(X::AbstractString, Y::AbstractString)
    if contains(X, "\n") || contains(Y, "\n")
        # we'll compare hashes of each line rather than the text itself, because
        # these comparisons are done many times
        xhashes = map(hash, split(X, '\n'))
        yhashes = map(hash, split(Y, '\n'))

        StringLineDiff(X, Y, deepdiff(xhashes, yhashes))
    else
        StringDiff(X, Y, deepdiff(collect(X), collect(Y)))
    end
end

const AllStringDiffs = Union{StringDiff, StringLineDiff}

before(diff::AllStringDiffs) = diff.before
after(diff::AllStringDiffs) = diff.after
added(diff::AllStringDiffs) = added(diff.diff)
removed(diff::AllStringDiffs) = removed(diff.diff)
changed(diff::AllStringDiffs) = []

function =={T<:AllStringDiffs}(d1::T, d2::T)
    d1.before == d2.before || return false
    d1.after == d2.after || return false
    d1.diff == d2.diff || return false

    true
end

function Base.show(io::IO, diff::StringLineDiff)
    xlines = split(diff.before, '\n')
    ylines = split(diff.after, '\n')
    println(io, "\"\"\"")
    visitall(diff.diff) do idx, state, last
        if state == :removed
            print_with_color(:red, io, "- ", escape_string(xlines[idx]))
        elseif state == :added
            print_with_color(:green, io, "+ ", escape_string(ylines[idx]))
        else
            print(io, "  ", escape_string(xlines[idx]))
        end
        if last
            print(io, "\"\"\"")
        else
            println(io)
        end
    end
end

function Base.show(io::IO, diff::StringDiff)
    xchars = before(diff.diff)
    ychars = after(diff.diff)
    laststate = :init

    print(io, "\"")
    visitall(diff.diff) do idx, state, last
        if !Base.have_color
            # check to see if we need to close a block
            if laststate == :removed && state != :removed
                print(io, "-}")
            elseif laststate == :added && state != :added
                print(io, "+}")
            end
            # check to see if we need to open a block
            if laststate != :removed && state == :removed
                print(io, "{-")
            elseif laststate != :added && state == :added
                print(io, "{+")
            end
        end
        if state == :removed
            print_with_color(:red, io, string(xchars[idx]))
        elseif state == :added
            print_with_color(:green, io, string(ychars[idx]))
        else
            print(io, xchars[idx])
        end
        laststate = state
    end
    if !Base.have_color
        if laststate == :removed
            print(io, "-}")
        elseif laststate == :added
            print(io, "+}")
        end
    end
    print(io, "\"")
end


text:
using Merly

webserverpath("/app")
webserverfiles("css")

content= File("merlyapp.html")

  @page "/" (;content=content)  begin

  return HTTP.Response(200
          , HTTP.mkheaders(["Content-Type" => "text/html"])
          , body= content)
  end

  notfound("merlynotfound.html")

start(host = "0.0.0.0", port = (∈("PORT", keys(ENV)) ? parse(Int, ENV["PORT"]) : 5000))

max_stars_repo_name: neomatrixcode/merly-app

max_issues_repo_name: neomatrixcode/merly-app

max_forks_repo_name: neomatrixcode/merly-app


text:
using ADCME
using PyPlot
using Random
using MAT
using DelimitedFiles
matplotlib.use("agg")

include("fwi_data.jl")
reset_default_graph()
tf.set_random_seed(123)
tf.random.set_random_seed(123)
θ0 = matread("results/weight$model.mat")["theta"][:]

μ = Variable(θ0)
ρ = Variable(zeros(length(θ0)))

if model=="step"
    σ = log(1 + exp(ρ))
else
    σ = 0.1*log(1 + exp(ρ))
end

# dt = 1e-10
dt = 1e-8
# dt = 0

function likelihood(z)
    θ = μ + z * σ

    R_ = constant(R)

    idx_var = pml+Int(round(0.1/Δx))

    inv_sigmoid(y) = @. - log(1.0/y - 1.0)

    # case = "fwi"
    case = "nnfwi"

    ## FC
    if case == "nnfwi"
    x0 = collect( range(0, 1, length=N-1-idx_var) )
    if model == "step"
        var= fc(x0, [30, 30, 30, 1], θ) * 0.1 + inv_sigmoid(1.3/2.0)
        var = -2.0*sigmoid(var)
    elseif model == "slop"
        var= fc(x0, [30, 30, 30, 1], θ) * 0.1
    end
    end

    cH = scatter_add(constant(cH_init), idx_var+1:N-1, var)

    cE = (cH[1:end-1]+cH[2:end])/2

    reg_TV = sum(abs(cH[2:end]-cH[1:end-1]))
    reg_smooth = sum((cH[1:end-2] + cH[3:end] - 2*cH[2:end-1])^2)

    function condition(i, E_arr, H_arr)
        i<=NT+1
    end

    function body(i, E_arr, H_arr)
        E = read(E_arr, i-1)
        H = read(H_arr, i-1)

        ΔH = cH * (E[2:end]-E[1:end-1])/Δx - σH*H
        H += ΔH * Δt
        ΔH = 1/(24Δx) * cH[2:N-2] * (-E[4:end] + 3E[3:end-1] - 3E[2:end-2] + E[1:end-3])
        H = scatter_add(H, 2:N-2, ΔH * Δt)

        ΔE = cE * (H[2:end]-H[1:end-1])/Δx - σE[2:end-1]*E[2:end-1] + R_[i] * Z
        E = scatter_add(E, 2:N-1, ΔE * Δt)
        ΔE = 1/(24Δx) * cE[2:end-1] * (-H[4:end] + 3H[3:end-1] - 3H[2:end-2] + H[1:end-3])
        E = scatter_add(E, 3:N-2, ΔE*Δt)

        i+1, write(E_arr, i, E), write(H_arr, i, H)
    end

    E_arr = TensorArray(NT+1)
    H_arr = TensorArray(NT+1)

    E_arr = write(E_arr, 1, zeros(N))
    H_arr = write(H_arr, 1, zeros(N-1))

    i = constant(2, dtype = Int32)

    _, E, H = while_loop(condition, body, [i, E_arr, H_arr])

    E = stack(E); E = set_shape(E, (NT+1, N))
    H = stack(H)

    loss = sum((E[:, idx_rcv] - E_true[:, idx_rcv])^2 * [1.0, 1e-3])

    lkh = dt*loss - sum((θ-μ)^2/σ^2) + sum((θ-θ0)^2)
    cH, lkh
end

z = tf.random_normal((8, length(θ0)), dtype = tf.float64)
# z = constant(randn(8, length(θ0)))
loss = constant(0.0)
for i = 1:size(z, 1)
    global cH, loss_ = likelihood(z[i])
    global loss += loss_
end
lr = placeholder(Float64, shape = [])
opt = AdamOptimizer(lr).minimize(loss)

sess = Session(); init(sess)

if dt>0.0
    global losses = []
    for i = 1:1000
        _, l = run(sess, [opt, loss], lr=>0.001)
        push!(losses, l)
        @info i, l
    end
    close("all")
    plot(losses)
    savefig("loss.png")
end
# error()
close("all")
cH, _ = likelihood(tf.random_normal((length(θ0),), dtype = tf.float64))
cH_est = zeros(length(cH), 100)
for i = 1:100
    cH_est[:,i] = run(sess, cH)
    plot(xH, cH_est[:,i], "--", color="gray", alpha = 0.5)
end
plot(xH, cH_true, "C0", label="True")
# plot(xH, cH0, "k--", label="Initial")
legend()

if dt≈0
    savefig("$(model)_prior.png")
else
    savefig("$model.png")
end


text:
droplets = getalldroplets!(client)
@testset "List all Droplets" begin
    @test length(droplets) == 1
    @test droplets[1].locked == false
    @test isempty(droplets[1].snapshotids)
    @test droplets[1].created_at == DateTime("2014-11-14T16:29:21")
    @test droplets[1].memory == 1024
    @test droplets[1].vcpus == 1
    @test droplets[1].name == "example.com"
    @test droplets[1].status == "active"
    @test droplets[1].networks["v4"][1].gateway == "104.236.0.1"
    @test droplets[1].networks["v4"][1].ipaddress == "104.236.32.182"
    @test droplets[1].networks["v4"][1].netmask == "255.255.192.0"
    @test droplets[1].networks["v4"][1].networktype == "public"
    @test droplets[1].networks["v6"][1].gateway == "2604:A880:0800:0010:0000:0000:0000:0001"
    @test droplets[1].networks["v6"][1].ipaddress == "2604:A880:0800:0010:0000:0000:02DD:4001"
    @test droplets[1].networks["v6"][1].netmask == 64
    @test droplets[1].networks["v6"][1].networktype == "public"
    @test droplets[1].id == 3164444
    @test droplets[1].disk == 25
    @test droplets[1].backupids == [7938002]
    @test droplets[1].sizeslug == "s-1vcpu-1gb"
    @test droplets[1].image.public == true
    @test droplets[1].image.name == "14.04 x64"
    @test droplets[1].image.created_at == DateTime("2014-10-17T20:24:33")
    @test droplets[1].image.sizegigabytes == 2.34
    @test droplets[1].image.id == 6918990
    @test droplets[1].image.distribution == "Ubuntu"
    @test droplets[1].image.slug == "ubuntu-16-04-x64"
    @test droplets[1].image.mindisksize == 20
    @test droplets[1].image.imagetype == "snapshot"
    @test droplets[1].image.regions == ["nyc1", "ams1", "sfo1", "nyc2", "ams2", "sgp1", "lon1", "nyc3", "ams3", "nyc3"]
    @test droplets[1].size == nothing
    @test droplets[1].region.name == "New York 3"
    @test droplets[1].region.features == ["virtio", "private_networking", "backups", "ipv6", "metadata"]
    @test droplets[1].region.slug == "nyc3"
    @test isempty(droplets[1].region.sizes)
    @test droplets[1].region.available == nothing
    @test isempty(droplets[1].volumeids)
    @test droplets[1].features == ["backups", "ipv6", "virtio"]
    @test droplets[1].kernel.name == "Ubuntu 14.04 x64 vmlinuz-3.13.0-37-generic"
    @test droplets[1].kernel.id == 2233
    @test droplets[1].kernel.version == "3.13.0-37-generic"
    @test isempty(droplets[1].tags)
end;

droplet = createdroplet!(client; name="example.com", region="nyc3",
                         size="s-1vcpu-1gb", image="ubuntu-16-04-x64",
                         backups=false, ipv6=true, tags=["web"])
@testset "Create a new Droplet" begin
    @test droplet.locked == true
    @test isempty(droplet.snapshotids)
    @test droplet.created_at == DateTime("2014-11-14T16:36:31")
    @test droplet.memory == 1024
    @test droplet.vcpus == 1
    @test droplet.name == "example.com"
    @test droplet.status == "new"
    @test isempty(droplet.networks)
    @test droplet.id == 3164494
    @test droplet.disk == 25
    @test isempty(droplet.backupids)
    @test droplet.sizeslug == "s-1vcpu-1gb"
    @test droplet.image == nothing
    @test droplet.size == nothing
    @test droplet.region == nothing
    @test isempty(droplet.volumeids)
    @test droplet.features == ["virtio"]
    @test droplet.kernel.name == "Ubuntu 14.04 x64 vmlinuz-3.13.0-37-generic"
    @test droplet.kernel.id == 2233
    @test droplet.kernel.version == "3.13.0-37-generic"
    @test droplet.tags == ["web"]
end;

kernels = getalldropletkernels!(client, droplet)
@testset "List all available Kernels for a Droplet" begin
    @test length(kernels) == 1
    @test kernels[1].name == "DO-recovery-static-fsck"
    @test kernels[1].id == 231
    @test kernels[1].version == "3.8.0-25-generic"
end;

snapshots = getalldropletsnapshots!(client, droplet)
@testset "List snapshots for a Droplet" begin
    @test snapshots[1].public == false
    @test snapshots[1].name == "nginx-fresh"
    @test snapshots[1].created_at == DateTime("2014-11-14T16:37:34")
    @test snapshots[1].id == 7938206
    @test snapshots[1].distribution == "Ubuntu"
    @test snapshots[1].slug == nothing
    @test snapshots[1].mindisksize == 20
    @test snapshots[1].backuptype == "snapshot"
    @test snapshots[1].regions == ["nyc3", "nyc3"]
end;

backups = getalldropletbackups!(client, droplet)
@testset "List backups for a Droplet" begin
    @test backups[1].public == false
    @test backups[1].name == "example.com 2014-11-14"
    @test backups[1].created_at == DateTime("2014-11-14T16:07:38")
    @test backups[1].id == 7622989
    @test backups[1].distribution == "Ubuntu"
    @test backups[1].slug == nothing
    @test backups[1].mindisksize == 20
    @test backups[1].backuptype == "snapshot"
    @test backups[1].regions == ["nyc3"]
end;

@testset "Delete a Droplet" begin
    @test deletedroplet!(client, droplet)
end;

neighbors = getdropletneighbors!(client, droplet)
@testset "List Neighbors for a Droplet" begin
    @test length(neighbors) == 1
    @test neighbors[1].locked == false
    @test isempty(neighbors[1].snapshotids)
    @test neighbors[1].created_at == DateTime("2014-11-14T16:29:21")
    @test neighbors[1].memory == 1024
    @test neighbors[1].vcpus == 1
    @test neighbors[1].name == "example.com"
    @test neighbors[1].status == "active"
    @test neighbors[1].id == 3164444
    @test neighbors[1].disk == 25
    @test neighbors[1].backupids == [7938002]
    @test neighbors[1].sizeslug == "s-1vcpu-1gb"
    @test isempty(neighbors[1].volumeids)
    @test neighbors[1].features == ["backups", "ipv6", "virtio"]
end;

neighbors = getalldropletneighbors!(client)
@testset "List all Droplet Neighbors" begin
    @test length(neighbors) == 1
    @test length(neighbors[1]) == 1
    @test neighbors[1][1].locked == false
    @test isempty(neighbors[1][1].snapshotids)
    @test neighbors[1][1].created_at == DateTime("2014-11-14T16:29:21")
    @test neighbors[1][1].memory == 1024
    @test neighbors[1][1].vcpus == 1
    @test neighbors[1][1].name == "example.com"
    @test neighbors[1][1].status == "active"
    @test neighbors[1][1].id == 3164444
    @test neighbors[1][1].disk == 25
    @test neighbors[1][1].backupids == [7938002]
    @test neighbors[1][1].sizeslug == "s-1vcpu-1gb"
    @test isempty(neighbors[1][1].volumeids)
    @test neighbors[1][1].features == ["backups", "ipv6", "virtio"]
end;


text: include("../../../../examples/inplace_mutation_with_referenceables.jl")


text:
# using Test
# using Radau
# using LinearAlgebra

# using Polynomials: Poly, polyval, polyder, coeffs
# using PolynomialRoots: roots
# include("test_T_lambda.jl")

include("test_exports.jl")
include("basic_test.jl")
include("test_robertson.jl")
# include("test_generate_butcher_table.jl")
include("test_time_dep.jl")

# TODO: add tests for calculating of Eigencalculatino of T and λ

# using GenericLinearAlgebra: eigvals
# using GenericSVD: svd


text:
cd(dirname(@__FILE__)) do
    download("https://api.plot.ly/v2/plot-schema?sha1", "plotschema.json")
    download("https://cdn.plot.ly/plotly-latest.min.js", "plotly-latest.min.js")
end

include("make_schema_docs.jl")
include("find_attr_groups.jl")


text:
using Printf, Statistics
using JLD2, PyPlot

Nxy, Nz = 128, 128
Ris = (0, 0.01, 0.04)

width, height = 12, 6.75

c = Dict(0 => "tab:blue", 0.01 => "tab:orange", 0.04 => "tab:green")

#####
##### Load data from JLD2
#####

statistic_files = Dict()
statistic_iters = Dict()

profile_files = Dict()
profile_iters = Dict()

field_files = Dict()
field_iters = Dict()

for Ri in Ris
    base_dir = @sprintf("stratified_couette_flow_data_Nxy%d_Nz%d_Ri%.2f", Nxy, Nz, Ri)
    prefix = @sprintf("stratified_couette_flow_Nxy%d_Nz%d_Ri%.2f", Nxy, Nz, Ri)

    statistic_filepath = joinpath(base_dir, prefix * "_statistics.jld2")
    profile_filepath = joinpath(base_dir, prefix * "_profiles.jld2")
    field_filepath = joinpath(base_dir, prefix * "_fields.jld2")

    statistic_files[Ri] = jldopen(statistic_filepath, "r")
    statistic_iters[Ri] = keys(statistic_files[Ri]["timeseries/t"])

    profile_files[Ri] = jldopen(profile_filepath, "r")
    profile_iters[Ri] = keys(profile_files[Ri]["timeseries/t"])

    field_files[Ri] = jldopen(field_filepath, "r")
    field_iters[Ri] = keys(field_files[Ri]["timeseries/t"])
end

#####
##### Plot Re and Nu timeseries
#####

fig, (ax1, ax2) = subplots(nrows=2, ncols=1, figsize=(width, height))

Reτ = Dict()
Nu  = Dict()

for Ri in Ris
    i = statistic_iters[Ri][end]

    t   = [statistic_files[Ri]["timeseries/t/"      * i]    for i in statistic_iters[Ri]]
    ReT = [statistic_files[Ri]["timeseries/Re_tau/" * i][1] for i in statistic_iters[Ri]]
    ReB = [statistic_files[Ri]["timeseries/Re_tau/" * i][2] for i in statistic_iters[Ri]]
    NuT = [statistic_files[Ri]["timeseries/Nu/"     * i][1] for i in statistic_iters[Ri]]
    NuB = [statistic_files[Ri]["timeseries/Nu/"     * i][2] for i in statistic_iters[Ri]]

    ax1.plot(t, ReT, color=c[Ri], linestyle="-",  label="Ri = $Ri (top wall)")
    ax1.plot(t, ReB, color=c[Ri], linestyle="--", label="Ri = $Ri (bottom wall)")

    N½ = length(t) ÷ 2
    Reτ_t = mean(ReT[N½:end])
    Reτ_b = mean(ReB[N½:end])
    Reτ[Ri] = (Reτ_t + Reτ_b) / 2

    ax1.axhline(y=Reτ[Ri], xmin=0.5, xmax=1, color=c[Ri], linestyle=":")
    ax1.text(x=1005, y=Reτ[Ri], s=@sprintf("%.0f", Reτ[Ri]), color=c[Ri])

    if Ri != 0
        ax2.plot(t, NuT, color=c[Ri], linestyle="-",  label="Ri = $Ri (top wall)")
        ax2.plot(t, NuB, color=c[Ri], linestyle="--", label="Ri = $Ri (bottom wall)")

        Nu_t = mean(NuT[N½:end])
        Nu_b = mean(NuB[N½:end])
        Nu[Ri] = (Nu_t + Nu_b) / 2

        ax2.axhline(y=Nu[Ri], xmin=0.5, xmax=1, color=c[Ri], linestyle=":")
        ax2.text(x=1005, y=Nu[Ri], s=@sprintf("%.1f", Nu[Ri]), color=c[Ri])
    end
end

ax1.set_xlabel(L"tU_w/h")
ax1.set_ylabel(L"Re$_\tau$")
ax1.set_xlim([0, 1000])
ax1.legend(ncol=3, frameon=false)

ax2.set_xlabel(L"tU_w/h")
ax2.set_ylabel("Nu")
ax2.set_xlim([0, 1000])
ax2.legend(ncol=2, frameon=false)

png_filepath = "stratified_couette_flow_Re_Nu_timeseries.png"
@info "Saving $png_filepath..."
savefig(png_filepath, dpi=200)
close(fig)

#####
##### Re and Nu scatter plot
#####

# Re and Nu values reported in runs 3-5 of Table 1, Vreugdenhil & Taylor (2018).
Reτ_VT18 = Dict(0 => 223,  0.01 => 212, 0.04 => 183)
Nu_VT18  = Dict(0 => 10.6, 0.01 => 9.6, 0.04 => 7.1)

# Re and Nu values reported in runs 1-3 of Table 1, Zhou, Taylor, and Caulfield (2017).
Reτ_ZTC17 = Dict(0 => 233, 0.01 => 215, 0.04 => 181)
Nu_ZTC17  = Dict(0 => 10.6, 0.01 => 9.26, 0.04 => 6.40)

fig, (ax1, ax2) = subplots(nrows=1, ncols=2, figsize=(width, height))

ax1.scatter(Ris, [Reτ_ZTC17[Ri] for Ri in Ris], color="tab:orange", marker="s", label="DNS 256x129x256 (ZTC 2017)")
ax1.scatter(Ris, [Reτ_VT18[Ri]  for Ri in Ris], color="tab:green",  marker="x", label="LES 64x49x64 (V&T 2018)")
ax1.scatter(Ris, [Reτ[Ri]       for Ri in Ris], color="tab:blue",   marker="o", label=L"$128^3$ Oceananigans")

ax2.scatter(Ris, [Nu_ZTC17[Ri] for Ri in Ris], color="tab:orange", marker="s", label="DNS 256x129x256 (ZTC 2017)")
ax2.scatter(Ris, [Nu_VT18[Ri]  for Ri in Ris], color="tab:green",  marker="x", label="LES 64x49x64 (V&T 2018)")
ax2.scatter((0.01, 0.04), [Nu[Ri] for Ri in (0.01, 0.04)], color="tab:blue",   marker="o", label=L"$128^3$ Oceananigans")

ax1.set_xlabel("Ri")
ax1.set_ylabel(L"Re$_\tau$")
ax1.legend(frameon=false)

ax2.set_xlabel("Ri")
ax2.set_ylabel("Nu")
ax2.legend(frameon=false)

png_filepath = "stratified_couette_flow_Re_Nu_scatter.png"
@info "Saving $png_filepath..."
savefig(png_filepath, dpi=200)
close(fig)

#####
##### Plot velocity and temperature profiles
#####

fig, (ax1, ax2) = subplots(nrows=1, ncols=2, figsize=(0.8width, 0.7height))

for Ri in Ris
    i = profile_iters[Ri][end]

    Uw = profile_files[Ri]["parameters/wall_velocity"]
    Θw = profile_files[Ri]["parameters/wall_temperature"]

    Nz = profile_files[Ri]["grid/Nz"]
    Hz = profile_files[Ri]["grid/Hz"]
    zC = profile_files[Ri]["grid/zC"]

    U = profile_files[Ri]["timeseries/u/" * i][1+Hz:Nz+Hz]
    Θ = profile_files[Ri]["timeseries/T/" * i][1+Hz:Nz+Hz]

               ax1.plot(U/Uw, zC .+ 1, color=c[Ri], label="Ri = $Ri")
    Ri != 0 && ax2.plot(Θ/Θw, zC .+ 1, color=c[Ri], label="Ri = $Ri")
end

ax1.set_xlabel(L"$U/U_w$")
ax1.set_ylabel(L"z/h")
ax1.set_xlim([-1, 1])
ax1.set_ylim([-1, 1])
ax1.legend(frameon=false)

ax2.set_xlabel(L"$Θ/Θ_w$")
ax2.set_xlim([-1, 1])
ax2.set_ylim([-1, 1])
ax2.legend(frameon=false)

png_filepath = "stratified_couette_flow_velocity_temperature_profiles.png"
@info "Saving $png_filepath..."
savefig(png_filepath, dpi=200)
close(fig)

#####
##### Plot LES model viscosity and diffusivity profiles
#####

fig, (ax1, ax2, ax3) = subplots(nrows=1, ncols=3, figsize=(width, height))

for Ri in Ris
    i = field_iters[Ri][end]

    ν = field_files[Ri]["parameters/viscosity"]
    κ = field_files[Ri]["parameters/diffusivity"]

    Nx, Ny, Nz = field_files[Ri]["grid/Nx"], field_files[Ri]["grid/Ny"], field_files[Ri]["grid/Nz"]
    Hx, Hy, Hz = field_files[Ri]["grid/Hx"], field_files[Ri]["grid/Hy"], field_files[Ri]["grid/Hz"]
    zC = field_files[Ri]["grid/zC"]

    νSGS = field_files[Ri]["timeseries/nu/"     * i][1+Hx:Nx+Hx, 1+Hy:Ny+Hy, 1+Hz:Nz+Hz]
    κSGS = field_files[Ri]["timeseries/kappaT/" * i][1+Hx:Nx+Hx, 1+Hy:Ny+Hy, 1+Hz:Nz+Hz]

    PrSGS = νSGS ./ κSGS

    νSGSp  = mean(νSGS,  dims=[1, 2])[:]
    κSGSp  = mean(κSGS,  dims=[1, 2])[:]
    PrSGSp = mean(PrSGS, dims=[1, 2])[:]

    ax1.plot((νSGSp .- ν) ./ ν, zC .+ 1, color=c[Ri], label="Ri = $Ri")
    ax2.plot((κSGSp .- κ) ./ κ, zC .+ 1, color=c[Ri], label="Ri = $Ri")
    ax3.plot(PrSGSp,            zC .+ 1, color=c[Ri], label="Ri = $Ri")
end

ax1.set_xlabel(L"$\nu_{SGS} / \nu_0$")
ax1.set_ylabel(L"$z/h$")
ax1.set_ylim([-1, 1])
ax1.legend(frameon=false)

ax2.set_xlabel(L"$\kappa_{SGS} / \kappa_0$")
ax2.set_ylim([-1, 1])
ax2.legend(frameon=false)

ax3.set_xlabel(L"Pr$_{SGS}$")
ax3.set_ylim([-1, 1])
ax3.legend(frameon=false)

png_filepath = "stratified_couette_flow_LES_profiles.png"
@info "Saving $png_filepath..."
savefig(png_filepath, dpi=200)
close(fig)

#####
##### Plot horizontal slices of velocity and temperature.
#####

fig, axes = subplots(nrows=3, ncols=2, figsize=(width, 1.5*height))

for (idx, Ri) in enumerate(Ris)
    i = field_iters[Ri][end]

    Uw = field_files[Ri]["parameters/wall_velocity"]
    Θw = field_files[Ri]["parameters/wall_temperature"]

    Nx, Ny, Nz = field_files[Ri]["grid/Nx"], field_files[Ri]["grid/Ny"], field_files[Ri]["grid/Nz"]
    Hx, Hy, Hz = field_files[Ri]["grid/Hx"], field_files[Ri]["grid/Hy"], field_files[Ri]["grid/Hz"]

    xC, xF, yC, zC = field_files[Ri]["grid/xC"], field_files[Ri]["grid/xF"], field_files[Ri]["grid/yC"], field_files[Ri]["grid/zC"]

    u = field_files[Ri]["timeseries/u/" * i][1+Hx:Nx+Hx, 1+Hy:Ny+Hy, 1+Hz:Nz+Hz]
    θ = field_files[Ri]["timeseries/T/" * i][1+Hx:Nx+Hx, 1+Hy:Ny+Hy, 1+Hz:Nz+Hz]

    k = 5
    z_k = zC[k]

    im1 = axes[idx, 1].pcolormesh(xF ./ π, yC ./ π, u[:, :, k]' ./ Uw, cmap="viridis")
    im2 = axes[idx, 2].pcolormesh(xC ./ π, yC ./ π, θ[:, :, k]' ./ Θw, cmap="inferno")

    fig.colorbar(im1, ax=axes[idx, 1])
    fig.colorbar(im2, ax=axes[idx, 2])

    axes[idx, 1].set_xlim([0, 2])
    axes[idx, 2].set_xlim([0, 2])
    axes[idx, 1].set_xlim([0, 4])
    axes[idx, 2].set_xlim([0, 4])

    axes[1, 1].set_title(L"$u(x,y)/U_w$ @ z = " * @sprintf("%.2f", z_k + 1))
    axes[1, 2].set_title(L"$\theta(x,y)/\Theta_w$ @ z = " * @sprintf("%.2f", z_k + 1))
end

axes[1, 1].set_ylabel(L"$y/\pi$")
axes[2, 1].set_ylabel(L"$y/\pi$")
axes[3, 1].set_ylabel(L"$y/\pi$")
axes[3, 1].set_xlabel(L"$x/\pi$")
axes[3, 2].set_xlabel(L"$x/\pi$")

png_filepath = "stratified_couette_flow_velocity_temperature_slices.png"
@info "Saving $png_filepath..."
savefig(png_filepath, dpi=200)
close(fig)


text:
module MyterialColors

include("types.jl")
include("codes.jl")
include("utils.jl")
include("palette.jl")

export RGB,Hex
export rgb2hex,hex2rgb
export Palette

export red_lighter,
    red_light,
    red,
    red_dark,
    red_darker,
    pink_lighter,
    pink_light,
    pink,
    pink_dark,
    pink_darker,
    purple_lighter,
    purple_light,
    purple,
    purple_dark,
    purple_darker,
    deep_purple_lighter,
    deep_purple_light,
    deep_purple,
    deep_purple_dark,
    deep_purple_darker,
    indigo_lighter,
    indigo_light,
    indigo,
    indigo_dark,
    indigo_darker,
    blue_lighter,
    blue_light,
    blue,
    blue_dark,
    blue_darker,
    light_blue_lighter,
    light_blue_light,
    light_blue,
    light_blue_dark,
    light_blue_darker,
    cyan_lighter,
    cyan_light,
    cyan,
    cyan_dark,
    cyan_darker,
    teal_lighter,
    teal_light,
    teal,
    teal_dark,
    teal_darker,
    green_lighter,
    green_light,
    green,
    green_dark,
    green_darker,
    light_green_lighter,
    light_green_light,
    light_green,
    light_green_dark,
    light_green_darker,
    lime_lighter,
    lime_light,
    lime,
    lime_dark,
    lime_darker,
    yellow_lighter,
    yellow_light,
    yellow,
    yellow_dark,
    yellow_darker,
    amber_lighter,
    amber_light,
    amber,
    amber_dark,
    amber_darker,
    orange_lighter,
    orange_light,
    orange,
    orange_dark,
    orange_darker,
    salmon_lighter,
    salmon_light,
    salmon,
    salmon_dark,
    salmon_darker,
    brown_lighter,
    brown_light,
    brown,
    brown_dark,
    brown_darker,
    grey_lighter,
    grey_light,
    grey,
    grey_dark,
    grey_darker,
    blue_grey_lighter,
    blue_grey_light,
    blue_grey,
    blue_grey_dark,
    blue_grey_darker,
    black,
    white

end


text:
using BottleNeckFlow
using CairoMakie
using GLMakie
using Test
using Logging

bn = BottleNeckFlow

ENV["JULIA_DEBUG"] = BottleNeckFlow

H = 1.
canal = CanalConfig(H)
params = SimParam()
params.cfl = 0.05
params.tol = 1e-3
params.dtdτ = 0.1
params.ramp = 400
gridbox = GridBox(canal, .05)
Re = 250
ubar = 3.
nu = ubar * canal.L / Re
rho = 1000
mu = nu * rho

canobs = Observable(CanalConfig(2.))
x = range(0, canal.L, length = 101)
xobs = range(0, L, length = 50) |> Observable
y = δ(x, canal)

u = UGrid(gridbox)
xu = getxs(u) |> collect
yu = getys(u) |> collect

v = VGrid(gridbox)
getxs(v) |> collect
getys(v) |> collect

p = PGrid(gridbox)
getxs(p) |> collect
getys(p) |> collect

poiseuille!(u, canal, params)

vort = VortGrid(u.grid)
vort = VortGrid(u, v)

io = open("log.log", "w")

sim = Simulation(params, gridbox, canal, SimpleLogger(io))
sim = Simulation(params, gridbox, canal)

fillghost!(sim.u, noslip, noslip)
fillghost!(sim.v, noslip, naturalbound)
bn.rightboundary!(sim)
step_euler!(sim)
step_poisson!(sim, 1e-5)
poisson_project!(sim)

poiseuille!(sim)
bn.boundaries!(sim)
bn.make_step!(sim)

@async runsim!(sim, 20)

xu = getxs(sim.u) |> collect
yu = getys(sim.u) |> collect
pos = [(x, y) for x in xu for y in yu]

step_euler!(sim); simobs[] = sim

gauss_seidel!(sim)
residual(sim)

simobs = Observable(Simulation(params, gridbox, canal))
uobs = Observable(simobs[].u)

Makie.Observables.onany(simobs) do sim
    uobs[] = sim.u
end
# poiseuille!(simobs[])
# step_euler!(simobs[])

divergence = DivGrid(sim.u, sim.v)

# f = Figure()
# ax = f[1, 1] = Axis(f, aspect = DataAspect())
# ax2 = f[2, 1] = Axis(f, aspect = DataAspect())

# xlims!(ax, (0, gridbox.L))
# ylims!(ax, (-0.5*gridbox.D, 0.5*gridbox.D))
# xlims!(ax2, (0, gridbox.L))
# ylims!(ax2, (-0.5*gridbox.D, 0.5*gridbox.D))

for ax in axes
    canalplot!(ax, x, canal)
end
canalplot!(ax, xobs, canobs)
canalplot(x, canal)

canobs[] = CanalConfig(1.5)
xobs[] = range(0, L-2, length= 20)

contourf!(ax, xu, yu, u, colormap = :jet)
contourf(xu, yu, u, colormap = :jet)
cont = contourf!(ax, getxs(vort), getys(vort), vort .|> abs, colormap = :jet)
cont = contourf(getxs(vort), getys(vort), vort .|> abs, colormap = :jet)
Colorbar(f[1, 2], cont)

plotgrid!(ax, sim.v, marker = :rect)
plotgrid!(ax, sim.u, marker = :circle, color = :green)

function newfig(gridbox)
    f = Figure()
    axes = [Axis(f[i, 1], aspect = DataAspect()) for i in 1:3]
    for ax in axes
        xlims!(ax, (0, gridbox.L))
        ylims!(ax, (-0.5*gridbox.D, 0.5*gridbox.D))
    end
    # ax1 = f[1, 1] = Axis(f, aspect = DataAspect(), xlims = (0, gridbox.L), ylims = (-0.5*gridbox.D, 0.5*gridbox.D))
    # ax2 = f[2, 1] = Axis(f, aspect = DataAspect(), xlims = (0, gridbox.L), ylims = (-0.5*gridbox.D, 0.5*gridbox.D))
    f, axes
end

function plotsim(axes, sim::Simulation)
    ax1, ax2, ax3 = axes
    griddedplot!(ax1, sim.u)
    griddedplot!(ax2, sim.v)
    griddedplot!(ax3, sim.p)
end
function plotsim(axes, g1::AbstractGrid, g2::AbstractGrid, g3::AbstractGrid)
    ax1, ax2, ax3 = axes
    griddedplot!(ax1, g1)
    griddedplot!(ax2, g2)
    griddedplot!(ax3, g3)
end
function plotsim(fig::Figure, g1::AbstractGrid, g2::AbstractGrid, g3::AbstractGrid)
    ax1, ax2, ax3 = axes
    griddedplot!(ax1, g1)
    griddedplot!(ax2, g2)
    griddedplot!(ax3, g3)
end
function plotsim(sim::Simulation)
    f, axes = newfig(gridbox);
    plotsim(axes, sim)
    [Colorbar(f[i, 2], axes[i].scene.plots[2].plots[1]) for i in 1:length(axes)]
    f
end

f, axes = newfig(gridbox);
ax1, ax2, ax3 = axes
f = plotsim(sim)
plotsim(axes, sim)
plotsim(axes, sim.u, divergence, sim.p)

simcopy = copy(sim)
gp = griddedplot!(axes[1], sim.u)
pl = gp.plots[1]
gp = griddedplot!(ax1, uobs)
gp = griddedplot!(ax2, sim.v)
gp = griddedplot!(ax2, sim.p)
gp = griddedplot!(ax3, div)
contourf!(getxs(div), getys(div), div.mesh)
@testset "BottleNeckFlow.jl" begin
    # Write your tests here.
end


text:
push!(LOAD_PATH, joinpath(@__DIR__, ".."))

using BenchmarkTools
using CUDA
using Oceananigans
using Oceananigans.Models: ShallowWaterModel
using Benchmarks
using Plots
pyplot()

# Benchmark function

function benchmark_shallow_water_model(Arch, FT, N)
    grid = RegularRectilinearGrid(FT, size=(N, N), extent=(1, 1), topology=(Periodic, Periodic, Flat), halo=(3, 3))
    model = ShallowWaterModel(architecture=Arch(), grid=grid, gravitational_acceleration=1.0)
    set!(model, h=1)

    time_step!(model, 1) # warmup

    trial = @benchmark begin
        CUDA.@sync blocking=true time_step!($model, 1)
    end samples=10

    return trial
end

# Benchmark parameters
#
#
Architectures = has_cuda() ? [CPU, GPU] : [CPU]
Float_types = [Float64]
Ns = [32, 64, 128, 256, 512, 1024, 2048, 4096]
# Run and summarize benchmarks

print_system_info()
suite = run_benchmarks(benchmark_shallow_water_model; Architectures, Float_types, Ns)

plot_num = length(Ns)
cpu_times = zeros(Float64, plot_num)
gpu_times = zeros(Float64, plot_num)
plot_keys = collect(keys(suite))
sort!(plot_keys, by = v -> [Symbol(v[1]), v[3]])

for i in 1:plot_num
    cpu_times[i] = mean(suite[plot_keys[i]].times) / 1.0e6
    gpu_times[i] = mean(suite[plot_keys[i+plot_num]].times) / 1.0e6
end

plt = plot(Ns, cpu_times, lw=4, label="cpu", xaxis=:log2, yaxis=:log, legend=:topleft,
          xlabel="Nx", ylabel="Times (ms)", title="Shallow Water Benchmarks: CPU vs GPU")
plot!(plt, Ns, gpu_times, lw=4, label="gpu")
display(plt)
savefig(plt, "shallow_water_times.png")

plt2 = plot(Ns, cpu_times./gpu_times, lw=4, xaxis=:log2, legend=:none,
            xlabel="Nx", ylabel="Speedup Ratio", title="Shallow Water Benchmarks: CPU/GPU")
display(plt2)
savefig(plt2, "shallow_water_speedup.png")

df = benchmarks_dataframe(suite)
sort!(df, [:Architectures, :Float_types, :Ns], by=(string, string, identity))
benchmarks_pretty_table(df, title="Shallow water model benchmarks")

if GPU in Architectures
    df_Δ = gpu_speedups_suite(suite) |> speedups_dataframe
    sort!(df_Δ, [:Float_types, :Ns], by=(string, identity))
    benchmarks_pretty_table(df_Δ, title="Shallow water model CPU to GPU speedup")
end


text:
# Like set_u0, but more limited and usable for DiscreteProblem
with_u0(prob::ODEProblem, u0) = ODEProblem(prob.f, u0, prob.tspan, prob.p)
with_u0(prob::DiscreteProblem, u0) =
    DiscreteProblem(prob.f, u0, prob.tspan, prob.p)

function augmented_vector(x0::AbstractVector, Q0::AbstractArray)
    u0 = similar(x0, (size(x0, 1), size(Q0, 2) + 1))
    u0[:, 1] = x0
    u0[:, 2:end] = Q0
    return u0
end

augmented_vector(x0::AbstractVector, Q0::UniformScaling) =
    augmented_vector(x0, Q0 * eye(size(x0, 1)))

@doc doc"""
    tangent_propagate(stage::Stageable
                      [, tangent_state];
                      phase_state,
                      <keyword arguments>)

Propagate `tangent_state` according to tangent dynamics defined by
`stage`.  Supplying `I` to `tangent_state` is a quick (but
not efficient) way to obtain the cocycle ``M_{k,n}`` (tangent space
"evolution operator") which is:

* for the maps: ``M_{k,n} = \prod_{i=0}^{i<k} df/dx(n+i)``.
* for the flows: the solution ``M_{k,n} = M(t_{k+n})`` of the linear
  ODE ``dM/dt = (df/dx(t)) M`` with the initial condition
  ``M(t_{n}) = I``.
"""
tangent_propagate(stage::Union{DEProblem, PhaseRelaxer, AbstractRenormalizer},
                  args...; kwargs...) =
    tangent_propagate(Val{:tangent}, stage, args...; kwargs...)

tangent_propagate(::Type{Val{:tangent}}, args...; kwargs...) =
    @view tangent_propagate(Val{:full}, args...; kwargs...)[:, 2:end]

tangent_propagate(::Type{Val{:full}}, args...; kwargs...) =
    current_state(tangent_propagate(Val{:integrator}, args...; kwargs...))

function tangent_propagate(::Type{Val{:integrator}},
                           tangent_prob::DEProblem,
                           dt::Real,
                           tangent_state = (@view tangent_prob.u0[:, 2:end]);
                           phase_state = (@view tangent_prob.u0[:, 1]),
                           kwargs...)
    u0 = augmented_vector(phase_state, tangent_state)
    new_prob = with_u0(tangent_prob, u0)
    integrator = get_integrator(new_prob; kwargs...)
    step!(integrator, dt, true)
    return integrator
end

tangent_propagate(::Type{Val{:integrator}},
                  stage::PhaseRelaxer,
                  dt::Real,
                  tangent_state = stage.tangent_state;
                  phase_state = stage.phase_state,
                  kwargs...) =
    tangent_propagate(Val{:integrator}, de_prob(stage), dt, tangent_state;
                      phase_state = phase_state,
                      kwargs...)

tangent_propagate(::Type{Val{:integrator}},
                  stage::AbstractRenormalizer,
                  tangent_state = stage.tangent_state;
                  phase_state = stage.phase_state,
                  dt = stage.t_renorm,
                  kwargs...) =
    tangent_propagate(Val{:integrator}, de_prob(stage), dt, tangent_state;
                      phase_state = phase_state,
                      kwargs...)


text:
"Hard code study programs"
programs() = [:MIE,:Law,:Sociology]

"Hard Code course IDs"
function courses()
    course = ["$j$i" for j in ["FC","TC1","TC2","OP1"], i in 1:3]
    course = vcat(course[:], ["OP2$i" for i in 1:2]...)
    DataFrame(course_id = 1:length(course), course_name = course)
end

"return a dict where all slots in a week are the keys, and the the values indicate which day of the week. Notice that now courses are on day 3, i.e. wednesday."
function slots_week(n=20,k=4)
    d = OrderedDict{Int,String}()
    f = round(Int,n/k)
    for ik in 1:k
        for i in 1:f
            d[i + (ik-1)*f] = ik > 2 ? Dates.dayname(ik + 1) : Dates.dayname(ik)
        end
    end
    d
end

function make_timesheet()
    dc = hcat(courses(), DataFrame(slot = [1,1,6,2,7,7,8,9,10,11,5,12,16,14]))
    d = hcat(DataFrame(program = repeat(programs(),inner=size(courses(),1) )), vcat(dc,dc,dc))
    sl = slots_week()
    dslots = DataFrame(slot = collect(keys(sl)), day = collect(values(sl)))
    join(d,dslots, on = :slot)
end

"""
    students_programs(;N=120)

Create a random dataset of student-program affilitions
"""
function students_programs(;N=120)
    w = rand(length(programs()))
    wgts = OrderedDict(zip(programs(),w ./ sum(w)))
    labs = collect(keys(wgts))
    g = Categorical(collect(values(wgts)))
    pr = labs[ rand(g, N) ]
    yr = rand(1:2,N)
    DataFrame(id = 1:N,program = pr, year = yr)

end

function make_students(n_courses;n=120)

    # list of students
    s_prog = students_programs(N = n)

    # time slots of courses
    timesheet = make_timesheet()

    # conflicts of course times
    timesheet_c = timesheet_conflicts(timesheet)

    # output a dict
    s = Dict(i =>
                Student(n_courses,
                    preferences = spzeros(n_courses,n_courses),
                    program = s_prog[i,:program],
                    year = s_prog[i,:year],
                    constraints = Constraints(n_courses, conflicts = sparse(timesheet_c[s_prog[i,:program]])),
                    req_FC = 1,
                    req_TC = 2,
                    req_EL = 1,
                    budget = rand()*100
                    )
            for i in 1:n)
    s
end

"takes a timesheet and produces a sparse matrix with scheduling conflicts for each study program"
function timesheet_conflicts(ts::DataFrame)
    o = Dict()
    for p in programs()
        td = @where(ts, :program .== p)
        sl = td[:slot]
        tmp = zeros(Int,size(td,1),size(td,1))
        for i in 1:size(tmp,1)
            for j in 1:size(tmp,2)
                tmp[i,j] = sl[i] == sl[j] ? 1 : 0
            end
        end
        o[p] = tmp
    end
    return o
end

"""
# constraints

`C` is the number of courses.

* `clash`: a sparse matrix C x C that flags with 1 if the ith element HAS a clash with jth element. 0 otherwise.
* `mandatory`: a vector C x 1. the i-th element is 1 if the course must be taken, 0 otherwise
* `notTCprogram`: a vector C x 1. the i-th element is 1 if the course is NOT in the TC of the students's program
* `notTCsemester`: a vector C x 1. the i-th element is 1 if the course is NOT in the TC of the students's semester
* `isTC`: a vector C x 1. the i-th element is 1 if the course IS in the TC of the students
* `notFCprogram`: a vector C x 1. the i-th element is 1 if the course is NOT in the FC of the students's program
* `notFCsemester`: a vector C x 1. the i-th element is 1 if the course is NOT in the FC of the students's semester
* `isFC`: a vector C x 1. the i-th element is 1 if the course IS in the FC of the students
* `notELprogram`: a vector C x 1. the i-th element is 1 if the course is NOT in the EL (electives) of the students's program
* `notELsemester`: a vector C x 1. the i-th element is 1 if the course is NOT in the EL of the students's semester
* `isEL`: a vector C x 1. the i-th element is 1 if the course IS in the EL of the students

"""
mutable struct Constraints
    clash         :: SparseMatrixCSC{Int,Int}
    mandatory     :: Vector{Bool}
    notTCprogram  :: Vector{Bool}
    notTCsemester :: Vector{Bool}
    isTC          :: Vector{Bool}
    notFCprogram  :: Vector{Bool}
    notFCsemester :: Vector{Bool}
    isFC          :: Vector{Bool}
    notELprogram  :: Vector{Bool}
    notELsemester :: Vector{Bool}
    isEL          :: Vector{Bool}
    function Constraints(n;conflicts = spzeros(n,n))
        this = new()
        this.clash = conflicts
        this.mandatory     = falses(n)
        this.notTCprogram  = falses(n)
        this.notTCsemester = falses(n)
        this.isTC          = falses(n)
        this.notFCprogram  = falses(n)
        this.notFCsemester = falses(n)
        this.isFC          = falses(n)
        this.notELprogram  = falses(n)
        this.notELsemester = falses(n)
        this.isEL          = falses(n)
        return this
    end
end

"random constraints"
function rand_constraints(N)
    if N<2
        error("N must be > 2")
    end
    clash = sparse(collect(1:2),collect(1:2),ones(Int,2),N,N)
    mandatory = rand([true false],N)
    notTCprogram = rand([true false false false false false],N)
    notTCsemester = rand([true false false false false false],N)
    isTC = rand([true false],N)
    notFCprogram = rand([true false false false false false],N)
    notFCsemester = rand([true false false false false false],N)
    isFC = rand([true false],N)
    notELprogram = rand([true false false false false false],N)
    notELsemester = rand([true false false false false false],N)
    isEL = rand([true false],N)
    return Constraints(clash,mandatory,notTCprogram,notTCsemester,isTC,notFCprogram,notFCsemester,isFC,notELprogram,notELsemester,isEL)
end

"""
# Student

## Fields

- `price` : a column vector of dimension C x 1, which i-th element is the price that was assigned to class i.
- `preferences` : an array containing S elements, and such that its n-th element is the (sparse) matrix representing student n preferences. Each of the matrices contained in that array should be a squared matrix of dimension C.
- `budget` : a column vector of dimension N x 1, which n-th element is the budget that was allocated to the n-th student.
- `constraints`: an instance of a [`Constraints`](@ref)
- `req_TC` : number of TC courses that the student is required to take
- `req_FC` : number of FC courses that the student is required to take
- `req_EL` : number of EL courses that the student is required to take
"""
mutable struct Student
    preferences :: SparseMatrixCSC{Int64, Int64}
    program :: Symbol
    year :: Int64
    constraints :: Constraints
    req_FC :: Int64
    req_TC :: Int64
    req_EL :: Int64
    budget :: Float64
    allocation :: Array{Int64,1}

    function Student(N = 10;preferences = spzeros(N,N),
                     program = :MIE,
                     year::Int = 1,
                     constraints = Constraints(N),
                     req_FC::Int = 1,
                     req_TC::Int = 2,
                     req_EL::Int = 1,
                     budget = rand()*100)
        new(
            preferences,
            program,
            year,
            constraints,
            req_FC,
            req_TC,
            req_EL,
            budget,
            zeros(N))
    end
end


text:
using ConstrainedParameter
using Test

@testset "ConstrainedParameter.jl" begin
    include("parameters.jl")
end


text:
"Implicit Euler"
function getTableauImplicitEuler()
    a = ones(Float64, 1, 1)
    b = [1.0]
    c = [1.0]
    o = 1

    TableauFIRK(:implicit_euler, o, a, b, c)
end

"Implicit Midpoint"
function getTableauImplicitMidpoint()
    a = 0.5*ones(Float64, 1, 1)
    b = [1.0]
    c = [0.5]
    o = 2

    TableauFIRK(:implicit_midpoint, o, a, b, c)
end

function getTableauGLRK(s::Int)
    TableauFIRK(getCoefficientsGLRK(s))
end

"Gauss-Lobatto-IIIA Runge-Kutta, s=2"
function getTableauLobIIIA2()
    TableauFIRK(getCoefficientsLobIIIA2())
end

"Gauss-Lobatto-IIIA Runge-Kutta, s=3"
function getTableauLobIIIA3()
    TableauFIRK(getCoefficientsLobIIIA3())
end

"Gauss-Lobatto-IIIA Runge-Kutta, s=4"
function getTableauLobIIIA4()
    TableauFIRK(getCoefficientsLobIIIA4())
end

"Gauss-Lobatto-IIIB Runge-Kutta, s=2"
function getTableauLobIIIB2()
    TableauFIRK(getCoefficientsLobIIIB2())
end

"Gauss-Lobatto-IIIB Runge-Kutta, s=3"
function getTableauLobIIIB3()
    TableauFIRK(getCoefficientsLobIIIB3())
end

"Gauss-Lobatto-IIIB Runge-Kutta, s=4"
function getTableauLobIIIB4()
    TableauFIRK(getCoefficientsLobIIIB4())
end

"Gauss-Lobatto-IIIC Runge-Kutta, s=2"
function getTableauLobIIIC2()
    TableauFIRK(getCoefficientsLobIIIC2())
end

"Gauss-Lobatto-IIIC Runge-Kutta, s=3"
function getTableauLobIIIC3()
    TableauFIRK(getCoefficientsLobIIIC3())
end

"Gauss-Lobatto-IIIC Runge-Kutta, s=4"
function getTableauLobIIIC4()
    TableauFIRK(getCoefficientsLobIIIC4())
end

"Gauss-Lobatto-IIID Runge-Kutta, s=2"
function getTableauLobIIID2()
    TableauFIRK(getCoefficientsLobIIID2())
end

"Gauss-Lobatto-IIID Runge-Kutta, s=3"
function getTableauLobIIID3()
    TableauFIRK(getCoefficientsLobIIID3())
end

"Gauss-Lobatto-IIID Runge-Kutta, s=4"
function getTableauLobIIID4()
    TableauFIRK(getCoefficientsLobIIID4())
end

"Gauss-Lobatto-IIIE Runge-Kutta, s=2"
function getTableauLobIIIE2()
    TableauFIRK(getCoefficientsLobIIIE2())
end

"Gauss-Lobatto-IIIE Runge-Kutta, s=3"
function getTableauLobIIIE3()
    TableauFIRK(getCoefficientsLobIIIE3())
end

"Gauss-Lobatto-IIIE Runge-Kutta, s=4"
function getTableauLobIIIE4()
    TableauFIRK(getCoefficientsLobIIIE4())
end

"Gauss-Lobatto-IIIF Runge-Kutta, s=2"
function getTableauLobIIIF2()
    TableauFIRK(getCoefficientsLobIIIF2())
end

"Gauss-Lobatto-IIIF Runge-Kutta, s=3"
function getTableauLobIIIF3()
    TableauFIRK(getCoefficientsLobIIIF3())
end

"Gauss-Lobatto-IIIF Runge-Kutta, s=4"
function getTableauLobIIIF4()
    TableauFIRK(getCoefficientsLobIIIF4())
end

"Gauss-Lobatto-IIIG Runge-Kutta, s=2"
function getTableauLobIIIG2()
    TableauFIRK(getCoefficientsLobIIIG2())
end

"Gauss-Lobatto-IIIG Runge-Kutta, s=3"
function getTableauLobIIIG3()
    TableauFIRK(getCoefficientsLobIIIG3())
end

"Gauss-Lobatto-IIIG Runge-Kutta, s=4"
function getTableauLobIIIG4()
    TableauFIRK(getCoefficientsLobIIIG4())
end

"Gauss-Radau-IIA Runge-Kutta, s=2"
function getTableauRadIIA2()
    TableauFIRK(getCoefficientsRadIIA2())
end

"Gauss-Radau-IIA Runge-Kutta, s=3"
function getTableauRadIIA3()
    TableauFIRK(getCoefficientsRadIIA3())
end

"Gauss-Legendre Runge-Kutta, s=3"
function getTableauSRK3()
    TableauFIRK(getCoefficientsSRK3())
end


text:
using BenhmarkTools
x = "id".*dec.(1:1_000_000, 10);
rx = rand(x, 100_000_000);

function fhash(z)
    pz = z |> pointer |> Ptr{UInt}
    h = pz |> unsafe_load |> hash
    h1 = (pz + 8) |> unsafe_load |> hash
    h * h1
end

@benchmark hash.($rx)
# BenchmarkTools.Trial:
#   memory estimate:  762.94 MiB
#   allocs estimate:  2
#   --------------
#   minimum time:     16.816 s (0.00% GC)
#   median time:      16.816 s (0.00% GC)
#   mean time:        16.816 s (0.00% GC)
#   maximum time:     16.816 s (0.00% GC)
#   --------------
#   samples:          1
#   evals/sample:     1
@benchmark fhash.($rx) # 8

@benchmark Base.crc32c.($rx)

a = (z->z |> pointer |> Ptr{UInt} |> unsafe_load).(rx)

fshash(z) = z |> pointer |> Ptr{UInt} |> unsafe_load |> hash
fshash1(z) = z |> pointer |> Ptr{UInt} |> unsafe_load

@benchmark fshash.($rx)
@benchmark fshash1.($rx)
@benchmark hash.($a)

@benchmark sort.(a)

@time aa = fshash1.(rx)
@benchmark sort($aa)
@benchmark sort($aa, alg=RadixSort)

max_stars_repo_path: benchmark/which is the fastest string hash.jl


text:
@testset "inverse" begin

    @testset "exact" begin
    # Reference: Philip (1960) Table 1, No. 13
    # https://doi.org/10.1071/PH600001
    ϕ = range(0, 20, length=100)

    D = inverse(ϕ, exp.(-ϕ))

    θ = range(1e-6, 0.99, length=100)

    @test all(@. isapprox(D(θ), 0.5*(1 - log(θ)), atol=5e-2))

    @test_throws DomainError D(1.001)
    @test_throws DomainError D(-1)
    @test_throws DomainError D(0)
    end
end


text:
# Termbox.jl: Wrapper around the Termbox library (https://github.com/nsf/termbox)
VERSION >= v"0.4.0-dev+6521" && __precompile__()
module Termbox
const depsfile = joinpath(dirname(dirname(@__FILE__)), "deps", "deps.jl")
if isfile(depsfile)
  include(depsfile)
else
  error("Termbox not properly installed. Please run Pkg.build(\"Termbox\")")
end

using Compat: Nothing
include("termbox_h.jl")

@static if VERSION >= v"0.7-"
    using Printf
end

export with_term
function with_term(f, args...)
    ret = tb_init()
    if ret != 0
        @printf stderr "tb_init() failed with error code %d\n" ret
        return 1
    end
    try
        f(args...)
    finally
        tb_shutdown()
    end
    return 0
end

end # module


text:
using Plots

const format = ":svg"
const figsize = (1000, 1000)
const keywords = Dict{Symbol, Any}

### work in progress


text:
# options checking specific to the Euler Equations

"""
  This function checks the options in the options dictionary after default
  values have been supplied and throws exceptions if unsupported/incompatable
  options are specified

  **Inputs**

   * opts: options dictionary
   * comm: MPI communicator

  **Outputs**

    none
"""
function checkOptions(opts, comm=MPI.COMM_WORLD)

  if opts["physics"] != PhysicsName
    error("physics not specified as $PhysicsName, are you lost?")
  end

  if opts["use_edge_res"]
    error("use_edge_res does not work correctly, do not use")
  end

  if opts["use_staggered_grid"]
    error("staggered grid not supported for $PhysicsName")
  end

  get!(opts, "calc_jac_explicit", false)

  if opts["calc_jac_explicit"]
    get!(opts, "preallocate_jacobian_coloring", false)
  else
    get!(opts, "preallocate_jacobian_coloring", true)
  end

  get!(opts, "Cip", 1.0)
  get!(opts, "isViscous", true)

  if !opts["isViscous"]
    error("cannot use $PhysicsName with isViscous == false")
  end

  if !(opts["precompute_q_face"] && opts["precompute_face_flux"] &&
       opts["precompute_q_bndry"] && opts["precompute_boundary_flux"])

    error("precomputing face and boundary quantities required for $PhysicsName")
  end

  return nothing
end


text:
include("../day17/day17.jl")
@async run_program!(tape)
ascii_interact!(tape)

max_stars_repo_name: st--/advent-of-code

max_issues_repo_name: st--/advent-of-code

max_forks_repo_name: st--/advent-of-code


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

struct EncryptionScopesApi <: SwaggerApi
    client::Swagger.Client
end

"""

Returns the properties for the specified encryption scope.
Param: resourceGroupName::String (required)
Param: accountName::String (required)
Param: api_version::String (required)
Param: subscriptionId::String (required)
Param: encryptionScopeName::String (required)
Return: EncryptionScope
"""
function _swaggerinternal_encryptionScopesGet(_api::EncryptionScopesApi, resourceGroupName::String, accountName::String, api_version::String, subscriptionId::String, encryptionScopeName::String; _mediaType=nothing)
    Swagger.validate_param("resourceGroupName", "encryptionScopesGet", :maxLength, resourceGroupName, 90)
    Swagger.validate_param("resourceGroupName", "encryptionScopesGet", :minLength, resourceGroupName, 1)

    Swagger.validate_param("accountName", "encryptionScopesGet", :maxLength, accountName, 24)
    Swagger.validate_param("accountName", "encryptionScopesGet", :minLength, accountName, 3)

    Swagger.validate_param("api_version", "encryptionScopesGet", :minLength, api_version, 1)

    Swagger.validate_param("subscriptionId", "encryptionScopesGet", :minLength, subscriptionId, 1)

    Swagger.validate_param("encryptionScopeName", "encryptionScopesGet", :maxLength, encryptionScopeName, 63)
    Swagger.validate_param("encryptionScopeName", "encryptionScopesGet", :minLength, encryptionScopeName, 3)

    _ctx = Swagger.Ctx(_api.client, "GET", EncryptionScope, "/subscriptions/{subscriptionId}/resourceGroups/{resourceGroupName}/providers/Microsoft.Storage/storageAccounts/{accountName}/encryptionScopes/{encryptionScopeName}", ["azure_auth"])
    Swagger.set_param(_ctx.path, "resourceGroupName", resourceGroupName)  # type String
    Swagger.set_param(_ctx.path, "accountName", accountName)  # type String
    Swagger.set_param(_ctx.path, "subscriptionId", subscriptionId)  # type String
    Swagger.set_param(_ctx.path, "encryptionScopeName", encryptionScopeName)  # type String
    Swagger.set_param(_ctx.query, "api-version", api_version)  # type String
    Swagger.set_header_accept(_ctx, ["application/json"])
    Swagger.set_header_content_type(_ctx, (_mediaType === nothing) ? ["application/json"] : [_mediaType])
    return _ctx
end

function encryptionScopesGet(_api::EncryptionScopesApi, resourceGroupName::String, accountName::String, api_version::String, subscriptionId::String, encryptionScopeName::String; _mediaType=nothing)
    _ctx = _swaggerinternal_encryptionScopesGet(_api, resourceGroupName, accountName, api_version, subscriptionId, encryptionScopeName; _mediaType=_mediaType)
    Swagger.exec(_ctx)
end

function encryptionScopesGet(_api::EncryptionScopesApi, response_stream::Channel, resourceGroupName::String, accountName::String, api_version::String, subscriptionId::String, encryptionScopeName::String; _mediaType=nothing)
    _ctx = _swaggerinternal_encryptionScopesGet(_api, resourceGroupName, accountName, api_version, subscriptionId, encryptionScopeName; _mediaType=_mediaType)
    Swagger.exec(_ctx, response_stream)
end

"""

Lists all the encryption scopes available under the specified storage account.
Param: resourceGroupName::String (required)
Param: accountName::String (required)
Param: api_version::String (required)
Param: subscriptionId::String (required)
Return: EncryptionScopeListResult
"""
function _swaggerinternal_encryptionScopesList(_api::EncryptionScopesApi, resourceGroupName::String, accountName::String, api_version::String, subscriptionId::String; _mediaType=nothing)
    Swagger.validate_param("resourceGroupName", "encryptionScopesList", :maxLength, resourceGroupName, 90)
    Swagger.validate_param("resourceGroupName", "encryptionScopesList", :minLength, resourceGroupName, 1)

    Swagger.validate_param("accountName", "encryptionScopesList", :maxLength, accountName, 24)
    Swagger.validate_param("accountName", "encryptionScopesList", :minLength, accountName, 3)

    Swagger.validate_param("api_version", "encryptionScopesList", :minLength, api_version, 1)

    Swagger.validate_param("subscriptionId", "encryptionScopesList", :minLength, subscriptionId, 1)

    _ctx = Swagger.Ctx(_api.client, "GET", EncryptionScopeListResult, "/subscriptions/{subscriptionId}/resourceGroups/{resourceGroupName}/providers/Microsoft.Storage/storageAccounts/{accountName}/encryptionScopes", ["azure_auth"])
    Swagger.set_param(_ctx.path, "resourceGroupName", resourceGroupName)  # type String
    Swagger.set_param(_ctx.path, "accountName", accountName)  # type String
    Swagger.set_param(_ctx.path, "subscriptionId", subscriptionId)  # type String
    Swagger.set_param(_ctx.query, "api-version", api_version)  # type String
    Swagger.set_header_accept(_ctx, ["application/json"])
    Swagger.set_header_content_type(_ctx, (_mediaType === nothing) ? ["application/json"] : [_mediaType])
    return _ctx
end

function encryptionScopesList(_api::EncryptionScopesApi, resourceGroupName::String, accountName::String, api_version::String, subscriptionId::String; _mediaType=nothing)
    _ctx = _swaggerinternal_encryptionScopesList(_api, resourceGroupName, accountName, api_version, subscriptionId; _mediaType=_mediaType)
    Swagger.exec(_ctx)
end

function encryptionScopesList(_api::EncryptionScopesApi, response_stream::Channel, resourceGroupName::String, accountName::String, api_version::String, subscriptionId::String; _mediaType=nothing)
    _ctx = _swaggerinternal_encryptionScopesList(_api, resourceGroupName, accountName, api_version, subscriptionId; _mediaType=_mediaType)
    Swagger.exec(_ctx, response_stream)
end

"""

Update encryption scope properties as specified in the request body. Update fails if the specified encryption scope does not already exist.
Param: resourceGroupName::String (required)
Param: accountName::String (required)
Param: api_version::String (required)
Param: subscriptionId::String (required)
Param: encryptionScopeName::String (required)
Param: encryptionScope::EncryptionScope (required)
Return: EncryptionScope
"""
function _swaggerinternal_encryptionScopesPatch(_api::EncryptionScopesApi, resourceGroupName::String, accountName::String, api_version::String, subscriptionId::String, encryptionScopeName::String, encryptionScope; _mediaType=nothing)
    Swagger.validate_param("resourceGroupName", "encryptionScopesPatch", :maxLength, resourceGroupName, 90)
    Swagger.validate_param("resourceGroupName", "encryptionScopesPatch", :minLength, resourceGroupName, 1)

    Swagger.validate_param("accountName", "encryptionScopesPatch", :maxLength, accountName, 24)
    Swagger.validate_param("accountName", "encryptionScopesPatch", :minLength, accountName, 3)

    Swagger.validate_param("api_version", "encryptionScopesPatch", :minLength, api_version, 1)

    Swagger.validate_param("subscriptionId", "encryptionScopesPatch", :minLength, subscriptionId, 1)

    Swagger.validate_param("encryptionScopeName", "encryptionScopesPatch", :maxLength, encryptionScopeName, 63)
    Swagger.validate_param("encryptionScopeName", "encryptionScopesPatch", :minLength, encryptionScopeName, 3)

    _ctx = Swagger.Ctx(_api.client, "PATCH", EncryptionScope, "/subscriptions/{subscriptionId}/resourceGroups/{resourceGroupName}/providers/Microsoft.Storage/storageAccounts/{accountName}/encryptionScopes/{encryptionScopeName}", ["azure_auth"], encryptionScope)
    Swagger.set_param(_ctx.path, "resourceGroupName", resourceGroupName)  # type String
    Swagger.set_param(_ctx.path, "accountName", accountName)  # type String
    Swagger.set_param(_ctx.path, "subscriptionId", subscriptionId)  # type String
    Swagger.set_param(_ctx.path, "encryptionScopeName", encryptionScopeName)  # type String
    Swagger.set_param(_ctx.query, "api-version", api_version)  # type String
    Swagger.set_header_accept(_ctx, ["application/json"])
    Swagger.set_header_content_type(_ctx, (_mediaType === nothing) ? ["application/json"] : [_mediaType])
    return _ctx
end

function encryptionScopesPatch(_api::EncryptionScopesApi, resourceGroupName::String, accountName::String, api_version::String, subscriptionId::String, encryptionScopeName::String, encryptionScope; _mediaType=nothing)
    _ctx = _swaggerinternal_encryptionScopesPatch(_api, resourceGroupName, accountName, api_version, subscriptionId, encryptionScopeName, encryptionScope; _mediaType=_mediaType)
    Swagger.exec(_ctx)
end

function encryptionScopesPatch(_api::EncryptionScopesApi, response_stream::Channel, resourceGroupName::String, accountName::String, api_version::String, subscriptionId::String, encryptionScopeName::String, encryptionScope; _mediaType=nothing)
    _ctx = _swaggerinternal_encryptionScopesPatch(_api, resourceGroupName, accountName, api_version, subscriptionId, encryptionScopeName, encryptionScope; _mediaType=_mediaType)
    Swagger.exec(_ctx, response_stream)
end

"""

Synchronously creates or updates an encryption scope under the specified storage account. If an encryption scope is already created and a subsequent request is issued with different properties, the encryption scope properties will be updated per the specified request.
Param: resourceGroupName::String (required)
Param: accountName::String (required)
Param: api_version::String (required)
Param: subscriptionId::String (required)
Param: encryptionScopeName::String (required)
Param: encryptionScope::EncryptionScope (required)
Return: EncryptionScope
"""
function _swaggerinternal_encryptionScopesPut(_api::EncryptionScopesApi, resourceGroupName::String, accountName::String, api_version::String, subscriptionId::String, encryptionScopeName::String, encryptionScope; _mediaType=nothing)
    Swagger.validate_param("resourceGroupName", "encryptionScopesPut", :maxLength, resourceGroupName, 90)
    Swagger.validate_param("resourceGroupName", "encryptionScopesPut", :minLength, resourceGroupName, 1)

    Swagger.validate_param("accountName", "encryptionScopesPut", :maxLength, accountName, 24)
    Swagger.validate_param("accountName", "encryptionScopesPut", :minLength, accountName, 3)

    Swagger.validate_param("api_version", "encryptionScopesPut", :minLength, api_version, 1)

    Swagger.validate_param("subscriptionId", "encryptionScopesPut", :minLength, subscriptionId, 1)

    Swagger.validate_param("encryptionScopeName", "encryptionScopesPut", :maxLength, encryptionScopeName, 63)
    Swagger.validate_param("encryptionScopeName", "encryptionScopesPut", :minLength, encryptionScopeName, 3)

    _ctx = Swagger.Ctx(_api.client, "PUT", EncryptionScope, "/subscriptions/{subscriptionId}/resourceGroups/{resourceGroupName}/providers/Microsoft.Storage/storageAccounts/{accountName}/encryptionScopes/{encryptionScopeName}", ["azure_auth"], encryptionScope)
    Swagger.set_param(_ctx.path, "resourceGroupName", resourceGroupName)  # type String
    Swagger.set_param(_ctx.path, "accountName", accountName)  # type String
    Swagger.set_param(_ctx.path, "subscriptionId", subscriptionId)  # type String
    Swagger.set_param(_ctx.path, "encryptionScopeName", encryptionScopeName)  # type String
    Swagger.set_param(_ctx.query, "api-version", api_version)  # type String
    Swagger.set_header_accept(_ctx, ["application/json"])
    Swagger.set_header_content_type(_ctx, (_mediaType === nothing) ? ["application/json"] : [_mediaType])
    return _ctx
end

function encryptionScopesPut(_api::EncryptionScopesApi, resourceGroupName::String, accountName::String, api_version::String, subscriptionId::String, encryptionScopeName::String, encryptionScope; _mediaType=nothing)
    _ctx = _swaggerinternal_encryptionScopesPut(_api, resourceGroupName, accountName, api_version, subscriptionId, encryptionScopeName, encryptionScope; _mediaType=_mediaType)
    Swagger.exec(_ctx)
end

function encryptionScopesPut(_api::EncryptionScopesApi, response_stream::Channel, resourceGroupName::String, accountName::String, api_version::String, subscriptionId::String, encryptionScopeName::String, encryptionScope; _mediaType=nothing)
    _ctx = _swaggerinternal_encryptionScopesPut(_api, resourceGroupName, accountName, api_version, subscriptionId, encryptionScopeName, encryptionScope; _mediaType=_mediaType)
    Swagger.exec(_ctx, response_stream)
end

export encryptionScopesGet, encryptionScopesList, encryptionScopesPatch, encryptionScopesPut


text:
module LimitOrderBook

println("test!")

end