text:
module AnalyzeResults

include("valuetostate.jl")
include("probability.jl")
include("evidence.jl")
include("results.jl")
include("idMap.jl")

function analyzeResults(resultsfile, expectedfile, downregulatedcutoff, upregulatedcutoff, verbose)
    expected_data = Results.getExpected(expectedfile)
    expected = expected_data["samplenodestate"]

    expected_zero_got_zero = 0
    expected_one_got_one = 0
    expected_two_got_two = 0
    expected_zero_got_one = 0
    expected_zero_got_two = 0
    expected_one_got_zero = 0
    expected_one_got_two = 0
    expected_two_got_one = 0
    expected_two_got_zero = 0

    results = Results.getResults(resultsfile)
    probability = 1;
    errors = Dict()
    for patientname in keys(expected)
        expected_patient_nodes = expected[patientname]
        results_patient_nodes = results[patientname]
        for nodename in keys(expected_patient_nodes)
            expectedState = expected_patient_nodes[nodename]
            resultValue = results_patient_nodes[nodename]

            resultState = ValueToState.getStateNumber(resultValue, downregulatedcutoff, upregulatedcutoff)
            if expectedState != resultState
                if haskey(errors, patientname) == false
                    errors[patientname] = Dict()
                end
                errors[patientname][nodename] = [expectedState, resultValue]
            end

            if expectedState == "0" && resultState == "0"
                expected_zero_got_zero += 1
            elseif expectedState == "1" && resultState == "1"
                expected_one_got_one += 1
            elseif expectedState == "2" && resultState == "2"
                expected_two_got_two += 1
            elseif expectedState == "0" && resultState == "1"
                expected_zero_got_one += 1
            elseif expectedState == "0" && resultState == "2"
                expected_zero_got_two += 1
            elseif expectedState == "1" && resultState == "0"
                expected_one_got_zero += 1
            elseif expectedState == "1" && resultState == "2"
                expected_one_got_two += 1
            elseif expectedState == "2" && resultState == "1"
                expected_two_got_one += 1
            elseif expectedState == "2" && resultState == "0"
                expected_two_got_zero += 1
            end
        end
    end

    TN = expected_one_got_one
    println("\ntrue negative (TN): $TN")

    TP = expected_two_got_two + expected_zero_got_zero
    println("true positive (TP): $TP")

    FP = expected_one_got_zero + expected_one_got_two + expected_two_got_zero + expected_zero_got_two
    println("false positive (FP): $FP")

    FN = expected_two_got_one + expected_zero_got_one
    println("false negative (FN): $FN")

    total = TN + FP + FN + TP
    println("Total number of cases: $total")

    P = TP + FN
    println("conditional positive (P): $P")

    N = TN + FP
    println("conditional negative (N): $N")

    TPR = 0
    if P != 0
        TPR = TP / P
    end
    println("sensitivity, recall, hit rate, or true positive rate (TPR): $TPR")

    TNR = 0
    if N != 0
        TNR = TN / N
    end
    println("specificity, selectivity or true negative rate (TNR): $TNR")

    PPV = 0
    if (TP + FP) != 0
        PPV = TP / (TP + FP)
    end
    println("precision or positive predictive value (PPV): $PPV")

    NPV = 0
    if (TN + FN) != 0
       NPV = TN / (TN + FN)
    end
    println("negative predictive value (NPV): $NPV")

    FNR = 1 - TPR
    println("miss rate or false negative rate (FNR): $FNR")

    FPR = 1 - TNR
    println("Fall-out or false positive rate (FPR): $FPR")

    FDR = 1 - PPV
    println("false discovery rate: $FDR")

    FOR = 1 - NPV
    println("false omission rate: $FOR")

    ACC = (TP + TN) / total
    println("accuracy: $ACC\n")

    Fone = 0
    if (2 * TP + FP + FN) != 0
        Fone = (2 * TP) / (2 * TP + FP + FN)
    end
    println("F1 Score: $Fone")

    MCC = 0
    divideBy = ((TP + FP) * (TP + FN) * (TN + FP) * (TN + FN))^(1/2)
    if divideBy != 0
        MCC = ((TP * TN) - (FP * FN)) / divideBy
    end
    println("Mathews correlation coefficient (MCC): $MCC")

    BM = TPR + TNR -1
    println("Informedness or Bookmaker Informedness (BM): $BM")

    MK = PPV + NPV -1
    println("Markedness (MK): $MK\n")

    println("Expected 0 predicted 0: $expected_zero_got_zero")
    println("Expected 0 predicted 1: $expected_zero_got_one")
    println("Expected 0 predicted 2: $expected_zero_got_two")
    println("Expected 1 predicted 0: $expected_one_got_zero")
    println("Expected 1 predicted 1: $expected_one_got_one")
    println("Expected 1 predicted 2: $expected_one_got_two")
    println("Expected 2 predicted 0: $expected_two_got_zero")
    println("Expected 2 predicted 1: $expected_two_got_one")
    println("Expected 2 predicted 2: $expected_two_got_two\n\n")

    println("Patient\tNode\tExpected\tActual\n")
    for patientname in keys(errors)
        patient = errors[patientname]
        for genename in keys(patient)
            values = errors[patientname][genename]
            println("$patientname\t$genename\t", values[1], "\t", values[2])
        end
    end
end

end

max_stars_repo_name: OICR/mp-biopath

max_issues_repo_name: OICR/mp-biopath

max_forks_repo_name: OICR/mp-biopath


text:
using DeconvolutionTests
using Test

ext: jl


text:
using ANOVA, Test, DataFrames, GLM

Base.isapprox(a::Anova, b::Anova) =
  a.Source == b.Source && a.DF ≈ b.DF && a.SS ≈ b.SS && a.MSS ≈ b.MSS &&
  a.F ≈ b.F && a.p ≈ b.p

# # write your own tests here
data = DataFrame(ID = categorical(1:25),
                 Tire = categorical(["GT", "GT", "MX", "MX", "LS", "GT", "LS", "LS", "LS", "MX", "MX", "GT", "LS", "GT", "MX", "MX", "GT", "MX", "LS", "LS", "GT", "MX", "GT", "LS", "LS"]),
                 Tread = categorical([10, 1.5, 1.5, 10, 10, 10, 1.5, 1.5, 10, 10, 10, 10, 1.5, 1.5, 10, 1.5, 10, 1.5, 1.5, 10, 1.5, 1.5, 1.5, 10, 10]),
                 ABS = categorical(["enabled", "disabled", "enabled", "enabled", "disabled", "enabled", "enabled", "disabled", "disabled", "disabled", "disabled", "disabled", "enabled", "disabled", "enabled", "enabled", "disabled", "disabled", "disabled", "enabled", "enabled", "disabled", "enabled", "enabled", "enabled"]),
                 Distance = 10 * [1.93557287740925, 2.33806943448595, 2.40077846814577, 2.50714195337437, 2.63983294771174, 1.86088838586581, 2.32992470891205, 2.61332521553405, 3.10241104060570, 2.61751523813390, 2.70498523179508, 2.46421568043517, 2.38080587240067, 2.59001814923499, 2.40457318442931, 2.48136216797396, 2.60228457322805, 2.72711342214876, 2.94439325048592, 2.40498649478329, 2.19492468934002, 2.62485867993076, 2.24742018940027, 2.10579535297714, 2.00579535297714])
# prepare test for ANOVA type III
anova1_expected = Anova(
          ["ABS", "Tread", "Tire","ABS & Tread", "ABS & Tire", "Tread & Tire", "&(ABS, Tread, Tire)", "Residuals"],
          [1, 1, 2, 1, 2, 2, 2, 13],
          [106.5991986929681445, 2.3651163588824207, 38.2807197340170120, 7.8051389666327555, 12.5399359441607761, 1.6429152697879308, 4.7578414794463368, 31.2374270953612125],
          [44.36311539929538128, 0.98428441534601785, 7.96559452580719363, 3.24824468598099525, 2.60935650648222861, 0.34186391923448084, 0.99002934915192564, 0],
          [1.5624161055791751e-05, 3.3925131671067088e-01, 5.5176093522618852e-03, 9.4722296495528976e-02, 1.1149792216959341e-01, 7.1664324192550988e-01, 3.9791832675247707e-01, 0]
          )
anova2_expected = Anova(
          ["ABS", "Tread", "Tire","ABS & Tread", "ABS & Tire", "Tread & Tire", "&(ABS, Tread, Tire)", "Residuals"],
          [1, 1, 2, 1, 2, 2, 2, 13],
          [107.3953075516341755, 2.3441186495561617, 39.0185831196514528, 7.1128272849473611, 12.5726139970648312, 1.6429152697879310, 4.7578414794463377, 31.2374270953612196],
          [44.69442998327386363, 0.97554585245452230, 8.11913188315684131, 2.96012710720490624, 2.61615627725810418, 0.34186391923448167,  0.99002934915192509, 0],
          [1.5039459685901507e-05, 3.4132855401856044e-01, 5.1516520460311733e-03, 1.0903910333788396e-01, 1.1095844647188244e-01, 7.1664324192550932e-01, 3.9791832675247707e-01, 0]
          )
anova3_expected = Anova(
          ["ABS", "Tread", "Tire","ABS & Tread", "ABS & Tire", "Tread & Tire", "&(ABS, Tread, Tire)", "Residuals"],
          [1, 1, 2, 1, 2, 2, 2, 13],
          [102.1138211805702980, 2.0356309732669118, 40.1164208238708397, 6.9792255733881632, 12.3635146287293978, 1.6557631208168218, 4.7578414794463484, 31.237427095361188],
          [42.49644733207060199, 0.84716332659802784, 8.34757403543913412, 2.90452642520995008, 2.57264610306765462, 0.34453734785690932, 0.99002934915192542, 0],
          [1.9453276992336526e-05, 3.7412049964586458e-01, 4.6577488940770014e-03, 1.1209905804371272e-01, 1.1446323312399301e-01, 7.1482574202441140e-01, 3.9791832675247707e-01, 0]
          )
model = fit(LinearModel,
            @formula(Distance ~ ABS * Tread * Tire),
            data,
            contrasts = Dict(:ABS => EffectsCoding(),
                             :Tread => EffectsCoding(),
                             :Tire => EffectsCoding()))
@testset "Three Factors Model" begin
    @test Anova(model, anovatype = 1) ≈ anova1_expected
    @test Anova(model, anovatype = 2) ≈ anova2_expected
    @test Anova(model) ≈ anova3_expected
end


text:
# This file is a part of Julia. License is MIT: http://julialang.org/license

module Read

import Base.LibGit2, ..Cache, ..Reqs, ...Pkg25.Pkg25Error, ..Dir, ..isdevmetadata
using ..Types

devmetapkgpath(s::AbstractString) = joinpath(uppercase(string(s[1])), s * ".versions")

readstrip(path...) = strip(readstring(joinpath(path...)))

function url(pkg::AbstractString, dev::Bool = false)
    if dev
        chomp(readline(joinpath(Dir.path("METADATA"), devmetapkgpath(pkg))))
    else
        readstrip(Dir.path("METADATA"), pkg, "url")
    end
end

# Should only be called when cache is guaranteed to be updated
sha1(pkg::AbstractString, ver::VersionNumber) = PKG_AVAILABLE_CACHE.pkgs[pkg][ver].sha1

type AvailableCache
    sha::String
    pkgs::Dict{String, Dict{VersionNumber, Available}}
end

PKG_AVAILABLE_CACHE = AvailableCache("", Dict{String, Dict{VersionNumber, Available}}())

# Reads
function read_version{T <: AbstractString}(s::Vector{T}, i::Int)
    version = s[i]; i += 1
    i += 1 # skip "-----" under the version
    sha = s[i]; i += 1
    requires = String[]
    while i <= length(s) && !isempty(s[i])
        push!(requires, s[i]); i += 1
    end
    i += 1 # skip the empty string
    return version, sha, requires, i
end

function read_package!(pkgs, lines::Vector, pkg_name::AbstractString)
    i = 3 # Skip url + empty line
    isempty(lines[i]) && return
    while i <= length(lines)
        ver, sha, requires, i = read_version(lines, i)
        haskey(pkgs, pkg_name) || (pkgs[pkg_name] = Dict{VersionNumber,Available}())
        z = Reqs.parse(requires)
        q = Available(sha, z)
        pkgs[pkg_name][VersionNumber(ver)] = q
    end
end

function available_dev(names)
    cd("METADATA") do
        pkgs = Dict{String,Dict{VersionNumber,Available}}()
        for pkg in names
            path = devmetapkgpath(pkg)
            !isfile(path) && continue
            read_package!(pkgs, split(readstring(path), '\n'), pkg)
        end
        return pkgs
    end
end

function available_orig(names)
    pkgs = Dict{String,Dict{VersionNumber,Available}}()
    for pkg in names
        isfile("METADATA", pkg, "url") || continue
        versdir = joinpath("METADATA", pkg, "versions")
        isdir(versdir) || continue
        for ver in readdir(versdir)
            ismatch(Base.VERSION_REGEX, ver) || continue
            isfile(versdir, ver, "sha1") || continue
            haskey(pkgs,pkg) || (pkgs[pkg] = Dict{VersionNumber,Available}())
            pkgs[pkg][convert(VersionNumber,ver)] = Available(
                readchomp(joinpath(versdir,ver,"sha1")),
                Reqs.parse(joinpath(versdir,ver,"requires"))
            )
        end
    end
    return pkgs
end

available(names) = isdevmetadata() ? available_dev(names) : available_orig(names)

function available(pkg::AbstractString)
    avail = isdevmetadata() ? available_dev([pkg]) : available_orig([pkg])
    get(avail, pkg, Dict{VersionNumber,Available}())
end

# Uses cached data if not outdated
function available(cache::AvailableCache = PKG_AVAILABLE_CACHE)
    if isdevmetadata()
        names = String[]
        cd("METADATA") do
            for firstletterpath in filter(x->isdir(x) && !startswith(x, '.'), readdir())
                append!(names, map(x -> split(x, ".versions")[1], readdir(firstletterpath)))
            end
        end
    else
        names = readdir("METADATA")
    end
    return LibGit2.with(LibGit2.GitRepo("METADATA")) do repo
        LibGit2.with(LibGit2.head(repo)) do head
            sha = string(Base.LibGit2.Oid(head))
            LibGit2.with(LibGit2.GitStatus(repo)) do status
                # Only use cache if nothing funky is going on, this should be true in the majority of cases
                if length(status) == 0
                    # Sha does not match, update the cache with the new pkgs
                    if cache.sha != sha
                        cache.pkgs = available(names)
                        cache.sha = sha
                    end
                    return cache.pkgs
                end
                return available(names)
            end
        end
    end
end

function latest(dev::Bool = false)
    pkgs = Dict{String,Available}()
    if dev
         # Use the cache in available() for dev version
        pkgs_all = available()
        for (pkg, v) in pkgs_all
            isempty(pkg) && continue
            ver = maximum(keys(v))
            pkgs[pkg] = v[ver]
        end
    else
        # For the current metadata version `isdirty` is actually slow enough
        # that using the cache is not worth it in terms of performance.
        names = readdir("METADATA")
        for pkg in names
            isfile("METADATA", pkg, "url") || continue
            versdir = joinpath("METADATA", pkg, "versions")
            isdir(versdir) || continue
            pkgversions = VersionNumber[]
            for ver in readdir(versdir)
                ismatch(Base.VERSION_REGEX, ver) || continue
                isfile(versdir, ver, "sha1") || continue
                push!(pkgversions, convert(VersionNumber,ver))
            end
            isempty(pkgversions) && continue
            ver = string(maximum(pkgversions))
            pkgs[pkg] = Available(
                    readchomp(joinpath(versdir,ver,"sha1")),
                    Reqs.parse(joinpath(versdir,ver,"requires"))
                )
        end
    end

    return pkgs
end

isinstalled(pkg::AbstractString) =
    pkg != "METADATA" && pkg != "REQUIRE" && pkg[1] != '.' && isdir(pkg)

function isfixed(pkg::AbstractString, prepo::LibGit2.GitRepo, avail::Dict=available(pkg))
    isinstalled(pkg) || throw(Pkg25Error("$pkg is not an installed package."))
    if isdevmetadata()
        isfile("METADATA", devmetapkgpath(pkg)) || return true
    else
        isfile("METADATA", pkg, "url") || return true
    end
    ispath(pkg, ".git") || return true

    LibGit2.isdirty(prepo) && return true
    LibGit2.isattached(prepo) && return true
    LibGit2.need_update(prepo)
    LibGit2.iszero(LibGit2.revparseid(prepo, "HEAD:REQUIRE")) && isfile(pkg,"REQUIRE") && return true

    head = string(LibGit2.head_oid(prepo))
    for (ver,info) in avail
        head == info.sha1 && return false
    end

    cache = Cache.path(pkg)
    cache_has_head = if isdir(cache)
        crepo = LibGit2.GitRepo(cache)
        LibGit2.iscommit(head, crepo)
    else
        false
    end
    res = true
    try
        for (ver,info) in avail
            if cache_has_head && LibGit2.iscommit(info.sha1, crepo)
                if LibGit2.is_ancestor_of(head, info.sha1, crepo)
                    res = false
                    break
                end
            elseif LibGit2.iscommit(info.sha1, prepo)
                if LibGit2.is_ancestor_of(head, info.sha1, prepo)
                    res = false
                    break
                end
            else
                Base.warn_once("unknown $pkg commit $(info.sha1[1:8]), metadata may be ahead of package cache")
            end
        end
    finally
        cache_has_head && LibGit2.finalize(crepo)
    end
    return res
end

function ispinned(pkg::AbstractString)
    ispath(pkg,".git") || return false
    LibGit2.with(LibGit2.GitRepo, pkg) do repo
        return ispinned(repo)
    end
end

function ispinned(prepo::LibGit2.GitRepo)
    LibGit2.isattached(prepo) || return false
    br = LibGit2.branch(prepo)
    # note: regex is based on the naming scheme used in Entry.pin()
    return ismatch(r"^pinned\.[0-9a-f]{8}\.tmp$", br)
end

function installed_version(pkg::AbstractString, prepo::LibGit2.GitRepo, avail::Dict=available(pkg))
    ispath(pkg,".git") || return typemin(VersionNumber)

    # get package repo head hash
    local head
    try
        head = string(LibGit2.head_oid(prepo))
    catch ex
        # refs/heads/master does not exist
        if isa(ex,LibGit2.GitError) &&
            ex.code == LibGit2.Error.EUNBORNBRANCH
            head = ""
        else
            rethrow(ex)
        end
    end
    isempty(head) && return typemin(VersionNumber)

    vers = collect(keys(filter((ver,info)->info.sha1==head, avail)))
    !isempty(vers) && return maximum(vers)

    cache = Cache.path(pkg)
    cache_has_head = if isdir(cache)
        crepo = LibGit2.GitRepo(cache)
        LibGit2.iscommit(head, crepo)
    else
        false
    end
    ancestors = VersionNumber[]
    descendants = VersionNumber[]
    try
        for (ver,info) in avail
            sha1 = info.sha1
            base = if cache_has_head && LibGit2.iscommit(sha1, crepo)
                LibGit2.merge_base(crepo, head, sha1)
            elseif LibGit2.iscommit(sha1, prepo)
                LibGit2.merge_base(prepo, head, sha1)
            else
                Base.warn_once("unknown $pkg commit $(sha1[1:8]), metadata may be ahead of package cache")
                continue
            end
            string(base) == sha1 && push!(ancestors,ver)
            string(base) == head && push!(descendants,ver)
        end
    finally
        cache_has_head && LibGit2.finalize(crepo)
    end
    both = sort!(intersect(ancestors,descendants))
    isempty(both) || warn("$pkg: some versions are both ancestors and descendants of head: $both")
    if !isempty(descendants)
        v = minimum(descendants)
        return VersionNumber(v.major, v.minor, v.patch, ("",), ())
    elseif !isempty(ancestors)
        v = maximum(ancestors)
        return VersionNumber(v.major, v.minor, v.patch, (), ("",))
    else
        return typemin(VersionNumber)
    end
end

function requires_path(pkg::AbstractString, avail::Dict=available(pkg))
    pkgreq = joinpath(pkg,"REQUIRE")
    ispath(pkg,".git") || return pkgreq
    repo = LibGit2.GitRepo(pkg)
    head = LibGit2.with(LibGit2.GitRepo, pkg) do repo
        LibGit2.isdirty(repo, "REQUIRE") && return pkgreq
        LibGit2.need_update(repo)
        LibGit2.iszero(LibGit2.revparseid(repo, "HEAD:REQUIRE")) && isfile(pkgreq) && return pkgreq
        string(LibGit2.head_oid(repo))
    end
    for (ver,info) in avail
        if head == info.sha1
            return joinpath("METADATA", pkg, "versions", string(ver), "requires")
        end
    end
    return pkgreq
end

requires_list(pkg::AbstractString, avail::Dict=available(pkg)) =
    collect(keys(Reqs.parse(requires_path(pkg,avail))))

requires_dict(pkg::AbstractString, avail::Dict=available(pkg)) =
    Reqs.parse(requires_path(pkg,avail))

function installed(avail::Dict=available())
    pkgs = Dict{String,Tuple{VersionNumber,Bool}}()
    for pkg in readdir()
        isinstalled(pkg) || continue
        ap = get(avail,pkg,Dict{VersionNumber,Available}())
        if ispath(pkg,".git")
            LibGit2.with(LibGit2.GitRepo, pkg) do repo
                ver = installed_version(pkg, repo, ap)
                fixed = isfixed(pkg, repo, ap)
                pkgs[pkg] = (ver, fixed)
            end
        else
            pkgs[pkg] = (typemin(VersionNumber), true)
        end
    end
    return pkgs
end

function fixed(avail::Dict=available(), inst::Dict=installed(avail), dont_update::Set{String}=Set{String}(),
    julia_version::VersionNumber=VERSION)
    pkgs = Dict{String,Fixed}()
    for (pkg,(ver,fix)) in inst
        (fix || pkg in dont_update) || continue
        ap = get(avail,pkg,Dict{VersionNumber,Available}())
        pkgs[pkg] = Fixed(ver,requires_dict(pkg,ap))
    end
    pkgs["julia"] = Fixed(julia_version)
    return pkgs
end

function free(inst::Dict=installed(), dont_update::Set{String}=Set{String}())
    pkgs = Dict{String,VersionNumber}()
    for (pkg,(ver,fix)) in inst
        (fix || pkg in dont_update) && continue
        pkgs[pkg] = ver
    end
    return pkgs
end

function issue_url(pkg::AbstractString)
    ispath(pkg,".git") || return ""
    m = match(LibGit2.GITHUB_REGEX, url(pkg))
    m === nothing && return ""
    return "https://github.com/" * m.captures[1] * "/issues"
end

end # module


text:
"""
Provides a `REPL` extension to `generate`
educational packages.
"""
module EduREPL

import REPL
import REPL: LineEdit, REPLCompletions
import REPL: TerminalMenus

const __DISCLOSURE = """
This was modified from `Cxx.jl`'s `replplane.jl`.
See _Extended Help_ for the `Cxx.jl` license, and more information.
"""

const __LICENSES = """
## `Cxx` License

The Cxx.jl package is licensed under the MIT Expat License.
Some files have differing Copyright:
  - Several Makefiles in deps/ and all files in deps/llvm-patches
    were extracted from julia. The relevant Copyright lines are noted
    there.
  - Parts of src/CxxREPL/replpane.jl are derived from Cling. Copyright/License
    are noted there.

> Copyright (c) 2013-2016: Keno Fischer and other contributors
>
> Permission is hereby granted, free of charge, to any person obtaining
> a copy of this software and associated documentation files (the
> "Software"), to deal in the Software without restriction, including
> without limitation the rights to use, copy, modify, merge, publish,
> distribute, sublicense, and/or sell copies of the Software, and to
> permit persons to whom the Software is furnished to do so, subject to
> the following conditions:
>
> The above copyright notice and this permission notice shall be
> included in all copies or substantial portions of the Software.
>
> THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
> EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
> MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
> IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
> CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
> TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
> SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

## `Cling` License

> Copyright (c) 2007-2014 by the Authors.
> All rights reserved.
>
> LLVM/Clang/Cling LICENSE text.
> Permission is hereby granted, free of charge, to any person obtaining a copy of
> this software and associated documentation files (the "Software"), to deal with
> the Software without restriction, including without limitation the rights to
> use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
> of the Software, and to permit persons to whom the Software is furnished to do
> so, subject to the following conditions:
>
>     * Redistributions of source code must retain the above copyright notice,
>       this list of conditions and the following disclaimers.
>
>     * Redistributions in binary form must reproduce the above copyright notice,
>       this list of conditions and the following disclaimers in the
>       documentation and/or other materials provided with the distribution.
>
>     * Neither the names of the LLVM Team, University of Illinois at
>       Urbana-Champaign, nor the names of its contributors may be used to
>       endorse or promote products derived from this Software without specific
>       prior written permission.
>
> THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
> IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
> FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE
> CONTRIBUTORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
> LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
> OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS WITH THE
> SOFTWARE.
"""

global runEduREPL

"""
Quits the REPL mode!

$(__DISCLOSURE)

# Extended Help

$(__LICENSES)
"""
function createDefaultDone()
    function(line)
        return :(true)
    end
end

# Inspired by cling's InputValidator.cpp
function isExpressionComplete(data)
    @assert data[end] == UInt8('\0')
end

"""
Creates the `EduREPL` extension.

$(__DISCLOSURE)

# Extended Help

$(__LICENSES)
"""
function createEduREPL(; prompt = "edu> ", name = :edu, onDoneCreator = createDefaultDone, repl = Base.active_repl,
    main_mode = repl.interface.modes[1])
    mirepl = isdefined(repl,:mi) ? repl.mi : repl

    # Setup edu panel
    panel = LineEdit.Prompt(prompt;
        # Copy colors from the prompt object
        prompt_prefix=Base.text_colors[:cyan],
        prompt_suffix=Base.text_colors[:white],
        on_enter = s->isExpressionComplete(push!(copy(LineEdit.buffer(s).data),0)))

    panel.on_done = REPL.respond(onDoneCreator(),repl,panel)

    main_mode == mirepl.interface.modes[1] &&
        push!(mirepl.interface.modes,panel)

    # 0.7 compat
    if isdefined(main_mode, :repl)
        panel.repl = main_mode.repl
    end

    hp = main_mode.hist
    hp.mode_mapping[name] = panel
    panel.hist = hp

    search_prompt, skeymap = LineEdit.setup_search_keymap(hp)
    mk = REPL.mode_keymap(main_mode)

    b = Dict{Any,Any}[skeymap, mk, LineEdit.history_keymap, LineEdit.default_keymap, LineEdit.escape_defaults]
    panel.keymap_dict = LineEdit.keymap(b)

    panel
end

"""
Runs the `EducationPkg` REPL mode.

$(__DISCLOSURE)

# Extended Help

$(__LICENSES)
"""
function runEduREPL(; prompt = "edu> ", name = :edu, key = '}', onDoneCreator = createDefaultDone)
    repl = Base.active_repl
    mirepl = isdefined(repl,:mi) ? repl.mi : repl
    main_mode = mirepl.interface.modes[1]

    panel = createEduREPL(; prompt=prompt, name=name, repl=repl, onDoneCreator=onDoneCreator)

    # Install this mode into the main mode
    edu_keymap = Dict{Any,Any}(
        key => function (s,args...)
            if isempty(s) || position(LineEdit.buffer(s)) == 0
                buf = copy(LineEdit.buffer(s))
                LineEdit.transition(s, panel) do
                    LineEdit.state(s, panel).input_buffer = buf
                end
            else
                LineEdit.edit_insert(s,key)
            end
        end
    )
    main_mode.keymap_dict = LineEdit.keymap_merge(main_mode.keymap_dict, edu_keymap);
    nothing
end

end


text:
const DTAG  = LittleDict{String,Set{String}}
const DTAGI = LittleDict{String,Vector{String}}

"""
Dictionary of variables accessible to all pages. Used as an initialiser for
`LOCAL_VARS` and modified by `config.md`.
Entries have the format KEY => PAIR where KEY is a string (e.g.: "author") and
PAIR is a pair where the first element is the current value for the variable
and the second is a tuple of accepted possible (super)types
for that value. (e.g.: "THE AUTHOR" => (String, Nothing))
"""
const GLOBAL_VARS = PageVars()

const GLOBAL_VARS_DEFAULT = [
    # Folder org
    "folder_structure" => Pair(FD_ENV[:STRUCTURE], (VersionNumber,)),
    # General
    "author"           => Pair("THE AUTHOR", (String, Nothing)),
    "date_format"      => Pair("U dd, yyyy", (String,)),
    "date_months"      => Pair(String[],     (Vector{String},)),
    "date_shortmonths" => Pair(String[],     (Vector{String},)),
    "date_days"        => Pair(String[],     (Vector{String},)),
    "date_shortdays"   => Pair(String[],     (Vector{String},)),
    "prepath"          => Pair("",           (String,)),
    # will be added to IGNORE_FILES
    "ignore"           => Pair(String[], (Vector{Any},)),
    # RSS
    "website_title"    => Pair("",   (String,)),
    "website_descr"    => Pair("",   (String,)),
    "website_url"      => Pair("",   (String,)),
    "generate_rss"     => Pair(true, (Bool,)),
    # div names
    "content_tag"      => Pair("div",              (String,)),
    "content_class"    => Pair("franklin-content", (String,)),
    "content_id"       => Pair("",                 (String,)),
    # auto detection of code / math (see hasmath/hascode)
    "autocode"         => Pair(true, (Bool,)),
    "automath"         => Pair(true, (Bool,)),
    # keep track page=>tags and tag=>pages
    "fd_page_tags"     => Pair(nothing, (DTAG,  Nothing)),
    "fd_tag_pages"     => Pair(nothing, (DTAGI, Nothing)),
    # LEGACY
    "div_content" => Pair("", (String,)), # see build_page
    ]

"""
Re-initialise the global page vars dictionary. (This is done once).
"""
 function def_GLOBAL_VARS!()::Nothing
    empty!(GLOBAL_VARS)
    for (v, p) in GLOBAL_VARS_DEFAULT
        GLOBAL_VARS[v] = p
    end
    return nothing
end

"""
Dictionary of variables accessible to the current page. It's initialised with
`GLOBAL_VARS` and modified via `@def ...`.
"""
const LOCAL_VARS = PageVars()

const LOCAL_VARS_DEFAULT = [
    # General
    "title"         => Pair(nothing,    (String, Nothing)),
    "hasmath"       => Pair(false,      (Bool,)),
    "hascode"       => Pair(false,      (Bool,)),
    "date"          => Pair(Date(1),    (String, Date, Nothing)),
    "lang"          => Pair("julia",    (String,)), # default lang indented code
    "reflinks"      => Pair(true,       (Bool,)),   # are there reflinks?
    "indented_code" => Pair(false,      (Bool,)),   # support indented code?
    "tags"          => Pair(String[],   (Vector{String},)),
    # -----------------
    # TABLE OF CONTENTS
    "mintoclevel" => Pair(1,  (Int,)), # set to 2 to ignore h1
    "maxtoclevel" => Pair(10, (Int,)), # set to 3 to ignore h4,h5,h6
    # ---------------
    # CODE EVALUATION
    "reeval"        => Pair(false,  (Bool,)), # whether to reeval all pg
    "showall"       => Pair(false,  (Bool,)), # if true, notebook style
    "fd_eval"       => Pair(false,  (Bool,)), # toggle re-eval
    # ------------------
    # RSS 2.0 specs [^2]
    "rss"             => Pair("",      (String,)),
    "rss_description" => Pair("",      (String,)),
    "rss_title"       => Pair("",      (String,)),
    "rss_author"      => Pair("",      (String,)),
    "rss_category"    => Pair("",      (String,)),
    "rss_comments"    => Pair("",      (String,)),
    "rss_enclosure"   => Pair("",      (String,)),
    "rss_pubdate"     => Pair(Date(1), (Date,)),
    # -------------
    # MISCELLANEOUS (should not be modified)
    "fd_ctime"  => Pair(Date(1),    (Date,)),   # time of creation
    "fd_mtime"  => Pair(Date(1),    (Date,)),   # time of last modification
    "fd_rpath"  => Pair("",         (String,)), # rpath to current page
    "fd_url"    => Pair("",         (String,)), # url to current page
    "fd_tag"    => Pair("",         (String,)), # (generated) current tag
    ]
#=
NOTE:
 2. only title, link and description *must*
        title       -- rss_title // fallback to title
    (*) link        -- [automatically generated]
        description -- rss // rss_description, if undefined, no item generated
        author      -- rss_author // fallback to author
        category    -- rss_category
        comments    -- rss_comments
        enclosure   -- rss_enclosure
    (*) guid        -- [automatically generated from link]
        pubDate     -- rss_pubdate // fallback date // fallback fd_ctime
    (*) source      -- [unsupported assumes for now there's only one channel]
=#

"""
Re-initialise the local page vars dictionary. (This is done for every page).
Note that a global default overrides a local default. So for instance if
`title` is defined in the config as `@def title = "Universal"`, then the
default title on every page will be that.
"""
function def_LOCAL_VARS!()::Nothing
    empty!(LOCAL_VARS)
    for (v, p) in LOCAL_VARS_DEFAULT
        LOCAL_VARS[v] = p
    end
    # Merge global page vars, if it defines anything that local defines, then
    # global takes precedence.
    merge!(LOCAL_VARS, GLOBAL_VARS)
    # which page we're on, see write_page which sets :CUR_PATH
    set_var!(LOCAL_VARS, "fd_rpath", FD_ENV[:CUR_PATH])
    set_var!(LOCAL_VARS, "fd_url", url_curpage())
    return nothing
end

"""
    locvar(name)

Convenience function to get the value associated with a local var.
Return `nothing` if the variable is not found.
"""
function locvar(name::Union{Symbol,String})
    name = String(name)
    return haskey(LOCAL_VARS, name) ? LOCAL_VARS[name].first : nothing
end

"""
    globvar(name)

Convenience function to get the value associated with a global var.
Return `nothing` if the variable is not found.
"""
function globvar(name::Union{Symbol,String})
    name = String(name)
    return haskey(GLOBAL_VARS, name) ? GLOBAL_VARS[name].first : nothing
end

"""
Dict to keep track of all pages and their vars. Each key is a relative path
to a page, values are PageVars.
The keys don't have the file extension so `"blog/pg1 => PageVars"`.
"""
const ALL_PAGE_VARS = Dict{String,PageVars}()

"""
    pagevar(rpath, name)

Convenience function to get the value associated with a var available to a page
corresponding to `rpath`. So for instance if `blog/index.md` has `@def var = 0`
then this can be accessed with `pagevar("blog/index", "var")`.
If `rpath` is not yet a key of `ALL_PAGE_VARS` then maybe the page hasn't been
processed yet so force a pass over that page.
"""
function pagevar(rpath::AS, name::Union{Symbol,String})
    # only split extension if it's .md or .html (otherwise can cause trouble
    # if there's a dot in the page name... not recommended but happens.)
    rpc = splitext(rpath)[1]
    if rpc[2] in (".md", ".html")
        rpath = rpc[1]
    end

    (:pagevar, "$rpath, $name (key: $(haskey(ALL_PAGE_VARS, rpath)))") |> logger

    if !haskey(ALL_PAGE_VARS, rpath)
        # does there exist a file with a `.md` ? if so go over it
        # otherwise return nothing
        fpath = rpath * ".md"
        candpath = FD_ENV[:STRUCTURE] < v"0.2" ?
                     joinpath(path(:src), fpath) :
                     joinpath(path(:folder), fpath)
        isfile(candpath) || return nothing
        # store curpath
        bk_path = locvar("fd_rpath")
        bk_path_ = splitext(bk_path)[1]

        (:pagevar, "!haskey, bkpath: $bk_path, rpath: $rpath") |> logger

        # set temporary cur path (so that defs go to the right place)
        set_cur_rpath(fpath, isrelative=true)
        # effectively we only care about the mddefs
        convert_md(read(fpath, String), pagevar=true)

        if !haskey(ALL_PAGE_VARS, bk_path_) # empty corner case in tests
            def_LOCAL_VARS!()
        else
            # re-set the cur path to what it was before
            set_cur_rpath(bk_path, isrelative=true)
            # re-set local vars using ALL_PAGE_VARS
            # NOTE: we must do this in place to messing things up.
            empty!(LOCAL_VARS)
            merge!(LOCAL_VARS, ALL_PAGE_VARS[bk_path_])
        end
    end
    name = String(name)
    haskey(ALL_PAGE_VARS[rpath], name) || return nothing
    return ALL_PAGE_VARS[rpath][name].first
end

"""
Keep track of the names declared in the Utils module.
"""
const UTILS_NAMES = Vector{String}()

"""
Keep track of seen headers. The key is the refstring, the value contains the
title, the occurence number for the first appearance of that title and the
level (1, ..., 6).
"""
const PAGE_HEADERS = LittleDict{AS,Tuple{AS,Int,Int}}()

"""
$(SIGNATURES)

Empties `PAGE_HEADERS`.
"""
 function def_PAGE_HEADERS!()::Nothing
    empty!(PAGE_HEADERS)
    return nothing
end

"""
PAGE_FNREFS

Keep track of name of seen footnotes; the order is kept as it's a list.
"""
const PAGE_FNREFS = String[]

"""
$(SIGNATURES)

Empties `PAGE_FNREFS`.
"""
 function def_PAGE_FNREFS!()::Nothing
    empty!(PAGE_FNREFS)
    return nothing
end

"""
PAGE_LINK_DEFS

Keep track of link def candidates
"""
const PAGE_LINK_DEFS = LittleDict{String,String}()

"""
$(SIGNATURES)

Empties `PAGE_LINK_DEFS`.
"""
 function def_PAGE_LINK_DEFS!()::Nothing
    empty!(PAGE_LINK_DEFS)
    return nothing
end

#= ==========================================
Convenience functions related to the page vars
============================================= =#

"""
$(SIGNATURES)

Convenience function taking a `DateTime` object and returning the corresponding
formatted string with the format contained in `LOCAL_VARS["date_format"]` and
with the locale data provided in `date_months`, `date_shortmonths`, `date_days`,
and `date_shortdays` local variables. If `short` variations are not provided,
automatically construct them using the first three letters of the names in
`date_months` and `date_days`.
"""
function fd_date(d::DateTime)
    # aliases for locale data and format from local variables
    format      = locvar("date_format")
    months      = locvar("date_months")
    shortmonths = locvar("date_shortmonths")
    days        = locvar("date_days")
    shortdays   = locvar("date_shortdays")
    # if vectors are empty, user has not defined custom locale,
    # defaults to english
    if all(isempty.((months, shortmonths, days, shortdays)))
        return Dates.format(d, format, locale="english")
    end
    # if shortdays or shortmonths are undefined,
    # automatically construct them from other lists
    if !isempty(days) && isempty(shortdays)
        shortdays = first.(days, 3)
    end
    if !isempty(months) && isempty(shortmonths)
        shortmonths = first.(months, 3)
    end
    # set locale for this page
    Dates.LOCALES["date_locale"] = Dates.DateLocale(months, shortmonths,
                                                    days, shortdays)
    return Dates.format(d, format, locale="date_locale")
end

"""
$(SIGNATURES)

Checks if a data type `t` is a subtype of a tuple of accepted types `tt`.
"""
function check_type(t::DataType, tt::NTuple{N,DataType} where N)::Bool
    any(valid_subtype(t, tᵢ) for tᵢ ∈ tt) && return true
    return false
end

valid_subtype(::Type{T1},
              ::Type{T2}) where {T1,T2} = T1 <: T2
valid_subtype(::Type{<:AbstractArray{T1,K}},
              ::Type{<:AbstractArray{T2,K}}) where {T1,T2,K} = T1 <: T2

"""
$(SIGNATURES)

Take a var dictionary `dict` and update the corresponding pair. This should
only be used internally as it does not check the validity of `val`. See
[`write_page`](@ref) where it is used to store a file's creation and last
modification time.
"""
set_var!(d::PageVars, k::K, v) where K = (d[k] = Pair(v, d[k].second); nothing)

#= =================================================
set_vars, the key function to assign site variables
==================================================== =#

"""
$(SIGNATURES)

Given a set of definitions `assignments`, update the variables dictionary
`vars`. Keys in `assignments` that do not match keys in `vars` are
ignored (a warning message is displayed).
The entries in `assignments` are of the form `KEY => STR` where `KEY` is a
string key (e.g.: "hasmath") and `STR` is an assignment to evaluate (e.g.:
"=false").
"""
function set_vars!(vars::PageVars, assignments::Vector{Pair{String,String}}
                   )::PageVars
    # if there's no assignment, cut it short
    isempty(assignments) && return vars
    # process each assignment in turn
    for (key, assign) ∈ assignments
        # at this point there may still be a comment in the assignment
        # string e.g. if it came from @def title = "blah" <!-- ... -->
        # so let's strip <!-- and everything after.
        # NOTE This is agressive so if it happened that the user wanted
        # this in a string it would fail (but come on...)
        idx = findfirst("<!--", assign)
        !isnothing(idx) && (assign = assign[1:prevind(assign, idx[1])])
        tmp, = Meta.parse(assign, 1)
        # try to evaluate the parsed assignment
        try
            tmp = eval(tmp)
        catch err
            throw(PageVariableError(
                "An error (of type '$(typeof(err))') occurred when trying " *
                "to evaluate '$tmp' in a page variable assignment."))
        end
        if haskey(vars, key)
            # if the retrieved value has the right type, assign it to the corresponding key
            type_tmp  = typeof(tmp)
            acc_types = vars[key].second
            if check_type(type_tmp, acc_types)
                vars[key] = Pair(tmp, acc_types)
            else
                @warn "Page var '$key' (type(s): $acc_types) can't be set " *
                      "to value '$tmp' (type: $type_tmp). Assignment ignored."
            end
        else
            # there is no key, so directly assign, the type is not checked
            vars[key] = Pair(tmp, (typeof(tmp), ))
        end
    end
    return vars
end

"""
    set_page_env()

Initialises all page dictionaries.
"""
function set_page_env()
    def_LOCAL_VARS!()       # page-specific variables
    def_PAGE_HEADERS!()     # all the headers
    def_PAGE_EQREFS!()      # page-specific equation dict (hrefs)
    def_PAGE_BIBREFS!()     # page-specific reference dict (hrefs)
    def_PAGE_FNREFS!()      # page-specific footnote dict
    def_PAGE_LINK_DEFS!()   # page-specific link definition candidates
    return nothing
end


text:
type PiecewiseYieldCurve{B <: BootstrapHelper, DC <: DayCount, P <: Interpolation, T <: BootstrapTrait, BT <: Bootstrap} <: InterpolatedCurve{P, T}
  lazyMixin::LazyMixin
  settlementDays::Int
  referenceDate::Date
  instruments::Vector{B}
  dc::DC
  interp::P
  trait::T
  accuracy::Float64
  boot::BT
  times::Vector{Float64}
  dates::Vector{Date}
  data::Vector{Float64}
  errors::Vector{Function}
  validCurve::Bool
end

function PiecewiseYieldCurve{B <: BootstrapHelper, DC <: DayCount, P <: Interpolation, T <: BootstrapTrait, BT <: Bootstrap}(referenceDate::Date, instruments::Vector{B}, dc::DC, interp::P, trait::T,
                                accuracy::Float64, boot::BT = IterativeBootstrap())
  # get the initial length of instruments
  n = length(instruments)
  # create an initial state of the curve
  pyc = PiecewiseYieldCurve(LazyMixin(),
                            0,
                            referenceDate,
                            instruments,
                            dc,
                            interp,
                            trait,
                            accuracy,
                            boot,
                            Vector{Float64}(n + 1),
                            Vector{Date}(n + 1),
                            Vector{Float64}(n + 1),
                            Vector{Function}(n + 1),
                            false)

  # initialize the bootstrapping
  initialize(pyc.boot, pyc)

  return pyc
end


text:
# MIT License
#
# Copyright (c) 2020 Joshua E Gentry

# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in all
# copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.
using StaticArrays

function g_1_5_9_13(input::SVector{16, UInt32}, mx::UInt32, my::UInt32)::SVector{16, UInt32}
    local a = input[1]
    local b = input[5]
    local c = input[9]
    local d = input[13]

    a = a + b + mx
    d = bitrotate(d ⊻ a, -16)
    c = c + d
    b = bitrotate(b ⊻ c, -12)

    a = a + b + my
    d = bitrotate(d ⊻ a, -8)
    c = c + d
    b = bitrotate(b ⊻ c, -7)

    return SVector(
        a, input[2],  input[3],  input[4],
        b, input[6],  input[7],  input[8],
        c, input[10], input[11], input[12],
        d, input[14], input[15], input[16]
    )
end

function g_2_6_10_14(input::SVector{16, UInt32}, mx::UInt32, my::UInt32)::SVector{16, UInt32}
    local a = input[2]
    local b = input[6]
    local c = input[10]
    local d = input[14]

    a = a + b + mx
    d = bitrotate(d ⊻ a, -16)
    c = c + d
    b = bitrotate(b ⊻ c, -12)

    a = a + b + my
    d = bitrotate(d ⊻ a, -8)
    c = c + d
    b = bitrotate(b ⊻ c, -7)

    return SVector(
        input[1],  a,  input[3],  input[4],
        input[5],  b,  input[7],  input[8],
        input[9],  c, input[11], input[12],
        input[13], d, input[15], input[16]
    )
end

function g_3_7_11_15(input::SVector{16, UInt32}, mx::UInt32, my::UInt32)::SVector{16, UInt32}
    local a = input[3]
    local b = input[7]
    local c = input[11]
    local d = input[15]

    a = a + b + mx
    d = bitrotate(d ⊻ a, -16)
    c = c + d
    b = bitrotate(b ⊻ c, -12)

    a = a + b + my
    d = bitrotate(d ⊻ a, -8)
    c = c + d
    b = bitrotate(b ⊻ c, -7)

    return SVector(
        input[1],  input[2],  a, input[4],
        input[5],  input[6],  b, input[8],
        input[9],  input[10], c, input[12],
        input[13], input[14], d, input[16]
    )
end

function g_4_8_12_16(input::SVector{16, UInt32}, mx::UInt32, my::UInt32)::SVector{16, UInt32}
    local a = input[4]
    local b = input[8]
    local c = input[12]
    local d = input[16]

    a = a + b + mx
    d = bitrotate(d ⊻ a, -16)
    c = c + d
    b = bitrotate(b ⊻ c, -12)

    a = a + b + my
    d = bitrotate(d ⊻ a, -8)
    c = c + d
    b = bitrotate(b ⊻ c, -7)

    return SVector(
        input[1],  input[2],  input[3],  a,
        input[5],  input[6],  input[7],  b,
        input[9],  input[10], input[11], c,
        input[13], input[14], input[15], d
    )
end

function g_1_6_11_16(input::SVector{16, UInt32}, mx::UInt32, my::UInt32)::SVector{16, UInt32}
    local a = input[1]
    local b = input[6]
    local c = input[11]
    local d = input[16]

    a = a + b + mx
    d = bitrotate(d ⊻ a, -16)
    c = c + d
    b = bitrotate(b ⊻ c, -12)

    a = a + b + my
    d = bitrotate(d ⊻ a, -8)
    c = c + d
    b = bitrotate(b ⊻ c, -7)

    return SVector(
        a,         input[2],  input[3],  input[4],
        input[5],  b,         input[7],  input[8],
        input[9],  input[10], c,         input[12],
        input[13], input[14], input[15], d
    )
end

function g_2_7_12_13(input::SVector{16, UInt32}, mx::UInt32, my::UInt32)::SVector{16, UInt32}
    local a = input[2]
    local b = input[7]
    local c = input[12]
    local d = input[13]

    a = a + b + mx
    d = bitrotate(d ⊻ a, -16)
    c = c + d
    b = bitrotate(b ⊻ c, -12)

    a = a + b + my
    d = bitrotate(d ⊻ a, -8)
    c = c + d
    b = bitrotate(b ⊻ c, -7)

    return SVector(
        input[1],  a,         input[3],  input[4],
        input[5],  input[6],  b,         input[8],
        input[9],  input[10], input[11], c,
        d,         input[14], input[15], input[16]
    )
end

function g_3_8_9_14(input::SVector{16, UInt32}, mx::UInt32, my::UInt32)::SVector{16, UInt32}
    local a = input[3]
    local b = input[8]
    local c = input[9]
    local d = input[14]

    a = a + b + mx
    d = bitrotate(d ⊻ a, -16)
    c = c + d
    b = bitrotate(b ⊻ c, -12)

    a = a + b + my
    d = bitrotate(d ⊻ a, -8)
    c = c + d
    b = bitrotate(b ⊻ c, -7)

    return SVector(
        input[1],  input[2],  a,         input[4],
        input[5],  input[6],  input[7],  b,
        c,         input[10], input[11], input[12],
        input[13], d,         input[15], input[16]
    )
end

function g_4_5_10_15(input::SVector{16, UInt32}, mx::UInt32, my::UInt32)::SVector{16, UInt32}
    local a = input[4]
    local b = input[5]
    local c = input[10]
    local d = input[15]

    a = a + b + mx
    d = bitrotate(d ⊻ a, -16)
    c = c + d
    b = bitrotate(b ⊻ c, -12)

    a = a + b + my
    d = bitrotate(d ⊻ a, -8)
    c = c + d
    b = bitrotate(b ⊻ c, -7)

    return SVector(
        input[1],  input[2],  input[3],  a,
        b,         input[6],  input[7],  input[8],
        input[9],  c,         input[11], input[12],
        input[13], input[14], d,         input[16]
    )
end

function round(state::SVector{16, UInt32}, m::SVector{16, UInt32})::SVector{16, UInt32}

    # Mix the columns
    state = g_1_5_9_13(state, m[1], m[2])
    state = g_2_6_10_14(state, m[3], m[4])
    state = g_3_7_11_15(state, m[5], m[6])
    state = g_4_8_12_16(state, m[7], m[8])
    # Mix the diagonals
    state = g_1_6_11_16(state, m[9],  m[10])
    state = g_2_7_12_13(state, m[11], m[12])
    state = g_3_8_9_14(state, m[13], m[14])
    state = g_4_5_10_15(state, m[15], m[16])

    return state
end

function permute(m::SVector{16, UInt32})::SVector{16, UInt32}
    return SVector(
        m[3],  m[7],  m[4],  m[11],
        m[8],  m[1],  m[5],  m[14],
        m[2],  m[12], m[13], m[6],
        m[10], m[15], m[16], m[9]
    )
end

function compress(
            chaining_value::SVector{8, UInt32},
            block::SVector{16, UInt32},
            counter::UInt64,
            block_len::UInt32,
            flags::UInt32
        )::SVector{16, UInt32}
    # @printf("CV1: 0x%08x 0x%08x 0x%08x 0x%08x\n", chaining_value[1], chaining_value[2], chaining_value[3], chaining_value[4])
    # @printf("CV2: 0x%08x 0x%08x 0x%08x 0x%08x\n", chaining_value[5], chaining_value[6], chaining_value[7], chaining_value[8])
    # @printf("BL1: 0x%08x 0x%08x 0x%08x 0x%08x\n", block[1], block[2], block[3], block[4])
    # @printf("BL2: 0x%08x 0x%08x 0x%08x 0x%08x\n", block[5], block[6], block[7], block[8])
    # @printf("BL3: 0x%08x 0x%08x 0x%08x 0x%08x\n", block[9], block[10], block[11], block[12])
    # @printf("BL4: 0x%08x 0x%08x 0x%08x 0x%08x\n", block[13], block[14], block[15], block[16])
    # @printf("block_len: %d counter: %d flags: %d\n", block_len, counter, flags)

    local state = SVector{16, UInt32}(
        chaining_value[1], chaining_value[2], chaining_value[3], chaining_value[4],
        chaining_value[5], chaining_value[6], chaining_value[7], chaining_value[8],
        IV[1], IV[2], IV[3], IV[4],
        UInt32(counter & 0xffffffff), UInt32(counter >> 32),
        block_len, flags
    )

    state = round(state, block) # round 1
    block = permute(block)
    state = round(state, block) # round 2
    block = permute(block)
    state = round(state, block) # round 3
    block = permute(block)
    state = round(state, block) # round 4
    block = permute(block)
    state = round(state, block) # round 5
    block = permute(block)
    state = round(state, block) # round 6
    block = permute(block)
    state = round(state, block) # round 7

    return SVector(
        state[1] ⊻ state[ 9], state[2] ⊻ state[10],
        state[3] ⊻ state[11], state[4] ⊻ state[12],
        state[5] ⊻ state[13], state[6] ⊻ state[14],
        state[7] ⊻ state[15], state[8] ⊻ state[16],
        state[ 9] ⊻ chaining_value[1], state[10] ⊻ chaining_value[2],
        state[11] ⊻ chaining_value[3], state[12] ⊻ chaining_value[4],
        state[13] ⊻ chaining_value[5], state[14] ⊻ chaining_value[6],
        state[15] ⊻ chaining_value[7], state[16] ⊻ chaining_value[8]
    )
end


text:
mutable struct GlobalMacroRecord
  name::String
  num_calls::Int
  body::String

  function GlobalMacroRecord(name, body)
    return new(name, 0, body)
  end
end

mutable struct LocalMacroRecord
  name::String
  num_calls::Int
  bodies::Vector{String}

  function LocalMacroRecord(name, body)
    return new(name, 0, [body])
  end
end

mutable struct UndefinedMacroCallRecord
  name::String
  line_numbers::Vector{Int}
  calls::Vector{String}
  lines::Vector{String}

  function UndefinedMacroCallRecord(name, line_number, call, line)
    return new(name, [line_number], [call], [line])
  end
end

mutable struct RunRecord
  global_macro_record_dict::Dict{String, GlobalMacroRecord}
  local_macro_record_dict::Dict{String, LocalMacroRecord}
  undefined_macro_call_dict::Dict{String, UndefinedMacroCallRecord}
  num_lines::Int

  function RunRecord()
    return new(Dict{String, GlobalMacroRecord}(),
               Dict{String, LocalMacroRecord}(),
               Dict{String, UndefinedMacroCallRecord}(),
               0)
  end
end

function report_undefined_macro_call(rec, line, macro_range, name, line_number)
  call = line[macro_range]
  if haskey(rec.undefined_macro_call_dict, name)
    push!(rec.undefined_macro_call_dict[name].line_numbers, line_number)
    push!(rec.undefined_macro_call_dict[name].calls, call)
    push!(rec.undefined_macro_call_dict[name].lines, line)
  else
    rec.undefined_macro_call_dict[name] =
      UndefinedMacroCallRecord(name, line_number, call, line)
  end
end

function find_last_macrocall(line, start=lastindex(line))
  while true
    l = findprev("<<<", line, start)
    if l === nothing
      return
    end
    r = findnext(">>>", line, l.stop)
    if r === nothing
      start = l.start-1
      continue
    end
    return l.start:r.stop
  end
end

function parse_args_indices(s)
  ans = Int[1]
  escaped = false
  for i in eachindex(s)
    if !escaped && (s[i] == ',')
      push!(ans, i+1)
      continue
    end
    if escaped && (s[i] == '\\')
      escaped = false
      continue
    end
    escaped = (s[i] == '\\')
  end
  return push!(ans, ncodeunits(s)+2)
end

function parse_arg_text(s)
  inds = parse_args_indices(s)
  ans = Array{SubString}(undef, length(inds)-1)
  for i in 1:length(inds)-1
    start = inds[i]
    stop = prevind(s, inds[i+1]-1, 1)
    ans[i] = replace(s[start:stop], "\\," => ",")
  end
  return ans
end

@enum LINE_TYPE begin
  LINE_TYPE_NORMAL
  LINE_TYPE_GLOBAL_MACRO_DEFINITION
  LINE_TYPE_LOCAL_MACRO_DEFINITION
end

function gokuro(r, w; take_record=false)
  global_macro = Dict{String, String}()
  local_macro = Dict{String, String}()

  if take_record
    rec = RunRecord()
  end

  line_number = 0
  while true
    line = readline(r, keep=true)
    if line == ""
      break
    end

    line_number += 1

    if line[end] != '\n'
      line *= "\n"
    end

    line_type = LINE_TYPE_NORMAL
    while true
      # glogal macro definition
      if startswith(line, "#+MACRO: ")
        m = match(r"#\+MACRO: (.*?) (.*?)\n", line)
        if m === nothing
          break
        end
        name = m[1]
        body = m[2]
        body = replace(body, r"(\$+)([^0-9])" => s"@\1@\2")
        global_macro[name] = body
        line_type = LINE_TYPE_GLOBAL_MACRO_DEFINITION
        if take_record
          rec.global_macro_record_dict[name] = GlobalMacroRecord(name, body)
        end
        break
      end

      # logal macro definition
      if startswith(line, "#+MACRO_LOCAL: ")
        m = match(r"#\+MACRO_LOCAL: (.*?) (.*?)\n", line)
        if m === nothing
          break
        end
        name = m[1]
        body = m[2]
        body = replace(body, r"\$([^0-9])" => s"@$@\1")
        local_macro[name] = body
        line_type = LINE_TYPE_LOCAL_MACRO_DEFINITION
        if take_record
          if haskey(rec.local_macro_record_dict, name)
            push!(rec.local_macro_record_dict[name].bodies, body)
          else
            rec.local_macro_record_dict[name] = LocalMacroRecord(name, body)
          end
        end
        break
      end

      # macro expansion
      macro_range = find_last_macrocall(line)
      if macro_range === nothing
        break
      end

      macro_prev_idx = prevind(line, macro_range.start)
      macro_next_idx = nextind(line, macro_range.stop)
      zero_or_valid(s, i) = i == 0 || isvalid(s, i)
      @assert zero_or_valid(line, macro_range.start)
      @assert zero_or_valid(line, macro_range.stop)
      @assert zero_or_valid(line, macro_prev_idx)
      @assert zero_or_valid(line, macro_next_idx)

      is_constant = !contains(SubString(line, macro_range), '(')
      if is_constant
        # macro without arguments.
        name_start = nextind(line, macro_range.start + 2) # 2 = length("<<<") - 1 = length(">>>") - 1
        name_stop = prevind(line, macro_range.stop - 2)
        name_range = name_start:name_stop
        name = SubString(line, name_range)
        if haskey(local_macro, name)
          body = local_macro[name]
          if take_record
            rec.local_macro_record_dict[name].num_calls += 1
          end
        elseif haskey(global_macro, name)
          body = global_macro[name]
          if take_record
            rec.global_macro_record_dict[name].num_calls += 1
          end
        else
          # name is not defined as macro.
          if take_record
            report_undefined_macro_call(rec, line, macro_range, name, line_number)
          end
          line = line[1:macro_prev_idx] * line[macro_next_idx:end]
          continue
        end
        body = replace(body, r"@(\$+)@" => s"\1")
        line = line[1:macro_prev_idx] * body * line[macro_next_idx:end]
        continue
      end

      # macro with arguments.
      m = match(r"<<<(.*?)\((.*?)\)>>>", SubString(line, macro_range))
      if m === nothing
        # this macro call is ill-formed.
        line = line[1:macro_prev_idx] * line[macro_next_idx:end]
        continue
      end
      name = m[1]
      if haskey(local_macro, name)
        body = local_macro[name]
        if take_record
          rec.local_macro_record_dict[name].num_calls += 1
        end
      elseif haskey(global_macro, name)
        body = global_macro[name]
        if take_record
          rec.global_macro_record_dict[name].num_calls += 1
        end
      else
        # name is not defined as macro.
        if take_record
          report_undefined_macro_call(rec, line, macro_range, name, line_number)
        end
        line = line[1:macro_prev_idx] * line[macro_next_idx:end]
        continue
      end
      arg_text = replace(m[2], "\$" => "@\$@")
      args = parse_arg_text(arg_text)
      body = replace(body, "\$0"=>arg_text)
      for (i, arg) in enumerate(args)
        body = replace(body, "\$$i"=>arg)
      end
      for i in length(args):9
        body = replace(body, "\$$i"=>"")
      end
      body = replace(body, r"@(\$+)@" => s"\1")
      line = line[1:macro_prev_idx] * body * line[macro_next_idx:end]
    end

    if line_type == LINE_TYPE_NORMAL
      print(w, line)
    end

    if line_type != LINE_TYPE_LOCAL_MACRO_DEFINITION
      empty!(local_macro)
    end
  end

  if take_record
    rec.num_lines = line_number
    return rec
  end
end

function gokuro(s; kwargs...)
  w = IOBuffer()
  rec_maybe = gokuro(IOBuffer(s), w; kwargs...)
  s = read(seekstart(w), String)
  if rec_maybe === nothing
    return s
  else
    return s, rec_maybe
  end
end

if PROGRAM_FILE != ""
  gokuro(stdin, stdout)
end

max_stars_repo_name: inzkyk/gokuro

max_issues_repo_name: inzkyk/gokuro

max_forks_repo_name: inzkyk/gokuro


text:
"""
$(DocStringExtensions.README)
"""
module GalacticOptim

using DocStringExtensions
using Reexport
@reexport using SciMLBase
using Requires
using DiffResults
using Logging, ProgressLogging, ConsoleProgressMonitor, TerminalLoggers, LoggingExtras
using ArrayInterfaceCore, Base.Iterators
using Pkg

import SciMLBase: OptimizationProblem, OptimizationFunction, AbstractADType

@enum ObjSense MinSense MaxSense

include("utils.jl")
include("function/function.jl")

function __init__()
    # AD backends
    @require FiniteDiff = "6a86dc24-6348-571c-b903-95158fe2bd41" include("function/finitediff.jl")
    @require ForwardDiff = "f6369f11-7733-5829-9624-2563aa707210" include("function/forwarddiff.jl")
    @require ReverseDiff = "37e2e3b7-166d-5795-8a7a-e32c996b4267" include("function/reversediff.jl")
    @require Tracker = "9f7883ad-71c0-57eb-9f7f-b5c9e6d3789c" include("function/tracker.jl")
    @require Zygote = "e88e6eb3-aa80-5325-afca-941959d7151f" include("function/zygote.jl")
    @require ModelingToolkit = "961ee093-0014-501f-94e3-6117800e7a78" include("function/mtk.jl")
end

export solve

end # module


text:
using Random
rng = MersenneTwister(1234)

using GR
GR.__init__()

x = 0:π/100:2π
y = sin.(x)
plot(x, y)

x = LinRange(0, 1, 51)
y = x .- x.^2
scatter(x, y)

sz = LinRange(50, 300, length(x))
c = LinRange(0, 255, length(x))
scatter(x, y, sz, c)

stem(x, y)

histogram(randn(rng, 10000))

plot(randn(rng, 50))

oplot(randn(rng, 50, 3))

x = LinRange(0, 30, 1000)
y = cos.(x) .* x
z = sin.(x) .* x
plot3(x, y, z)

angles = LinRange(0, 2pi, 40)
radii = LinRange(0, 2, 40)
polar(angles, radii)

x = 2 .* rand(rng, 100) .- 1
y = 2 .* rand(rng, 100) .- 1
z = 2 .* rand(rng, 100) .- 1
scatter3(x, y, z)

c = 999 .* rand(rng, 100) .+ 1
scatter3(x, y, z, c)

x = randn(rng, 100000)
y = randn(rng, 100000)
hexbin(x, y)

x = 8 .* rand(rng, 100) .- 4
y = 8 .* rand(rng, 100) .- 4
z = sin.(x) .+ cos.(y)
contour(x, y, z)

x = LinRange(-2, 2, 40)
y = LinRange(0, pi, 20)
z = sin.(x') .+ cos.(y)
contour(x, y, z)

x = 8 .* rand(rng, 100) .- 4
y = 8 .* rand(rng, 100) .- 4
z = sin.(x) .+ cos.(y)
contourf(x, y, z)

x = LinRange(-2, 2, 40)
y = LinRange(0, pi, 20)
z = sin.(x') .+ cos.(y)
contourf(x, y, z)

x = 8 .* rand(rng, 100) .- 4
y = 8 .* rand(rng, 100) .- 4
z = sin.(x) + cos.(y)
tricont(x, y, z)

x = 8 .* rand(rng, 100) .- 4
y = 8 .* rand(rng, 100) .- 4
z = sin.(x) .+ cos.(y)
surface(x, y, z)

x = LinRange(-2, 2, 40)
y = LinRange(0, pi, 20)
z = sin.(x') .+ cos.(y)
surface(x, y, z)

x = 8 .* rand(rng, 100) .- 4
y = 8 .* rand(rng, 100) .- 4
z = sin.(x) .+ cos.(y)
trisurf(x, y, z)

z = peaks()
surface(z)

x = LinRange(-2, 2, 40)
y = LinRange(0, pi, 20)
z = sin.(x') .+ cos.(y)
wireframe(x, y, z)

x = LinRange(-2, 2, 40)
y = LinRange(0, pi, 20)
z = sin.(x') .+ cos.(y)
heatmap(z)

imshow(z)

ρ = LinRange(0., 7, 200)
θ = LinRange(0., 2π, 360)
polarheatmap(θ, ρ, sin.(2ρ) .* cos.(θ'))

s = LinRange(-1, 1, 40)
v = 1 .- (s .^ 2 .+ (s .^ 2)' .+ reshape(s,1,1,:) .^ 2) .^ 0.5
isosurface(v, isovalue=0.2)

GR.GR3.terminate()

volume(randn(rng, 50, 50, 50))

N = 1_000_000
x = randn(rng, N)
y = randn(rng, N)
shade(x, y)

clearws()
xd = -2 .+ 4 * rand(rng, 100)
yd = -2 .+ 4 * rand(rng, 100)
zd = [xd[i] * exp(-xd[i]^2 - yd[i]^2) for i = 1:100]

setwsviewport(0, 0.1, 0, 0.1)
setwswindow(0, 1, 0, 1)

setviewport(0.1, 0.95, 0.1, 0.95)
setwindow(-2, 2, -2, 2)
setspace(-0.5, 0.5, 0, 90)

setcolormap(0)
setlinecolorind(1)
setmarkersize(1)
setmarkertype(-1)
setmarkercolorind(1)
setcharheight(0.024)
settextalign(2, 0)
settextfontprec(3, 0)

x, y, z = gridit(xd, yd, zd, 200, 200)
h = -0.5:0.05:0.5
surface(x, y, z, 5)
contour(x, y, h, z, 0)
polymarker(xd, yd)
GR.axes(0.25, 0.25, -2, -2, 2, 2, 0.01)

updatews()


text:
"Initialize LIKWIDs GPU topology module."
function init_topology_gpu()
    ret = LibLikwid.topology_gpu_init()
    if ret == 0
        _gputopo[] = unsafe_load(LibLikwid.get_gpuTopology())
        _build_jl_gputopo()
        gputopo_initialized[] = true
        return true
    end
    return false
end

function _build_jl_gputopo()
    gt = _gputopo[]

    ndevices = gt.numDevices
    _devices = unsafe_wrap(Array, gt.devices, ndevices)

    devices = Vector{GpuDevice}(undef, ndevices)
    for (i, dev) in enumerate(_devices)
        devices[i] = GpuDevice(
            dev.devid,
            dev.numaNode,
            unsafe_string(dev.name),
            unsafe_string(dev.short_name),
            dev.mem,
            dev.ccapMajor,
            dev.ccapMinor,
            dev.maxThreadsPerBlock,
            convert(NTuple{3, Int}, dev.maxThreadsDim),
            convert(NTuple{3, Int}, dev.maxGridSize),
            dev.sharedMemPerBlock,
            dev.totalConstantMemory,
            dev.simdWidth,
            dev.memPitch,
            dev.regsPerBlock,
            dev.clockRatekHz,
            dev.textureAlign,
            dev.surfaceAlign,
            dev.l2Size,
            dev.memClockRatekHz,
            dev.pciBus,
            dev.pciDev,
            dev.pciDom,
            dev.maxBlockRegs,
            dev.numMultiProcs,
            dev.maxThreadPerMultiProc,
            dev.memBusWidth,
            dev.unifiedAddrSpace,
            dev.ecc,
            dev.asyncEngines,
            dev.mapHostMem,
            dev.integrated,
        )
    end

    gputopo[] = GpuTopology(
        ndevices,
        devices
    )
    return nothing
end

"Get GPU topology"
function get_gpu_topology()
    if !gputopo_initialized[]
        init_topology_gpu() || error("Couldn't init gpu topology.")
    end
    return gputopo[]
end

"Finalize LIKWIDs GPU topology module."
function finalize_topology_gpu()
    LibLikwid.topology_gpu_finalize()
    gputopo_initialized[] = false
    _gputopo[] = nothing
    gputopo[] = nothing
    return nothing
end