text:
# generated
SummonerSpells = Dict{Int,NamedTuple}(
    21 => (id = "SummonerBarrier", name = "Barrier", description = "Shields your champion from 115-455 damage (depending on champion level) for 2 seconds.", tooltip = "Temporarily shields {{ f1 }} damage from your champion for 2 seconds.", maxrank = 1, cooldown = Any[180], cooldownBurn = "180", cost = Any[0], costBurn = "0", datavalues = NamedTuple(), effect = Any[nothing, Any[95], Any[20], Any[0], Any[0], Any[0], Any[0], Any[0], Any[0], Any[0], Any[0]], effectBurn = Any[nothing, "95", "20", "0", "0", "0", "0", "0", "0", "0", "0"], vars = Any[], key = "21", summonerLevel = 4, modes = Any["NEXUSBLITZ", "URF", "PRACTICETOOL", "TUTORIAL", "CLASSIC", "ARAM", "DOOMBOTSTEEMO", "ULTBOOK", "ONEFORALL", "ARSR", "ASSASSINATE", "FIRSTBLOOD", "PROJECT", "STARGUARDIAN"], costType = "No Cost", maxammo = "-1", range = Any[1200], rangeBurn = "1200", image = (full = "SummonerBarrier.png", sprite = "spell0.png", group = "spell", x = 0, y = 0, w = 48, h = 48), resource = "No Cost"),
    1 => (id = "SummonerBoost", name = "Cleanse", description = "Removes all disables (excluding suppression and airborne) and summoner spell debuffs affecting your champion and lowers the duration of incoming disables by 65% for 3 seconds.", tooltip = "Removes all disables (excluding suppression and airborne) and summoner spell debuffs affecting your champion and reduces the duration of disables by 65% for the next {{ f1 }} seconds.", maxrank = 1, cooldown = Any[210], cooldownBurn = "210", cost = Any[0], costBurn = "0", datavalues = NamedTuple(), effect = Any[nothing, Any[0.65], Any[3], Any[0], Any[0], Any[0], Any[0], Any[0], Any[0], Any[0], Any[0]], effectBurn = Any[nothing, "0.65", "3", "0", "0", "0", "0", "0", "0", "0", "0"], vars = Any[], key = "1", summonerLevel = 9, modes = Any["URF", "NEXUSBLITZ", "ARSR", "ONEFORALL", "ARAM", "CLASSIC", "PRACTICETOOL", "DOOMBOTSTEEMO", "TUTORIAL", "ULTBOOK", "FIRSTBLOOD", "PROJECT"], costType = "No Cost", maxammo = "-1", range = Any[200], rangeBurn = "200", image = (full = "SummonerBoost.png", sprite = "spell0.png", group = "spell", x = 48, y = 0, w = 48, h = 48), resource = "No Cost"),
    14 => (id = "SummonerDot", name = "Ignite", description = "Ignites target enemy champion, dealing 70-410 true damage (depending on champion level) over 5 seconds, grants you vision of the target, and reduces healing effects on them for the duration.", tooltip = "Ignite deals {{ f1 }} true damage to target enemy champion over 5 seconds, grants you vision of the target and applies {{ grievousamount*100 }}% Grievous Wounds for the duration.

<rules>(Grievous Wounds reduces healing and regeneration by a percentage equal to the highest value applied. This vision does not reveal stealthed enemies.)</rules>", maxrank = 1, cooldown = Any[180], cooldownBurn = "180", cost = Any[0], costBurn = "0", datavalues = NamedTuple(), effect = Any[nothing, Any[5], Any[10], Any[4], Any[100], Any[0], Any[0], Any[0], Any[0], Any[0], Any[0]], effectBurn = Any[nothing, "5", "10", "4", "100", "0", "0", "0", "0", "0", "0"], vars = Any[], key = "14", summonerLevel = 9, modes = Any["NEXUSBLITZ", "URF", "PRACTICETOOL", "TUTORIAL", "CLASSIC", "ARAM", "DOOMBOTSTEEMO", "ULTBOOK", "ONEFORALL", "ARSR", "ASSASSINATE", "FIRSTBLOOD"], costType = "No Cost", maxammo = "-1", range = Any[600], rangeBurn = "600", image = (full = "SummonerDot.png", sprite = "spell0.png", group = "spell", x = 96, y = 0, w = 48, h = 48), resource = "No Cost"),
    3 => (id = "SummonerExhaust", name = "Exhaust", description = "Exhausts target enemy champion, reducing their Move Speed by 30%, and their damage dealt by 40% for 3 seconds.", tooltip = "Exhausts target enemy champion, reducing their Move Speed by {{ f3 }}%, and their damage dealt by {{ f2 }}% for 3 seconds.", maxrank = 1, cooldown = Any[210], cooldownBurn = "210", cost = Any[0], costBurn = "0", datavalues = NamedTuple(), effect = Any[nothing, Any[3], Any[40], Any[0], Any[0], Any[30], Any[0], Any[0], Any[0], Any[0], Any[0]], effectBurn = Any[nothing, "3", "40", "0", "0", "30", "0", "0", "0", "0", "0"], vars = Any[], key = "3", summonerLevel = 4, modes = Any["NEXUSBLITZ", "URF", "PRACTICETOOL", "TUTORIAL", "CLASSIC", "ARAM", "DOOMBOTSTEEMO", "ULTBOOK", "ONEFORALL", "ARSR"], costType = "No Cost", maxammo = "-1", range = Any[650], rangeBurn = "650", image = (full = "SummonerExhaust.png", sprite = "spell0.png", group = "spell", x = 144, y = 0, w = 48, h = 48), resource = "No Cost"),
    4 => (id = "SummonerFlash", name = "Flash", description = "Teleports your champion a short distance toward your cursor's location.", tooltip = "Teleports your champion a short distance toward your cursor's location.", maxrank = 1, cooldown = Any[300], cooldownBurn = "300", cost = Any[0], costBurn = "0", datavalues = NamedTuple(), effect = Any[nothing, Any[400], Any[0], Any[0], Any[0], Any[0], Any[0], Any[0], Any[0], Any[0], Any[0]], effectBurn = Any[nothing, "400", "0", "0", "0", "0", "0", "0", "0", "0", "0"], vars = Any[], key = "4", summonerLevel = 7, modes = Any["NEXUSBLITZ", "URF", "PRACTICETOOL", "SNOWURF", "TUTORIAL", "CLASSIC", "ARAM", "DOOMBOTSTEEMO", "ULTBOOK", "ONEFORALL", "ARSR", "ASSASSINATE", "FIRSTBLOOD", "PROJECT", "STARGUARDIAN"], costType = "No Cost", maxammo = "-1", range = Any[425], rangeBurn = "425", image = (full = "SummonerFlash.png", sprite = "spell0.png", group = "spell", x = 192, y = 0, w = 48, h = 48), resource = "No Cost"),
    6 => (id = "SummonerHaste", name = "Ghost", description = "For 10 seconds, your champion can move through units and gains 24 - 48% Movement Speed (depending on champion level). Ghost extends its duration on takedown.", tooltip = "For {{ duration }} seconds, your champion can move through units and gains {{ movespeedmod }} Movement Speed.

Ghost extends its duration by {{ takedownextension }} seconds on takedown.", maxrank = 1, cooldown = Any[210], cooldownBurn = "210", cost = Any[0], costBurn = "0", datavalues = NamedTuple(), effect = Any[nothing, Any[0], Any[0], Any[0], Any[0], Any[0], Any[0], Any[0], Any[0], Any[0], Any[0]], effectBurn = Any[nothing, "0", "0", "0", "0", "0", "0", "0", "0", "0", "0"], vars = Any[], key = "6", summonerLevel = 1, modes = Any["NEXUSBLITZ", "TUTORIAL_MODULE_2", "URF", "PRACTICETOOL", "TUTORIAL", "CLASSIC", "ARAM", "DOOMBOTSTEEMO", "ULTBOOK", "TUTORIAL_MODULE_1", "ONEFORALL", "ARSR", "ASSASSINATE", "FIRSTBLOOD", "PROJECT", "STARGUARDIAN"], costType = "No Cost", maxammo = "-1", range = Any[200], rangeBurn = "200", image = (full = "SummonerHaste.png", sprite = "spell0.png", group = "spell", x = 240, y = 0, w = 48, h = 48), resource = "No Cost"),
    7 => (id = "SummonerHeal", name = "Heal", description = "Restores 90-345 Health (depending on champion level) and grants 30% Movement Speed for 1 second to you and target allied champion. This healing is halved for units recently affected by Summoner Heal.", tooltip = "Restores {{ f1 }} Health and grants 30% Movement Speed for 1 second to your champion and target allied champion. This healing is halved for units recently affected by Summoner Heal.

If this spell cannot find a target, it will cast on the most wounded allied champion in range.", maxrank = 1, cooldown = Any[240], cooldownBurn = "240", cost = Any[0], costBurn = "0", datavalues = NamedTuple(), effect = Any[nothing, Any[0.3], Any[75], Any[15], Any[0.5], Any[826], Any[0.5], Any[0], Any[0], Any[0], Any[0]], effectBurn = Any[nothing, "0.3", "75", "15", "0.5", "826", "0.5", "0", "0", "0", "0"], vars = Any[], key = "7", summonerLevel = 1, modes = Any["NEXUSBLITZ", "TUTORIAL_MODULE_2", "URF", "PRACTICETOOL", "TUTORIAL", "CLASSIC", "ARAM", "DOOMBOTSTEEMO", "ULTBOOK", "TUTORIAL_MODULE_1", "ONEFORALL", "ARSR", "ASSASSINATE", "PROJECT", "STARGUARDIAN"], costType = "No Cost", maxammo = "-1", range = Any[850], rangeBurn = "850", image = (full = "SummonerHeal.png", sprite = "spell0.png", group = "spell", x = 288, y = 0, w = 48, h = 48), resource = "No Cost"),
    13 => (id = "SummonerMana", name = "Clarity", description = "Restores 50% of your champion's maximum Mana. Also restores allies for 25% of their maximum Mana.", tooltip = "Restores {{ f1 }}% maximum Mana to your Champion and {{ f2 }}% to nearby allies.", maxrank = 1, cooldown = Any[240], cooldownBurn = "240", cost = Any[0], costBurn = "0", datavalues = NamedTuple(), effect = Any[nothing, Any[50], Any[25], Any[0], Any[0], Any[0], Any[0], Any[0], Any[0], Any[0], Any[0]], effectBurn = Any[nothing, "50", "25", "0", "0", "0", "0", "0", "0", "0", "0"], vars = Any[], key = "13", summonerLevel = 6, modes = Any["ARAM", "FIRSTBLOOD"], costType = "No Cost", maxammo = "-1", range = Any[600], rangeBurn = "600", image = (full = "SummonerMana.png", sprite = "spell0.png", group = "spell", x = 336, y = 0, w = 48, h = 48), resource = "No Cost"),
    30 => (id = "SummonerPoroRecall", name = "To the King!", description = "Quickly travel to the Poro King's side.", tooltip = "Passive: Hitting an enemy champion with a Poro gives your team a Poro Mark. Upon reaching 10 Poro Marks, your team summons the Poro King to fight alongside them. While the Poro King is active, no Poro Marks can be scored by either team.

Active: Quickly dash to King Poro's side. Can only be cast while the Poro King is summoned for your team.

''Poros tug the heartstrings. The rest of you just comes along for the ride.''</mainText>", maxrank = 1, cooldown = Any[10], cooldownBurn = "10", cost = Any[0], costBurn = "0", datavalues = NamedTuple(), effect = Any[nothing, Any[3000], Any[0], Any[0], Any[0], Any[0], Any[0], Any[0], Any[0], Any[0], Any[0]], effectBurn = Any[nothing, "3000", "0", "0", "0", "0", "0", "0", "0", "0", "0"], vars = Any[], key = "30", summonerLevel = 1, modes = Any["KINGPORO"], costType = "No Cost", maxammo = "-1", range = Any[200], rangeBurn = "200", image = (full = "SummonerPoroRecall.png", sprite = "spell0.png", group = "spell", x = 384, y = 0, w = 48, h = 48), resource = "No Cost"),
    31 => (id = "SummonerPoroThrow", name = "Poro Toss", description = "Toss a Poro at your enemies. If it hits, you can quickly travel to your target as a follow up.", tooltip = "Toss a Poro a long distance, dealing {{ f2 }} true damage to the first enemy unit hit, granting True Sight of the target.

This ability can be recast for 3 seconds if it hits an enemy to dash to the target hit, dealing {{ f2 }} more true damage and reducing the cooldown of the next Poro Toss by {{ e4 }} seconds.

Poros are not blocked by spell shields or wind walls because they are animals, not spells!

''Poros are a model for Runeterran aerodynamics.''</mainText>", maxrank = 1, cooldown = Any[20], cooldownBurn = "20", cost = Any[0], costBurn = "0", datavalues = NamedTuple(), effect = Any[nothing, Any[20], Any[10], Any[3], Any[5], Any[0], Any[0], Any[0], Any[0], Any[0], Any[0]], effectBurn = Any[nothing, "20", "10", "3", "5", "0", "0", "0", "0", "0", "0"], vars = Any[], key = "31", summonerLevel = 1, modes = Any["KINGPORO"], costType = "No Cost", maxammo = "-1", range = Any[2500], rangeBurn = "2500", image = (full = "SummonerPoroThrow.png", sprite = "spell0.png", group = "spell", x = 432, y = 0, w = 48, h = 48), resource = "No Cost"),
    11 => (id = "SummonerSmite", name = "Smite", description = "Deals @SmiteBaseDamage@ true damage to target epic, large, or medium monster or minion.", tooltip = "Deals {{ f1 }} true damage to target monster or minion. Against monsters, additionally restores {{ f6 }} (+{{ f7 }}) Health.

Smite regains a charge every {{ ammorechargetime }} seconds, up to a maximum of 2 charges.

<rules>Cannot be cast on small monsters.</rules>", maxrank = 1, cooldown = Any[15], cooldownBurn = "15", cost = Any[0], costBurn = "0", datavalues = NamedTuple(), effect = Any[nothing, Any[15], Any[0], Any[0], Any[0], Any[0], Any[0], Any[0], Any[0], Any[0], Any[0]], effectBurn = Any[nothing, "15", "0", "0", "0", "0", "0", "0", "0", "0", "0"], vars = Any[], key = "11", summonerLevel = 9, modes = Any["NEXUSBLITZ", "URF", "PRACTICETOOL", "TUTORIAL", "CLASSIC", "DOOMBOTSTEEMO", "ULTBOOK", "ONEFORALL", "ARSR"], costType = "No Cost", maxammo = "2", range = Any[500], rangeBurn = "500", image = (full = "SummonerSmite.png", sprite = "spell0.png", group = "spell", x = 0, y = 48, w = 48, h = 48), resource = "No Cost"),
    39 => (id = "SummonerSnowURFSnowball_Mark", name = "Mark", description = "Throw a snowball in a straight line at your enemies. If it hits an enemy, they become marked, granting True Sight, and your champion can quickly travel to the marked target as a follow up.", tooltip = "Throw a snowball a long distance, dealing {{ f1 }} true damage to the first enemy unit hit and granting True Sight of the target. If it hits an enemy, this ability can be recast for {{ e3 }} seconds to Dash to the tagged unit, dealing an additional {{ f1 }} true damage. Dashing to the target will reduce the cooldown of Mark by {{ e4 }}%.

Mark projectiles are not stopped by spell shields or projectile mitigation.", maxrank = 1, cooldown = Any[80], cooldownBurn = "80", cost = Any[0], costBurn = "0", datavalues = NamedTuple(), effect = Any[nothing, Any[10], Any[5], Any[3], Any[25], Any[0], Any[0], Any[0], Any[0], Any[0], Any[0]], effectBurn = Any[nothing, "10", "5", "3", "25", "0", "0", "0", "0", "0", "0"], vars = Any[], key = "39", summonerLevel = 6, modes = Any["SNOWURF"], costType = "No Cost", maxammo = "-1", range = Any[8000], rangeBurn = "8000", image = (full = "SummonerSnowURFSnowball_Mark.png", sprite = "spell0.png", group = "spell", x = 48, y = 48, w = 48, h = 48), resource = "No Cost"),
    32 => (id = "SummonerSnowball", name = "Mark", description = "Throw a snowball in a straight line at your enemies. If it hits an enemy, they become marked, granting True Sight, and your champion can quickly travel to the marked target as a follow up.", tooltip = "Throw a snowball a long distance, dealing {{ f1 }} true damage to the first enemy unit hit and granting True Sight of the target. If it hits an enemy, this ability can be recast for {{ e3 }} seconds to Dash to the tagged unit, dealing an additional {{ f1 }} true damage. Dashing to the target will reduce the cooldown of Mark by {{ e4 }}%.

Mark projectiles are not stopped by spell shields or projectile mitigation.", maxrank = 1, cooldown = Any[80], cooldownBurn = "80", cost = Any[0], costBurn = "0", datavalues = NamedTuple(), effect = Any[nothing, Any[10], Any[5], Any[3], Any[25], Any[0], Any[0], Any[0], Any[0], Any[0], Any[0]], effectBurn = Any[nothing, "10", "5", "3", "25", "0", "0", "0", "0", "0", "0"], vars = Any[], key = "32", summonerLevel = 6, modes = Any["ARAM", "FIRSTBLOOD"], costType = "No Cost", maxammo = "-1", range = Any[1600], rangeBurn = "1600", image = (full = "SummonerSnowball.png", sprite = "spell0.png", group = "spell", x = 96, y = 48, w = 48, h = 48), resource = "No Cost"),
    12 => (id = "SummonerTeleport", name = "Teleport", description = "After channeling for 4 seconds, teleports your champion to target allied structure, minion, or ward and grants a Move Speed boost. The cooldown of Teleport scales from 420-210 seconds depending on champion level.", tooltip = "After channeling for {{ channelduration }} seconds, your champion teleports to target allied structure, minion, or ward and grants a {{ msamount*100 }}% Move Speed boost for {{ msduration }} seconds.", maxrank = 1, cooldown = Any[0], cooldownBurn = "0", cost = Any[0], costBurn = "0", datavalues = NamedTuple(), effect = Any[nothing, Any[0], Any[0], Any[0], Any[0], Any[0], Any[0], Any[0], Any[0], Any[0], Any[0]], effectBurn = Any[nothing, "0", "0", "0", "0", "0", "0", "0", "0", "0", "0"], vars = Any[], key = "12", summonerLevel = 7, modes = Any["ONEFORALL", "PRACTICETOOL", "CLASSIC", "TUTORIAL", "ULTBOOK", "ARSR", "ASSASSINATE", "DOOMBOTSTEEMO"], costType = "No Cost", maxammo = "-1", range = Any[25000], rangeBurn = "25000", image = (full = "SummonerTeleport.png", sprite = "spell0.png", group = "spell", x = 144, y = 48, w = 48, h = 48), resource = "No Cost"),
    54 => (id = "Summoner_UltBook_Placeholder", name = "Placeholder", description = "", tooltip = "Will be replaced by your Ultimate Summoner Spell selection.", maxrank = 1, cooldown = Any[0], cooldownBurn = "0", cost = Any[0], costBurn = "0", datavalues = NamedTuple(), effect = Any[nothing, Any[0], Any[0], Any[0], Any[0], Any[0], Any[0], Any[0], Any[0], Any[0], Any[0]], effectBurn = Any[nothing, "0", "0", "0", "0", "0", "0", "0", "0", "0", "0"], vars = Any[], key = "54", summonerLevel = 1, modes = Any["ULTBOOK"], costType = " ", maxammo = "-1", range = Any[400], rangeBurn = "400", image = (full = "Summoner_UltBook_Placeholder.png", sprite = "spell0.png", group = "spell", x = 192, y = 48, w = 48, h = 48), resource = " "),
)

ext: jl


text:
"""
    get_comments_pages(
        target::String;
        concatenate::Bool=false,
    ) -> Tuple{Vector{String}, Vector{Int32}}

Extract the comments and pages from a passed PDF or all PDFs found recursively in the
passed directory.

# Arguments
- `target::String`: $(TARGET_PDF_ARGUMENT)

# Keywords
- `concatenate::Bool=false`: $(CONCATENATE_KEYWORD)

# Returns
- `Tuple{Vector{String}, Vector{Int32}}`: the comments and pages

# Throws
- Exceptions from: [`get_highlights_comments_pages`](@ref)

# Example
```jldoctest; output = false
using PDFHighlights

path_to_pdf_dir = joinpath(pathof(PDFHighlights) |> dirname |> dirname, "test", "pdf")
path_to_pdf = joinpath(path_to_pdf_dir, "TestPDF.pdf")

get_comments_pages(path_to_pdf_dir; concatenate=true) ==
get_comments_pages(path_to_pdf; concatenate=true) ==
(
    String["Comment 1", "Comment 2 Comment 3", "Comment 4", "", "", "", ""],
    Int32[1, 2, 4, 6, 7, 8, 9],
)

# output

true
```
"""
function get_comments_pages(
    target::String;
    concatenate::Bool=false,
)::Tuple{Vector{String}, Vector{Int32}}

    _, comments, pages = get_highlights_comments_pages(target; concatenate)
    return comments, pages

end


text:
# ---
# title: 131. Palindrome Partitioning
# id: problem131
# author: Indigo
# date: 2021-03-08
# difficulty: Medium
# categories: Backtracking
# link: <https://leetcode.com/problems/palindrome-partitioning/description/>
# hidden: true
# ---
#
# Given a string _s_ , partition _s_ such that every substring of the partition
# is a palindrome.
#
# Return all possible palindrome partitioning of _s_.
#
# **Example:**
#
#
#
#     Input:  "aab"
#     Output:
#     [
#       ["aa","b"],
#       ["a","a","b"]
#     ]
#
#
#
## @lc code=start
using LeetCode

function partition_ps(s::String)
    len = length(s)
    is_ps = fill(true, len, len)
    for i in len:-1:1, j in (i + 1):len
        is_ps[i, j] = (s[i] == s[j]) && is_ps[i + 1, j - 1]
    end
    function dfs(start::Int)
        start == len && return [[SubString(s, len, len)]]
        res = Vector{String}[]
        for ed in start:len
            !is_ps[start, ed] && continue
            for it in dfs(ed + 1)
                push!(res, pushfirst!(it, SubString(s, start, ed)))
            end
        end
        return res
    end
    return dfs(1)
end
## @lc code=end


text:
using ALifeBenchmark
using Serialization
using Plots
using Measures

type = ARGS[1]

if "png" in ARGS
    file_format = "png"
else
    file_format = "svg"
end

if type == "simple_plot"
    data_file = ARGS[2]
    data = deserialize(data_file)

    x_axis_label = ARGS[3]
    y_axis_label = ARGS[4]

    times, values = data

    plot(times, values,
            title = "",
            label = "",
            xguide = x_axis_label,
            yguide = y_axis_label,
            seriestype = :scatter,
            markersize = 1.5,
            markerstrokewidth = 0,
            size = (900, 600),
            margin = 10mm,
            dpi = 1000)

    savefig("$(data_file).$file_format")
end

if type == "2D"
    data_file = ARGS[2]
    data = deserialize(data_file)

    x_axis_label = ARGS[3]
    y_axis_label = ARGS[4]

    time_x, time_y, values = data

    if "remove_zeros" in ARGS
        # remove zeros
        time_x = [t for (i, t) in enumerate(time_x) if values[i] != 0]
        time_y = [t for (i, t) in enumerate(time_y) if values[i] != 0]
        values = [v for v in values if v != 0]
    elseif "replace_zero" in ARGS
        eps = parse(Float64, ARGS[end])
        values = [v == 0 ? eps : v for v in values]
    end

    plot(time_x, time_y,
        marker_z  = ("log" in ARGS) ? log10.(values) : values,
        title = "",
        label = "",
        xguide = x_axis_label,
        yguide = y_axis_label,
        seriestype = :scatter,
        markersize = ("small" in ARGS) ? 1.75 : 4,
        markerstrokewidth = 0,
        size = (900, 600),
        margin = 10mm,
        dpi = 1000)

    savefig("$(data_file).$file_format")
end

if type == "two_plots"
    if length(ARGS) == 5
        data_file_1, data_file_2 = ARGS[2], ARGS[3]
        x_axis_label = ARGS[4]
        y_axis_label = ARGS[5]

        data_1 = deserialize(data_file_1)
        data_2 = deserialize(data_file_2)

        times, values_1 = data_1
        _, values_2 = data_2
    else
        data_file = ARGS[2]
        x_axis_label = ARGS[3]
        y_axis_label = ARGS[4]

        data = deserialize(data_file)
        times, values_1, values_2 = data
    end

    plot(times, [values_1 values_2],
    title = "",
    label = "",
    xguide = x_axis_label,
    yguide = y_axis_label,
    seriestype = :scatter,
    markersize = 1.5,
    markerstrokewidth = 0,
    size = (900, 600),
    margin = 10mm,
    dpi = 1000)

    if length(ARGS) == 5
        savefig("$(data_file_1) $(data_file_2).$file_format")
    else
        savefig("$(data_file).$file_format")
    end
end

if type == "two_log_plots"
    if length(ARGS) == 6
        data_file_1, data_file_2 = ARGS[2], ARGS[3]
        x_axis_label = ARGS[4]
        y_axis_label = ARGS[5]

        data_1 = deserialize(data_file_1)
        data_2 = deserialize(data_file_2)

        times, values_1 = data_1
        _, values_2 = data_2
    else
        data_file = ARGS[2]
        x_axis_label = ARGS[3]
        y_axis_label = ARGS[4]

        data = deserialize(data_file)
        times, values_1, values_2 = data
    end

    plot(times, [values_1 values_2],
    title = "",
    label = "",
    xguide = x_axis_label,
    yguide = y_axis_label,
    xscale = :log10,
    seriestype = :scatter,
    markersize = 1.5,
    markerstrokewidth = 0,
    size = ("half" in ARGS) ? (450, 300) : (900, 600),
    margin = 10mm,
    dpi = 1000)

    if length(ARGS) == 6
        savefig("$(data_file_1) $(data_file_2).$file_format")
    else
        savefig("$(data_file).$file_format")
    end
end

max_stars_repo_name: tochsner/ALifeBenchmark

max_issues_repo_name: tochsner/ALifeBenchmark

max_forks_repo_name: tochsner/ALifeBenchmark


text:
module ExceptionRequest

export StatusError

import ..Layer, ..request
import ..HTTP
using ..Messages.iserror

"""
    request(ExceptionLayer, ::URI, ::Request, body) -> HTTP.Response

Throw a `StatusError` if the request returns an error response status.
"""

abstract type ExceptionLayer{Next <: Layer} <: Layer end
export ExceptionLayer

function request(::Type{ExceptionLayer{Next}}, a...; kw...) where Next

    res = request(Next, a...; kw...)

    if iserror(res)
        throw(StatusError(res.status, res))
    end

    return res
end

"""
The `Response` has a `4xx`, `5xx` or unrecognised status code.

Fields:
 - `status::Int16`, the response status code.
 - `response` the [`HTTP.Response`](@ref)
"""

struct StatusError <: Exception
    status::Int16
    response::HTTP.Response
end

end # module ExceptionRequest


text:
using Ripserer
using SparseArrays
using Test
using TupleTools

using Ripserer: adjacency_matrix, births

include("interfacetest.jl")

@testset "Custom filtration 1" begin
    flt = Custom(
        [
            (1,) => 0,
            (4,) => 0,
            (1, 3) => 2,
            (1, 4) => 3,
            (3, 4) => 6.0,
            (1, 2, 3) => 7,
            (1, 2, 4) => 8,
            (1, 3, 4) => 9,
            (1, 2, 3, 4) => 9,
            (3,) => 10_000,
        ];
        threshold=8,
    )

    @test flt isa Custom{Int,Float64}
    @test dim(flt) == 3
    @test sort(flt[0]; by=index) ==
        [Simplex{0}(1, 0.0), Simplex{0}(2, 7.0), Simplex{0}(3, 2.0), Simplex{0}(4, 0.0)]
    @test isempty(flt[10])
    @test simplex(flt, Val(2), (1, 2, 4)) === Simplex{2}((4, 2, 1), 8.0)
    @test simplex(flt, Val(2), (4, 3, 1)) === nothing

    @test all(eachindex(adjacency_matrix(flt))) do cart_ind
        i, j = TupleTools.sort(Tuple(cart_ind); rev=true)
        if i ≠ j
            idx = index((i, j))
            return adjacency_matrix(flt)[i, j] == haskey(flt.dicts[2], idx)
        else
            return !adjacency_matrix(flt)[i, j]
        end
    end

    d0, d1, d2 = ripserer(flt; dim_max=2)
    @test d0 == [(0, 3), (0, Inf)]
    @test d1 == [(6, Inf)]
    @test d2 == []
end

@testset "Custom filtration vs Rips" begin
    dist = [
        0 1 2 1
        1 0 1 2
        2 1 0 1
        1 2 1 0
    ]

    cf = Custom(
        [
            [(i,) => 0 for i in 1:4]
            [(i, j) => dist[i, j] for i in 1:4 for j in (i + 1):4]
            [
                (i, j, k) => max(dist[i, j], dist[i, k], dist[j, k]) for i in 1:4 for
                j in (i + 1):4 for k in (j + 1):4
            ]
        ],
    )
    @test simplex(cf, Val(3), (4, 3, 2, 1)) == nothing
    @test births(cf) == zeros(4)

    @test ripserer(dist) == ripserer(cf)
end

@testset "Custom filtration vs sparse Rips" begin
    spdist = sparse(
        [
            0 1 0 0 0 1
            1 0 1 0 0 0
            0 1 0 1 0 0
            0 0 1 0 1 0
            0 0 0 1 0 1
            1 0 0 0 1 0
        ]
    )
    cf = Custom([
        (1,) => 0,
        (2,) => 0,
        (3,) => 0,
        (4,) => 0,
        (5,) => 0,
        (6,) => 0,
        (1, 2) => 1,
        (2, 3) => 1,
        (3, 4) => 1,
        (4, 5) => 1,
        (5, 6) => 1,
        (6, 1) => 1,
    ])
    sprips = Rips(spdist)

    @test adjacency_matrix(cf) == Bool.(spdist)
    @test threshold(cf) == 1
    @test ripserer(cf) == ripserer(sprips)
end

@testset "Overflow" begin
    big_simplex = Tuple(Ripserer._vertices(Int128(typemax(Int64) ÷ 2), Val(6)))
    @test_throws OverflowError Custom{Int}([big_simplex => 1])
    @test begin
        Custom{Int128}([big_simplex => 1])
        true
    end
end

@testset "Interface" begin
    data = [
        (1,) => 0,
        (4,) => 0,
        (1, 3) => 2,
        (1, 4) => 3,
        (3, 4) => 6.0,
        (1, 2, 3) => 7,
        (1, 2, 4) => 8,
        (1, 3, 4) => 9,
        (1, 2, 3, 4) => 9,
        (3,) => 10_000,
    ]

    test_filtration(Custom, data; flt_kwargs=(; threshold=9), dim_max=2)
end


text:
"""
Adds an [`AbstractAnalysis`](@ref) to an [`EGraph`](@ref).
An [`EGraph`](@ref) can only contain one analysis of type
`AnType`.
"""
function addanalysis!(g::EGraph, AnType::Type{<:AbstractAnalysis}, args...; lazy=false)
    for i ∈ g.analyses
        typeof(i) isa AnType && return nothing
    end
    analysis = AnType(g, args...)
    push!(g.analyses, analysis)

    !islazy(analysis) && analyze!(g, analysis, collect(keys(g.M)))

    return analysis
end

# FIXME doesnt work on cycles.
"""
**WARNING**. This function is unstable.
"""
function analyze!(g::EGraph, analysis::AbstractAnalysis, ids::Vector{Int64})
    ids = sort(ids)

    did_something = true
    while did_something
        did_something = false

        for id ∈ ids
            id = find(g, id)
            pass = make_pass(g, analysis, id)
            # pass = make_pass(G, analysis, find(G,id))

            if pass !== missing
                if pass !== get(analysis, id, missing)
                    analysis[id] = pass
                    did_something = true
                    # modify!(analysis, id)
                    push!(g.dirty, id)
                end
            end
        end
    end

    rebuild!(g)

    for id ∈ ids
        id = find(g, id)
        if !haskey(analysis, id)
            display(g.M); println()
            display(analysis.data); println()
            error("failed to compute analysis for eclass ", id)
        end
    end

    return analysis
end

analyze!(g::EGraph, an::AbstractAnalysis, id::Int64) =
    analyze!(g, an, reachable(g, id))

function make_pass(g::EGraph, analysis::AbstractAnalysis, id::Int64)
    class = g.M[id]
    # FIXME this check breaks things. wtf
    # for n ∈ class
    #     if n isa Expr
    #         start = Meta.isexpr(n, :call) ? 2 : 1
    #         # if !all(x -> haskey(analysis, find(g, x.id)), n.args[start:end])
    #         # any(x -> find(g, x.id) == find(g, id), n.args[start:end]) &&
    #         #     continue
    #         if !all(x -> haskey(analysis, x.id), n.args[start:end])
    #             return missing
    #         end
    #     end
    # end

    joined = make(analysis, class[1])

    for n ∈ class
        datum = make(analysis, n)
        # println(datum)
        joined = join(analysis, joined, datum)
    end
    return joined
end

# TODO document AbstractAnalysis

modify!(analysis::AbstractAnalysis, id::Int64) =
    error("Analysis does not implement modify!")
join(analysis::AbstractAnalysis, a, b) =
    error("Analysis does not implement join")
make(analysis::AbstractAnalysis, a) =
    error("Analysis does not implement make")

Base.haskey(analysis::AbstractAnalysis, id::Int64) =
    error("Analysis does not implement haskey")
Base.haskey(analysis::AbstractAnalysis, ec::EClass) =
    haskey(analysis, ec.id)

Base.getindex(analysis::AbstractAnalysis, id::Int64) =
    error("Analysis does not implement getindex")
Base.getindex(analysis::AbstractAnalysis, ec::EClass) = analysis[ec.id]

Base.setindex!(analysis::AbstractAnalysis, value, id::Int64) =
    error("Analysis does not implement setindex!")
Base.setindex!(analysis::AbstractAnalysis, value, ec::EClass) =
    setindex!(analysis, ec.id, value)

Base.delete!(analysis::AbstractAnalysis, id::Int64) =
    error("Analysis does not implement delete!")
Base.delete!(analysis::AbstractAnalysis, ec::EClass) =
    setindex!(analysis, ec.id, value)

Base.get(an::AbstractAnalysis, id::Int64, default) = haskey(an, id) ? an[id] : default
Base.get(an::AbstractAnalysis, ec::EClass, default) = get(an, ec.id, default)

islazy(an::AbstractAnalysis)::Bool = false


text:
"""
    OneHotEncoding{R<:Real} <: Transform

One-hot encode the categorical value for each target element.

Construct a n-by-p binary matrix, given a `Vector` of target data `x` (of length n) and a
`Vector` of all unique possible values in x (of length p).

The element [i, j] is `true` if the i^th target in `x` corresponds to the j^th possible
value and `false` otherwise. Note that `R`can be specified to determine the return type
of results. It defaults to a `Matrix` of `Bool`s.

Note that this Transform does not support specifying dims other than `:` (all dims) because
it is a one-to-many transform (for example a `Vector` input produces a `Matrix` output).

Note that `OneHotEncoding` needs to be first encoded with the expected categories before it
can be used. This is because the data might be missing certain categories which will lead to
incomplete classification.
"""
struct OneHotEncoding{R<:Real} <: Transform
    categories::Dict

    function OneHotEncoding{R}(possible_values::AbstractVector) where {R<:Real}
        if length(unique(possible_values)) < length(possible_values)
            throw(ArgumentError("Expected a list of all unique possible values"))
        end

        # Create a dictionary that maps unique values in the input array to column positions
        # in the sparse matrix that results from applying the OneHotEncoding transform
        categories = Dict(value => i for (i, value) in enumerate(possible_values))
        return new{R}(categories)
    end
end

cardinality(::OneHotEncoding) = OneToMany()

function OneHotEncoding(possible_values::AbstractVector{T}) where T
    return OneHotEncoding{Bool}(possible_values)
end

function _apply(x, encoding::OneHotEncoding{R}; kwargs...) where R <: Real
    n_categories = length(encoding.categories)
    results = zeros(R, length(x), n_categories)

    for (i, value) in enumerate(x)
        col_pos = encoding.categories[value]
        results[i, col_pos] = true
    end

    return results
end


text:
for f in (:mean, :std, :var, :median)
    f2 = Symbol(:_, f)
    @eval begin
        function Statistics.$(f)(a::IndicesArray; dims=:, kwargs...)
            d = to_dims(a, dims)
            return rebuild(
                a,
                Statistics.$(f)(parent(a); dims=d, kwargs...),
                reduce_axes(a, d)
               )
        end
    end
end

for f in (:cor, :cov)
    @eval begin
        function Statistics.$f(a::IndicesMatrix; dims=1, kwargs...)
            d = to_dims(a, dims)
            return rebuild(
                a,
                Statistics.$f(parent(a); dims=d, kwargs...),
                covcor_axes(a, d)
               )
        end

        Statistics.$f(a::IndicesVector) = Statistics.$f(parent(a))
    end
end


text:
using MPI
using Trilinos
using Test
using BenchmarkTools
#using Cartesian

if isinteractive()
  using Plots
end

const IndexT = Int

# abstract type IndexMapping end

# """
# Store a global mapping between grid GID and linear system GIDs
# """
# struct FilteredMapping{ArrayT} <: IndexMapping
#   array::ArrayT
#   grid::CartesianGrid
# end

# function FilteredMapping(grid, nodes::Function, filter::Function, startindex)
#   mappingarray = fill(-1, size(nodes(grid)))
#   gid = startindex
#   for I in nodes(grid)
#     if filter(grid, I)
#       mappingarray[I] = gid
#       gid += 1
#     end
#   end
#   return FilteredMapping(mappingarray, grid)
# end

# Base.getindex(m::IndexMapping, I::CartesianIndex) = m.array[I]
# Base.in(I::CartesianIndex, m::IndexMapping) = m[I] != -1

# function test_grid_functions(myid, numprocs)
#   nx = 5
#   h = 2/(nx-1)
#   grid = CartesianGrid(nx,nx,h)

#   @test length(globalnodes(grid)) == nx*nx
#   @test numglobalnodes(grid) == nx*nx

#   cartesian_coords = CartesianCoordinates(grid)

#   for I in globalnodes(grid)
#     @test cartesian_coords[I] == ((I[1]-1)*h-1.0, (I[2]-1)*h-1.0)
#   end

#   coords = coordinates(grid)

#   for (i,I) in enumerate(globalnodes(grid))
#     @test cartesian_coords[I] == (coords[i,1], coords[i,2])
#   end

#   # mesh_to_system_gid = FilteredMapping(grid, globalnodes, (::Any,::Any) -> true, 0)

#   # @show localnodes(grid, 4, 1)
#   # @show localnodes(grid, 4, 2)
#   # @show localnodes(grid, 4, 3)
#   # @show localnodes(grid, 4, 4)

#   # @show c = coordinates.(grid, globalnodes(grid))
#   # @show reinterpret(Array{Float64,2}, c)
# end

# MPI setup
if !MPI.Initialized()
  MPI.Init()
end

comm = Teuchos.MpiComm(MPI.CComm(MPI.COMM_WORLD))
my_rank = Teuchos.getRank(comm)
nb_procs = Teuchos.getSize(comm)

if my_rank != 0
  redirect_stdout(open("/dev/null", "w"))
  redirect_stderr(open("/dev/null", "w"))
end

# @testset "Grid tests" begin
#   test_grid_functions(my_rank+1, nb_procs)
# end

if isinteractive()

  const nx = 10
  const h = 2/(nx-1)
  const grid = CartesianGrid(nx,nx,h)

  coords = coordinates(grid)

  nb_procs = 4
  @show ln1 = localnodes(grid, nb_procs, 1)
  @show ln2 = localnodes(grid, nb_procs, 2)
  @show ln3 = localnodes(grid, nb_procs, 3)
  @show ln4 = localnodes(grid, nb_procs, 4)

  o1 = filter(i -> (partition(ln1,i) == 1), eachindex(ln1))
  o2 = filter(i -> (partition(ln2,i) == 2), eachindex(ln2))
  o3 = filter(i -> (partition(ln3,i) == 3), eachindex(ln3))
  o4 = filter(i -> (partition(ln4,i) == 4), eachindex(ln4))

  g1 = filter(i -> (partition(ln1,i) != 1), eachindex(ln1))
  g2 = filter(i -> (partition(ln2,i) != 2), eachindex(ln2))
  g3 = filter(i -> (partition(ln3,i) != 3), eachindex(ln3))
  g4 = filter(i -> (partition(ln4,i) != 4), eachindex(ln4))

  @show length(ln1)
  @show length(ln2)
  @show length(ln3)
  @show length(ln4)

  @show numglobalnodes(grid)
  @show length(ln1) + length(ln2) + length(ln3) + length(ln4)

  scatter( coords[ln1[o1],1], coords[ln1[o1],2], marker=:utriangle, legend=false, aspectratio=1)
  scatter!(coords[ln2[o2],1], coords[ln2[o2],2], marker=:dtriangle)
  scatter!(coords[ln3[o3],1], coords[ln3[o3],2], marker=:+)
  scatter!(coords[ln4[o4],1], coords[ln4[o4],2], marker=:x)
  scatter!(coords[ln1[g1],1], coords[ln1[g1],2], alpha=0.4, marker=:utriangle, legend=false)
  scatter!(coords[ln2[g2],1], coords[ln2[g2],2], alpha=0.4, marker=:dtriangle)
  scatter!(coords[ln3[g3],1], coords[ln3[g3],2], alpha=0.4, marker=:+)
  scatter!(coords[ln4[g4],1], coords[ln4[g4],2], alpha=0.4, marker=:x)

else
  MPI.Finalize()
end


text:
# If we have a relative extension L/K with defining polynomial f, we
# parametrize the infinite places of L using tuples (p, a), where p is a place of K
# and a is a root of p(g).
#
# We store the the following data on the field L itself, which can be accessed via
# _conjugate_data_arb(L, prec)):
#   Vector{Tuple{Vector{InfPlc{K}, Vector{acb}, Vector{arb}, Vector{acb}}
#
# We use the following ordering, which is very important and must not be changed:
# Let (p, rts, r_rts, c_rts) be such an entry.
#
# if p is real, then rts contains
#   - the real roots (isreal returning true), order from -oo to oo
#   - then all complex roots with positive imaginary part, order from 0 to oo
# if p is complex, then rts contains
#   - all complex roots with positive imaginary part, ordered -oo to oo
#
# For each place p of K, we have a tuple
#   (p, roots of p(g), arb[], acb[]) if p is complex, and
#   (p, roots of p(g), real roots of g(a), complex roots of g(a) (one per pair))
#   if p is real.
#
# To compute the conjugates of an element represented by h, we iterate over the
# (p, rts, r_rts, c_rts) and first add [ p(g)(r) for r in r_rts if isreal(p)]
# and then [p(g)(rts) for r in r_rts if !isreal(p)]
mutable struct InfPlcNfRel{S, T}
  field::T
  base_field_place::S
  isreal::Bool
  i::Int
  r::acb
  absolute_index::Int
end

absolute_index(P::InfPlcNfRel) = P.absolute_index

number_field(P::InfPlcNfRel) = P.field

place_type(::Type{AnticNumberField}) = InfPlc

place_type(::AnticNumberField) = InfPlc

place_type(::Type{NfRel{T}}) where {T} = InfPlcNfRel{place_type(parent_type(T)), NfRel{T}}

place_type(K::NfRel{T}) where {T} = place_type(NfRel{T})

isreal(P::InfPlcNfRel) = P.isreal

sign(x::NumFieldElem, P) = _signs(x)[P.absolute_index]

function _signs(a)
  if iszero(a)
    error("element must not be zero")
  end

  p = 16
  r1, r2 = signature(parent(a))

  if r1 == 0
    return Int[]
  end

  s = Array{Int}(undef, r1)
  while true
    c = conjugates(a, p)
    done = true
    for i=1:r1
      if contains(real(c[i]), 0)
        p *= 2
        done = false
        break
      end
      s[i] = ispositive(real(c[i])) ? 1 : -1
    end
    if done
      return s
    end
  end
end

function real_places(K::NfRel)
  res = place_type(K)[]
  for p in infinite_places(K)
    if isreal(p)
      push!(res, p)
    end
  end
  return res
end

function _root(P::InfPlcNfRel)
  return P.r
end

function evaluate(a::NfRelElem, P::InfPlcNfRel, prec::Int = 32)
  r = conjugates_arb(a, prec)
  L = parent(a)
  return r[P.absolute_index]
end

function infinite_places(L::NfRel{T}) where {T}
  S = place_type(parent_type(T))
  _res = get_special(L, :infinite_places)
  if _res !== nothing
    return _res::Vector{InfPlcNfRel{S}}
  end
  K = base_field(L)
  data = _conjugates_data(L, 32)
  r, s = signature(L)
  plcs = Vector{InfPlcNfRel{S}}(undef, r+s)
  r_cnt = 1
  c_cnt = 1
  for (P, rts, r_rts, c_rts) in data
    if isreal(P)
      for i in 1:length(r_rts)
        plcs[r_cnt] = InfPlcNfRel{S, typeof(L)}(L, P, true, i, rts[i], r_cnt)
        r_cnt += 1
      end
      rr = length(r_rts)
      for i in 1:length(c_rts)
        plcs[r + c_cnt] = InfPlcNfRel{S, typeof(L)}(L, P, false, rr + i, rts[rr + i], r + c_cnt)
        c_cnt +=1
      end
    else
      for i in 1:length(rts)
        plcs[r + c_cnt] = InfPlcNfRel{S, typeof(L)}(L, P, false, i, rts[i], r + c_cnt)
        c_cnt += 1
      end
    end
  end
  set_special(L, :infinite_places => plcs)
  return plcs
end

function Base.show(io::IOContext, P::InfPlcNfRel{S}) where {S}
  print(io, "Infinite place of\n")
  println(io, number_field(P))
  print(io, "extending the place\n", P.base_field_place, "\n")
  print(io, "with root $(_root(P))")
end

function _roots(f::PolyElem{<: NumFieldElem}, P; prec::Int = 64, sort::Bool = true)
  wprec = Int(floor(1.3 * prec))
  # We definitely want to isolate the real roots as well as identify conjugated roots
  local rts::Vector{acb}
  while true
    con = acb[evaluate(coeff(f, i), P, wprec) for i in 0:degree(f)]
    isreal(P) && @assert all(isreal, con)
    # We need a strictly real polynomial to isolate the real roots
    _wprec = maximum([precision(parent(c)) for c in con])
    CC = AcbField(wprec, cached = false)
    CCy, y = PolynomialRing(CC, cached = false)
    _f = CCy(con)
    try
      rts = roots(_f, target = prec)
      break
    catch e
      if !(isa(e, ErrorException))
        rethrow(e)
      else
        wprec = 2 * wprec
        continue
      end
    end
  end

  if isreal(P)
    r = 0
    real_rts = Vector{arb}()
    for i in 1:length(rts)
      if !isreal(rts[i])
        break
      end
      r += 1
      push!(real_rts, real(rts[i]))
    end

    # let's order the roots

    if sort
      @assert all(i -> isreal(rts[i]), 1:r)
      _rts = view(rts, (r+1):length(rts))
      # Sort the complex one from oo to -oo
      sort!(_rts, by = x -> imag(x), rev = true)
      s = div(length(rts) - r, 2)
      _rts = view(rts, (r+1):(r + s))
      # Sort the first s complex ones from 0 to oo
      sort!(_rts, by = x -> imag(x), rev = false)
    end

    compl_rts = Vector{acb}()

    s = 0

    for i in (r+1):length(rts)
      if ispositive(imag(rts[i]))
        s += 1
        push!(compl_rts, rts[i])
      end
    end
    @assert r + 2*s == degree(f)
  else
    if sort
      sort!(rts, by = x -> imag(x),  rev = false)
    end
    real_rts = arb[]
    compl_rts = acb[]
  end
  return rts, real_rts, compl_rts
end

function _conjugates_data(L::NfRel{T}, prec::Int) where {T}
  c = get_special(L, :conjugate_data_arb)
  S = place_type(parent_type(T))
  if c === nothing
    D = Dict{Int, Vector{Tuple{S, Vector{acb}, Vector{arb}, Vector{acb}}}}()
    z = _get_conjugate_data(L, prec)
    D[prec] = z
    set_special(L, :conjugate_data_arb => D)
    return z
  else
    D = c
    if haskey(D, prec)
      return D[prec]::Vector{Tuple{S, Vector{acb}, Vector{arb}, Vector{acb}}}
    else
      z = _get_conjugate_data(L, prec)
      D[prec] = z
      return z
    end
  end
end

function _get_conjugate_data(L::NfRel{T}, prec::Int) where {T}
  K = base_field(L)
  S = place_type(parent_type(T))
  g = defining_polynomial(L)
  pls = infinite_places(K)
  data = Tuple{S, Vector{acb}, Vector{arb}, Vector{acb}}[]
  for p in  pls
    push!(data, (p, _roots(g, p, prec = prec)...))
  end
  return data
end

function conjugates_arb(a::NfRelElem, prec::Int = 64)
  z = acb[]
  L = parent(a)
  g = defining_polynomial(L)
  K = base_field(L)
  conju = Vector{acb}(undef, absolute_degree(parent(a)))
  wprec = prec
  d = absolute_degree(L)

  while true
    data = _get_conjugate_data(L, wprec)

    CC = AcbField(wprec, cached = false)
    CCy, y = PolynomialRing(CC, cached = false)

    r, s = signature(parent(a))
    _r, _s = signature(K)
    real_cnt = 1
    compl_cnt = r + 1

    for i in 1:length(data)
      P, rts, real_rts, compl_rts = data[i]
      a_as_poly = a.data
      poly_evaluated = CCy(acb[evaluate(coeff(a_as_poly, i), P, wprec) for i in 0:degree(a_as_poly)])
      if isreal(P)
        _conjs = evaluate(poly_evaluated, rts[1:length(real_rts)])
        for rt in _conjs
          conju[real_cnt] = rt
          real_cnt += 1
        end
        _conjs = evaluate(poly_evaluated, rts[(length(real_rts) + 1):(length(real_rts) + length(compl_rts))])
        for rt in _conjs
          conju[compl_cnt] = rt
          conju[compl_cnt + s] = conj(rt)
          compl_cnt += 1
        end
      else
        _conjs = evaluate(poly_evaluated, rts)
        for rt in _conjs
          conju[compl_cnt] = rt
          conju[compl_cnt + s] = conj(rt)
          compl_cnt += 1
        end
      end
    end

    if all(x -> radiuslttwopower(x, -prec), conju)
      break
    else
      wprec = 2 * wprec
      continue
    end
  end

  for i in 1:d
    expand!(conju[i], -prec)
  end

  return conju
end

function infinite_places(L::NfRel{nf_elem}, p)
  return [P for P in infinite_places(L) if P.base_field_place == p]
end


text:
using Graphs
using Random

Random.seed!(1234)

function dijkstra_spt(g, v_src_initial)
  out = dijkstra_shortest_paths(g, [v_src_initial]; allpaths=true, trackvertices=true)
  paths = out.predecessors
  edges = Edge{Int}[]
  for v_dst_final in eachindex(paths)
    v_src = v_src_initial
    p = paths[v_dst_final]
    @show v_src_initial, p, v_dst_final
    for v_src in p
      push!(edges, Edge(v_src => v_dst_final))
    end
  end
  return edges
end

g = Graph(6, 10)

@show collect(edges(g))

t_mst = kruskal_mst(g)

@show t_mst

t_bfs = bfs_tree(g, 1)

@show collect(edges(t_bfs))

t_spt = dijkstra_spt(g, 1)

@show t_spt


text:
# Raytracer.jl
# Raytracing for the generation of photorealistic images in Julia
# Copyright (c) 2021 Samuele Colombo, Paolo Galli

# Trace utility to generate an image by shooting light rays through each of its pixels

"""
    ImageTracer

Trace an image by shooting light rays through each of its pixels.

To fill an image store it into `ImageTracer` along with the desired camera and apply [`fire_all_rays!`](@ref)
to it. Alternatively apply iteratively [`fire_ray(::ImageTracer, ::Int, ::Int; ::Float32, ::Float32)`](@ref)
on the desired ranges.

# Fields

- `image::HdrImage`: a [`HdrImage`](@ref) in which save the rendered image.
- `camera::Camera`: a [`Camera`](@ref) holding the observer informations.
- `samples_per_side::Int`: the number of samples per side of a pixel for antialiasing algorithm.
- `rng::PCG`: a [`PCG`](@ref) random number generator for antialiasing algorithm.

If `samples_per_side` is larger than zero, antialiasing will be applied to each pixel in the image,
using the random number generator `rng`.
"""
struct ImageTracer
    image::HdrImage
    camera::Camera
    samples_per_side::Int
    rng::PCG
end

@doc """
    ImageTracer(image::HdrImage, camera::Camera, samples_per_side::Int, rng::PCG)

Constructor for an [`ImageTracer`](@ref) instance.
""" ImageTracer(::HdrImage, ::Camera, ::Int, ::PCG)

"""
    ImageTracer(image::HdrImage, camera::Camera
                ; samples_per_side::Int = 0,
                  rng::PCG = PCG())

Construct a [`ImageTracer`](@ref).

If `samples_per_side` is not specified, antialiasing will be disabled and `rng` is ignored.
"""
function ImageTracer(image::HdrImage, camera::Camera;
                     samples_per_side::Int = 0,
                     rng::PCG = PCG())
    ImageTracer(image, camera, samples_per_side, rng)
end

"""
    fire_ray(tracer::ImageTracer, col::Int, row::Int
             ; u_pixel::Float32 = 0.5f0,
               v_pixel::Float32 = 0.5f0)

Shoot a [`Ray`](@ref) through the pixel `(col, row)` of the image contained in an [`ImageTracer`], using its
camera informations.

The function use the `fire_ray` function of the associated camera ([`fire_ray(::OrthogonalCamera, ::Float32, ::Float32)`](@ref),
    [`fire_ray(::PerspectiveCamera, ::Float32, ::Float32)`](@ref))

The parameters `col` and `row` are measured in the same way as they are in [`HdrImage`](@ref): the bottom left
corner is placed at ``(0, 0)``. The values of `u_pixel` and `v_pixel` are floating-point numbers in the range
``[0, 1]``: they specify where the ray should cross the pixel; passing 0.5 to both means that the ray will pass
through the pixel's center.

See also: [`fire_all_rays!`](@ref)
"""
function fire_ray(tracer::ImageTracer, col::Int, row::Int;
                  u_pixel::Float32 = 0.5f0,
                  v_pixel::Float32 = 0.5f0)
    size_col, size_raw = size(tracer.image)
    u = (col - 1f0 + u_pixel) / size_col
    v = 1f0 - (row - 1f0 + v_pixel) / size_raw
    fire_ray(tracer.camera, u, v)
end

function fire_all_rays_loop(tracer::ImageTracer, ind::CartesianIndex{2}, renderer::Renderer)
    if tracer.samples_per_side > 0
        cum_color = BLACK
        for inter_pixel_row in 0:tracer.samples_per_side-1
            for inter_pixel_col in 0:tracer.samples_per_side-1
                u_pixel = (inter_pixel_col + rand(tracer.rng, Float32)) / tracer.samples_per_side
                v_pixel = (inter_pixel_row + rand(tracer.rng, Float32)) / tracer.samples_per_side
                ray = fire_ray(tracer, Tuple(ind)..., u_pixel=u_pixel, v_pixel=v_pixel)
                cum_color += renderer(ray)
            end
        end
        tracer.image.pixel_matrix[ind] = cum_color * (1 / tracer.samples_per_side^2)
    else
        ray = fire_ray(tracer, Tuple(ind)...)
        tracer.image.pixel_matrix[ind] = renderer(ray)
    end
end

"""
    fire_all_rays!(tracer::ImageTracer, renderer::Renderer
                   ; use_threads::Bool = true,
                     enable_progress_bar::Bool = true)

Render an image with informations stored in an [`ImageTracer`](@ref) using the specified [`Renderer`](@ref).

This function apply iteratively [`fire_ray(::ImageTracer, ::Int, ::Int; ::Float32, ::Float32)`](@ref) for
each pixel in the image contained in `tracer` using its camera, and then render the point using `renderer`.

If `use_threads` is `true`, the function will use the `Threads.@threads` macro to parallelize the computation.

If `enable_progress_bar` is `true`, the function will display a progress bar during the computation; this is thread safe.

See also: [`fire_ray(::ImageTracer, ::Int, ::Int; ::Float32, ::Float32)`](@ref)
"""
function fire_all_rays!(tracer::ImageTracer, renderer::Renderer;
                        use_threads::Bool = true,
                        enable_progress_bar::Bool = true)
    indices = CartesianIndices(tracer.image.pixel_matrix)
    p = Progress(length(indices), color=:white, enabled=enable_progress_bar)
    if use_threads
        Threads.@threads for ind ∈ indices
            fire_all_rays_loop(tracer, ind, renderer)
            next!(p)
        end
    else
        for ind ∈ indices
            fire_all_rays_loop(tracer, ind, renderer)
            next!(p)
        end
    end
end

################
# Miscellaneous

function show(io::IO, ::MIME"text/plain", t::ImageTracer)
    println(io, typeof(t), " with camera of type ", typeof(t.camera))
    print(io, "image of size ", join(size(t.image), "x"), " and of type ", typeof(t.image));
end

eltype(::ImageTracer) = Float32
eltype(::Type{ImageTracer}) = Float32


text:
module MultivariateDistributions

    using StaticArrays

    export Gaussian

    include("gaussian.jl")

end

max_stars_repo_name: andreasnoack/MultivariateDistributions

max_issues_repo_name: andreasnoack/MultivariateDistributions

max_forks_repo_name: andreasnoack/MultivariateDistributions


text:
@testset "Primitives" begin
    @test Pyramid(Point(0,0,0),1,2) == Pyramid{Int}(Point{3,Int}((0,0,0)),1,2)
    @test Particle(Point(0,0,0), Vec(1,1,1)) == Particle{3,Int}(Point{3,Int}((0,0,0)),Vec{3,Int}((1,1,1)))
    @test HyperCube(Vec(0,0,0), 1) == HyperCube{3,Int}(Vec{3,Int}(0,0,0), 1)

    centered_rect = centered(HyperCube)
    @test centered_rect == HyperCube{3,Float32}(Vec3f0(-0.5), 1f0)
    centered_rect = centered(HyperCube{2})
    @test centered_rect == HyperCube{2,Float32}(Vec2f0(-0.5f0), 1f0)

    centered_rect = centered(HyperCube{2, Float64})
    @test centered_rect == HyperCube{2,Float64}(Vec(-0.5,-0.5), 1.)

    centered_rect = centered(HyperCube{3, Float32})
    @test centered_rect == HyperCube{3,Float32}(Vec3f0(-0.5), 1f0)

end

@testset "Simplex" begin
    s = Simplex(Point(1,2,3))
    @test s == Simplex{1,Point{3,Int}}((Point{3,Int}((1,2,3)),))
    s = Simplex(Point(1,2), Point(2,3))
    @test s == Simplex{2,Point{2,Int}}((Point{2,Int}((1,2)),Point{2,Int}((2,3))))
end


text:
# conversion.jl
# this file contains all functions related to converting from one set of
# variables to another

# organization:
#  The methods that blindly convert a vector of values from one set of
#  variables to another have names ending with an underscore (convertToEntropy_)
#  The functions that look at the static parameters of the params object
#  to determine what variables the input vector is in and then does the proper
#  conversion (or no-op, if that is the case) do not have an underscore (ie.
#  (convertToConservative, convertToEntropy).  Note that it is safe to
#  convert conservative variables to conservative variables, the
#  proper conversion is a no-op. Anyone writing physics code should call
#  the non-underscore methods, never the underscore methods.
#
#  There are also the concepts of the natural variables and the working
#  variables.
#  The natural variables are the ones the governing equation is usually
#  written in (conservative for the Euler equations).  The working variables
#  are the ones the equation is being solved in, as determined by the
#  static parameters of the params object.  The function
#  convertFromNaturalToWorking vars does the transformation as indicated
#  It is ocasionally useful for writing generic code because it enables a
#  function that doesn't know what type of variables it is using to convert
#  to conservative and and then convert back (converting back is the problem
#  this function solves).

#------------------------------------------------------------------------------
# convert variables without checking what type they currently are (unsafe)
#------------------------------------------------------------------------------
@doc """
### EulerEquationMod.convertToEntropy_

  Converts the conservative variables at a node to the entropy variables
  regardless of the static parameter var_type.

  Inputs:
  params  : ParamType{Tdim} used to dispatch to the proper method
  qe  : vector (of length 4 or 5) of conservative variables

  Inputs/outputs
  qc : vector (of length 4 or 5) of entropy variables.  Contents of vector are
       overwritten

  Aliasing: none (qc and qe *can* be the same vector)

  Ths is a low level function.
"""->

function convertToEntropy_(params::ParamType{2},
                     qc::AbstractArray{Tsol,1},
                     qe::AbstractArray{Tsol, 1}) where Tsol

  gamma = params.gamma
  gamma_1 = params.gamma_1

  k1 = 0.5*(qc[2]^2 + qc[3]^2)/qc[1]
  rho_int = qc[4] -k1
  s = log(gamma_1*rho_int/(qc[1]^gamma))
  fac = 1.0/rho_int
  tmp1 = -qc[1]*fac
  qe[1] = (rho_int*(gamma + 1 -s) - qc[4])*fac
  qe[2] = qc[2]*fac
  qe[3] = qc[3]*fac
  qe[4] = tmp1

  return nothing
end

function convertToEntropy_(params::ParamType{3},
                     qc::AbstractArray{Tsol,1},
                     qe::AbstractArray{Tsol, 1}) where Tsol

  gamma = params.gamma
  gamma_1 = params.gamma_1

  k1 = 0.5*(qc[2]^2 + qc[3]^2 + qc[4]^2)/qc[1]
  rho_int = qc[5] - k1
  s = log(gamma_1*rho_int/(qc[1]^gamma))
  fac = 1.0/rho_int
  tmp1 = -qc[1]*fac
  qe[1] = (rho_int*(gamma + 1 -s) - qc[5])*fac
  qe[2] = qc[2]*fac
  qe[3] = qc[3]*fac
  qe[4] = qc[4]*fac
  qe[5] = tmp1

  return nothing
end

@doc """
### EulerEquationMod.convertToConservative_

  Converts  the entropy variables at a node to the conservative variables
  regardless of the static parameter var_type.

  Inputs:
  params  : ParamType{Tdim} used to dispatch to the proper method
  qe  : vector (of length 4 or 5) of entropy variables

  Inputs/outputs
  qc : vector (of length 4 or 5) of conservative variables.  Contents of vector are
       overwritten

  Aliasing: none (qc and qe *can* be the same vector)

  Ths is a low level function.
"""->
function convertToConservative_(params::ParamType{2},
                          qe::AbstractArray{Tsol,1},
                          qc::AbstractArray{Tsol, 1}) where Tsol

  gamma = params.gamma
  gamma_1 = params.gamma_1
  k1 = 0.5*(qe[2]^2 + qe[3]^2)/qe[4]
  s = gamma - qe[1] + k1
  rho_int = exp(-s/gamma_1)*(gamma_1/(-qe[4])^gamma)^(1/gamma_1)
  qc[1] = -qe[4]*rho_int
  qc[2] = qe[2]*rho_int
  qc[3] = qe[3]*rho_int
  qc[4] = (1.0 - k1)*rho_int
end

function convertToConservative_(params::ParamType{3},
                          qe::AbstractArray{Tsol,1},
                          qc::AbstractArray{Tsol, 1}) where Tsol

  gamma = params.gamma
  gamma_1 = params.gamma_1
  k1 = 0.5*(qe[2]^2 + qe[3]^2 + qe[4]^2)/qe[5]
  s = gamma - qe[1] + k1
  rho_int = exp(-s/gamma_1)*(gamma_1/(-qe[5])^gamma)^(1/gamma_1)
  qc[1] = -qe[5]*rho_int
  qc[2] = qe[2]*rho_int
  qc[3] = qe[3]*rho_int
  qc[4] = qe[4]*rho_int
  qc[5] = (1.0 - k1)*rho_int
end

@doc """
### EulerEquationMod.convertToIR_

  Converts the conservative variables at a node to the entropy variables
  of Ismail and Roe regardless of the static parameter var_type.

  Inputs:
  params  : ParamType{Tdim} used to dispatch to the proper method
  qe  : vector (of length 4 or 5) of conservative variables

  Inputs/outputs
  qc : vector (of length 4 or 5) of entropy variables.  Contents of vector are
       overwritten

  Aliasing: none (qc and qe *can* be the same vector)

  Ths is a low level function.
"""->
function convertToIR_(params::ParamType{2},
                     qc::AbstractArray{Tsol, 1},
                     qe::AbstractArray{Tsol, 1}) where Tsol
# this is the same code as convertToEntropy_, scaled by 1/gamma_1
# it is necessary to copy it because qc and qe can alias

  gamma = params.gamma
  gamma_1 = params.gamma_1
  gamma_1i = 1/gamma_1

  k1 = 0.5*(qc[2]^2 + qc[3]^2)/qc[1]
  rho_int = qc[4] -k1
  s = log(gamma_1*rho_int/(qc[1]^gamma))
  fac = 1.0/rho_int
  tmp1 = -qc[1]*fac*gamma_1i
  qe[1] = ((rho_int*(gamma + 1 -s) - qc[4])*fac)*gamma_1i
  qe[2] = qc[2]*fac*gamma_1i
  qe[3] = qc[3]*fac*gamma_1i
  qe[4] = tmp1

  return nothing
end

function convertToIR_(params::ParamType{3},
                     qc::AbstractArray{Tsol, 1},
                     qe::AbstractArray{Tsol, 1}) where Tsol

  gamma = params.gamma
  gamma_1 = params.gamma_1
  gamma_1i = 1/gamma_1

  k1 = 0.5*(qc[2]^2 + qc[3]^2 + qc[4]^2)/qc[1]
  rho_int = qc[5] - k1
  s = log(gamma_1*rho_int/(qc[1]^gamma))
  fac = 1.0/rho_int
  tmp1 = -qc[1]*fac*gamma_1i
  qe[1] = ((rho_int*(gamma + 1 -s) - qc[5])*fac)*gamma_1i
  qe[2] = qc[2]*fac*gamma_1i
  qe[3] = qc[3]*fac*gamma_1i
  qe[4] = qc[4]*fac*gamma_1i
  qe[5] = tmp1

  return nothing
end

@doc """
### EulerEquationMod.convertToConservativeFromIR_

  Converts the IR entropy variables at a node to the conservative variables
  regardless of the static parameter var_type.

  Inputs:
  params  : ParamType{Tdim} used to dispatch to the proper method
  qe  : vector (of length 4 or 5) of entropy variables

  Inputs/outputs
  qc : vector (of length 4 or 5) of conservative variables.  Contents of
       vector are overwritten

  Aliasing: none (qc and qe *can* be the same vector)

  Ths is a low level function.
"""->
function convertToConservativeFromIR_(params::ParamType{2},
                                qe::AbstractArray{Tsol, 1},
                                qc::AbstractArray{Tsol, 1}) where Tsol
  # this is very siilar to the convertToConservative code, except qe is
  # scaled by gamma_1.  There is no way to do this operation by calling
  # convertToConservative_ in the case where qe and qc alias

  gamma = params.gamma
  gamma_1 = params.gamma_1
  k1 = 0.5*gamma_1*(qe[2]^2 + qe[3]^2)/qe[4]
  s = gamma - gamma_1*qe[1] + k1
  rho_int = exp(-s/gamma_1)*(gamma_1/(-gamma_1*qe[4])^gamma)^(1/gamma_1)
  rho_int *= gamma_1
  qc[1] = -qe[4]*rho_int
  qc[2] = qe[2]*rho_int
  qc[3] = qe[3]*rho_int
  qc[4] = (1.0 - k1)*rho_int/gamma_1
end

function convertToConservativeFromIR_(params::ParamType{3},
                                qe::AbstractArray{Tsol,1},
                                qc::AbstractArray{Tsol, 1}) where Tsol

  gamma = params.gamma
  gamma_1 = params.gamma_1
  k1 = 0.5*gamma_1*(qe[2]^2 + qe[3]^2 + qe[4]^2)/qe[5]
  s = gamma - gamma_1*qe[1] + k1
  rho_int = exp(-s/gamma_1)*(gamma_1/(-gamma_1*qe[5])^gamma)^(1/gamma_1)
  rho_int *= gamma_1
  qc[1] = -qe[5]*rho_int
  qc[2] = qe[2]*rho_int
  qc[3] = qe[3]*rho_int
  qc[4] = qe[4]*rho_int
  qc[5] = (1.0 - k1)*rho_int/gamma_1
end

#------------------------------------------------------------------------------
# converting to conservative variables safely
#------------------------------------------------------------------------------
@doc """
### EulerEquationMod.convertToConservative

  Converts  the entropy variables at a node to the conservative variables.

  Inputs:
  params  : ParamType{2, :entropy} used to dispatch to the proper method
  qe  : vector (of length 4) of entropy variables

  Inputs/outputs
  qc : vector (of length 4) of conservative variables.  Contents of vector are
       overwritten

  Aliasing: none (qc and qe *can* be the same vector)

  Ths is a low level function.
"""->
function convertToConservative(params::ParamType{Tdim, :entropy},
                   qe::AbstractArray{Tsol,1},
                   qc::AbstractArray{Tsol, 1}) where {Tdim, Tsol}

  convertToConservative_(params, qe, qc)

end

@doc """
# low level function

  Converts conservative variables to conservative variables (ie. it
  copies the input to the output).  This method exists to values can be
  converted without knowing whether they are conservative or entropy.

  Aliasing restrictions: none (qc and qe *can* be the same vector)

"""->
function convertToConservative(params::ParamType{Tdim,
                  :conservative}, qe::AbstractArray{Tsol,1},
                  qc::AbstractArray{Tsol, 1}) where {Tdim, Tsol}
# maybe the compiler will be smart enough to make this a no-op?
  for i=1:length(qe)
    qc[i] = qe[i]
  end

  return nothing
end

@doc """
### EulerEquationMod.convertFromNaturlaToWorkingVars

  This function converts a vector qc from the "natural" set of variables to
  write the equation to the set of variables the equation is being solved in,
  defined by the static parameter var_type of the params.  For the Euler
  equations, the "natural" variables are the conservative variables.
  Every new set of variables must extend this function with a new method.

  Inputs:
    params:  ParamType{Tdim, var_type}
    qc: vector of "natural" variables

  Inputs/Outputs:
    qe: vector to be populated with the new variables

  Low level function.

  Aliasing restrictions: none (this requires that every method of this function
                         support in-place conversion).

"""->
function convertFromNaturalToWorkingVars(
                             params::ParamType{Tdim, :conservative},
                             qc::AbstractArray{Tsol,1},
                             qe::AbstractArray{Tsol,1}) where {Tdim, Tsol}
  for i=1:length(qc)
    qe[i] = qc[i]
  end

end

@doc """
# mid level function

  Converts the array (3D form) of entropy variables to conservative variables
  in place.  If the array is already in conservative variables this is a no-op.
  Other methods exist if q_arr is a vector.

  Inputs:
    mesh
    sbp
    eqn
    opts

  Inputs/Outputs:
    q_arr: array of values (3D) to be converted

  Aliasing: no restrictions
"""->
#3D array entropy -> conservative
function convertToConservative(mesh::AbstractMesh{Tmsh},
       sbp::AbstractOperator,
       eqn::EulerData{Tsol, Tres, Tdim, :entropy},
       opts, q_arr::AbstractArray{Tsol, 3}) where {Tmsh, Tsol, Tdim, Tres}

  for i=1:mesh.numEl  # loop over elements
    for j=1:mesh.numNodesPerElement
      q_view = sview(q_arr, :, j, i)  # reuse memory
      convertToConservative(eqn.params, q_view, q_view)
    end
  end

  return nothing
end

# 3D array conservative -> conservative
function convertToConservative(mesh::AbstractMesh{Tmsh},
       sbp::AbstractOperator,
       eqn::EulerData{Tsol, Tres, Tdim, :conservative},
       opts, q_arr::AbstractArray{Tsol, 3}) where {Tmsh, Tsol, Tdim, Tres}

  return nothing
end

# q_vec conversion entropy -> conservative
function convertToConservative(mesh::AbstractMesh{Tmsh},
       sbp::AbstractOperator,
       eqn::EulerData{Tsol, Tres, Tdim, :entropy},
       opts, q_vec::AbstractArray{Tsol, 1}) where {Tmsh, Tsol, Tdim, Tres}

  for i=1:mesh.numDofPerNode:mesh.numDof
    q_view = sview(q_vec, i:(i+mesh.numDofPerNode-1))
    convertToConservative(eqn.params, q_view, q_view)
  end

  return nothing
end

# q_vec conversion conservative -> conservative
function convertToConservative(mesh::AbstractMesh{Tmsh},
       sbp::AbstractOperator,
       eqn::EulerData{Tsol, Tres, Tdim, :conservative},
       opts, q_arr::AbstractArray{Tsol, 1}) where {Tmsh, Tsol, Tdim, Tres}

  return nothing
end

#------------------------------------------------------------------------------
# converting to entropy variables
#------------------------------------------------------------------------------

@doc """
### EulerEquationMod.convertToEntropy

  Converts the conservative variables at a node to entropy variables

  Input:
  params : ParamType{s, :conservative}
  qc  : vector (of length 4 or 5) of conservative variables

  Outputs:
  qe : vector (of length 4 or 5) of conservative variables.  Contents of
       vector areoverwritten

  # low level function

  Aliasing restrictions: none (qc and qe *can* be the same vector)

"""->
function convertToEntropy(params::ParamType{Tdim, :conservative},
               qc::AbstractArray{Tsol,1}, qe::AbstractArray{Tsol,1}) where {Tdim, Tsol}

  convertToEntropy_(params, qc, qe)

end

@doc """
### EulerEquationMod.convertToEntropy

  Converts the entropy variables to entropy variables (ie. it copies the
  input to the output).  This method exists so variables can be converted
  to entropy variables without knowing what type they are.

  # low level function

  Aliasing restrictions: none (qc and qe *can* be the same vector)

"""->
function convertToEntropy(params::ParamType{Tdim, :entropy},
               qc::AbstractArray{Tsol,1}, qe::AbstractArray{Tsol,1}) where {Tdim, Tsol}

  for i=1:length(qc)
    qe[i] = qc[i]
  end

  return nothing
end

#doc """

function convertFromNaturalToWorkingVars(
               params::ParamType{Tdim, :entropy},
               qc::AbstractArray{Tsol,1}, qe::AbstractArray{Tsol,1}) where {Tdim, Tsol}

  convertToEntropy_(params, qc, qe)
end

@doc """
  Convert from conservative variables to entropy variables.  Solving the
  equation in IR variables is not yet supported, so the corresponding
  convertToConservative method is not defined.
"""
function convertToIR(params::ParamType{Tdim, :conservative},
               qc::AbstractArray{Tsol,1}, qe::AbstractArray{Tsol,1}) where {Tdim, Tsol}

  convertToIR_(params, qc, qe)

  return nothing
end

@doc """
# mid level function

  Converts the array (3D form) of conservative variables to entropy variables
  in place.  If the array is already in entropy variables this is a no-op

  Methods also exist for the 1D form.

  Inputs:
    mesh
    sbp
    eqn
    opts

  Inputs/Outputs:
    q_arr: array of values (3D) to be converted

  Aliasing: no restrictions
"""->
# 3D array conservative -> entropy
function convertToEntropy(mesh::AbstractMesh{Tmsh},
  sbp::AbstractOperator,
  eqn::EulerData{Tsol, Tres, Tdim, :conservative},
  opts, q_arr::AbstractArray{Tsol, 3}) where {Tmsh, Tsol, Tdim, Tres}

  for i=1:mesh.numEl  # loop over elements
    for j=1:mesh.numNodesPerElement
      q_view = sview(q_arr, :, j, i)  # reuse memory
      convertToEntropy(eqn.params, q_view, q_view)
    end
  end

  return nothing
end

# 3D array entropy -> entropy
function convertToEntropy(mesh::AbstractMesh{Tmsh},
  sbp::AbstractOperator,
  eqn::EulerData{Tsol, Tres, Tdim, :entropy}, opts,
  q_arr::AbstractArray{Tsol, 3}) where {Tmsh, Tsol, Tdim, Tres}

  return nothing
end

# q_vec conversion conservative -> entropy
function convertToEntropy(mesh::AbstractMesh{Tmsh},
  sbp::AbstractOperator,
  eqn::EulerData{Tsol, Tres, Tdim, :conservative}, opts,
  q_vec::AbstractArray{Tsol, 1}) where {Tmsh, Tsol, Tdim, Tres}

  for i=1:mesh.numDofPerNode:mesh.numDof
    q_view = sview(q_vec, i:(i+mesh.numDofPerNode-1))
    convertToEntropy_(eqn.params, q_view, q_view)
  end

  return nothing
end

# q_vec conversion entropy -> entropy
function convertToEntropy(mesh::AbstractMesh{Tmsh},
  sbp::AbstractOperator,
  eqn::EulerData{Tsol, Tres, Tdim, :entropy}, opts,
  q_arr::AbstractArray{Tsol, 1}) where {Tmsh, Tsol, Tdim, Tres}

  return nothing
end

#------------------------------------------------------------------------------
# converting to IR
#------------------------------------------------------------------------------
"""
  convert 3D array of conservative variables to IR (inplace)
"""
function convertToIR(mesh::AbstractMesh{Tmsh},
  sbp::AbstractOperator,
  eqn::EulerData{Tsol, Tres, Tdim, :conservative},
  opts, q_arr::AbstractArray{Tsol, 3}) where {Tmsh, Tsol, Tdim, Tres}

  for i=1:mesh.numEl  # loop over elements
    for j=1:mesh.numNodesPerElement
      q_view = sview(q_arr, :, j, i)  # reuse memory
      convertToIR(eqn.params, q_view, q_view)
    end
  end

  return nothing
end

# we don't have var_type = :IR yet,
#=
# 3D array entropy -> entropy
function convertToIR(mesh::AbstractMesh{Tmsh},
  sbp::AbstractOperator,
  eqn::EulerData{Tsol, Tres, Tdim, IR}, opts,
  q_arr::AbstractArray{Tsol, 3}) where {Tmsh, Tsol, Tdim, Tres}

  return nothing
end
=#

# q_vec conversion conservative -> entropy
"""
  convert an eqn.q_vec shaped vector of conservative variables to IR (inplace)
"""
function convertToIR(mesh::AbstractMesh{Tmsh},
  sbp::AbstractOperator,
  eqn::EulerData{Tsol, Tres, Tdim, :conservative}, opts,
  q_vec::AbstractArray{Tsol, 1}) where {Tmsh, Tsol, Tdim, Tres}

  for i=1:mesh.numDofPerNode:mesh.numDof
    q_view = sview(q_vec, i:(i+mesh.numDofPerNode-1))
    convertToIR_(eqn.params, q_view, q_view)
  end

  return nothing
end

#=
# q_vec conversion entropy -> entropy
function convertToIR(mesh::AbstractMesh{Tmsh},
  sbp::AbstractOperator,
  eqn::EulerData{Tsol, Tres, Tdim, :IT}, opts,
  q_arr::AbstractArray{Tsol, 1}) where {Tmsh, Tsol, Tdim, Tres}

  return nothing
end
=#


text:
using StateSpaceReconstruction

@recipe function f(s::T) where {T <: AbstractSimplex}
    seriestype := :path
    label --> ""
    lc --> :black
    splitaxes(connectvertices(s))
end

@recipe function f(simplices::Vararg{T,N}) where {T <: AbstractSimplex, N}
    seriestype := :path
    legend --> false

    for simplex in simplices
        @series begin
            label --> ""
            lc --> :black
            lw --> 0.8
            splitaxes(connectvertices(simplex))
        end
    end

end

@recipe function f(simplices::AbstractVector{T}) where {T <: AbstractSimplex}
    seriestype := :path
    legend --> false

    for simplex in simplices
        @series begin
            label --> ""
            lc --> :black
            lw --> 0.8
            splitaxes(connectvertices(simplex))
        end
    end

end

export f


text:
@testset "Generating CTB data file..." begin
        home = joinpath("d:\\", "ctb8.0")
        ctb = KongYiji.ChTreebank(home; nf=0)
        @show ctb
        ctb = ctb |> KongYiji.mask
        @show ctb

        file = KongYiji.dir("ctb.jld2")
        @time Knet.save(file, "ctb", ctb)
        @time zfile = KongYiji.compress(file)
        rm(file)
        @time file = KongYiji.decompress(zfile)
        @time ctb2 = Knet.load(file, "ctb")
        @test ctb == ctb2
end


text:
# Print contents of the default_registry in a Prometheus text format
import HTTP
import Base

metric_type(m::Gauge) = "gauge"
metric_type(m::Counter) = "counter"
metric_type(m::ServerMetrics.AbstractMetric) = nothing

# Escapes special characters (", \, \n) in label value string representation.
function escape_label_value(v::Any)
    return replace(
        replace(replace("$v", "\\" => "\\\\"), "\"" => "\\\""),
        "\n" => "\\\n"
    )
end

function labels_to_string(labels::MetricLabels)
    if Base.isempty(labels)
        return ""
    end
    kv = join(["$k=\"$(escape_label_value(v))\"" for (k, v) in labels], ",")
    return "{$kv}"
end

function print_metric(b::Base.IOBuffer, m::AbstractMetric)
    println(b, "# TYPE $(name(m)) $(metric_type(m))")
    # TODO(janrous): sorting of cells and conversion of labels key=values
    # to string happens repeatedly here. We could optimize this by e.g.
    # caching the string representation of these and perhaps using SortedDict
    # to hold the cells, but this does not seem to be possible if the cell
    # keys are SortedDict{Symbol,Any}.
    # There are several factors that may make this a non-issue, specifically:
    # - we are not actually using prometheus to scrape this at all
    # - we may be exporting small number of metrics with small number of
    #   cells each (sort complexity)
    # - we may be only scraping the server infrequently so redoing this work
    #   may be okay.
    cells_with_labels = SortedDict{String,NumericMetric}(
        labels_to_string(cell.labels) => cell
        for cell in get_cells(m)
    )
    for (label_str, cell) in cells_with_labels
        println(b, "$(name(m))$label_str $(cell.value[])")
    end
    println(b)
end

"""
    handle_metrics()

This http handler renders contents of the _default registry_ in a prometheus-compatible text
format.

This can be registered to `/metrics` HTTP endpoint to enable integration with prometheus
monitoring system.
"""
function handle_metrics()
    buffer = Base.IOBuffer()
    registry = get_default_registry()
    for (name, metric) in registry.metrics
        print_metric(buffer, metric)
        # Values of metrics may not come from the exact same timepoint
        # but that's okay because we are not relying on that behavior
        # in any way.
        # What matters is the change between two sampling periods and
        # that is going to work reasonably well.
    end
    HTTP.Response(String(take!(buffer)))
    # TODO(janrous): we may consider streaming to avoid buffering the
    # entire (potentially large) response in memory.
end


text:
## ROC Analysis

function AUC(roc::ROCData)
	auc=zero(Float64)
	for i in 2:length(roc.thresholds)
		dx = roc.FPR[i] - roc.FPR[i-1]
		dy = roc.TPR[i] - roc.TPR[i-1]
		auc += ( (dx*roc.TPR[i-1]) + (0.5*dx*dy) )
	end
	auc
end

function AUC(roc::ROCData, FPRstop::Float64)
	auc=zero(Float64)
	if FPRstop <= 0.0 || FPRstop >= 1.0
		error("FPRstop should be in (0,1)")
	end
	for i in 2:length(roc.thresholds)
		if roc.FPR[i] > FPRstop
			dx = roc.FPR[i] - roc.FPR[i-1]
			dy = roc.TPR[i] - roc.TPR[i-1]
			dxstop = FPRstop - roc.FPR[i-1]
			dystop = (dy/dx)*dxstop
			auc += ( (dxstop*roc.TPR[i-1]) + (0.5*dxstop*dystop) )
			break
		end
		dx = roc.FPR[i] - roc.FPR[i-1]
		dy = roc.TPR[i] - roc.TPR[i-1]
		auc += ( (dx*roc.TPR[i-1]) + (0.5*dx*dy) )
	end
	auc
end

PPV(roc::ROCData) = roc.TP ./ 1:length(roc.thresholds)
PPV(roc::ROCData,n::Int) = roc.TP[1:length(roc.thresholds) .== n ] / n

cutoffs(roc::ROCData) = roc.thresholds


text:
libname = "libtiff"

using Pkg; Pkg.add("Libtiff_jll")

using Libtiff_jll
jllroot = dirname(dirname(Libtiff_jll.libtiff_path))

using Clang
const LIB_INCLUDE = joinpath(jllroot, "include") |> normpath
const HEADERS = filter(x->endswith(x, ".h"), readdir(LIB_INCLUDE))
const LIB_HEADERS = [joinpath(LIB_INCLUDE, header) for header in HEADERS]

refdir = joinpath.(@__DIR__, "ref_headers")
!isdir(refdir) && mkdir(refdir)
refpaths = joinpath.(refdir, basename.(LIB_HEADERS))
cp.(LIB_HEADERS, refpaths, follow_symlinks=true, force=true)

@show HEADERS

wc = init(; headers = LIB_HEADERS,
            output_file = joinpath(@__DIR__, "$(libname)_api.jl"),
            common_file = joinpath(@__DIR__, "$(libname)_common.jl"),
            clang_includes = vcat(LIB_INCLUDE, CLANG_INCLUDE),
            clang_args = ["-I", joinpath(LIB_INCLUDE, "..")],
            header_wrapped = (root, current)->root == current,
            header_library = x->libname,
            clang_diagnostics = true,
            )

run(wc)
rm(joinpath(@__DIR__, "LibTemplate.jl"))

open(joinpath(@__DIR__, "$(libname)_fixes.jl"), "w") do io
    write(io, "# manual fixes\n\n")
    write(io, "const ptrdiff_t = Cptrdiff_t\n")
    write(io, "const TIFF_STRIPCHOP = 8000\n")
    write(io, "const FILE = 0 # This fixes loading, but breaks TIFFPrintDirectory(arg1, arg2, arg3)\n")
end


text:
# ============================================================
# Solution Algorithm
# ============================================================

"""
Collision solver of universal Boltzmann equation
* u & weights kept in args for multi-dispatch

"""
function step_ube!(fwL, fhL, fbL, w, prim, h, b, fwR, fhR, fbR, u, weights, p; mode=:nn)

    K, γ, μ, ω, Pr, dx, dt, RES, AVG = p[1:9]
    ann = p[10:end]

    #--- record W^{n} ---#
    w_old = deepcopy(w)
    h_old = deepcopy(h)
    b_old = deepcopy(b)

    H_old = maxwellian(u, prim)
    B_old = H_old .* K ./ (2. .* prim[end])
    τ_old = vhs_collision_time(prim, μ, ω)

    #--- update W^{n+1} ---#
    @. w += (fwL - fwR) / dx
    prim .= conserve_prim(w, γ)

    H = maxwellian(u, prim)
    B = H .* K ./ (2. .* prim[end])
    τ = vhs_collision_time(prim, μ, ω)

    #--- update f^{n+1} ---#
    for i in eachindex(h)
        h[i] += (fhL[i] - fhR[i]) / dx
        b[i] += (fbL[i] - fbR[i]) / dx
    end

    if mode == :bgk
        @. h = (h + H / τ * dt) / (1. + dt / τ)
        @. b = (b + B / τ * dt) / (1. + dt / τ)
    elseif mode == :shakhov
        qf = heat_flux(h, b, prim, u, weights)
        H1, B1 = shakhov(u, H, B, qf, prim, Pr, K)
        H .+= H1
        B .+= B1

        @. h = (h + H / τ * dt) / (1. + dt / τ)
        @. b = (b + B / τ * dt) / (1. + dt / τ)
    elseif mode == :nn
        df = ube_dfdt([h_old; b_old], ([H_old; B_old], τ_old, ann), dt)

        @. h += df[1:end÷2] * dt
        @. b += df[end÷2+1:end] *dt
    end

    #--- record residuals ---#
    @. RES += (w - w_old)^2
    @. AVG += abs(w)

end


text:
using Distributed
using Snopt
using DelimitedFiles
using PyPlot
import ForwardDiff
using BenchmarkTools

# set up boundary constraint wrapper function
function boundary_wrapper(x, params)

    # get number of turbines
    nturbines = Int(length(x)/2)

    # extract x and y locations of turbines from design variables vector
    turbine_x = x[1:nturbines]
    turbine_y = x[nturbines+1:end]

    # get and return boundary distances
    return ff.circle_boundary(params.boundary_center, params.boundary_radius, turbine_x, turbine_y)
end

# set up spacing constraint wrapper function
function spacing_wrapper(x, params)

    # get number of turbines
    nturbines = Int(length(x)/2)

    # extract x and y locations of turbines from design variables vector
    turbine_x = x[1:nturbines]
    turbine_y = x[nturbines+1:end]

    # get and return spacing distances
    return 2.0*params.rotor_diameter[1] .- ff.turbine_spacing(turbine_x,turbine_y)
end

# set up objective wrapper function
function aep_wrapper(x, params)

    # get number of turbines
    nturbines = Int(length(x)/2)

    # extract x and y locations of turbines from design variables vector
    turbine_x = x[1:nturbines]
    turbine_y = x[nturbines+1:end]

    # calculate AEP
    AEP = obj_scale*ff.calculate_aep(turbine_x, turbine_y, params.turbine_z, params.rotor_diameter,
    params.hub_height, params.turbine_yaw, params.ct_models, params.generator_efficiency, params.cut_in_speed,
    params.cut_out_speed, params.rated_speed, params.rated_power, params.windresource, params.power_models, params.model_set,
                rotor_sample_points_y=params.rotor_points_y,rotor_sample_points_z=params.rotor_points_z, hours_per_year=365.0*24.0)

    # return the objective as an array
    return [AEP]
end

# set up optimization problem wrapper function
function wind_farm_opt(x)

    # calculate spacing constraint value and jacobian
    spacing_con = spacing_wrapper(x)
    ds_dx = ForwardDiff.jacobian(spacing_wrapper, x)

    # calculate boundary constraint and jacobian
    boundary_con = boundary_wrapper(x)
    db_dx = ForwardDiff.jacobian(boundary_wrapper, x)

    # combine constaint values and jacobians into overall constaint value and jacobian arrays
    c = [spacing_con; boundary_con]
    dcdx = [ds_dx; db_dx]

    # calculate the objective function and jacobian (negative sign in order to maximize AEP)
    AEP = -aep_wrapper(x)[1]
    dAEP_dx = -ForwardDiff.jacobian(aep_wrapper,x)

    # set fail flag to false
    fail = false

    # return objective, constraint, and jacobian values
    return AEP, c, dAEP_dx, dcdx, fail
end
# import model set with wind farm and related details
include("./model_sets/model_set_7_ieacs3.jl")

# scale objective to be between 0 and 1
obj_scale = 1E-12

# set wind farm boundary parameters
boundary_center = [6000.0,6000.0]
boundary_radius = 5500.0

# set globals for use in wrapper functions
struct params_struct{MS, AF, F, I, ACTM, WR, APM}
    model_set::MS
    rotor_points_y::AF
    rotor_points_z::AF
    turbine_z::AF
    rotor_diameter::AF
    boundary_center::AF
    boundary_radius::F
    obj_scale::I
    hub_height::AF
    turbine_yaw::AF
    ct_models::ACTM
    generator_efficiency::AF
    cut_in_speed::AF
    cut_out_speed::AF
    rated_speed::AF
    rated_power::AF
    windresource::WR
    power_models::APM
end

params1 = params_struct(model_set, rotor_points_y, rotor_points_z, turbine_z,
    rotor_diameter, boundary_center, boundary_radius, obj_scale, hub_height, turbine_yaw,
    ct_models, generator_efficiency, cut_in_speed, cut_out_speed, rated_speed, rated_power,
    windresource, power_models)

# initialize design variable array
x = [copy(turbine_x);copy(turbine_y)]
xinit = deepcopy(x)
state_aeps = ff.calculate_state_aeps(turbine_x, turbine_y, turbine_z, rotor_diameter,
                hub_height, turbine_yaw, ct_models, generator_efficiency, cut_in_speed,
                cut_out_speed, rated_speed, rated_power, windresource, power_models, model_set;
                rotor_sample_points_y=[0.0], rotor_sample_points_z=[0.0], hours_per_year=365.0*24.0)

dir_aep = zeros(20)
for i in 1:20
    for j in 1:20
        dir_aep[i] += state_aeps[(i-1)*20 + j]
    end
end

# report initial objective value
println("nturbines: ", nturbines)
println("rotor diameter: ", rotor_diameter[1])
println("starting AEP value (MWh): ", aep_wrapper(x, params1)[1])
# println("frequencies ", windprobabilities)
println("Directional AEP at start: ", dir_aep.*1E-6)

# add initial turbine location to plot
for i = 1:length(turbine_x)
    plt.gcf().gca().add_artist(plt.Circle((turbine_x[i],turbine_y[i]), rotor_diameter[1]/2.0, fill=false,color="C0"))
end

# set general lower and upper bounds for design variables
lb = zeros(length(x)) .- boundary_radius .+ boundary_center[1]
ub = zeros(length(x)) .+ boundary_radius .+ boundary_center[2]

# set up options for SNOPT
options = Dict{String, Any}()
options["Derivative option"] = 1
options["Verify level"] = 3
options["Major optimality tolerance"] = 1e-5
options["Major iteration limit"] = 1e6
options["Summary file"] = "summary.out"
options["Print file"] = "print.out"

# generate wrapper function surrogates
spacing_wrapper(x) = spacing_wrapper(x, params1)
@everywhere aep_wrapper(x) = aep_wrapper(x, params1)
boundary_wrapper(x) = boundary_wrapper(x, params1)

# # run and time optimization
# t1 = time()
# xopt, fopt, info = snopt(wind_farm_opt, x, lb, ub, options)
# t2 = time()
# clkt = t2-t2

# # print optimization results
# println("Finished in : ", clkt, " (s)")
# println("info: ", info)
# println("end objective value: ", aep_wrapper(xopt))

# # extract final turbine locations
# turbine_x = copy(xopt[1:nturbines])
# turbine_y = copy(xopt[nturbines+1:end])

# # add final turbine locations to plot
# for i = 1:length(turbine_x)
#     plt.gcf().gca().add_artist(plt.Circle((turbine_x[i],turbine_y[i]), rotor_diameter[1]/2.0, fill=false,color="C1", linestyle="--"))
# end

# # add wind farm boundary to plot
# plt.gcf().gca().add_artist(plt.Circle((boundary_center[1],boundary_center[2]), boundary_radius, fill=false,color="C2"))

# # set up and show plot
# axis("square")
# xlim(-boundary_radius+boundary_center[1],boundary_radius+boundary_center[1])
# ylim(-boundary_radius+boundary_center[2],boundary_radius+boundary_center[2])
# plt.show()
println("CPUs: ", nprocs())
@benchmark ForwardDiff.jacobian(aep_wrapper, x) setup=(x=xinit)
# @benchmark aep_wrapper(xinit)


text:
######################
# assignment wrapper #
######################

struct StaticIRTraceAssmt{T} <: ChoiceMap
    trace::T
end

function get_schema end

get_address_schema(::Type{StaticIRTraceAssmt{T}}) where {T} = get_schema(T)

Base.isempty(choices::StaticIRTraceAssmt) = isempty(choices.trace)

static_has_value(choices::StaticIRTraceAssmt, key) = false

function get_value(choices::StaticIRTraceAssmt, key::Symbol)
    static_get_value(choices, Val(key))
end

function has_value(choices::StaticIRTraceAssmt, key::Symbol)
    static_has_value(choices, Val(key))
end

function get_submap(choices::StaticIRTraceAssmt, key::Symbol)
    static_get_submap(choices, Val(key))
end

get_value(choices::StaticIRTraceAssmt, addr::Pair) = _get_value(choices, addr)
has_value(choices::StaticIRTraceAssmt, addr::Pair) = _has_value(choices, addr)
get_submap(choices::StaticIRTraceAssmt, addr::Pair) = _get_submap(choices, addr)

#########################
# trace type generation #
#########################

abstract type StaticIRTrace <: Trace end

const arg_prefix = gensym("arg")
const choice_value_prefix = gensym("choice_value")
const choice_score_prefix = gensym("choice_score")
const subtrace_prefix = gensym("subtrace")
const julia_prefix = gensym("julia_prefix")

function get_value_fieldname(node::ArgumentNode)
    Symbol("$(arg_prefix)_$(node.name)")
end

function get_value_fieldname(node::RandomChoiceNode)
    Symbol("$(choice_value_prefix)_$(node.addr)")
end

function get_value_fieldname(node::JuliaNode)
    Symbol("$(julia_prefix)_$(node.name)")
end

function get_score_fieldname(node::RandomChoiceNode)
    Symbol("$(choice_score_prefix)_$(node.addr)")
end

function get_subtrace_fieldname(node::GenerativeFunctionCallNode)
    Symbol("$(subtrace_prefix)_$(node.addr)")
end

const num_nonempty_fieldname = gensym("num_nonempty")
const total_score_fieldname = gensym("score")
const total_noise_fieldname = gensym("noise")
const return_value_fieldname = gensym("retval")

struct TraceField
    fieldname::Symbol
    typ::Union{Symbol,Expr,QuoteNode}
end

function get_trace_fields(ir::StaticIR, options::StaticIRGenerativeFunctionOptions)
    fields = TraceField[]
    for node in ir.arg_nodes
        fieldname = get_value_fieldname(node)
        push!(fields, TraceField(fieldname, node.typ))
    end
    for node in ir.choice_nodes
        value_fieldname = get_value_fieldname(node)
        push!(fields, TraceField(value_fieldname, node.typ))
        score_fieldname = get_score_fieldname(node)
        push!(fields, TraceField(score_fieldname, QuoteNode(Float64)))
    end
    for node in ir.call_nodes
        subtrace_fieldname = get_subtrace_fieldname(node)
        subtrace_type = QuoteNode(get_trace_type(node.generative_function))
        push!(fields, TraceField(subtrace_fieldname, subtrace_type))
    end
    if options.cache_julia_nodes
        for node in ir.julia_nodes
            fieldname = get_value_fieldname(node)
            push!(fields, TraceField(fieldname, node.typ))
        end
    end
    push!(fields, TraceField(total_score_fieldname, QuoteNode(Float64)))
    push!(fields, TraceField(total_noise_fieldname, QuoteNode(Float64)))
    push!(fields, TraceField(num_nonempty_fieldname, QuoteNode(Int)))
    push!(fields, TraceField(return_value_fieldname, ir.return_node.typ))
    return fields
end

const static_ir_gen_fn_ref = gensym("gen_fn")

function generate_trace_struct(ir::StaticIR, trace_struct_name::Symbol, options::StaticIRGenerativeFunctionOptions)
    mutable = false
    fields = get_trace_fields(ir, options)
    field_exprs = map((f) -> Expr(:(::), f.fieldname, f.typ), fields)
    Expr(:struct, mutable, Expr(:(<:), trace_struct_name, QuoteNode(StaticIRTrace)),
         Expr(:block, field_exprs..., Expr(:(::), static_ir_gen_fn_ref, QuoteNode(Any))))
end

function generate_isempty(trace_struct_name::Symbol)
    Expr(:function,
        Expr(:call, :(Base.isempty), :(trace::$trace_struct_name)),
        Expr(:block, :(trace.$num_nonempty_fieldname == 0)))
end

function generate_get_score(trace_struct_name::Symbol)
    Expr(:function,
        Expr(:call, Expr(:(.), Gen, QuoteNode(:get_score)), :(trace::$trace_struct_name)),
        Expr(:block, :(trace.$total_score_fieldname)))
end

function generate_get_args(ir::StaticIR, trace_struct_name::Symbol)
    args = Expr(:tuple, [:(trace.$(get_value_fieldname(node)))
                         for node in ir.arg_nodes]...)
    Expr(:function,
        Expr(:call, Expr(:(.), Gen, QuoteNode(:get_args)), :(trace::$trace_struct_name)),
        Expr(:block, args))
end

function generate_get_retval(ir::StaticIR, trace_struct_name::Symbol)
    Expr(:function,
        Expr(:call, Expr(:(.), Gen, QuoteNode(:get_retval)), :(trace::$trace_struct_name)),
        Expr(:block, :(trace.$return_value_fieldname)))
end

function generate_get_choices(trace_struct_name::Symbol)
    Expr(:function,
        Expr(:call, Expr(:(.), Gen, QuoteNode(:get_choices)), :(trace::$trace_struct_name)),
        Expr(:if, :(!isempty(trace)),
            :($(QuoteNode(StaticIRTraceAssmt))(trace)),
            :($(QuoteNode(EmptyChoiceMap))())))
end

function generate_get_values_shallow(ir::StaticIR, trace_struct_name::Symbol)
    elements = []
    for node in ir.choice_nodes
        addr = node.addr
        value = :(choices.trace.$(get_value_fieldname(node)))
        push!(elements, :(($(QuoteNode(addr)), $value)))
    end
    Expr(:function,
        Expr(:call, Expr(:(.), Gen, QuoteNode(:get_values_shallow)),
                    :(choices::$(QuoteNode(StaticIRTraceAssmt)){$trace_struct_name})),
        Expr(:block, Expr(:tuple, elements...)))
end

function generate_get_submaps_shallow(ir::StaticIR, trace_struct_name::Symbol)
    elements = []
    for node in ir.call_nodes
        addr = node.addr
        subtrace = :(choices.trace.$(get_subtrace_fieldname(node)))
        push!(elements, :(($(QuoteNode(addr)), get_choices($subtrace))))
    end
    Expr(:function,
        Expr(:call, Expr(:(.), Gen, QuoteNode(:get_submaps_shallow)),
                    :(choices::$(QuoteNode(StaticIRTraceAssmt)){$trace_struct_name})),
        Expr(:block, Expr(:tuple, elements...)))
end

function generate_static_get_value(ir::StaticIR, trace_struct_name::Symbol)
    methods = Expr[]
    for node in ir.choice_nodes
        push!(methods, Expr(:function,
            Expr(:call, Expr(:(.), Gen, QuoteNode(:static_get_value)),
                        :(choices::$(QuoteNode(StaticIRTraceAssmt)){$trace_struct_name}),
                        :(::Val{$(QuoteNode(node.addr))})),
            Expr(:block, :(choices.trace.$(get_value_fieldname(node))))))
    end
    methods
end

function generate_static_has_value(ir::StaticIR, trace_struct_name::Symbol)
    methods = Expr[]
    for node in ir.choice_nodes
        push!(methods, Expr(:function,
            Expr(:call, Expr(:(.), Gen, QuoteNode(:static_has_value)),
                        :(choices::$(QuoteNode(StaticIRTraceAssmt)){$trace_struct_name}),
                        :(::Val{$(QuoteNode(node.addr))})),
            Expr(:block, :(true))))
    end
    methods
end

function generate_static_get_submap(ir::StaticIR, trace_struct_name::Symbol)
    methods = Expr[]
    for node in ir.call_nodes
        push!(methods, Expr(:function,
            Expr(:call, Expr(:(.), Gen, QuoteNode(:static_get_submap)),
                        :(choices::$(QuoteNode(StaticIRTraceAssmt)){$trace_struct_name}),
                        :(::Val{$(QuoteNode(node.addr))})),
            Expr(:block,
                :(get_choices(choices.trace.$(get_subtrace_fieldname(node)))))))
    end

    # throw a KeyError if get_submap is run on an address containing a value
    for node in ir.choice_nodes
         push!(methods, Expr(:function,
            Expr(:call, Expr(:(.), Gen, QuoteNode(:static_get_submap)),
                        :(choices::$(QuoteNode(StaticIRTraceAssmt)){$trace_struct_name}),
                        :(::Val{$(QuoteNode(node.addr))})),
            Expr(:block, :(throw(KeyError($(QuoteNode(node.addr))))))))
    end
    methods
end

function generate_get_schema(ir::StaticIR, trace_struct_name::Symbol)
    choice_addrs = [QuoteNode(node.addr) for node in ir.choice_nodes]
    call_addrs = [QuoteNode(node.addr) for node in ir.call_nodes]
    addrs = vcat(choice_addrs, call_addrs)
    Expr(:function,
        Expr(:call, Expr(:(.), Gen, QuoteNode(:get_schema)), :(::Type{$trace_struct_name})),
        Expr(:block,
            :($(QuoteNode(StaticAddressSchema))(
                Set{Symbol}([$(addrs...)])))))
end

function generate_trace_type_and_methods(ir::StaticIR, name::Symbol, options::StaticIRGenerativeFunctionOptions)
    trace_struct_name = gensym("StaticIRTrace_$name")
    trace_struct_expr = generate_trace_struct(ir, trace_struct_name, options)
    isempty_expr = generate_isempty(trace_struct_name)
    get_score_expr = generate_get_score(trace_struct_name)
    get_args_expr = generate_get_args(ir, trace_struct_name)
    get_retval_expr = generate_get_retval(ir, trace_struct_name)
    get_choices_expr = generate_get_choices(trace_struct_name)
    get_schema_expr = generate_get_schema(ir, trace_struct_name)
    get_values_shallow_expr = generate_get_values_shallow(ir, trace_struct_name)
    get_submaps_shallow_expr = generate_get_submaps_shallow(ir, trace_struct_name)
    static_get_value_exprs = generate_static_get_value(ir, trace_struct_name)
    static_has_value_exprs = generate_static_has_value(ir, trace_struct_name)
    static_get_submap_exprs = generate_static_get_submap(ir, trace_struct_name)
    exprs = Expr(:block, trace_struct_expr, isempty_expr, get_score_expr,
                 get_args_expr, get_retval_expr,
                 get_choices_expr, get_schema_expr, get_values_shallow_expr,
                 get_submaps_shallow_expr, static_get_value_exprs...,
                 static_has_value_exprs..., static_get_submap_exprs...)
    (exprs, trace_struct_name)
end

export StaticIRTrace

max_stars_repo_name: dendisuhubdy/Gen

max_issues_repo_name: kumsh/Gen

max_forks_repo_name: kumsh/Gen


text:
abstract type AbstractDiff end

struct Insertion <: AbstractDiff
	text
end

struct Deletion <: AbstractDiff
	text
end

struct Equality <: AbstractDiff
	text
end

text(d::AbstractDiff) = d.text
to_string(d::T) where {T<:AbstractDiff} = T(string(text(d)))

"Groups diffs of the same type together using `join`."
function merge_diffs(vec::Vector{<:AbstractDiff}; join_with="")
	out = AbstractDiff[]

	current_diff = vec[1]
	i = 1

	while i < length(vec)
		current_diff_type = typeof(current_diff)

		new_diff = vec[i+1]
		if typeof(new_diff) == current_diff_type
			text = join((current_diff.text, new_diff.text), join_with)
			current_diff = current_diff_type(text)
		else
			push!(out, current_diff)
			current_diff = new_diff
		end

		i += 1
	end
	push!(out, current_diff)

	out
end


text:
function safe_get(cur_dict::Dict, cur_field, cur_default)
  cur_symbol = Symbol(cur_field)
  haskey(cur_dict, cur_symbol) || return cur_default
  cur_dict[cur_symbol]
end

function safe_get(cur_array::AbstractArray, cur_field, cur_default)
  safe_get(Dict(cur_array), cur_field, cur_default)
end

function safe_get(cur_reactor::AbstractReactor, cur_field)
  cur_symbol = Symbol(cur_field)

  isdefined(cur_reactor, cur_symbol) && return getfield(cur_reactor, cur_symbol)

  cur_func = getfield(Fussy, cur_symbol)
  cur_value = cur_func(cur_reactor)

  if isa(cur_value, SymEngine.Basic)
    cur_value = subs(cur_value, symbols(:sigma_v) => calc_sigma_v(cur_reactor))
    cur_value = cur_value |> N
  end

  cur_value
end


text:
# Module for The King is Dead bot
# This houses all of the types

import Base: show

Base.show(io::IO, b::Board) = begin
    regions = [merge(Dict("name" => r.name), r.followers) for
                        r in b.regions]
    df = vcat(DataFrame.(regions)...)
    local followers = Symbol.(sort(collect(keys(b.regions[1].followers))))
    select!(df, [:name, followers...])
    println(df)
    print("Resolved regions: ", )
    for (i, r) in enumerate(b.resolved_regions)
        if i == length(b.resolved_regions)
            print(r.name)
        else
            print(r.name, ", ")
        end
    end
    print("\nRegions to be resolved in order: ", )
    for (i, r) in enumerate(b.unresolved_regions)
        if i == length(b.unresolved_regions)
            println(r.name)
        else
            print(r.name, ", ")
        end
    end
    println("Invasions: ", b.invasions)
end
# Base.show(io::IO, b::Board) = begin
#     total_followers = Dict{String, Int64}()
#     for (f_name, _) in b.regions[1].followers
#         total_followers[f_name] = 0
#     end
#     for r in b.regions
#         print(r.name, " has: ")
#         for (i, (f_name, f_num)) in enumerate(r.followers)
#             total_followers[f_name] += f_num
#             if (i == length(r.followers))
#                 println(f_num, " ", f_name, " followers.")
#             else
#                 print(f_num, " ", f_name, ", ")
#             end
#         end
#     end
#     print("\nFor a total of ")
#     for (i, (f_name, f_num)) in enumerate(total_followers)
#         if (i == length(total_followers))
#             println(f_num, " ", f_name, " followers.")
#         else
#             print(f_num, " ", f_name, ", ")
#         end
#     end
#
# end

Base.show(io::IO, p::Player) = begin
    print(p.name, " has ")
    for (i, (f_name, f_num)) in enumerate(p.court)
        if (i == length(p.court))
            println(f_num, " ", f_name, " followers.")
        else
            print(f_num, " ", f_name, ", ")
        end
    end
end

Base.show(io::IO, ps::Array{Player}) = begin
    [Base.show(p) for p in ps]
end

max_stars_repo_name: danielpicone/the-king-is-dead

max_issues_repo_name: danielpicone/the-king-is-dead

max_forks_repo_name: danielpicone/the-king-is-dead