text:
################################################################################
#                  MakieGallery.jl documentation build script                  #
################################################################################
##############################
#      Generic imports       #
##############################

using Documenter, Markdown, Pkg, Random, FileIO

##############################
#      Specific imports      #
##############################

using MakieGallery, AbstractPlotting
AbstractPlotting.inline!(true)

makiegallerydir = dirname(dirname(pathof(Base.moduleroot(MakieGallery))))

import AbstractPlotting: to_string

using MakieGallery: print_table

################################################################################
#                              Utility functions                               #
################################################################################

################################################################################
#                                    Setup                                     #
################################################################################

database = MakieGallery.load_database()

pathroot  = normpath(@__DIR__, "..")
docspath  = joinpath(pathroot, "docs")
srcpath   = joinpath(docspath, "src")
srcgenpath   = joinpath(docspath, "src_generation")
buildpath = joinpath(docspath, "build")
genpath   = joinpath(srcpath, "generated")

mkpath(genpath)

################################################################################
#                          Syntax highlighting theme                           #
################################################################################

@info("Writing highlighting stylesheet")
open(joinpath(srcpath, "assets", "syntaxtheme.css"), "w") do io
    MakieGallery.Highlights.stylesheet(io, MIME("text/css"), MakieGallery.DEFAULT_HIGHLIGHTER[])
end

################################################################################
#                      Automatic Markdown page generation                      #
################################################################################

########################################
#       Plot attributes overview       #
########################################

# automatically generate an overview of the plot attributes (keyword arguments), using a source md file
@info("Generating attributes page")
include(joinpath(makiegallerydir, "src/plot_attr_desc.jl"))
path = joinpath(genpath, "plot-attributes.md")
srcdocpath = joinpath(srcgenpath, "src-plot-attributes.md")
open(path, "w") do io
    !ispath(srcdocpath) && error("source document doesn't exist!")
    println(io, "# Plot attributes")
    src = read(srcdocpath, String)
    println(io, src)
    print(io, "\n")
    println(io, "## List of attributes")
    print_table(io, plot_attr_desc)
end

########################################
#       Axis attributes overview       #
########################################

# automatically generate an overview of the axis attributes, using a source md file
@info("Generating axis page")
path = joinpath(genpath, "axis.md")
srcdocpath = joinpath(srcgenpath, "src-axis.md")
include(joinpath(makiegallerydir, "src/Axis2D_attr_desc.jl"))
include(joinpath(makiegallerydir, "src/Axis3D_attr_desc.jl"))

open(path, "w") do io
    !ispath(srcdocpath) && error("source document doesn't exist!")
    src = read(srcdocpath, String)
    println(io, src)
    print(io)
    # Axis2D section
    println(io, "## `Axis2D`")
    println(io, "### `Axis2D` attributes groups")
    print_table(io, Axis2D_attr_desc)
    print(io)
    for (k, v) in Axis2D_attr_groups
        println(io, "#### `:$k`\n")
        print_table(io, v)
        println(io)
    end
    # Axis3D section
    println(io, "## `Axis3D`")
    println(io, "### `Axis3D` attributes groups")
    print_table(io, Axis3D_attr_desc)
    print(io)
    for (k, v) in Axis3D_attr_groups
        println(io, "#### `:$k`\n")
        print_table(io, v)
        println(io)
    end

end

########################################
#     Function signatures overview     #
########################################

# automatically generate an overview of the function signatures, using a source md file
@info("Generating signatures page")
path = joinpath(genpath, "signatures.md")
srcdocpath = joinpath(srcgenpath, "src-signatures.md")
open(path, "w") do io
    !ispath(srcdocpath) && error("source document doesn't exist!")
    println(io, "# Plot function signatures")
    src = read(srcdocpath, String)
    println(io, src)
    print(io, "\n")
    println(io, "```@docs")
    println(io, "convert_arguments")
    println(io, "```\n")

    println(io, "See [Plot attributes](@ref) for the available plot attributes.")
end

########################################
#          Colormap reference          #
########################################

@info "Generating colormap reference"

include(joinpath(srcgenpath, "colormap_generation.jl"))

generate_colorschemes_markdown(
    joinpath(srcgenpath, "src-colors.md"),
    joinpath(genpath, "colors.md")
)

################################################################################
#                 Building HTML documentation with Documenter                  #
################################################################################

@info("Running `makedocs` with Documenter.")

makedocs(
    doctest = false, clean = true,
    repo = "https://github.com/JuliaPlots/AbstractPlotting.jl/blob/{commit}{path}#{line}",
    format = Documenter.HTML(
        prettyurls = false,
        assets = [
            "assets/favicon.ico",
            "assets/syntaxtheme.css"
        ],
    ),
    sitename = "Makie.jl",
    expandfirst = [
        "plotting_functions.md",
    ],
    pages = Any[
        "Home" => "index.md",
        "Basics" => [
            "basic-tutorial.md",
            "animation.md",
            "interaction.md",
            "plotting_functions.md",
        ],
        "Documentation" => [
            "scenes.md",
            "generated/axis.md",
            "convenience.md",
            "generated/signatures.md",
            "generated/plot-attributes.md",
            "generated/colors.md",
            "lighting.md",
            "theming.md",
            "cameras.md",
            "recipes.md",
            "output.md",
            "backends.md",
            "troubleshooting.md"
        ],
        "MakieLayout" => [
            "Tutorial" => "makielayout/tutorial.md",
            "GridLayout" => "makielayout/grids.md",
            "LAxis" => "makielayout/laxis.md",
            "Special Plots" => "makielayout/special_plots.md",
            "LLegend" => "makielayout/llegend.md",
            "Layoutables Examples" => "makielayout/layoutables_examples.md",
            "Theming Layoutables" => "makielayout/theming.md",
            "How Layouting Works" => "makielayout/layouting.md",
            "Frequently Asked Questions" => "makielayout/faq.md",
            "API Reference" => "makielayout/reference.md",
        ],
        "Developer Documentation" => [
            "why-makie.md",
            "devdocs.md",
            "AbstractPlotting Reference" => "abstractplotting_api.md",
        ],
    ]
)

################################################################################
#                           Deploying documentation                            #
################################################################################

# add a custom configuration for Gitlab so the GPU-powered docs can be
# automatically uploaded to Github Pages

struct Gitlab <: Documenter.DeployConfig
    commit_branch::String
    pull_request_iid::String
    repo_slug::String
    commit_tag::String
    pipeline_source::String
end

function Gitlab()
    commit_branch = get(ENV, "CI_COMMIT_BRANCH", "")
    pull_request_iid = get(ENV, "CI_EXTERNAL_PULL_REQUEST_IID", "")
    repo_slug = get(ENV, "CI_PROJECT_PATH_SLUG", "")
    commit_tag = get(ENV, "CI_COMMIT_TAG", "")
    pipeline_source = get(ENV, "CI_PIPELINE_SOURCE", "")
    Gitlab(
        commit_branch,
        pull_request_iid,
        repo_slug,
        commit_tag,
        pipeline_source,
    )
end

function Documenter.deploy_folder(cfg::Gitlab;
        repo,
        repo_previews = repo,
        devbranch,
        push_preview,
        devurl,
        branch = "gh-pages",
        branch_previews = branch,
        kwargs...)

    marker(x) = x ? "✔" : "✘"

    io = IOBuffer()
    all_ok = true

    println(io, "\nGitlab config:")
    println(io, "  Commit branch: \"", cfg.commit_branch, "\"")
    println(io, "  Pull request IID: \"", cfg.pull_request_iid, "\"")
    println(io, "  Repo slug: \"", cfg.repo_slug, "\"")
    println(io, "  Commit tag: \"", cfg.commit_tag, "\"")
    println(io, "  Pipeline source: \"", cfg.pipeline_source, "\"")

    build_type = if cfg.pull_request_iid != ""
        :preview
    elseif cfg.commit_tag != ""
        :release
    else
        :devbranch
    end

    println(io, "Detected build type: ", build_type)

    if build_type == :release
        tag_nobuild = Documenter.version_tag_strip_build(cfg.commit_tag)
        ## If a tag exist it should be a valid VersionNumber
        tag_ok = tag_nobuild !== nothing

        println(io, "- $(marker(tag_ok)) ENV[\"CI_COMMIT_TAG\"] contains a valid VersionNumber")
        all_ok &= tag_ok

        is_preview = false
        subfolder = tag_nobuild
        deploy_branch = branch
        deploy_repo = repo

    elseif build_type == :preview
        pr_number = tryparse(Int, cfg.pull_request_iid)
        pr_ok = pr_number !== nothing
        all_ok &= pr_ok
        println(io, "- $(marker(pr_ok)) ENV[\"CI_EXTERNAL_PULL_REQUEST_IID\"]=\"$(cfg.pull_request_iid)\" is a number")
        btype_ok = push_preview
        all_ok &= btype_ok
        is_preview = true
        println(io, "- $(marker(btype_ok)) `push_preview` keyword argument to deploydocs is `true`")
        ## deploy to previews/PR
        subfolder = "previews/PR$(something(pr_number, 0))"
        deploy_branch = branch_previews
        deploy_repo = repo_previews
    else
        branch_ok = !isempty(cfg.commit_tag) || cfg.commit_branch == devbranch
        all_ok &= branch_ok
        println(io, "- $(marker(branch_ok)) ENV[\"CI_COMMIT_BRANCH\"] matches devbranch=\"$(devbranch)\"")
        is_preview = false
        subfolder = devurl
        deploy_branch = branch
        deploy_repo = repo
    end

    key_ok = haskey(ENV, "DOCUMENTER_KEY")
    println(io, "- $(marker(key_ok)) ENV[\"DOCUMENTER_KEY\"] exists")
    all_ok &= key_ok

    print(io, "Deploying to folder \"$(subfolder)\": $(marker(all_ok))")
    @info String(take!(io))

    return Documenter.DeployDecision(; all_ok = all_ok, branch = deploy_branch, repo = deploy_repo,
        subfolder = subfolder, is_preview = is_preview)
end

Documenter.authentication_method(::Gitlab) = Documenter.SSH

function Documenter.documenter_key(::Gitlab)
    return ENV["DOCUMENTER_KEY"]
end

deploydocs(
    repo = "github.com/JuliaPlots/MakieDocumentation",
    deploy_config = Gitlab(),
    push_preview = true
)


text:
@discord_object struct Ban
    reason::String
    user::User
end

ext: jl


text:
import JuMP
using Allocations
using LightGraphs
using Random: seed!
using Test

# For matching test:
using Allocations: bipartite_matching

# For Counts test:
using Allocations: Category

function runtests(; slow_tests = true)

    seed!(4252170447285279131)

@testset "Types" begin

    @testset "Allocations" begin

        @testset "Basics" begin

            n, m = 3, 5

            A = Allocation(n, m)

            @test string(A) == "[{}, {}, {}]"

            @test na(A) == length(agents(A)) == n
            @test ni(A) == length(items(A)) == m

            @test isempty(bundle(A, 2))

            give!(A, 2, 4)
            give!(A, 2, 3)
            give!(A, 1, 2)

            @test string(A) == "[{2}, {3, 4}, {}]"

            give!(A, 3, [1, 5])

            @test string(A) == "[{2}, {3, 4}, {1, 5}]"

            @test 4 in bundle(A, 2)

            deny!(A, 2, 4)

            @test string(A) == "[{2}, {3}, {1, 5}]"
            @test summary(A) == "Allocation with 3 agents and 5 items, " *
                                "1 unallocated"

            deny!(A, 2, 3)

            @test isempty(bundle(A, 2))

            for g = 1:m
                give!(A, 1, g)
            end

            @test summary(A) == "Allocation with 3 agents and 5 items"

            A = Allocation(2, 4)
            give!(A, 2, 1)
            fill_even!(A)
            @test owner(A, 2) == 1
            @test length(bundle(A, 1)) == length(bundle(A, 2)) == 2

        end

    end

    @testset "Profiles" begin

        let

            X = [1 2 3; 3 2 1]

            V = Additive(X)
            V′ = Profile(X)

            @test V == V′

            i, g, h = 2, 3, 2

            @test value(V, i, g) == 1

            A = Allocation(5, 10)

            @test value(V, i, A) == 0

            give!(A, i, g)
            give!(A, i, h)

            @test value(V, i, A) == 3

        end

        let

            n, m = 5, 10

            V = Additive(n, m)

            i, g = 2, 3

            @test length(agents(V)) == na(V) == n
            @test length(items(V)) == ni(V) == m
            @test value(V, i, g) == 0

            value!(V, i, g, 4)

            @test value(V, i, g) == 4

        end

    end

    @testset "Counts" begin

        C = Counts(
            [1, 2, 3] => 2,
            [4, 5, 6] => 1
        )

        @test C isa Counts
        for c in C
            @test c isa Category
        end

        @test C[2].threshold == 1

    end

    @testset "Conflicts" begin

        C = Conflicts(star_graph(10))

        @test C isa Conflicts

        @test graph(C) isa AbstractGraph

    end

end

@testset "Utilities" begin

    @testset "Matching" begin

        X = [0 1 1 0 0 0
             0 0 0 0 0 0
             1 0 0 1 0 0
             0 0 1 0 0 0
             0 0 1 1 0 0
             0 0 0 0 0 1]

        # M = bipartite_matching(Bool.(X))
        M = falses(size(X))
        for (i, g) in bipartite_matching(X)
            M[i, g] = true
        end

        @test all(sum(M, dims=1) .<= 1)
        @test all(sum(M, dims=2) .<= 1)
        @test sum(M) == 5

    end

end

@testset "Basic checks" begin

    A = Allocation(2, 2)

    give!(A, 1, 1)

    @test !check_partition(A)
    @test !check_complete(A)

    give!(A, 2, 2)

    @test check_partition(A)
    @test check_complete(A)

    give!(A, 1, 2)

    @test !check_partition(A)
    @test check_complete(A)

end

@testset "EF checks" begin

    n, m = 2, 3

    V = Additive(ones(n, m))

    A = Allocation(n, m)

    @test check_ef(V, A)

    give!(A, 1, 1)

    @test !check_ef(V, A)
    @test check_ef1(V, A)

    give!(A, 1, 2)
    give!(A, 2, 3)

    @test check_ef1(V, A)
    @test check_efx(V, A)

    value!(V, 2, 1, 2)

    @test check_ef1(V, A)
    @test !check_efx(V, A)

end

@testset "Measures" begin

    V = Additive([1 3; 3 1])
    A = Allocation(2, 2)
    give!(A, 1, 2)
    give!(A, 2, 1)

    @test nash_welfare(V, A) ≈ 9
    @test utility(V, A) ≈ 6
    @test prop_alpha(V, A) ≈ 3 / (4 / 2)

end

@testset "MIPs" begin

    V₀ = Additive(rand(1:10, 3, 15))

    @testset "MNW" begin

        V = V₀

        let res = alloc_mnw(V)

            @test res.alloc isa Allocation
            @test check_ef1(V, res.alloc)
            @test res.mnw > 0

        end

        let res = alloc_mnw([1 2 3; 4 3 1])

            @test string(res.alloc) == "[{3}, {1, 2}]"
            @test res.mnw ≈ 3 * (4 + 3)

            @test res.model isa JuMP.Model

        end

    end

    @testset "MNW with constraints" for C in [
            Conflicts(path_graph(ni(V₀))),
            Counts(
                [1, 2, 3, 4]     => 3,
                [5, 6, 7]        => 2,
                [8, 9, 10]       => 2,
                [11, 12, 13, 14] => 3,
                [15]             => 1
            )
        ]

        V = V₀

        res = alloc_mnw(V)
        resc = alloc_mnw(V, C)

        @test check(V, resc.alloc, C)

        @test resc.alloc isa Allocation
        @test resc.mnw > 0

        # Adding constraint can't improve objective.
        @test resc.mnw <= res.mnw

        @test res.model isa JuMP.Model
        @test resc.model isa JuMP.Model

    end

    @testset "EF1 with conflicts" begin

        n, m = 3, 9
        nv, ne = m, 2m

        V = Additive(rand(n, m))

        # Random graph
        C = Conflicts(SimpleGraph(nv, ne))

        res = alloc_ef1(V, C)

        @test res.model isa JuMP.Model

        @test check_ef1(V, res.alloc)

        # Check that it's usable without constraints, even though we're not
        # supplying a single-argument MIP implementation:
        @test check_ef1(V, alloc_ef1(V, nothing).alloc)

    end

    @testset "MNW+EF1 with conflicts" begin

        # Specific example (V and G) where MNW does not lead to EF1:

        V = Additive([10  5  8  1  2  9; 6  1  9  6  8  9])

        G = SimpleGraph(ni(V))
        add_edge!(G, 3, 4)
        add_edge!(G, 5, 6)

        C = Conflicts(G)

        res = alloc_mnw(V, C)

        @test !check_ef1(V, res.alloc)

        res1 = alloc_mnw_ef1(V, C)

        @test res.model isa JuMP.Model
        @test res1.alloc isa Allocation
        @test check_ef1(V, res1.alloc)

        # MNW did not yield EF1, so enforcing EF1 should reduce MNW:
        @test res1.mnw < res.mnw

    end

    @testset "EFX" begin

        V = V₀

        alloc_efx([1 1 1; 1 1 1]) # Bug fix: EFX but not EF

        res = alloc_efx(V)

        A = res.alloc

        @test res.model isa JuMP.Model
        @test A isa Allocation

        @test check_partition(A)
        @test check_complete(A)

        @test check_efx(V, A)

    end

    @testset "Maximin" begin

        V = V₀

        res = alloc_mm(V)

        A = res.alloc
        N = agents(V)

        @test res.model isa JuMP.Model
        @test A isa Allocation
        @test res.mm == minimum(value(V, i, A) for i in N)

    end

    slow_tests &&
    @testset "MMS" begin

        V = V₀

        let res = alloc_mms(V)

            @test res.alloc isa Allocation
            @test mms_alpha(V, res.alloc, res.mmss) ≈ res.alpha

        end

        let res = alloc_mms([3 1 2; 4 4 5])

            @test res.model isa JuMP.Model
            @test length(res.mms_models) == 2
            @test all(isa.(res.mms_models, JuMP.Model))

            @test res.alpha ≈ 1.0
            @test res.mmss ≈ [3.0, 5.0]

        end

        let res = alloc_mms([2 1; 1 2])

            @test res.alpha ≈ 2.0

        end

        let res = alloc_mms([2 1; 1 2], cutoff=true)

            @test res.alpha ≈ 1.0

        end

    end

    @testset "GGI" begin

        V = V₀

        let res = alloc_ggi(V)

            @test res.alloc isa Allocation
            @test res.model isa JuMP.Model

        end

        let res = alloc_ggi([1 1 3; 1 1 2])

            @test string(res.alloc) == "[{3}, {1, 2}]"

        end

    end

end

@testset "Algorithms" begin

    V₀ = Additive(rand(1:10, 4, 12))

    @testset "Random" begin

        V = V₀

        res = alloc_rand(V)

        A = res.alloc

        @test A isa Allocation
        @test check_partition(A)

    end

    @testset "Random with conflicts" begin

        n, m = 10, 50
        nv, ne = m, 30

        V = Additive(zeros(n, m)) # Irrelevant

        # Random graph
        # (Could fail the Δ < n check, if we're unlucky)
        C = Conflicts(SimpleGraph(nv, ne))

        res = alloc_rand(V, C)
        A = res.alloc

        @test A isa Allocation
        # Implied by check_partition, but useful checkpoint:
        @test check_complete(A)
        @test check_partition(A)
        @test check(V, A, C)

    end

    slow_tests &&
    @testset "BKV18(1)" begin

        α = 1.061 # Approximation ratio, for geomean version of NW

        for _ = 1:10

            n = rand(2:4)
            m = 3n
            # Identical valuations
            V = Profile(repeat(rand(1:5, 1, m), n))

            res = alloc_bkv18_1(V)
            A = res.alloc

            @test A isa Allocation
            @test check_partition(A)
            @test α*nash_welfare(V, A)^(1/n) ≥ alloc_mnw(V).mnw^(1/n)
            @test check_efx(V, A)

        end

    end

    @testset "BKV18(2)" begin

        V = Profile([1 1 0 1 0
                     0 1 0 0 1
                     1 1 1 0 1])

        res = alloc_bkv18_2(V)
        A = res.alloc

        @test A isa Allocation
        @test check_partition(A)
        @test res.mnw == alloc_mnw(V).mnw

        V = Profile([1 0; 1 0; 0 1])

        res = alloc_bkv18_2(V)
        A = res.alloc
        @test res.mnw == nash_welfare(V, A) == 1
        @test nash_welfare(V, A, nonzero=false) == 0

        @test alloc_bkv18_2(Profile([0 0; 0 0])).mnw == 0

        V = Profile(ones(2, 10))
        for _ = 1:10
            @test alloc_bkv18_2(V).mnw == 25
        end

        for _ = 1:10
            V = Profile(rand(Bool, rand(2:5), rand(5:15)))
            @test alloc_bkv18_2(V).mnw == alloc_mnw(V).mnw
        end

        # Regression test
        V = Profile([0 0 1 0 1 1; 0 1 0 1 1 1; 0 1 0 1 0 0])
        for _ = 1:10
            alloc_bkv18_2(V) # Should not throw an exception
        end

        # Even distribution of unvalued items:
        V = Profile([1 1 1 0 0 0 0 0 0
                     0 0 0 0 0 0 0 0 0
                     0 0 0 0 0 0 0 0 0])
        A = alloc_bkv18_2(V).alloc
        for g = 4:9
            @test !owned(A, g)
        end

        res = alloc_bkv18_2(V, complete=true)
        @test res.mnw == 3
        A = res.alloc
        for i in agents(A)
            @test length(bundle(A, i)) == 3
        end

    end

    @testset "GHSS18(4)" begin

        # A modified version of the valuation function from the instance
        # where the best possible allocation has α = 3/4. The MMS with this
        # valuation function and m = 2n items, is 2.
        function v(m, B)
            if length(B) != 2
                return length(B)
            end

            g, g′ = minimum(B), maximum(B)
            if g == 1 && g′ == m || floor(g / 2) == floor(g′ / 2)
                return 2
            end

            return 3/2
        end

        # A function that creates a new set, with all items being incremented by
        # 1 (if m ∈ B, then 1 is in the new set). Setting the valuation of an
        # agent to `v(m, inc(m, B))` gives the agent an MMS of 2.
        inc(m, B) = Set(mod(g, 1:m) for g in B)

        n = 5
        m = 2n
        Vf = vcat([B -> v(m, B) for _ in 1:(n - 1)], [B -> v(m, inc(m, B))])
        V = Submodular(Vf, m)

        # Test when the MMS of each agent is known
        res = alloc_ghss18_4(V, repeat([2], n))

        @test !res.fail

        # The value each agent receives should be at least 1/3 * μᵢ = 2/3
        A = res.alloc
        for i in agents(V)
            @test value(V, i, bundle(A, i)) ≥ 2/3
        end

        # Test when the MMS of each agent is unknown
        res = alloc_ghss18_4b(V)

        # The value each agent receives should be at least 1/3 * μᵢ = 2/3
        for i in agents(V)
            @test value(V, i, bundle(A, i)) ≥ 2/3
        end
    end

    slow_tests &&
    @testset "MMS approximation with card. constr." begin

        # A default test set for all algorithms
        V₁ = V₀
        C₁ = Counts(
            [1, 3, 7, 9]          => 1,
            [4, 6, 8, 10, 11, 12] => 3,
            [2, 5]                => 2,
        )
        MMSs₁ = [mms(V₁, i, C₁).mms for i in agents(V₁)]

        # Check if alg finds an allocation that is complete, does not break the
        # cardinality constraints and gives each agent a bundle valued at no
        # less than `α * MMSs[i]`.
        function test_alg(alg, α, V, C, MMSs)

            A = alg(V, C).alloc

            @test A isa Allocation
            @test check_partition(A)

            # The allocation must not break the cardinality constraints
            for i in agents(V), c in C
                @test sum(owner(A, g) == i for g in c) ≤ threshold(c)
            end

            for i in agents(V)
                @test value(V, i, bundle(A, i)) ≥ α * MMSs[i]
            end

        end

        @testset "1/3-MMS - BB18(3)" begin

            test_alg(alloc_bb18_3, 1/3, V₁, C₁, MMSs₁)

        end

        @testset "1/2-MMS - " begin

            test_alg(alloc_half_mms, 1/2, V₁, C₁, MMSs₁)

            # The second half of the bag filling algorithm, where the floor_n(C)
            # highest-valued items are not worth 1/2 and thus ceil_n(C) must be
            # used instead, does not often get run when a random instance is
            # created. Thus, this tests the workings of that part
            let

                C = Counts(
                    [1, 5, 6]           => 3,
                    [2]                 => 1,
                    [3]                 => 1,
                    [4]                 => 3,
                    [7, 8, 9, 10]       => 5,
                )

                V = Additive([
                    0.2 0.4 0.4 0.4 0.1 0.1 0.1 0.1 0.1 0.1;
                    0.2 0.4 0.4 0.4 0.1 0.1 0.1 0.1 0.1 0.1
                ])

                test_alg(alloc_half_mms, 1/2, V, C, [1, 1])

            end

        end

    end

end

return nothing

end


text:
# Convert between representations of tensors

"""
    cijkl(C) -> c

Convert the 6x6 Voigt matrix `C` into the 3x3x3x3 tensor `c`
"""
function cijkl(C)
    a = VOIGT_CONTRACTION_MATRIX
    if ! is_6x6(C)
        error("CIJ.cijkl: Input must be a 6x6 array")
    end
    c = MArray{Tuple{3,3,3,3},eltype(C),4,81}(undef)
    @inbounds for i = 1:3, j = 1:3, k = 1:3, l = 1:3
        c[i,j,k,l] = C[a[i,j],a[k,l]]
    end
    return c
end

"""
    cij(c) -> C

Convert the 3x3x3x3 elasticity tensor `c` into the 6x6 Voigt matrix `C`
"""
function cij(c)
    a = VOIGT_CONTRACTION_MATRIX
    if size(c) != (3,3,3,3)
        error("CIJ.cij: Input must be a 3x3x3x3 tensor")
    end
    C = zero(EC{float(eltype(c))})
    @inbounds for i = 1:3, j = i:3, k = 1:3, l = k:3
        C[a[i,j],a[k,l]] = c[i,j,k,l]
    end
    return C
end

"""
    C2S(C) -> S

Return the inverse of the stiffness matrix `C`, the compliance matrix `S`.
"""
C2S(C) = LinearAlgebra.inv(C)
C2S(C::EC{T}) where T = EC{T}(C2S(C.data))

"""
    S2C(S) -> C

Return the inverse of the compliance matrix `S`, the stiffness matrix `C`.
"""
S2C(S) = C2S(S)

"""
    C2S!(C) -> S

Invert the stiffness matrix `C` in place, giving the compliance matrix `S`.
"""
C2S!(C) = C .= LinearAlgebra.inv!(LinearAlgebra.lu(C))
C2S!(C::EC) = C .= C2S(C.data)

"""
    S2C!(S) -> C

Invert the compliance matrix `S` in place, giving the siffness matrix `C`.
"""
S2C!(S) = C2S!(S)


text:
include("common.jl")

# Just run one example file
file = "./examples/mp-21202.cif"
formation_energy = compute_formation_energy_per_atom(file)
mpi_master() && @show (file, formation_energy)

max_stars_repo_name: mfherbst/DFTK-materialsproject

max_issues_repo_name: mfherbst/DFTK-materialsproject

max_forks_repo_name: mfherbst/DFTK-materialsproject


text:
@testset "164.maximum-gap.jl" begin
    @test maximum_gap([3, 6, 9, 1]) == 3
    @test maximum_gap(Int[]) == 0
end


text:
# This file implements the adaptive SIS model from :
#
#      Gross, Dommar, Blasius: "Epidemic dynamics on an adaptive network"
#      Physical Review Letters 96, 208701, 2006
#      doi: 10.1103/PhysRevLett.96.208701

using Fastnet

const S=1                   # Node state 1: Susceptible node
const I=2                   # Node state 2: Infected node
const SI=1                  # Link state 1: Susceptible-Infected link

p=0.004                     # Infection rate (per SI-link)
r=0.002                     # Recovery rate (per I-node)
w=0.2                       # Rewiring rate (per SI-link)

SI_link=LinkType(S,I)     # This describes what we mean by SI-link

# Lets make a network of 100k nodes and 400k links, 2 node states, that keeps track of SI links
net=FastNet(100000,1000000,2,[SI_link]; nodealias=["S","I"], linkalias=["SI"])

randomgraph!(net)           # Initialize as ER-random graph (all nodes will be in state 1: S)

for i=1:96000               # Infect 20 nodes at random
    node=randomnode(net,S)
    nodestate!(net,node,I)
end

function rates!(rates,t)    # This functins computes the rates of processes
    infected=countnodes_f(net,I)        # count the infected nodes
    activelinks=countlinks_f(net,SI)    # count the SI links
    infrate=p*activelinks               # compute total infection rate
    recrate=r*infected                  # compute total recovery rate
    rewrate=w*activelinks               # compute total rewiring rate
    rates[1]=infrate                    # Return the values by filling the rates array
    rates[2]=recrate
    rates[3]=rewrate
    nothing
end

function recovery!()        # This is what we do when the recovery process is triggered
    inode=randomnode_f(net,I)                   # Find a random infected node
    nodestate_f!(net,inode,S)                   # Set the state of the node to susceptible
end

function infection!()       # This is what we do when the infection process is triggered
    alink=randomlink_f(net,SI)                   # Find a random SI link
    nodestate_f!(net,linksrc_f(net,alink),I)     # Set both endpoints of the link to infected
    nodestate_f!(net,linkdst_f(net,alink),I)
end

function rewire!()         # This is what we do  when
    lnk=randomlink_f(net,SI)                    # Find a random SI link
    src=linksrc_f(net,lnk)
    dst=linkdst_f(net,lnk)
    sus=randomnode_f(net,S)
    destroylink_f!(net,lnk)
    if nodestate_f(net,src)==S
        makelink_f!(net,src,sus)
    else
        makelink_f!(net,sus,dst)
    end
end

sim=FastSim(net,rates!,[infection!,recovery!,rewire!])   # initialize the simulation

@time runsim!(sim,500,20)                       # Run for 60 timeunits (reporting every 5)


text:
# using Pkg
# Pkg.add(["DataFrames", "DataFramesMeta", "CSV"])
# Pkg.add(PackageSpec(url="https://github.com/FNj/Hose.jl.git", rev="indexing"))

lib_load_time = @elapsed using DataFrames, CSV, DataFramesMeta, Hose, Statistics, StatsBase, Dates
# import Base.Meta.@dump
# const dir = @__DIR__

optionally_convert_to_df(df::DataFrame) = df
optionally_convert_to_df(df) = df |> DataFrame

file_load_compilation_time = @elapsed CSV.File("data/bitstamp.csv", missingstring="NA") |> optionally_convert_to_df
file_load_time = @elapsed const bitstamp = CSV.File("data/bitstamp.csv", missingstring="NA") |> optionally_convert_to_df

file_load_compilation_time -= file_load_time

# size(bitstamp, 1) / 227496
# names(hflights)
# head(hflights)
# describe(hflights)

# macro sample(df, n::Int)
#     esc(:($df[sample(1:nrow($df), $n), :]))
# end

# macro sample(df, q::Real)
#     esc(:($df[sample(1:nrow($df), round(Int, $q * nrow($df))), :]))
# end

function preprocess(df)
    @hose df |> @transform(Datetime = Dates.unix2datetime.(:Timestamp)) |> @transform(YearMonth = Dates.yearmonth.(:Datetime))
end

preprocess_compilation_time = @elapsed preprocess(bitstamp)
preprocess_exec_time = @elapsed const bitstamp_dt = preprocess(bitstamp)
preprocess_compilation_time -= preprocess_exec_time

# describe(bitstamp_dt)

# describe(bitstamp_dt)
# @time @hose bitstamp_dt |> @where(first.(:YearMonth) .< 2018)

function process(df)
    @hose df |>
    @by(:YearMonth, AvgClose = mean(:Close), MaxHigh = maximum(:High))
end

process_compile_time = @elapsed process(bitstamp_dt[1:5, :])
const N = 10
times = zeros(N)
# GC.enable(false)
# for i=1:N
#     GC.gc()
#     sleep(0.01)
#     times[i] = @elapsed process(hflights)
# end
# GC.enable(true)
#
# mean_time_no_GC = mean(times)

for i=1:N
    times[i] = @elapsed process(bitstamp_dt)
end

process_exec_time = mean(times)
process_compile_time -= process_exec_time
# process_strrev(df) = @hose df |> @transform(TailNum2=reverse.(:TailNum))
#
# string_reversal_compile_time = @elapsed process_strrev(hflights)
# string_reversal_time = @elapsed process_strrev(hflights)

println("""Stats:
- Libraries loading time: $lib_load_time s,
- File loading compilation time: $file_load_compilation_time s,
- File loading time: $file_load_time s,
- Preprocessing compilation time: $preprocess_compilation_time s,
- Preprocessing execution time: $preprocess_exec_time s,
- Group by compilation time: $process_compile_time s,
- Group by execution time: $process_exec_time s.""")

using Dates
task_names = vcat(["Library loading"], fill("File loading", 2), fill("Preprocessing", 2), fill("Processing", 2))
task_types = vcat(["Loading"], repeat(["Compilation", "Execution"], 3))
task_exec_times = [lib_load_time, file_load_compilation_time, file_load_time, preprocess_compilation_time, preprocess_exec_time, process_compile_time, process_exec_time]
stats_df = DataFrame(DateTime=fill(Dates.now(), length(task_names)), Language=fill("Julia", length(task_names)), Dataset=fill("Bitstamp", length(task_names)), TaskNames=task_names, TaskTypes=task_types, TaskExecTimes=task_exec_times)

try
    vcat(CSV.read("logs/log.csv") |> DataFrame, stats_df)
catch
    stats_df
end |> CSV.write("logs/log.csv")

max_stars_repo_name: RevoltBI/Julia-Meetup

max_issues_repo_name: RevoltBI/Julia-Meetup

max_forks_repo_name: RevoltBI/Julia-Meetup


text:
machine = (function ()
    isinteractive() && @info "Compiling FASTQ FSM..."

    re = Automa.RegExp

    hspace = re"[ \t\v]"

    header1 = let
        identifier = re.rep(re.any() \ re.space())
        identifier.actions[:enter] = [:pos]
        identifier.actions[:exit]  = [:header1_identifier]

        description = re.cat(re.any() \ re.space(), re"[^\r\n]*")
        description.actions[:enter] = [:pos]
        description.actions[:exit]  = [:header1_description]

        re.cat('@', identifier, re.opt(re.cat(re.rep1(hspace), re.opt(description))))
    end

    sequence = re"[A-z]*"
    sequence.actions[:enter] = [:pos]
    sequence.actions[:exit]  = [:sequence]

    header2 = let
        identifier = re.rep1(re.any() \ re.space())
        description = re.cat(re.any() \ hspace, re"[^\r\n]*")
        re.cat('+', re.opt(re.cat(identifier, re.opt(re.cat(re.rep1(hspace), description)))))
    end
    header2.actions[:enter] = [:pos]
    header2.actions[:exit]  = [:header2]

    quality = re"[!-~]*"
    quality.actions[:enter] = [:pos]
    quality.actions[:exit]  = [:quality]

    newline = let
        lf = re"\n"
        lf.actions[:enter] = [:countline]
        re.cat(re.opt('\r'), lf)
    end

    sep = re.opt('\r') * re"\n"
    sep.actions[:exit] = [:countline, :sep]

    record = re.cat(header1, newline, sequence, newline, header2, newline, quality)
    record.actions[:enter] = [:mark]
    record.actions[:exit] = [:record]

    fastq = re.rep(re.cat(record, sep)) * re.opt(record)

    Automa.compile(fastq)
end)()

#write("fastq.dot", Automa.machine2dot(machine))
#run(`dot -Tsvg -o fastq.svg fastq.dot`)

function appendfrom!(dst, dpos, src, spos, n)
    if length(dst) < dpos + n - 1
        resize!(dst, dpos + n - 1)
    end
    copyto!(dst, dpos, src, spos, n)
    return dst
end

function is_same_mem(data, pos1, pos2, len)
    checkbounds(data, 1:pos1+len-1)
    checkbounds(data, 1:pos2+len-1)
    return ccall(:memcmp, Cint, (Ptr{Cvoid}, Ptr{Cvoid}, Csize_t), pointer(data, pos1), pointer(data, pos2), len) == 0
end

actions = Dict(
    :mark => :(@mark),
    :pos => :(pos = @relpos(p)),
    :countline => :(linenum += 1),
    :header1_identifier => :(record.identifier = pos:@relpos(p-1)),
    :header1_description => :(record.description = pos:@relpos(p-1)),
    :sequence => :(record.sequence = pos:@relpos(p-1)),
    :header2 => :(second_header_pos = pos+1; second_header_len = @relpos(p)-(pos+1)),
    :quality => :(record.quality = pos:@relpos(p-1)),
    :sep => :(@escape),
    :record => quote
        appendfrom!(record.data, 1, data, @markpos, p-@markpos)
        record.filled = 1:(p-@markpos)
        found = true
    end,
)

initcode = quote
    pos = 0
    second_header_pos = 0
    second_header_len = 0
    found = false
    initialize!(record)
    cs, linenum = state
end

loopcode = quote
    if cs < 0
        throw(ArgumentError("malformed FASTQ file at line $(linenum)"))
    elseif found && length(record.sequence) != length(record.quality)
        throw(ArgumentError("mismatched sequence and quality length"))
    elseif found && second_header_len > 0
        first_header_pos = first(record.identifier)
        first_header_len = max(last(record.identifier), last(record.description)) - first_header_pos + 1
        if first_header_len != second_header_len || !is_same_mem(record.data, first_header_pos, second_header_pos, first_header_len)
            throw(ArgumentError("mismatched headers"))
        end
    elseif found && transform != nothing
        transform(record.data, record.sequence)
    end
    found && @goto __return__
end

returncode = :(return cs, linenum, found)

context = Automa.CodeGenContext(generator = :goto, checkbounds=false, loopunroll=4)

isinteractive() && @info "Generating FASTQ parsing code..."

Automa.Stream.generate_reader(
    :readrecord!,
    machine,
    arguments = (:(record::Record), :(state::Tuple{Int,Int}), :(transform)),
    actions = actions,
    context = context,
    initcode = initcode,
    loopcode = loopcode,
    returncode = returncode
) |> eval


text:
# Autogenerated wrapper script for PETSc_jll for x86_64-apple-darwin-libgfortran5
export libpetsc, libpetsc_Float32_Complex_Int32, libpetsc_Float32_Complex_Int64, libpetsc_Float32_Real_Int32, libpetsc_Float32_Real_Int64, libpetsc_Float64_Complex_Int32, libpetsc_Float64_Complex_Int64, libpetsc_Float64_Real_Int32, libpetsc_Float64_Real_Int64

using OpenBLAS32_jll
using MPICH_jll
using MicrosoftMPI_jll
using CompilerSupportLibraries_jll
JLLWrappers.@generate_wrapper_header("PETSc")
JLLWrappers.@declare_library_product(libpetsc, "@rpath/libpetsc.3.15.dylib")
JLLWrappers.@declare_library_product(libpetsc_Float32_Complex_Int32, "@rpath/libpetsc.3.15.dylib")
JLLWrappers.@declare_library_product(libpetsc_Float32_Complex_Int64, "@rpath/libpetsc.3.15.dylib")
JLLWrappers.@declare_library_product(libpetsc_Float32_Real_Int32, "@rpath/libpetsc.3.15.dylib")
JLLWrappers.@declare_library_product(libpetsc_Float32_Real_Int64, "@rpath/libpetsc.3.15.dylib")
JLLWrappers.@declare_library_product(libpetsc_Float64_Complex_Int32, "@rpath/libpetsc.3.15.dylib")
JLLWrappers.@declare_library_product(libpetsc_Float64_Complex_Int64, "@rpath/libpetsc.3.15.dylib")
JLLWrappers.@declare_library_product(libpetsc_Float64_Real_Int32, "@rpath/libpetsc.3.15.dylib")
JLLWrappers.@declare_library_product(libpetsc_Float64_Real_Int64, "@rpath/libpetsc.3.15.dylib")
function __init__()
    JLLWrappers.@generate_init_header(OpenBLAS32_jll, MPICH_jll, MicrosoftMPI_jll, CompilerSupportLibraries_jll)
    JLLWrappers.@init_library_product(
        libpetsc,
        "lib/petsc/double_real_Int32/lib/libpetsc.3.15.2.dylib",
        RTLD_LAZY | RTLD_DEEPBIND,
    )

    JLLWrappers.@init_library_product(
        libpetsc_Float32_Complex_Int32,
        "lib/petsc/single_complex_Int32/lib/libpetsc.3.15.2.dylib",
        RTLD_LAZY | RTLD_DEEPBIND,
    )

    JLLWrappers.@init_library_product(
        libpetsc_Float32_Complex_Int64,
        "lib/petsc/single_complex_Int64/lib/libpetsc.3.15.2.dylib",
        RTLD_LAZY | RTLD_DEEPBIND,
    )

    JLLWrappers.@init_library_product(
        libpetsc_Float32_Real_Int32,
        "lib/petsc/single_real_Int32/lib/libpetsc.3.15.2.dylib",
        RTLD_LAZY | RTLD_DEEPBIND,
    )

    JLLWrappers.@init_library_product(
        libpetsc_Float32_Real_Int64,
        "lib/petsc/single_real_Int64/lib/libpetsc.3.15.2.dylib",
        RTLD_LAZY | RTLD_DEEPBIND,
    )

    JLLWrappers.@init_library_product(
        libpetsc_Float64_Complex_Int32,
        "lib/petsc/double_complex_Int32/lib/libpetsc.3.15.2.dylib",
        RTLD_LAZY | RTLD_DEEPBIND,
    )

    JLLWrappers.@init_library_product(
        libpetsc_Float64_Complex_Int64,
        "lib/petsc/double_complex_Int64/lib/libpetsc.3.15.2.dylib",
        RTLD_LAZY | RTLD_DEEPBIND,
    )

    JLLWrappers.@init_library_product(
        libpetsc_Float64_Real_Int32,
        "lib/petsc/double_real_Int32/lib/libpetsc.3.15.2.dylib",
        RTLD_LAZY | RTLD_DEEPBIND,
    )

    JLLWrappers.@init_library_product(
        libpetsc_Float64_Real_Int64,
        "lib/petsc/double_real_Int64/lib/libpetsc.3.15.2.dylib",
        RTLD_LAZY | RTLD_DEEPBIND,
    )

    JLLWrappers.@generate_init_footer()
end  # __init__()


text:
# This file holds the definition of the functions pertaining to the
#   Transition type.

# The functions of the Transition type require the State type.
requiredTypes = [ "state", "transition" ]

for reqType in requiredTypes
    if !isdefined( Symbol( uppercase( string( reqType[ 1 ] ) ) * reqType[ 2:end ] ) )
        include( joinpath( typePath, reqType * ".jl" ) )
    end  # if !isdefined( Symbol( ...
end  # for reqType in requiredTypes

export setName,
       setState,
       setSchedule,
       addCondition!,
       clearConditions!,
       addAttributeChange!,
       clearAttributeChanges!,
       setTransProbabilities,
       setMaxAttempts,
       setTimeBetweenAttempts,
       setFireAfterFail,
       setMaxFlux,
       setHasPriority

dummyState = State( "Dummy" )

"""
```
setName( trans::Transition,
         name::String )
```
This function sets the name of the transition `trans` to `name`.

This function returns `nothing`.
"""
function setName( trans::Transition, name::String )::Void

    trans.name = name
    return

end  # setName( trans, name )

"""
```
setState( trans::Transition,
          state::State,
          isEndState::Bool = false )
```
This function sets one of the states (start/end) of the transition `trans` to
`state`. If `isEndState` is `true`, the end state will be set, otherwise the
start state will be set.

This function returns `nothing`.
"""
function setState( trans::Transition, state::State,
    isEndState::Bool = false )::Void

    if isEndState
        trans.endState = state
    else
        trans.startState = state
    end  # if isEndState

    return

end  # setState( trans, state, isEndState )

"""
```
setSchedule( trans::Transition,
             freq::T1,
             offset::T2 = 0.0 )
```
This function sets the schedule of the transition `trans`. This transition will
be checked every `freq` time units with an offset of `offset` with respect to
the start of the simulation.

This function returns `nothing`. If the entered period is ⩽ 0.0, the function
will issue a warning and not change the schedule.
"""
function setSchedule( trans::Transition, freq::T1, offset::T2 = 0.0 )::Void where T1 <: Real where T2 <: Real

    if freq <= 0.0
        warn( "Time between two transition checks must be > 0.0" )
        return
    end  # if freq <= 0.0

    trans.freq = freq
    trans.offset = offset % freq + ( offset < 0.0 ? freq : 0.0 )
    return

end  # setSchedule( trans, freq, offset )

"""
```
addCondition!( trans::Transition,
               cond::Condition )
```
This function adds the consition `cond` as extra condition to the transition
`trans`. This function does NOT check if conditions are contradictory with each
other or with the start state.

This function returns `nothing`.
"""
function addCondition!( trans::Transition, cond::Condition )::Void

    push!( trans.extraConditions, cond )
    return

end  # addCondition!( trans, cond )

"""
```
clearConditions!( trans::Transition )
```
This function clear all extra conditions from the transition `trans`.

This function returns `nothing`.
"""
function clearConditions!( trans::Transition )::Void

    empty!( trans.extraConditions )
    return

end  # clearConditions!( trans )

"""
```
addAttributeChange!( trans::Transition,
                     attr::String,
                     newVal::String )
```
This function adds an extra attribute change to the transition `trans`, ensuring
that the attribute `attr` is changed to the value `newVal`. If the transition
already changes the attribute, the change gets overwritten and the function
issues a warning.

This function returns `nothing`.
"""
function addAttributeChange!( trans::Transition, attr::String,
    newVal::String )::Void

    # XXX Logic overhaul needed?
    changeIndex = findfirst( tmpAttr -> tmpAttr.name == attr,
        trans.extraChanges )

    if changeIndex == 0
        newAttr = PersonnelAttribute( attr, Dict( newVal => 1.0 ), false )
        push!( trans.extraChanges, newAttr )
    else
        warn( "Attribute change for attribute '$attr' already recorded. Overwriting the change." )
        setAttrValues!( trans.extraChanges, Dict( newVal => 1.0 ) )
    end  # if changeIndex == 0

    return

end  # addAttributeChange!( trans, attr, newVal )

"""
```
clearAttributeChanges!( trans::Transition )
```
This function clears all attribute changes from the transition `trans`.

This function returns `nothing`.
"""
function clearAttributeChanges!( trans::Transition )::Void

    empty!( trans.extraChanges)
    return

end  # clearAttributeChanges!( trans )

"""
```
setTransProbabilities( trans::Transition,
                       probs::Vector{Float64} )
```
This function sets the execution probabilities of the state transition `trans`
to the valid entries in `probs`. Valid entries are between 0.0 and 1.0
inclusive, and invalid entries are removed. Additionally, all leading 0.0
entries will be removed as well.

This function returns `nothing`. If there are no valid probabilities, the
function will issue a warning to that effect, and won't make any changes.
"""
function setTransProbabilities( trans::Transition,
    probs::Vector{Float64} )::Void

    # Filter out all invalid probabilities.
    tmpProbs = filter( prob -> 0.0 <= prob <= 1.0, probs )
    firstNonzero = findfirst( prob -> 0.0 < prob, tmpProbs )

    # If there are no nonzero entries in the list of valid probabilities, issue
    #   a warning and don't make changes.
    if firstNonzero == 0
        warn( "No valid probabilities entered. Not making any changes to transition." )
    else
        tmpProbs = tmpProbs[ firstNonzero:end ]
        trans.probabilityList = tmpProbs
    end  # if firstNonzero == 0

    return

end  # setTransProbabilities( trans, probs )

"""
```
setMaxAttempts( trans::Transition,
                maxAttempts::T )
    where T <: Integer
```
This function sets the maximum number of times a personnel member may attempt
the transition `trans` to `maxAttempts`. Values below -1 are invalid. A value of
-1 means that there are an infinite number of attempts, and 0 means there are as
many attempts as entries in the vector of probabilities.

This function returns `nothing`. If an invalid maximum number of attempts is
entered, the function issues a warning to that effect and makes no changes to
the transition.
"""
function setMaxAttempts( trans::Transition, maxAttempts::T )::Void where T <: Integer

    if maxAttempts < -1
        warn( "Max number of attempts must be >= -1. Not making any changes." )
        return
    end  # if maxAttempts < -1

    trans.maxAttempts = maxAttempts
    return

end  # setMaxAttempts( trans, maxAttempts )

"""
```
function setTimeBetweenAttempts( trans::Transition,
                                 timeBetweenAttempts::T )
    where T <: Real
```
This function sets the time between two successive attempts at the transition
`trans` to `timeBetweenAttempts`. Values < 0.0 are invalid, and 0.0 means that
the time is the same as the schedule time of the transition.

This function returns `nothing`. If an invalid time is entered, the function
will issue a warning to that effect and makes no changes to the transition.
"""
function setTimeBetweenAttempts( trans::Transition,
    timeBetweenAttempts::T )::Void where T <: Real

    if timeBetweenAttempts < 0.0
        warn( "Time between attempts must be >= 0.0. Not making any changes." )
        return
    end  # if timeBetweenAttempts < 0.0

    trans.timeBetweenAttempts = timeBetweenAttempts
    return

end  # setTimeBetweenAttempts( trans, timeBetweenAttempts )

"""
```
setFireAfterFail( trans::Transition,
                  isFiredOnFail::Bool )
```
This function changes the isFiredOnFail flag of the transition `trans` to
`isFiredOnFail`.

This function returns `nothing`.
"""
function setFireAfterFail( trans::Transition, isFiredOnFail::Bool )::Void

    trans.isFiredOnFail = isFiredOnFail
    return

end  # setFireAfterFail( trans, isFiredOnFail )

"""
```
setMaxFlux( trans::Transition,
            maxFlux::T )
    where T <: Integer
```
This function sets the maximum flux of the transition `trans` to `maxFlux`.
Values below -1 are invalid. A value of -1 means that there is no limit on the
flux.

This function returns `nothing`. If an invalid maximum flux is entered,
the function issues a warning to that effect and makes no changes to the
transition.
"""
function setMaxFlux( trans::Transition, maxFlux::T )::Void where T <: Integer

    if maxFlux < -1
        warn( "Max flux must be >= -1. Not making any changes." )
        return
    end  # if maxFlux < -1

    trans.maxFlux = maxFlux
    return

end  # setMaxFlux( trans, maxFlux )

"""
```
setHasPriority( trans::Transition,
                hasPrio::Bool )
```
This function sets the `hasPriority` flag of the transition `trans` to
`hasPrio`, where `true` means the transition can overrule the target state's
population target.

This function returns `nothing`.
"""
function setHasPriority( trans::Transition, hasPrio::Bool )::Void

    trans.hasPriority = hasPrio
    trans.transPriority = hasPrio ? 0 : 1
    return

end  # setHasPriority( trans, hasPrio )

function Base.show( io::IO, trans::Transition )

    print( io, "    Transition $(trans.name): '$(trans.startState.name)' to '$(trans.endState.name)'" )
    print( io, "\n      Occurs with period $(trans.freq) (offset $(trans.offset))" )

    if !isempty( trans.extraConditions )
        print( io, "\n      Extra conditions: $(trans.extraConditions)" )
    end  # if !isempty( trans.extraConditions )

    if !isempty( trans.extraChanges )
        print( io, "\n      Extra changes: $(trans.extraChanges)" )
    end

    if trans.maxAttempts == -1
        print( io, "\n      Infinite number of" )
    else
        nAttempts = trans.maxAttempts == 0 ? length( trans.probabilityList ) :
            trans.maxAttempts
        print( io, "\n      Max $nAttempts" )
    end  # if trans.maxAttempts == -1

    print( io, " attempts" )

    if trans.isFiredOnFail && ( trans.maxAttempts != -1 )
        print( io, "\n      Personnel members are fired if transition isn't succesful." )
    end  # if trans.isFiredOnFail

    if trans.maxFlux >= 0
        print( io, "\n      Max flux: $(trans.maxFlux)" )

        if trans.hasPriority
            print( io, " (overrides state pop. constraint)" )
        end  # if trans.hasPriority
    end  # if trans.maxFlux >= 0

    print( io, "\n      Transition execution probabilities: " )
    print( io, join( trans.probabilityList .* 100, "%, " ) * '%' )
    print( io, "\n      Transition at priority ", - trans.transPriority )

end  # Base.show( io, trans )

# ==============================================================================
# Non-exported methods.
# ==============================================================================

function readTransition( sheet::XLSX.Worksheet, sLine::T ) where T <: Integer

    newTrans = Transition( sheet[ "A$sLine" ], dummyState, dummyState )
    startState, endState = sheet[ "B$sLine" ], sheet[ "C$sLine" ]
    setSchedule( newTrans, sheet[ "D$sLine" ], sheet[ "E$sLine" ] )

    # Read time related conditions.
    sCol = 7
    nTimeConds = Int( sheet[ XLSX.CellRef( sLine, sCol ) ] )

    for ii in 1:nTimeConds
        newCond, isCondOkay = processCondition(
            sheet[ XLSX.CellRef( sLine, sCol + ii ) ],
            sheet[ XLSX.CellRef( sLine + 1, sCol + ii ) ],
            sheet[ XLSX.CellRef( sLine + 2, sCol + ii ) ] )

        if isCondOkay
            addCondition!( newTrans, newCond )
        end  # if isCondOkay
    end  # for ii in 1:nTimeConds

    # Read other/attribute conditions.
    sCol += nTimeConds + 2
    nOtherConds = Int( sheet[ XLSX.CellRef( sLine, sCol ) ] )

    for ii in 1:nOtherConds
        newCond, isCondOkay = processCondition(
            sheet[ XLSX.CellRef( sLine, sCol + ii ) ],
            sheet[ XLSX.CellRef( sLine + 1, sCol + ii ) ],
            sheet[ XLSX.CellRef( sLine + 2, sCol + ii ) ] )

        if isCondOkay
            addCondition!( newTrans, newCond )
        end  # if isCondOkay
    end  # for ii in 1:nOtherConds

    # Read dynamics.
    sCol += nOtherConds + 2
    maxFlux = sheet[ XLSX.CellRef( sLine, sCol ) ]
    setMaxFlux( newTrans, isa( maxFlux, Missings.Missing ) ? -1 :
        Int( maxFlux ) )
    setHasPriority( newTrans, XLSX.CellRef( sLine, sCol + 1 ) == "NO" )
    maxAttempts = sheet[ XLSX.CellRef( sLine, sCol + 2 ) ]
    setMaxAttempts( newTrans, isa( maxAttempts, Missings.Missing ) ? -1 :
        Int( maxAttempts ) )

    if isa( maxAttempts, Missings.Missing )
        setFireAfterFail( newTrans, false )
    else
        setFireAfterFail( newTrans,
            sheet[ XLSX.CellRef( sLine, sCol + 3 ) ] == "YES" )
    end  # if !isa( maxAttempts, Missings.Missing )

    # Read probability vector.
    sCol += 4
    nProbs = Int( sheet[ XLSX.CellRef( sLine, sCol ) ] )
    probs = Vector{Float64}( nProbs )

    for ii in 1:nProbs
        probs[ ii ] = sheet[ XLSX.CellRef( sLine, sCol + ii ) ]
    end  # for ii in 1:nProbs

    setTransProbabilities( newTrans, probs )

    # Read extra attribute changes.
    sCol += nProbs + 2
    nExtraChanges = Int( sheet[ XLSX.CellRef( sLine, sCol ) ] )

    for ii in 1:nExtraChanges
        addAttributeChange!( newTrans,
            sheet[ XLSX.CellRef( sLine, sCol + ii ) ],
            sheet[ XLSX.CellRef( sLine + 1, sCol + ii ) ] )
    end  # for ii in 1:nExtraChanges

    return newTrans, startState, endState

end  # readTransition( sheet, sLine )

function purgeRedundantExtraChanges( trans::Transition )::Void

    endStateReqs = collect( keys( trans.endState.requirements ) )
    indsToRemove = find( attr -> attr.name ∈ endStateReqs, trans.extraChanges )
    deleteat!( trans.extraChanges, indsToRemove )
    return

end  # purgeRedundantExtraChanges( trans )

function initiateTransitionProcesses( mpSim::ManpowerSimulation )::Void

    processTransPriorities( mpSim )
    assignTransPriorities( mpSim )

    for state in keys( mpSim.initStateList ),
        trans in mpSim.initStateList[ state ]
        @process transitionProcess( mpSim.sim, trans, mpSim )
    end  # for state in keys( mpSim.initStateList ), ...

    for state in keys( mpSim.otherStateList ),
        trans in mpSim.otherStateList[ state ]
        @process transitionProcess( mpSim.sim, trans, mpSim )
    end  # for state in keys( mpSim.otherStateList ), ...

    return

end  # initiateTransitionProcesses( mpSim )

@resumable function transitionProcess( sim::Simulation, trans::Transition,
    mpSim::ManpowerSimulation )

    processTime = Dates.Millisecond( 0 )
    tStart = now()

    # Initialize the schedule.
    timeOfCheck = now( sim ) - trans.offset
    timeOfCheck = floor( timeOfCheck / trans.freq + 1.0 ) * trans.freq +
        trans.offset
    priority = - trans.transPriority
    maxAttempts = trans.maxAttempts == 0 ? length( trans.probabilityList ) :
        trans.maxAttempts
    nAttempts = Dict{String, Int}()

    while timeOfCheck <= mpSim.simLength
        processTime += now() - tStart
        @yield( timeout( sim, timeOfCheck - now( sim ), priority = priority ) )
        tStart = now()
        timeOfCheck += trans.freq

        # Identify all persons who're in the start state long enough and are not
        #   already going to transition to another state.
        eligibleIDs = getEligibleIDs( trans, nAttempts, maxAttempts, mpSim )
        checkedIDs = checkExtraConditions( trans, eligibleIDs, mpSim )
        updateAttemptsAndIDs!( nAttempts, maxAttempts, checkedIDs )
        transIDs = determineTransitionIDs( trans, checkedIDs, nAttempts )

        # Halt execution until the transition candidates for all transitions at
        #   the current time are determined.
        processTime += now() - tStart
        # @yield( timeout( sim, 0, priority = priority - 1 ) )
        tStart = now()
        executeTransitions( trans, transIDs, mpSim )
        newStateList = updateStates( trans, transIDs, nAttempts, mpSim )
        updateTimeToAttrition( newStateList, mpSim )
        firePersonnel( trans, checkedIDs, transIDs, nAttempts, maxAttempts,
            mpSim )
    end  # while timeOfCheck <= mpSim.simLength

    processTime += now() - tStart
    println( "Transition process for '$(trans.name)' took $(processTime.value / 1000) seconds." )

end  # transitionProcess( sim, trans, mpSim )

function getEligibleIDs( trans::Transition, nAttempts::Dict{String, Int},
    maxAttempts::Int, mpSim::ManpowerSimulation )::Vector{String}

    eligibleIDs = collect( keys( trans.startState.inStateSince ) )
    filter!( id -> !trans.startState.isLockedForTransition[ id ], eligibleIDs )

    # Check if attempts have been exhausted (imprtant if transition doesn't fire
    #   after exhaustion of attempts).
    if maxAttempts > 0
        # The first condition means 0 attempts so far.
        filter!( id -> !haskey( nAttempts, id ) ||
            ( nAttempts[ id ] < maxAttempts ), eligibleIDs )
    end  # if maxAttempts > 0

    for cond in filter( cond -> cond.attr == "time in state",
        trans.extraConditions )
        filter!( id -> cond.rel( now( mpSim ) -
            trans.startState.inStateSince[ id ], cond.val ), eligibleIDs )
    end  # for cond in filter( ...

    return eligibleIDs

end  # getEligibleIDs( trans, mpSim )

function updateAttemptsAndIDs!( nAttempts::Dict{String, Int}, maxAttempts::Int,
    eligibleIDs::Vector{String} )::Void

    # Increase number of attempts for all eligible IDs. 0 attempts means
    #   the personnel member has already executed the transition
    #   succesfully while stil being eligible for it.
    for id in eligibleIDs
        if haskey( nAttempts, id ) && ( ( maxAttempts == -1 ) ||
            ( nAttempts[ id ] <= maxAttempts ) ) && ( nAttempts[ id ] > 0 )
            nAttempts[ id ] += 1
        else
            nAttempts[ id ] = 1
        end  # if haskey( nAttempts, id ) && ...
    end  # for id in eligibleIDs

    return

end  # updateAttemptsAndIDs!( nAttempts, eligibleIDs )

function checkExtraConditions( trans::Transition, eligibleIDs::Vector{String},
    mpSim::ManpowerSimulation )::Vector{String}

    checkedIDs = eligibleIDs

    # Check extra conditions.
    if !isempty( eligibleIDs ) && !isempty( trans.extraConditions )
        queryCmd = "SELECT *, ageAtRecruitment + $(now( mpSim )) - timeEntered age, $(now( mpSim )) - timeEntered tenure
            FROM $(mpSim.personnelDBname)
            WHERE $(mpSim.idKey) IN ('$(join( eligibleIDs, "', '" ))')"
        eligibleIDsState = SQLite.query( mpSim.simDB, queryCmd )
        isIDokay = isa.( eligibleIDs, String )

        for cond in filter( cond -> cond.attr != "time in state",
            trans.extraConditions )
            isIDokay = isIDokay .& map( ii -> cond.rel(
                eligibleIDsState[ Symbol( cond.attr ) ][ ii ], cond.val ),
                eachindex( isIDokay ) )
        end  # for cond in filter( ...

        checkedIDs = eligibleIDsState[ Symbol( mpSim.idKey ) ][ isIDokay ]
    end  # if !isempty( eligibleIDs )

    return checkedIDs

end  # checkExtraConditions( trans, eligibleIDs, mpSim )

function determineTransitionIDs( trans::Transition, eligibleIDs::Vector{String},
    nAttempts::Dict{String, Int} )::Vector{String}

    transIDs = eligibleIDs

    if !isempty( eligibleIDs )
        # Determine who undergoes transition.
        nEligible = length( eligibleIDs )
        transRolls = rand( nEligible )
        transIDs = eligibleIDs[ map( ii -> transRolls[ ii ] <
            trans.probabilityList[ min( length( trans.probabilityList ),
            nAttempts[ eligibleIDs[ ii ] ] ) ], 1:nEligible ) ]
            # The n-th probability in the list where n is the lesser of the
            #   size of the probability list and the number of attempts by
            #   the ii-th eligible person.

        # Check how many spots are available in the end state, ONLY if the end
        #   state differs from the start state.
        available = -1

        if ( trans.startState != trans.endState ) &&
            ( trans.endState.stateTarget >= 0 )
            available = max( trans.endState.stateTarget -
                length( trans.endState.inStateSince ), 0 )
        end  # if ( trans.startState != trans.endState ) && ...

        # Determine the number of personnel members to undergo the transition.
        #   This is the minimum of the number of the max flux and the number of
        #   vacancies in the end state.
        toChoose = trans.maxFlux

        if trans.maxFlux == -1
            toChoose = available
        elseif available != -1
            toChoose = min( available, toChoose )
        end  # if trans.maxFlux == -1

        # Take only toChoose random IDs if the number of eligible IDs exceeds
        #   this.
        if ( toChoose != -1 ) && ( length( transIDs ) > toChoose )
            transIDs = transIDs[ sortperm( rand( length( transIDs ) ) )[
                1:toChoose ] ]
                # Get the first toChoose entries of the permutation
                #   vector that puts the vector of random numbers in sorted
                #   order.
        end  # if ( toChoose != -1 ) && ...

        # Lock the chosen IDs for transition.
        foreach( id -> trans.startState.isLockedForTransition[ id ] = true,
            transIDs )
    end  # if !isempty( eligibleIDs )

    return transIDs

end  # determineTransitionIDs( trans, eligibleIDs, nAttempts )

function executeTransitions( trans::Transition, transIDs::Vector{String},
    mpSim::ManpowerSimulation )

    if !isempty( transIDs )
        # Get current state of the personnel members undergoing the
        #   transition.
        queryCmd = "SELECT * FROM $(mpSim.personnelDBname)
            WHERE $(mpSim.idKey) IN ('$(join( transIDs, "', '" ))')"
        transIDsState = SQLite.query( mpSim.simDB, queryCmd )
        tmpIDs = transIDsState[ Symbol( mpSim.idKey ) ]

        # Make changes in personnel database.
        persChangesCmd = Vector{String}()
        changedAttrs = Vector{String}()
        newAttrValues = Vector{String}()

        # End state attributes.
        for attr in keys( trans.endState.requirements )
            push!( changedAttrs, attr )
            push!( newAttrValues,
                trans.endState.requirements[ attr ][ 1 ] )
            push!( persChangesCmd, "'$attr' = '$(newAttrValues[ end ])'" )
        end  # for attr in keys( trans.endState.requirements )

        # Extra attribute changes.
        for attr in trans.extraChanges
            push!( changedAttrs, attr.name )
            push!( newAttrValues, collect( keys( attr.values ) )[ 1 ] )
            push!( persChangesCmd, "'$(attr.name)' = '$(newAttrValues[ end ])'" )
        end  # for attr in trans.extraChanges

        persChangesCmd = "UPDATE $(mpSim.personnelDBname)
            SET $(join( persChangesCmd, ", " ))
            WHERE $(mpSim.idKey) IN ('$(join( tmpIDs, "', '" ))')"
        SQLite.execute!( mpSim.simDB, persChangesCmd )

        # Record attribute changes in history database.
        histChangesCmd = Vector{String}()

        for ii in eachindex( changedAttrs )
            oldAttrValues = transIDsState[ Symbol( changedAttrs[ ii ] ) ]
            changedIDs = tmpIDs[ oldAttrValues .!= newAttrValues[ ii ] ]

            if !isempty( changedIDs )
                suffix = "', '$(changedAttrs[ ii ])', $(now( mpSim )), '$(newAttrValues[ ii ])')"
                push!( histChangesCmd,
                    join( "('" .* changedIDs .* suffix, ", " ) )
            end  # if !isempty( changedIDs )
        end  # for attr in changedAttrs

        if !isempty( histChangesCmd )
            histChangesCmd = "INSERT INTO $(mpSim.historyDBname)
                ($(mpSim.idKey), attribute, timeIndex, strValue) VALUES
                $(join( histChangesCmd, ", "))"
            SQLite.execute!( mpSim.simDB, histChangesCmd )
        end  # if !isempty( histChangesCmd )
    end  # if !isempty( transIDs )

end  # executeTransitions( trans, transIDs, mpSim )

function updateStates( trans::Transition, transIDs::Vector{String},
    nAttempts::Dict{String, Int},
    mpSim::ManpowerSimulation )::Dict{String, Vector{State}}

    newStateList = Dict{String, Vector{State}}()

    if isempty( transIDs )
        return newStateList
    end  # if isempty( transIDs )

    stateUpdates = Vector{String}( length( transIDs ) )
    queryCmd = "SELECT $(mpSim.idKey), ageAtRecruitment, timeEntered
        FROM $(mpSim.personnelDBname)
        WHERE $(mpSim.idKey) in ('$(join( transIDs, "', '" ))')"
    importantTimes = SQLite.query( mpSim.simDB, queryCmd )

    for ii in eachindex( transIDs )
        pid = importantTimes[ ii, 1 ]
        newStateList[ pid ] = [ trans.endState ]
        delete!( trans.startState.inStateSince, pid )
        delete!( trans.startState.isLockedForTransition, pid )
        delete!( nAttempts, pid )
        trans.endState.inStateSince[ pid ] = now( mpSim )
        trans.endState.isLockedForTransition[ pid ] = false
        stateUpdates[ ii ] = "('$pid', $(now( mpSim )), '$(trans.name)', '$(trans.startState.name)', '$(trans.endState.name)')"
        newRetirementTime = computeExpectedRetirementTime( mpSim,
            mpSim.retirementScheme, importantTimes[ ii, 2 ],
            trans.endState.stateRetAge, importantTimes[ ii, 3 ] )
        retireUpdate = "UPDATE $(mpSim.personnelDBname)
            SET expectedRetirementTime = $newRetirementTime
            WHERE $(mpSim.idKey) IS '$pid'"
        SQLite.execute!( mpSim.simDB, retireUpdate )
    end  # for id in transIDs

    transChangesCmd = "INSERT INTO $(mpSim.transitionDBname)
        ($(mpSim.idKey), timeIndex, transition, startState, endState) VALUES
        $(join( stateUpdates, ", " ))"
    SQLite.execute!( mpSim.simDB, transChangesCmd )
    return newStateList

end  # function updateStates( trans, transIDs, nAttempts, mpSim )

function firePersonnel( trans::Transition, checkedIDs::Vector{String},
    transIDs::Vector{String}, nAttempts::Dict{String, Int}, maxAttempts::Int,
    mpSim::ManpowerSimulation )::Void

    # Fire the personnel members who haven't succesfully made the transition
    #   after the maximum number of attempts.
    if trans.isFiredOnFail && ( maxAttempts >= 0 ) && !isempty( checkedIDs )
        fireIDs = filter( id -> ( id ∉ transIDs ) &&
            ( nAttempts[ id ] == maxAttempts ), checkedIDs )

        if !isempty( fireIDs )
            foreach( id -> delete!( nAttempts, id ), fireIDs )
            retirePersons( mpSim, fireIDs, "fired" )
        end  # if !isempty( fireIDs )
    end  # if maxAttempts >= 0

    return

end  # firePersonnel( trans, checkedIDs, transIDs, nAttempts, maxAttempts,
     #   mpSim )

function buildTransitionNetwork( mpSim::ManpowerSimulation,
    states::String... )

    # Filter out non-existing nodes.
    stateList = merge( mpSim.initStateList, mpSim.otherStateList )
    tmpStates = collect( Iterators.filter(
        stateName -> any( state -> state.name == stateName, keys( stateList ) ),
        states ) )
    graphStates = tmpStates
    graphTrans = Vector{String}()

    # Initialise directed graph.
    nStates = length( graphStates )
    graph = MetaDiGraph( DiGraph( nStates + 2 ) )
    inNodeIndex = nStates + 1
    outNodeIndex = nStates + 2
    set_prop!( graph, nStates + 1, :state, "In" )
    set_prop!( graph, nStates + 2, :state, "Out" )

    # Add node labels and transitions for initial states.
    for ii in eachindex( tmpStates )
        set_prop!( graph, ii, :state, tmpStates[ ii ] )
        state = mpSim.stateList[ tmpStates[ ii ] ]

        # Add recruitment edge for initial state.
        if haskey( mpSim.initStateList, state )
            add_edge!( graph, inNodeIndex, ii )
            set_prop!( graph, inNodeIndex, ii, :trans, "recruitment" )
        end  # if any( state -> state.name == tmpStates[ ii ], ...

        # Add retirement edge for states with defined retirement scheme.
        retScheme = mpSim.retirementScheme

        if ( isa( retScheme, Retirement ) &&
            ( ( retScheme.maxCareerLength != 0.0 ) ||
                ( retScheme.retireAge != 0.0 ) ) ) ||
            ( state.stateRetAge != 0.0 )
            add_edge!( graph, ii, outNodeIndex )
            set_prop!( graph, ii, outNodeIndex, :trans, "retirement" )
        end  # if ( isa( retScheme, Retirement ) && ...
    end  # for ii in eachindex( tmpStates )

    push!( graphStates, "In", "Out" )
    nStates += 2

    # Add all other transitions.
    for state in keys( stateList )
        # Is the state in the original list?
        if state.name ∈ tmpStates
            # Find its index.
            startStateIndex = findfirst( tmpState -> tmpState == state.name,
                tmpStates )

            # Add all transitions starting from there.
            for trans in stateList[ state ]
                endStateIndex = findfirst(
                    tmpState -> tmpState == trans.endState.name, graphStates )

                # If end state hasn't been put in the list, add it.
                if endStateIndex == 0
                    add_vertex!( graph )
                    push!( graphStates, trans.endState.name )
                    nStates += 1
                    set_prop!( graph, nStates, :state, trans.endState.name )
                    endStateIndex = nStates
                end  # if endStateIndex == 0

                add_edge!( graph, startStateIndex, endStateIndex )
                set_prop!( graph, startStateIndex, endStateIndex, :trans,
                    trans.name )

                if trans.isFiredOnFail
                    add_edge!( graph, startStateIndex, outNodeIndex )
                    set_prop!( graph, startStateIndex, outNodeIndex, :trans,
                        "fired\n(failed $(trans.name))" )
                end  # if trans.isFiredOnFail
            end  # for trans in stateList[ state ]
        else
            startStateIndex = findfirst( tmpState -> tmpState == state.name,
                graphStates )
            isStateCreated = startStateIndex > 0

            for trans in stateList[ state ]
                endStateIndex = findfirst(
                    tmpState -> tmpState == trans.endState.name, tmpStates )
                # Add the transition if the end state is in the list of original
                #   states.
                if endStateIndex > 0
                    # Create the state if it isn't in the list.
                    if !isStateCreated
                        add_vertex!( graph )
                        push!( graphStates, trans.startState.name )
                        nStates += 1
                        set_prop!( graph, nStates, :state,
                            trans.startState.name )
                        startStateIndex = nStates
                        isstateCreated = true
                    end  # if !isStateCreated

                    add_edge!( graph, startStateIndex, endStateIndex )
                    set_prop!( graph, startStateIndex, endStateIndex, :trans,
                        trans.name )
                end  # if trans.endState.name ∈ tmpStates
            end  # for trans in stateList[ state ]
        end  # if state.name ∈ graphStates
    end  # for state in keys( stateList )

    return graph, inNodeIndex, outNodeIndex

end  # buildTransitionNetwork( mpSim, states )


text:
export BrickletLoadCellWeightCallbackThreshold
struct BrickletLoadCellWeightCallbackThreshold
    option::Char
    min::Integer
    max::Integer
end

export BrickletLoadCellConfiguration
struct BrickletLoadCellConfiguration
    rate::Integer
    gain::Integer
end

export BrickletLoadCellIdentity
struct BrickletLoadCellIdentity
    uid::String
    connected_uid::String
    position::Char
    hardware_version::Vector{Integer}
    firmware_version::Vector{Integer}
    device_identifier::Integer
end

export BrickletLoadCell
"""
Measures weight with a load cell
"""
mutable struct BrickletLoadCell <: TinkerforgeDevice
    ipcon::IPConnection
    deviceInternal::PyObject

    """
    Creates an object with the unique device ID *uid* and adds it to
    the IP Connection *ipcon*.
    """
    function BrickletLoadCell(uid::String, ipcon::IPConnection)
        package = pyimport("tinkerforge.bricklet_load_cell")
        deviceInternal = package.BrickletLoadCell(uid, ipcon.ipconInternal)

        return new(ipcon, deviceInternal)
    end
end

export get_weight
"""
    $(SIGNATURES)

Returns the currently measured weight.

If you want to get the weight periodically, it is recommended
to use the :cb:`Weight` callback and set the period with
:func:`Set Weight Callback Period`.
"""
function get_weight(device::BrickletLoadCell)
    return device.deviceInternal.get_weight()
end

export set_weight_callback_period
"""
    $(SIGNATURES)

Sets the period with which the :cb:`Weight` callback is triggered
periodically. A value of 0 turns the callback off.

The :cb:`Weight` callback is only triggered if the weight has changed since the
last triggering.
"""
function set_weight_callback_period(device::BrickletLoadCell, period)
    device.deviceInternal.set_weight_callback_period(period)
end

export get_weight_callback_period
"""
    $(SIGNATURES)

Returns the period as set by :func:`Set Weight Callback Period`.
"""
function get_weight_callback_period(device::BrickletLoadCell)
    return device.deviceInternal.get_weight_callback_period()
end

export set_weight_callback_threshold
"""
    $(SIGNATURES)

Sets the thresholds for the :cb:`Weight Reached` callback.

The following options are possible:

.. csv-table::
 :header: "Option", "Description"
 :widths: 10, 100

 "'x'",    "Callback is turned off"
 "'o'",    "Callback is triggered when the weight is *outside* the min and max values"
 "'i'",    "Callback is triggered when the weight is *inside* the min and max values"
 "'<'",    "Callback is triggered when the weight is smaller than the min value (max is ignored)"
 "'>'",    "Callback is triggered when the weight is greater than the min value (max is ignored)"
"""
function set_weight_callback_threshold(device::BrickletLoadCell, option, min, max)
    device.deviceInternal.set_weight_callback_threshold(option, min, max)
end

export get_weight_callback_threshold
"""
    $(SIGNATURES)

Returns the threshold as set by :func:`Set Weight Callback Threshold`.
"""
function get_weight_callback_threshold(device::BrickletLoadCell)
    return device.deviceInternal.get_weight_callback_threshold()
end

export set_debounce_period
"""
    $(SIGNATURES)

Sets the period with which the threshold callback

* :cb:`Weight Reached`

is triggered, if the threshold

* :func:`Set Weight Callback Threshold`

keeps being reached.
"""
function set_debounce_period(device::BrickletLoadCell, debounce)
    device.deviceInternal.set_debounce_period(debounce)
end

export get_debounce_period
"""
    $(SIGNATURES)

Returns the debounce period as set by :func:`Set Debounce Period`.
"""
function get_debounce_period(device::BrickletLoadCell)
    return device.deviceInternal.get_debounce_period()
end

export set_moving_average
"""
    $(SIGNATURES)

Sets the length of a `moving averaging <https://en.wikipedia.org/wiki/Moving_average>`__
for the weight value.

Setting the length to 1 will turn the averaging off. With less
averaging, there is more noise on the data.
"""
function set_moving_average(device::BrickletLoadCell, average)
    device.deviceInternal.set_moving_average(average)
end

export get_moving_average
"""
    $(SIGNATURES)

Returns the length moving average as set by :func:`Set Moving Average`.
"""
function get_moving_average(device::BrickletLoadCell)
    return device.deviceInternal.get_moving_average()
end

export led_on
"""
    $(SIGNATURES)

Turns the LED on.
"""
function led_on(device::BrickletLoadCell)
    device.deviceInternal.led_on()
end

export led_off
"""
    $(SIGNATURES)

Turns the LED off.
"""
function led_off(device::BrickletLoadCell)
    device.deviceInternal.led_off()
end

export is_led_on
"""
    $(SIGNATURES)

Returns *true* if the led is on, *false* otherwise.
"""
function is_led_on(device::BrickletLoadCell)
    return device.deviceInternal.is_led_on()
end

export calibrate
"""
    $(SIGNATURES)

To calibrate your Load Cell Bricklet you have to

* empty the scale and call this function with 0 and
* add a known weight to the scale and call this function with the weight.

The calibration is saved in the EEPROM of the Bricklet and only
needs to be done once.

We recommend to use the Brick Viewer for calibration, you don't need
to call this function in your source code.
"""
function calibrate(device::BrickletLoadCell, weight)
    device.deviceInternal.calibrate(weight)
end

export tare
"""
    $(SIGNATURES)

Sets the currently measured weight as tare weight.
"""
function tare(device::BrickletLoadCell)
    device.deviceInternal.tare()
end

export set_configuration
"""
    $(SIGNATURES)

The measurement rate and gain are configurable.

The rate can be either 10Hz or 80Hz. A faster rate will produce more noise.
It is additionally possible to add a moving average
(see :func:`Set Moving Average`) to the measurements.

The gain can be 128x, 64x or 32x. It represents a measurement range of
±20mV, ±40mV and ±80mV respectively. The Load Cell Bricklet uses an
excitation voltage of 5V and most load cells use an output of 2mV/V. That
means the voltage range is ±15mV for most load cells (i.e. gain of 128x
is best). If you don't know what all of this means you should keep it at
128x, it will most likely be correct.

The configuration is saved in the EEPROM of the Bricklet and only
needs to be done once.

We recommend to use the Brick Viewer for configuration, you don't need
to call this function in your source code.
"""
function set_configuration(device::BrickletLoadCell, rate, gain)
    device.deviceInternal.set_configuration(rate, gain)
end

export get_configuration
"""
    $(SIGNATURES)

Returns the configuration as set by :func:`Set Configuration`.
"""
function get_configuration(device::BrickletLoadCell)
    return device.deviceInternal.get_configuration()
end

export get_identity
"""
    $(SIGNATURES)

Returns the UID, the UID where the Bricklet is connected to,
the position, the hardware and firmware version as well as the
device identifier.

The position can be 'a', 'b', 'c', 'd', 'e', 'f', 'g' or 'h' (Bricklet Port).
A Bricklet connected to an :ref:`Isolator Bricklet <isolator_bricklet>` is always at
position 'z'.

The device identifier numbers can be found :ref:`here <device_identifier>`.
|device_identifier_constant|
"""
function get_identity(device::BrickletLoadCell)
    return device.deviceInternal.get_identity()
end

export register_callback
"""
Registers the given *function* with the given *callback_id*.
"""
function register_callback(device::BrickletLoadCell, callback_id, function_)
    if isnothing(function_)
        device.registered_callbacks.pop(callback_id, None)
    else
        device.registered_callbacks[callback_id] = function_
    end
end


text:
"""
    (lon_f, lat_f) = lon_lat_fine(lon, lat,nf)

Interpolate longitude and latitude arrays (2d or 1d) `lon` and `lat` to higher resolution by a factor `nf`.
"""
function lon_lat_fine(lon_mat, lat_mat,nf)
#using Interpolations

ns=size(lon_mat)

if(length(ns)==2)
  nlon=ns[1]; nlat=ns[2];

  xc=1:nlon
  yc=1:nlat
  xf=(0.5+1/(2*nf)):1/nf:(nlon+0.5-1/(2*nf))
  yf=(0.5+1/(2*nf)):1/nf:(nlat+0.5-1/(2*nf))
  knots=(collect(yc),collect(xc))

if VERSION >= v"0.7.0-DEV.3449"
  itplon = extrapolate(interpolate(knots, lon_mat, Gridded(Linear())), Line());
  itplat = extrapolate(interpolate(knots, lat_mat, Gridded(Linear())), Line());
else
  itplon = interpolate(knots, lon_mat, Gridded(Linear()))
  itplat = interpolate(knots, lat_mat, Gridded(Linear()))
end

  lon_f=itplon(collect(yf),collect(xf))
  lat_f=itplat(collect(yf),collect(xf))
else
  (nlat,)=size(lat_mat);
  (nlon,)=size(lon_mat);

  xc=1:nlon
  yc=1:nlat

  xf=(0.5+1/(2*nf)):1/nf:(nlon+0.5-1/(2*nf))
  yf=(0.5+1/(2*nf)):1/nf:(nlat+0.5-1/(2*nf))
  knotsx=collect(xc)
  knotsy=collect(yc)

if VERSION >= v"0.7.0-DEV.3449"
  itplon = extrapolate(interpolate((knotsx,), lon_mat, Gridded(Linear())), Line());
  itplat = extrapolate(interpolate((knotsy,), lat_mat, Gridded(Linear())), Line());
else
  itplon = interpolate((knotsx,), lon_mat, Gridded(Linear()))
  itplat = interpolate((knotsy,), lat_mat, Gridded(Linear()));
end

  lon_f=itplon(collect(xf))
  lat_f=itplat(collect(yf))
end

return lon_f, lat_f
end


text:
module MyFirstPkg

my_f(x) = 2x + 3

export my_f

end # module


text:
abstract type AbstractCMPS{T,N} end

function leftreducedoperator(op::FieldOperator, Ψ::AbstractCMPS, ρL = nothing; kwargs...)
    if isnothing(ρL)
        ρL = leftenv(Ψ; kwargs...)
    end
    _leftreducedoperator(op, Ψ.Q, Ψ.Rs, ρL)
end
function rightreducedoperator(op::FieldOperator, Ψ::AbstractCMPS, ρR = nothing; kwargs...)
    if isnothing(ρR)
        ρR = rightenv(Ψ; kwargs...)
    end
    _rightreducedoperator(op, Ψ.Q, Ψ.Rs, ρR)
end

function _leftreducedoperator(op::FieldOperator, Q, Rs, ρL)
    A, B = _ketbrafactors(op, Q, Rs)
    return B'*(ρL*A)
end

function _rightreducedoperator(op::FieldOperator, Q, Rs, ρR)
    A, B = _ketbrafactors(op, Q, Rs)
    return (A*ρR)*B'
end

function _expval(op::FieldOperator, Q, Rs, ρL, ρR)
    A, B = _ketbrafactors(op, Q, Rs)
    return localdot(ρL*B, A*ρR)
end

function leftreducedoperator(ops::LocalOperator, Ψ::AbstractCMPS, ρL = nothing; kwargs...)
    if isnothing(ρL)
        ρL, = leftenv(Ψ; kwargs...)
    end
    hL = zero(ρL)
    for (coeff, op) in zip(coefficients(ops), operators(ops))
        if coeff isa Number
            axpy!(coeff, leftreducedoperator(op, Ψ, ρL), hL)
        else
            hL += coeff * leftreducedoperator(op, Ψ, ρL)
        end
    end
    return hL
end

function rightreducedoperator(ops::LocalOperator, Ψ::AbstractCMPS, ρR = nothing; kwargs...)
    if isnothing(ρR)
        ρR, = rightenv(Ψ; kwargs...)
    end
    hR = zero(ρR)
    for (coeff, op) in zip(coefficients(ops), operators(ops))
        if coeff isa Number
            axpy!(coeff, rightreducedoperator(op, Ψ, ρR), hR)
        else
            hR += coeff * rightreducedoperator(op, Ψ, ρR)
        end
    end
    return hR
end

function expval(ops::LocalOperator, Ψ::AbstractCMPS, ρL = nothing, ρR = nothing; kwargs...)
    if isnothing(ρL)
        ρL, = leftenv(Ψ; kwargs...)
    end
    if isnothing(ρR)
        ρR, = rightenv(Ψ; kwargs...)
    end
    Z = real(dot(ρL, ρR))
    ev = sum(coeff*_expval(op, Ψ.Q, Ψ.Rs, ρL, ρR)
                for (coeff, op) in zip(coefficients(ops), operators(ops)))
    return ev/Z
end


text:
using Test

@testset "Antiplane line dislocation" begin
    u1 = dc3d(0.0, -1.0, -1.0, 2/3, 0.0, 90.0, [-1e4, 1e4], [-4.0, -3.0], [1.0, 0.0, 0.0])
    u2 = disp_antiplane_seg2(1.0, -1.0, 0.0, 4.0, 3.0, 1.0)
    @test u1[1] ≈ u2 atol=1e-6 # strike displacement
    G = 2.0
    σxy1 = G * (u1[5] + u1[7])
    σxz1 = G * (u1[6] + u1[10])
    σxy2, σxz2 = stress_antiplane_seg2(1.0, -1.0, 0.0, 4.0, 3.0, G, 1.0)
    @test σxy1 ≈ -σxy2 atol=1e-6
    @test σxz1 ≈ σxz2 atol=1e-6
end

@testset "Inplane line dislocation" begin
    θ = 33.0
    G = 2.0
    λ = 4.0
    α = (λ + G) / (λ + 2G)
    ν = λ / 2 / (λ + G)
    uo = dc3d(0.0, -1.0, -1.0, α, 0.0, θ, [-1e5, 1e5], [-4.0, -3.0], [0.0, 1.0, 0.0])
    u1, u2 = disp_inplane_seg2(1.0, -1.0, 4.0*cosd(θ), -4.0*sind(θ), 3.0*cosd(θ), -3.0*sind(θ), -cosd(θ), sind(θ), ν)
    @test uo[2] ≈ -u1 atol=1e-6
    @test uo[3] ≈ u2 atol=1e-6
    σ11, σ22, σ12 = stress_inplane_seg2(1.0, -1.0, 4.0*cosd(θ), -4.0*sind(θ), 3.0*cosd(θ), -3.0*sind(θ), -cosd(θ), sind(θ), G, ν)
    ukk = uo[4] + uo[8] + uo[12]
    σyy = λ * ukk + 2G * uo[8]
    σzz = λ * ukk + 2G * uo[12]
    σyz = G * (uo[9] + uo[11])
    @test σ11 ≈ σyy atol=1e-6
    @test σ22 ≈ σzz atol=1e-6
    @test σ12 ≈ -σyz atol=1e-6
end


text:
# Raccolta di funzioni per il calcolo della quota del centro
# del pennacchio come in ISC3.
#
# Scritto da: Patrizia Favaron
#
# Questo codice è coperto dalla licenza MIT.

# Stima della velocità del vento alla quota geometrica
# della ciminiera
function us(
    zr::Float64,    # Quota alla quale è misurato il vento (m)
    ur::Float64,    # Velocità del vento misurata alla quota 'zr' (m/s)
    hs::Float64,    # Altezza geometrica della ciminiera (m)
    istab::Int32,   # Categoria di stabilità (1=A, 2=B, ..., 6=F)
    env_id::Int32   # Identificatore di tipo ambiente (1=rurale, 2=urbano)
)

    # Basic parameter check
    if istab < 1 || istab > 6 || env_id < 1 || env_id > 2
        return missing
    end

    # Determina l'esponente in base alla stabilità
    # ed al tipo di ambiente
    p_rural = [0.07, 0.07, 0.10, 0.15, 0.35, 0.55]
    p_urban = [0.15, 0.15, 0.20, 0.25, 0.30, 0.30]
    if env_id == 1 # Rurale
        p = p_rural[istab]
    elseif env_id == 2 # Urbano
        p = p_urban[istab]
    else
        return missing
    end

    # Il calcolo vero e proprio
    us_value = ur*(hs/zr)^p
    return us_value

end

# Abbassamento del pennacchio per effetto di scia
# al vertice della ciminiera.
function h_prime(
    hs::Float64, # Altezza geometrica della ciminiera (m)
    vs::Float64, # Velocità verticale dei fumi all'uscita dalla ciminiera (m/s)
    ds::Float64, # Diametro della ciminiera al vertice (m)
    us::Float64  # Velocità del vento alla quota geometrica della ciminiera (m/s)
)
    if vs < 1.5*us
        hs_p = hs + 2*ds*(vs/us - 1.5)
    else
        hs_p = hs
    end
    return hs_p
end

# Flusso di galleggiamento
function Fb(
    vs::Float64, # Velocità verticale dei fumi all'uscita dalla ciminiera (m/s)
    ds::Float64, # Diametro della ciminiera al vertice (m)
    Ts::Float64, # Temperatura dei fumi all'uscita dalla ciminiera (K)
    Ta::Float64  # Temperatura ambiente (K)
)
    G = 9.807
    return G * vs * ds^2 * (Ts-Ta)/(4. * Ts)
end

function Fm(
    vs::Float64, # Velocità verticale dei fumi all'uscita dalla ciminiera (m/s)
    ds::Float64, # Diametro della ciminiera al vertice (m)
    Ts::Float64, # Temperatura dei fumi all'uscita dalla ciminiera (K)
    Ta::Float64  # Temperatura ambiente (K)
)
    return vs^2 * ds^2 * Ta/(4. * Ts)
end

function is_stable(istab::Int32)
    return istab >= 5
end

function max_x(
    istab::Int32, # Categoria di stabilità (1=A, 2=B, ..., 6=F)
    us::Float64,  # Velocità del vento alla quota del vertice della ciminiera (m/s)
    vs::Float64,  # Velocità verticale dei fumi all'uscita dalla ciminiera (m/s)
    ds::Float64,  # Diametro della ciminiera al vertice (m)
    Ts::Float64,  # Temperatura dei fumi all'uscita dalla ciminiera (K)
    Ta::Float64   # Temperatura ambiente (K)
)

    # Stable, or neutral/convective?
    stable = is_stable(istab)

    # Calcolo dei flussi di galleggiamento e
    # di quantità di moto
    fb = Fb(vs,ds,Ts,Ta)

    # Stima della distanza sottovento alla quale
    # il pennacchio raggiunge la sua quota massima
    if stable
        if istab == 5
            s = 0.020
        else
            s = 0.035
        end
        xb = 2.0715 * us/sqrt(s)
        xm = 3.1415927/2. * us/sqrt(s)
    else
        if fb > 55.
            xb = 119.0 * fb^(2. / 5.)
        elseif fb > 0.
            xb = 49.0 * fb^(5. / 8.)
        else
            xb = 4.0 * ds * (vs + 3. * ds)^2/(vs*ds)
        end
        xm = 4.0 * ds * (vs + 3. * ds)^2/(vs*ds)
    end
    xmax = max(xb, xm)

    # Leave
    return xmax

end

function max_z(
    istab::Int32, # Categoria di stabilità (1=A, 2=B, ..., 6=F)
    hs::Float64,  # Altezza geometrica della ciminiera (m)
    us::Float64,  # Velocità del vento alla quota del vertice della ciminiera (m/s)
    vs::Float64,  # Velocità verticale dei fumi all'uscita dalla ciminiera (m/s)
    ds::Float64,  # Diametro della ciminiera al vertice (m)
    Ts::Float64,  # Temperatura dei fumi all'uscita dalla ciminiera (K)
    Ta::Float64   # Temperatura ambiente (K)
)

    # Stable, or neutral/convective?
    stable = is_stable(istab)

    # Calcolo dei flussi di galleggiamento e
    # di quantità di moto
    fb = Fb(vs,ds,Ts,Ta)
    fm = Fm(vs,ds,Ts,Ta)

    # Calcolo della differenza di Temperatura
    # critica
    if stable
        if istab == 5
            s = 0.020
        else
            s = 0.035
        end
        delta_t_crit = 0.019582 * Ts * vs * sqrt(s)
    else
        s = missing
        if fb < 55.0
            delta_t_crit = 0.0297 * Ts * vs^(1. / 3.) / ds^(2. / 3.)
        else
            delta_t_crit = 0.00575 * Ts * vs^(2. / 3.) / us^(1. / 3.)
        end
    end

    # Stima della quota massima raggiunta dal pennacchio
    delta_t = max(Ts - Ta, 0.)
    hps = h_prime(hs, vs, ds, us)
    if stable
        if delta_t > delta_t_crit
            hmax = hps + 2.6 * (fb / (us * s))^(1. / 3.)
        else
            hmax = hps + 1.5 * (fb / (us * sqrt(s)))^(1. / 3.)
        end
    else
        if delta_t > delta_t_crit
            if fb < 55.
                hmax = hps + 21.425 * fb^(3. / 4.) / us
            else
                hmax = hps + 38.75 * fb^(3. / 5.) / us
            end
        else
            hmax = hps + 3. * ds * vs / us
        end
    end

    # Leave
    return hmax

end

max_stars_repo_name: mafavaron/books-pbl-software

max_issues_repo_name: mafavaron/books-pbl-software

max_forks_repo_name: mafavaron/books-pbl-software


text:
##
using AeroMDAO

## Test function
function doublet_source_case(foil, uniform)
    @time system = solve_case(foil,
                              uniform;
                              num_panels = 80)

    cl = lift_coefficient(system)
    cls, cms, _ = surface_coefficients(system)

    println("Cl: $cl")
    println("Σᵢ Clᵢ: $(sum(cls))")
    println("Σᵢ Cmᵢ: $(sum(cms))")
end

uniform = Uniform2D(angle = 5)

## Foil processing
naca_foil = naca4(2,4,1,2) # NACA 4-digit

doublet_source_case(naca_foil, uniform)

## Cosine spacing
cos_foil = cosine_interpolation(naca_foil, 81)

doublet_source_case(cos_foil, uniform)

## Camber-thickness transformations
xcamthick = camber_thickness(cos_foil)
foiler = Foil(camber_thickness_to_coordinates(xcamthick[:,1], xcamthick[:,2], xcamthick[:,3]))

doublet_source_case(foiler, uniform)

## Fitting to CST
num_dv = 8

# Coordinates fitting
up, low  = split_surface(naca_foil)
alpha_u  = coordinates_to_CST(up, num_dv)
alpha_l  = coordinates_to_CST(low, num_dv)
cst_foil = kulfan_CST(alpha_u, alpha_l, (0., 0.), (0., 0.))

doublet_source_case(cst_foil, uniform)

## Camber-thickness fitting
alpha_c, alpha_t = camber_thickness_to_CST(cos_foil, num_dv)
cam_foil = camber_CST(alpha_c, alpha_t, 0., 0)

doublet_source_case(cam_foil, uniform)

## Control surface deflection
con_foil = control_surface(cos_foil; angle = 8., hinge = 0.75)

doublet_source_case(con_foil, uniform)

## Plotting library
using Plots
gr(dpi = 300);

## Cosine and camber-thickness
plot(aspect_ratio = 1)
plot!(naca_foil.x, naca_foil.y, label = "Original")
plot!(cos_foil.x, cos_foil.y, label = "Cosine")
plot!(xcamthick[:,1], xcamthick[:,2], ls = :dot, label = "Camber")
plot!(xcamthick[:,1], xcamthick[:,3], ls = :dot, label = "Thickness")
plot!(foiler.x, foiler.y, label = "Inverse Camber-Thickness")
plot!(cst_foil.x, cst_foil.y, label = "CST Coordinates Fit")
plot!(cam_foil.x, cam_foil.y, label = "CST Camber-Thickness Fit")
plot!(con_foil.x, con_foil.y, ls = :dash, label = "Control Surface Deflection")


text:
@testset "4.2 Cosine" begin
    include("4.2.0 (a cos)^m (b trg)^n.jl")
    include("4.2.1.1 (a+b cos)^n.jl")
    include("4.2.1.2 (g sin)^p (a+b cos)^m.jl")
    include("4.2.1.3 (g tan)^p (a+b cos)^m.jl")
    include("4.2.10 (c+d x)^m (a+b cos)^n.jl")
    include("4.2.12 (e x)^m (a+b cos(c+d x^n))^p.jl")
    include("4.2.13 (d+e x)^m cos(a+b x+c x^2)^n.jl")
    include("4.2.2.1 (a+b cos)^m (c+d cos)^n.jl")
    include("4.2.2.2 (g sin)^p (a+b cos)^m (c+d cos)^n.jl")
    include("4.2.2.3 (g cos)^p (a+b cos)^m (c+d cos)^n.jl")
    include("4.2.3.1 (a+b cos)^m (c+d cos)^n (A+B cos).jl")
    include("4.2.4.1 (a+b cos)^m (A+B cos+C cos^2).jl")
    include("4.2.4.2 (a+b cos)^m (c+d cos)^n (A+B cos+C cos^2).jl")
    include("4.2.7 (d trig)^m (a+b (c cos)^n)^p.jl")
    include("4.2.8 (a+b cos)^m (c+d trig)^n.jl")
    include("4.2.9 trig^m (a+b cos^n+c cos^(2 n))^p.jl")
end

max_stars_repo_path: test/4 Trig functions/4.2 Cosine/.jl

max_issues_repo_path: test/4 Trig functions/4.2 Cosine/.jl

max_forks_repo_path: test/4 Trig functions/4.2 Cosine/.jl


text:
module NumDiffTools

import Printf: @sprintf

include("extrapolate.jl")

include("derivative.jl")

include("gradient.jl")

include("jacobian.jl")

include("hessian.jl")

end # module


text:
export SmallTank, MediumTank, LargeTank

mutable struct SmallTank <: Tank
    propellant::Float64
end

mutable struct MediumTank <: Tank
    propellant::Float64
end

mutable struct LargeTank <: Tank
    propellant::Float64
end

function SmallTank()
    refill!(SmallTank(0))
end

function MediumTank()
    refill!(MediumTank(0))
end

function LargeTank()
    refill!(LargeTank(0))
end

# These weights are roughly based on the Electron rocket
drymass(::SmallTank) = 40.0
drymass(::MediumTank) = 250.0
drymass(::LargeTank) = 950.0

totalmass(::SmallTank) = 410.0
totalmass(::MediumTank) = 2300.0
totalmass(::LargeTank) = 10200.0


text:
export Machine, exists, start_machine, remove!

abstract AbstractMachine

type VirtualBoxMachine <: AbstractMachine
  name::String
  memory::Integer
  disk::Integer
  cpucount::Integer
end

const docker_machine = "docker-machine"
const docker = "docker"

# Creates a virtual machine within which to run docker containers
# Parameters
#    name: the name of the machine...
#    vm: Only virtual box for now. Defaults to "VirtualBox"
#    start: Whether to start the marchine or not. Defaults to true. Disabled if docreate is false.
#    docreate: Whether to create the marchine or not. Defaults to true.
#    memory: In megabytes. Defaults to 0, which defaults to whatever docker wants.
#    disk: In megabytes. Defaults to 0, which defaults to whatever docker wants.
#    cpucount: Defaults to 1.
function Machine(name, vm::String="VirtualBox";
    start=true, docreate=true, memory::Int=0, disk::Int=0, cpucount::Int=1)
  if vm != "VirtualBox"
    error("I only know of VirtualBox for now")
  end
  result = VirtualBoxMachine(name, memory, disk, cpucount)
  const does_exist = exists(result)
  if docreate && !does_exist
    run(create(result))
  elseif start && does_exist
    run(`$docker_machine start $name`)
  end
  result
end

function create(machine::VirtualBoxMachine)
  result = `$docker_machine create -d virtualbox`
  if machine.memory > 0; result = `$result --virtualbox-memory $(machine.memory)` end
  if machine.disk > 0; result = `$result --virtualbox-disk-size $(machine.disk)` end
  if machine.cpucount > 1; result = `$result --virtualbox-cpu-count $(machine.cpucount)` end
  `$result $(machine.name)`
end

function exists(machine::AbstractMachine)
    open(`$docker_machine ls`) do stdout
      readline(stdout)
      for line in readlines(stdout)
        if split(chomp(line))[1] == machine.name return true end
      end
      false
    end
end

function start_machine(machine::AbstractMachine)
  if exists(machine)
    run(`$docker_machine start $(machine.name)`)
  else
    run(create(machine))
  end
  activate_impl(machine)
end

activate_impl(machine::AbstractMachine)= run(`$docker_machine start $(machine.name)`)

function remove!(machine::AbstractMachine)
  if exists(machine)
    run(`$docker_machine rm $(machine.name)`)
  end
end

function config(machine::AbstractMachine)
  if !exists(machine)
    error("machine $(machine.name) does not exist")
  end
  activate_impl(machine)
  readchomp(`$docker_machine config $(machine.name)`)
end


text:
using AnyBoost
using Plots
using DataFrames
using CSV
try
    using XGBoost
catch
    println("!!!!!!!!!!!!!!!!!!! XGBoost not installed. Will Error eventually.")
end

const def_params = [
    :eta=>.3,
    :max_depth=>6
    ]
xgloss(::AnyBoost.MSELoss) = "reg:linear"
xgloss(::AnyBoost.LogitLoss) = "binary:logistic"

function xgb(trdata, tedata, l; numround=300)
    trdm = DMatrix(trdata.X, label = trdata.y)
    tedm = DMatrix(tedata.X, label = tedata.y)
    bst = xgboost(trdm, numround, objective=xgloss(l))
    al = altloss(l)
    trerrs = Array{Float64}(numround)
    teerrs = Array{Float64}(numround)
    altrerrs = Array{Float64}(numround)
    alteerrs = Array{Float64}(numround)
    norms = Array{Float64}(numround)
    trnpredlast = zeros(trdata.y)
    time = @elapsed for i in 1:numround
        trnpred = MyXGBoost.predict(bst, trdata.X, ntree_limit=i, output_margin=true)  # Get untransformed for logistic
        trerrs[i] = l(trdata.y, trnpred)/trdata.n
        altrerrs[i] = al(trdata.y, trnpred)/trdata.n
        tespred = MyXGBoost.predict(bst, tedata.X, ntree_limit=i, output_margin=true)
        teerrs[i] = l(tedata.y, tespred)/tedata.n
        alteerrs[i] = al(tedata.y, tespred)/tedata.n
        norms[i] = norm(trnpred-trnpredlast)
        trnpredlast = trnpred
    end
    cards = collect(1:numround)
    norms = cumsum(norms)
    bayes = l(tedata.y, tedata.signal)/tedata.n
    albayes = al(tedata.y, tedata.signal)/tedata.n
    cards, norms, trerrs, teerrs, bayes, altrerrs, alteerrs, albayes, time
end