text:
# The multivariate density discussed in the following blog posts:
# http://dmbates.blogspot.com/2012_05_01_archive.html
# http://darrenjw.wordpress.com/2011/07/16/gibbs-sampler-in-various-languages-revisited/

# Requires extras/Rmath.jl
# extras/options.jl
load("examples/multivariate/mv_slice_sampler.jl")

function f(v::Array{Float64,2})
  x = v[1]
  y = v[2]
  if x < 0
    return -Inf
  end
  2*log(x) - x*y^2 - y^2 + 2*y - 4*x
end
function gradient(v::Array{Float64,2})
  x = v[1]
  y = v[2]
  # TODO: Deal with x < 0 boundary
  dx = 2/x - y^2 - 4
  dy = -2*x*y - 2*y + 2
  [dx dy]
end

# Sampler that draws from the conditional distributions
function gibbs_sampler(v::Array{Float64}, dd, params)
  x = v[1]
  y = v[2]
  x = rgamma(1,3,1/(y*y + 4))[1]
  y = rnorm(1, 1/(x+1),1/sqrt(2*(x + 1)))[1]
  [x y], dd.f([x y])
end

# General MCMC routine for collecting samples from a chain.
# We enforce samplers to return the value of the density f at the new state.
function mcmc(x::Array{Float64}, dd, sampler::Function, opts::Options, niter::Int64)
  P = length(x)
  xs = zeros((niter,P))
  progress = zeros(niter)
  for iter = 1:niter
    x, fx = sampler(x, dd, opts)
    xs[iter,:] = x
    progress[iter] = fx
  end
  return xs, progress
end

x0 = [0.1 0.5]
num_iterations = 10000

dd = DifferentiableDensity(f,gradient)

opts = Options(:stepsize,0.0001,
               :numsteps,int(50),
               :bounded,[1],          # list of dimensions that are bounded
               :lower_bounds,[0],     # lower bound for 1st dim
               :upper_bounds,[Inf])   # upper bound for 1st dim
@time xs, progress = mcmc(x0, dd, bounded_hmc_sampler, opts, num_iterations);
csvwrite("examples/multivariate/hmc.dat",xs)

opts = Options()
@time xs, progress = mcmc(x0, dd, gibbs_sampler, opts, num_iterations);
csvwrite("examples/multivariate/gibbs.dat",xs)

opts = Options()
@time xs, progress = mcmc(x0, dd, mv_slice_sampler, opts, num_iterations);
csvwrite("examples/multivariate/slice.dat",xs)


text:
using JuMP, BARON, Base.Test

m = Model(solver=BaronSolver())
ub = [2, 2, 1]
@variable(m, 0 ≤ x[i=1:3] ≤ ub[i])
@variable(m, y[1:3], Bin)

@NLconstraints(m, begin
    0.8log(x[2]+1) + 0.96log(x[1]-x[2]+1) - 0.8x[3]         ≥  0
       log(x[2]+1) + 1.20log(x[1]-x[2]+1)  -   x[3] - 2y[3] ≥ -2
       x[2] ≤  x[1]
       x[2] ≤ 2y[1]
       x[1] - x[2] ≤ 2y[2]
       y[1] + y[2] ≤ 1
end)

@NLobjective(m, Min, 5y[1] + 6y[2] + 8y[3] + 10x[1] - 7x[3] - 18log(x[2]+1) -
                        19.2log(x[1]-x[2]+1) + 10)

solve(m)

ext: jl


text:
sample = [3, 8, 9, 1, 2, 5, 4, 6, 7]
input = [1, 2, 3, 4, 8, 7, 5, 9, 6]

mutable struct Cup
    val::Int
    next::Union{Cup,Nothing}
end

function link!(cups)
    for i in 1:length(cups)
        next = i + 1 > length(cups) ? 1 : i + 1
        cups[i].next = cups[next]
    end
end

function play!(cups, rounds)
    addresses = Dict()
    for cup in cups
        addresses[cup.val] = cup
    end

    cmax = maximum(map(c -> c.val, cups))

    current = first(cups)

    for i in 1:rounds
        rem = rem!(current)
        d = dest(current, rem, cmax, addresses)
        ins!(d, rem)

        current = current.next
    end
end

function rem!(current)
    x = current.next
    y = x.next
    z = y.next
    current.next = z.next
    return [x, y, z]
end

function dest(current, rem, cmax, addresses)
    used = append!(map(c -> c.val, rem), [current.val])
    i = current.val
    while i in used
        i -= 1
        if i == 0
            i = cmax
        end
    end
    return addresses[i]
end

function ins!(dest, rem)
    tmp = dest.next
    dest.next = first(rem)
    last(rem).next = tmp
end

function val(cups)
    res = ""
    current = first(cups)

    while current.val != 1
        current = current.next
    end
    current = current.next
    while current.val != 1
        res = string(res, current.val)
        current = current.next
    end
    return parse(Int, res)
end

function stars(cups)
    current = first(cups)

    while current.val != 1
        current = current.next
    end
    return current.next.val * current.next.next.val
end

cups = map(n -> Cup(n, nothing), input)
link!(cups)

play!(cups, 100)
p1 = val(cups)

cups = map(n -> Cup(n, nothing), append!(input, collect((maximum(input) + 1):1000000)))
link!(cups)

play!(cups, 10000000)
p2 = stars(cups)

println("-----------------------------------------------------------------------")
println("crab cups -- part one :: $p1")
println("crab cups -- part two :: $p2")
println("-----------------------------------------------------------------------")

@assert(p1 == 47598263)
@assert(p2 == 248009574232)


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

module REPLMode

using Markdown
using UUIDs

import REPL
import REPL: LineEdit, REPLCompletions

import ..devdir, ..Types.casesensitive_isdir, ..TOML
using ..Types, ..Display, ..Operations, ..API

#################
# Git revisions #
#################
struct Rev
    rev::String
end

###########
# Options #
###########
struct OptionSpec
    name::String
    short_name::Union{Nothing,String}
    api::Pair{Symbol, Any}
    is_switch::Bool
end

@enum(OptionClass, OPT_ARG, OPT_SWITCH)
const OptionDeclaration = Tuple{Union{String,Vector{String}}, # name + short_name?
                                OptionClass, # arg or switch
                                Pair{Symbol, Any} # api keywords
                                }

function OptionSpec(x::OptionDeclaration)::OptionSpec
    get_names(name::String) = (name, nothing)
    function get_names(names::Vector{String})
        @assert length(names) == 2
        return (names[1], names[2])
    end

    is_switch = x[2] == OPT_SWITCH
    api = x[3]
    (name, short_name) = get_names(x[1])
    #TODO assert matching lex regex
    if !is_switch
        @assert api.second === nothing || hasmethod(api.second, Tuple{String})
    end
    return OptionSpec(name, short_name, api, is_switch)
end

function OptionSpecs(decs::Vector{OptionDeclaration})::Dict{String, OptionSpec}
    specs = Dict()
    for x in decs
        opt_spec = OptionSpec(x)
        @assert get(specs, opt_spec.name, nothing) === nothing # don't overwrite
        specs[opt_spec.name] = opt_spec
        if opt_spec.short_name !== nothing
            @assert get(specs, opt_spec.short_name, nothing) === nothing # don't overwrite
            specs[opt_spec.short_name] = opt_spec
        end
    end
    return specs
end

struct Option
    val::String
    argument::Union{Nothing,String}
    Option(val::AbstractString) = new(val, nothing)
    Option(val::AbstractString, arg::Union{Nothing,String}) = new(val, arg)
end
Base.show(io::IO, opt::Option) = print(io, "--$(opt.val)", opt.argument == nothing ? "" : "=$(opt.argument)")

function parse_option(word::AbstractString)::Option
    m = match(r"^(?: -([a-z]) | --([a-z]{2,})(?:\s*=\s*(\S*))? )$"ix, word)
    m == nothing && pkgerror("malformed option: ", repr(word))
    option_name = (m.captures[1] != nothing ? m.captures[1] : m.captures[2])
    option_arg = (m.captures[3] == nothing ? nothing : String(m.captures[3]))
    return Option(option_name, option_arg)
end

meta_option_declarations = OptionDeclaration[
    ("preview", OPT_SWITCH, :preview => true)
]
meta_option_specs = OptionSpecs(meta_option_declarations)

################
# Command Spec #
################
@enum(CommandKind, CMD_HELP, CMD_RM, CMD_ADD, CMD_DEVELOP, CMD_UP,
                   CMD_STATUS, CMD_TEST, CMD_GC, CMD_BUILD, CMD_PIN,
                   CMD_FREE, CMD_GENERATE, CMD_RESOLVE, CMD_PRECOMPILE,
                   CMD_INSTANTIATE, CMD_ACTIVATE, CMD_PREVIEW,
                   CMD_REGISTRY_ADD,
                   )
@enum(ArgClass, ARG_RAW, ARG_PKG, ARG_VERSION, ARG_REV, ARG_ALL)
struct ArgSpec
    count::Pair
    parser::Function
    parser_keys::Vector{Pair{Symbol, Any}}
end
const CommandDeclaration = Tuple{CommandKind,
                                 Vector{String}, # names
                                 Union{Nothing,Function}, # handler
                                 Tuple{Pair, # count
                                       Function, # parser
                                       Vector{Pair{Symbol, Any}}, # parser keys
                                       }, # arguments
                                 Vector{OptionDeclaration}, # options
                                 String, #description
                                 Union{Nothing, Markdown.MD}, #help
                                 }
struct CommandSpec
    kind::CommandKind
    canonical_name::String
    short_name::Union{Nothing,String}
    handler::Union{Nothing,Function}
    argument_spec::ArgSpec
    option_specs::Dict{String, OptionSpec}
    description::String
    help::Union{Nothing, Markdown.MD}
end
command_specs = Dict{String,CommandSpec}() # TODO remove this ?

function SuperSpecs(foo)::Dict{String,Dict{String,CommandSpec}}
    super_specs = Dict()
    for x in foo
        sub_specs = CommandSpecs(x.second)
        for name in x.first
            @assert get(super_specs, name, nothing) === nothing
            super_specs[name] = sub_specs
        end
    end
    return super_specs
end

# populate a dictionary: command_name -> command_spec
function CommandSpecs(declarations::Vector{CommandDeclaration})::Dict{String,CommandSpec}
    specs = Dict()
    for dec in declarations
        names = dec[2]
        spec = CommandSpec(dec[1],
                           names[1],
                           length(names) == 2 ? names[2] : nothing,
                           dec[3],
                           ArgSpec(dec[4]...),
                           OptionSpecs(dec[5]),
                           dec[6],
                           dec[end])
        for name in names
            # TODO regex check name
            @assert get(specs, name, nothing) === nothing
            specs[name] = spec
        end
    end
    return specs
end

###################
# Package parsing #
###################
let uuid = raw"(?i)[0-9a-z]{8}-[0-9a-z]{4}-[0-9a-z]{4}-[0-9a-z]{4}-[0-9a-z]{12}(?-i)",
    name = raw"(\w+)(?:\.jl)?"
    global const name_re = Regex("^$name\$")
    global const uuid_re = Regex("^$uuid\$")
    global const name_uuid_re = Regex("^$name\\s*=\\s*($uuid)\$")
end

# packages can be identified through: uuid, name, or name+uuid
# additionally valid for add/develop are: local path, url
function parse_package(word::AbstractString; add_or_develop=false)::PackageSpec
    word = replace(word, "~" => homedir())
    if add_or_develop && casesensitive_isdir(word)
        return PackageSpec(Types.GitRepo(word))
    elseif occursin(uuid_re, word)
        return PackageSpec(UUID(word))
    elseif occursin(name_re, word)
        return PackageSpec(String(match(name_re, word).captures[1]))
    elseif occursin(name_uuid_re, word)
        m = match(name_uuid_re, word)
        return PackageSpec(String(m.captures[1]), UUID(m.captures[2]))
    elseif add_or_develop
        # Guess it is a url then
        return PackageSpec(Types.GitRepo(word))
    else
        pkgerror("`$word` cannot be parsed as a package")
    end
end

################
# REPL parsing #
################
mutable struct Statement
    command::Union{Nothing,CommandSpec}
    options::Vector{String}
    arguments::Vector{String}
    meta_options::Vector{String}
    Statement() = new(nothing, [], [], [])
end

struct QuotedWord
    word::String
    isquoted::Bool
end

function unwrap_option(option::String)
    if startswith(option, "--")
        return length(option) == 2 ? "" : option[3:end]
    elseif length(option) == 2
        return option[end]
    end
end

wrap_option(option::String) =
    length(option) == 1 ? "-$option" : "--$option"

function _statement(words)
    is_option(word) = first(word) == '-'

    word = popfirst!(words)
    # meta options
    while is_option(word)
        if isempty(words)
            if unwrap_option(word) in keys(meta_option_specs)
                return :cmd, "", nothing, true
            else
                return :meta, word, nothing, true
            end
        end
        word = popfirst!(words)
    end
    # command
    if word == "preview"
        if isempty(words)
            return :cmd, "", nothing, true
        end
        word = popfirst!(words)
    end
    if word in keys(super_specs) # have a super command
        super_name = word
        super = super_specs[word]
        if isempty(words)
            return :sub, "", super_name, true
        end
        word = popfirst!(words)
        command = get(super, word, nothing)
        if command === nothing
            if isempty(words)
                return :sub, word, super_name, true
            else
                return nothing
            end
        end
    elseif get(super_specs["package"], word, nothing) !== nothing # given a "package" command
        command = get(super_specs["package"], word, nothing)
    elseif isempty(words) # try to complete the super command
        return :cmd, word, nothing, true
    else
        return nothing
    end
    if isempty(words)
        return :arg, "", command, true
    end
    word = words[end]
    manifest = any(x->x in ["--manifest", "-m"], filter(is_option, words))
    return is_option(word) ?
        (:opt, word, command, true) :
        (:arg, word, command, !manifest)
end

function parse(cmd::String; for_completions=false)
    # replace new lines with ; to support multiline commands
    cmd = replace(replace(cmd, "\r\n" => "; "), "\n" => "; ")
    # tokenize accoring to whitespace / quotes
    qwords = parse_quotes(cmd)
    # tokenzie unquoted tokens according to pkg REPL syntax
    words = lex(qwords)
    # break up words according to ";"(doing this early makes subsequent processing easier)
    word_groups = group_words(words)
    # create statements
    if for_completions
        return _statement(word_groups[end])
    end
    return map(Statement, word_groups)
end

# vector of words -> structured statement
# minimal checking is done in this phase
function Statement(words)::Statement
    is_option(word) = first(word) == '-'
    statement = Statement()

    word = popfirst!(words)
    # meta options
    while is_option(word)
        push!(statement.meta_options, word)
        isempty(words) && pkgerror("no command specified")
        word = popfirst!(words)
    end
    # command
    # special handling for `preview`, just convert it to a meta option under the hood
    if word == "preview"
        if !("--preview" in statement.meta_options)
            push!(statement.meta_options, "--preview")
        end
        isempty(words) && pkgerror("preview requires a command")
        word = popfirst!(words)
    end
    if word in keys(super_specs)
        super = super_specs[word]
        isempty(words) && pkgerror("no subcommand specified")
        word = popfirst!(words)
    else
        super = super_specs["package"]
    end
    command = get(super, word, nothing)
    command !== nothing || pkgerror("expected command. instead got [$word]")
    statement.command = command
    # command arguments
    for word in words
        push!((is_option(word) ? statement.options : statement.arguments), word)
    end
    return statement
end

# break up words according to `;`(doing this early makes subsequent processing easier)
# the final group does not require a trailing `;`
function group_words(words)::Vector{Vector{String}}
    statements = Vector{String}[]
    x = String[]
    for word in words
        if word == ";"
            isempty(x) ? pkgerror("empty statement") : push!(statements, x)
            x = String[]
        else
            push!(x, word)
        end
    end
    isempty(x) || push!(statements, x)
    return statements
end

const lex_re = r"^[\?\./\+\-](?!\-) | ((git|ssh|http(s)?)|(git@[\w\-\.]+))(:(//)?)([\w\.@\:/\-~]+)(\.git)(/)? | [^@\#\s;]+\s*=\s*[^@\#\s;]+ | \#\s*[^@\#\s;]* | @\s*[^@\#\s;]* | [^@\#\s;]+|;"x

function lex(qwords::Vector{QuotedWord})::Vector{String}
    words = String[]
    for qword in qwords
        if qword.isquoted
            push!(words, qword.word)
        else
            append!(words, map(m->m.match, eachmatch(lex_re, qword.word)))
        end
    end
    return words
end

function parse_quotes(cmd::String)::Vector{QuotedWord}
    in_doublequote = false
    in_singlequote = false
    qwords = QuotedWord[]
    token_in_progress = Char[]

    push_token!(is_quoted) = begin
        push!(qwords, QuotedWord(String(token_in_progress), is_quoted))
        empty!(token_in_progress)
    end

    for c in cmd
        if c == '"'
            if in_singlequote # raw char
                push!(token_in_progress, c)
            else # delimiter
                in_doublequote ? push_token!(true) : push_token!(false)
                in_doublequote = !in_doublequote
            end
        elseif c == '\''
            if in_doublequote # raw char
                push!(token_in_progress, c)
            else # delimiter
                in_singlequote ? push_token!(true) : push_token!(false)
                in_singlequote = !in_singlequote
            end
        else
            push!(token_in_progress, c)
        end
    end
    if (in_doublequote || in_singlequote)
        pkgerror("unterminated quote")
    else
        push_token!(false)
    end
    # to avoid complexity in the main loop, empty tokens are allowed above and
    # filtered out before returning
    return filter(x->!isempty(x.word), qwords)
end

##############
# PkgCommand #
##############
const Token = Union{String, VersionRange, Rev}
const ArgToken = Union{VersionRange, Rev}
const PkgToken = Union{String, VersionRange, Rev}
const PkgArguments = Union{Vector{String}, Vector{PackageSpec}}
struct PkgCommand
    meta_options::Vector{Option}
    spec::CommandSpec
    options::Vector{Option}
    arguments::PkgArguments
    PkgCommand() = new([], "", [], [])
    PkgCommand(meta_opts, cmd_name, opts, args) = new(meta_opts, cmd_name, opts, args)
end

const APIOptions = Dict{Symbol, Any}
APIOptions(command::PkgCommand)::Dict{Symbol, Any} =
    APIOptions(command.options, command.spec.option_specs)

function APIOptions(options::Vector{Option},
                    specs::Dict{String, OptionSpec},
                    )::Dict{Symbol, Any}
    keyword_vec = map(options) do opt
        spec = specs[opt.val]
        # opt is switch
        spec.is_switch && return spec.api
        # no opt wrapper -> just use raw argument
        spec.api.second === nothing && return spec.api.first => opt.argument
        # given opt wrapper
        return spec.api.first => spec.api.second(opt.argument)
    end
    return Dict(keyword_vec)
end

function enforce_argument_count(spec::Pair, args::PkgArguments)
    count = length(args)
    spec.first <= count <= spec.second ||
        pkgerror("Wrong number of arguments")
end

# Only for PkgSpec
function package_args(args::Vector{Token}; add_or_dev=false)::Vector{PackageSpec}
    pkgs = PackageSpec[]
    for arg in args
        if arg isa String
            push!(pkgs, parse_package(arg; add_or_develop=add_or_dev))
        elseif arg isa VersionRange
            pkgs[end].version = VersionSpec(arg)
        elseif arg isa Rev
            pkg = pkgs[end]
            if pkg.repo == nothing
                pkg.repo = Types.GitRepo("", arg.rev)
            else
                pkgs[end].repo.rev = arg.rev
            end
        else
            assert(false)
        end
    end
    return pkgs
end

# Only for PkgSpec
function word2token(word::AbstractString)::Token
    if first(word) == '@'
        return VersionRange(word[2:end])
    elseif first(word) == '#'
        return Rev(word[2:end])
    else
        return String(word)
    end
end

# Only for PkgSpec
function enforce_argument_order(args::Vector{Token})
    prev_arg = nothing
    function check_prev_arg(valid_type::DataType, error_message::AbstractString)
        prev_arg isa valid_type || pkgerror(error_message)
    end

    for arg in args
        if arg isa VersionRange
            check_prev_arg(String, "package name/uuid must precede version spec `@$arg`")
        elseif arg isa Rev
            check_prev_arg(String, "package name/uuid must precede rev spec `#$(arg.rev)`")
        end
        prev_arg = arg
    end
end

function parse_pkg(raw_args::Vector{String}; valid=[], add_or_dev=false)
    args::Vector{PkgToken} = map(word2token, raw_args)
    enforce_argument_order(args)
    # enforce spec
    push!(valid, String) # always want at least PkgSpec identifiers
    if !all(x->typeof(x) in valid, args)
        pkgerror("invalid token")
    end
    # convert to final arguments
    return package_args(args; add_or_dev=add_or_dev)
end

function enforce_argument(raw_args::Vector{String}, spec::ArgSpec)::PkgArguments
    args = spec.parser(raw_args; spec.parser_keys...)
    enforce_argument_count(spec.count, args)
    return args
end

function enforce_option(option::String, specs::Dict{String,OptionSpec})::Option
    opt = parse_option(option)
    spec = get(specs, opt.val, nothing)
    spec !== nothing ||
        pkgerror("option '$(opt.val)' is not a valid option")
    if spec.is_switch
        opt.argument === nothing ||
            pkgerror("option '$(opt.val)' does not take an argument, but '$(opt.argument)' given")
    else # option takes an argument
        opt.argument !== nothing ||
            pkgerror("option '$(opt.val)' expects an argument, but no argument given")
    end
    return opt
end

function enforce_meta_options(options::Vector{String}, specs::Dict{String,OptionSpec})::Vector{Option}
    meta_opt_names = keys(specs)
    return map(options) do opt
        tok = enforce_option(opt, specs)
        tok.val in meta_opt_names ||
            pkgerror("option '$opt' is not a valid meta option.")
            #TODO hint that maybe they intended to use it as a command option
        return tok
    end
end

function enforce_opts(options::Vector{String}, specs::Dict{String,OptionSpec})::Vector{Option}
    unique_keys = Symbol[]
    get_key(opt::Option) = specs[opt.val].api.first

    # final parsing
    toks = map(x->enforce_option(x,specs),options)
    # checking
    for opt in toks
        # valid option
        opt.val in keys(specs) ||
            pkgerror("option '$(opt.val)' is not supported")
        # conflicting options
        key = get_key(opt)
        if key in unique_keys
            conflicting = filter(opt->get_key(opt) == key, toks)
            pkgerror("Conflicting options: $conflicting")
        else
            push!(unique_keys, key)
        end
    end
    return toks
end

# this the entry point for the majority of input checks
function PkgCommand(statement::Statement)::PkgCommand
    meta_opts = enforce_meta_options(statement.meta_options,
                                     meta_option_specs)
    args = enforce_argument(statement.arguments,
                            statement.command.argument_spec)
    opts = enforce_opts(statement.options, statement.command.option_specs)
    return PkgCommand(meta_opts, statement.command, opts, args)
end

Context!(ctx::APIOptions)::Context = Types.Context!(collect(ctx))

#############
# Execution #
#############
function do_cmd(repl::REPL.AbstractREPL, input::String; do_rethrow=false)
    try
        statements = parse(input)
        commands = map(PkgCommand, statements)
        for cmd in commands
            do_cmd!(cmd, repl)
        end
    catch err
        if do_rethrow
            rethrow(err)
        end
        if err isa PkgError || err isa ResolverError
            Base.display_error(repl.t.err_stream, ErrorException(sprint(showerror, err)), Ptr{Nothing}[])
        else
            Base.display_error(repl.t.err_stream, err, Base.catch_backtrace())
        end
    end
end

function do_cmd!(command::PkgCommand, repl)
    context = APIOptions(command.meta_options, meta_option_specs)

    # REPL specific commands
    if command.spec.kind == CMD_HELP
        return Base.invokelatest(do_help!, command, repl)
    end

    # API commands
    # TODO is invokelatest still needed?
    api_opts = APIOptions(command)
    if applicable(command.spec.handler, context, command.arguments, api_opts)
        Base.invokelatest(command.spec.handler, context, command.arguments, api_opts)
    else
        Base.invokelatest(command.spec.handler, command.arguments, api_opts)
    end
end

function CommandSpec(command_name::String)::Union{Nothing,CommandSpec}
    # maybe a "package" command
    spec = get(super_specs["package"], command_name, nothing)
    if spec !== nothing
        return spec
    end
    # maybe a "compound command"
    m = match(r"(\w+)-(\w+)", command_name)
    m !== nothing || (return nothing)
    super = get(super_specs, m.captures[1], nothing)
    super !== nothing || (return nothing)
    return get(super, m.captures[2], nothing)
end

function do_help!(command::PkgCommand, repl::REPL.AbstractREPL)
    disp = REPL.REPLDisplay(repl)
    if isempty(command.arguments)
        Base.display(disp, help)
        return
    end
    help_md = md""
    for arg in command.arguments
        spec = CommandSpec(arg)
        if spec === nothing
            pkgerror("'$arg' does not name a command")
        end
        spec.help === nothing &&
            pkgerror("Sorry, I don't have any help for the `$arg` command.")
        isempty(help_md.content) ||
            push!(help_md.content, md"---")
        push!(help_md.content, spec.help)
    end
    Base.display(disp, help_md)
end

# TODO set default Display.status keyword: mode = PKGMODE_COMBINED
do_status!(ctx::APIOptions, args::PkgArguments, api_opts::APIOptions) =
    Display.status(Context!(ctx), get(api_opts, :mode, PKGMODE_COMBINED))

# TODO , test recursive dependencies as on option.
function do_test!(ctx::APIOptions, args::PkgArguments, api_opts::APIOptions)
    foreach(arg -> arg.mode = PKGMODE_MANIFEST, args)
    API.test(Context!(ctx), args; collect(api_opts)...)
end

function do_registry_add!(ctx::APIOptions, args::PkgArguments, api_opts::APIOptions)
    println("This is a dummy function for now")
    println("My args are:")
    for arg in args
        println("- $arg")
    end
end

do_precompile!(ctx::APIOptions, args::PkgArguments, api_opts::APIOptions) =
    API.precompile(Context!(ctx))

do_resolve!(ctx::APIOptions, args::PkgArguments, api_opts::APIOptions) =
    API.resolve(Context!(ctx))

do_gc!(ctx::APIOptions, args::PkgArguments, api_opts::APIOptions) =
    API.gc(Context!(ctx); collect(api_opts)...)

do_instantiate!(ctx::APIOptions, args::PkgArguments, api_opts::APIOptions) =
    API.instantiate(Context!(ctx); collect(api_opts)...)

do_generate!(ctx::APIOptions, args::PkgArguments, api_opts::APIOptions) =
    API.generate(Context!(ctx), args[1])

do_build!(ctx::APIOptions, args::PkgArguments, api_opts::APIOptions) =
    API.build(Context!(ctx), args; collect(api_opts)...)

do_rm!(ctx::APIOptions, args::PkgArguments, api_opts::APIOptions) =
    API.rm(Context!(ctx), args; collect(api_opts)...)

do_free!(ctx::APIOptions, args::PkgArguments, api_opts::APIOptions) =
    API.free(Context!(ctx), args; collect(api_opts)...)

do_up!(ctx::APIOptions, args::PkgArguments, api_opts::APIOptions) =
    API.up(Context!(ctx), args; collect(api_opts)...)

function do_activate!(args::PkgArguments, api_opts::APIOptions)
    if isempty(args)
        return API.activate()
    else
        return API.activate(args[1]; collect(api_opts)...)
    end
end

function do_pin!(ctx::APIOptions, args::PkgArguments, api_opts::APIOptions)
    for arg in args
        # TODO not sure this is correct
        if arg.version.ranges[1].lower != arg.version.ranges[1].upper
            pkgerror("pinning a package requires a single version, not a versionrange")
        end
    end
    API.pin(Context!(ctx), args; collect(api_opts)...)
end

function do_add!(ctx::APIOptions, args::PkgArguments, api_opts::APIOptions)
    api_opts[:mode] = :add
    API.add_or_develop(Context!(ctx), args; collect(api_opts)...)
end

function do_develop!(ctx::APIOptions, args::PkgArguments, api_opts::APIOptions)
    api_opts[:mode] = :develop
    API.add_or_develop(Context!(ctx), args; collect(api_opts)...)
end

######################
# REPL mode creation #
######################

# Provide a string macro pkg"cmd" that can be used in the same way
# as the REPLMode `pkg> cmd`. Useful for testing and in environments
# where we do not have a REPL, e.g. IJulia.
struct MiniREPL <: REPL.AbstractREPL
    display::TextDisplay
    t::REPL.Terminals.TTYTerminal
end
function MiniREPL()
    MiniREPL(TextDisplay(stdout), REPL.Terminals.TTYTerminal(get(ENV, "TERM", Sys.iswindows() ? "" : "dumb"), stdin, stdout, stderr))
end
REPL.REPLDisplay(repl::MiniREPL) = repl.display

const minirepl = Ref{MiniREPL}()

#= __init__() = =# minirepl[] = MiniREPL()

macro pkg_str(str::String)
    :($(do_cmd)(minirepl[], $str; do_rethrow=true))
end

pkgstr(str::String) = do_cmd(minirepl[], str; do_rethrow=true)

# handle completions
mutable struct CompletionCache
    commands::Vector{String}
    canonical_names::Vector{String}
    meta_options::Vector{String}
    options::Dict{CommandKind, Vector{String}}
    subcommands::Dict{String, Vector{String}}
    CompletionCache() = new([],[],[],Dict(),Dict())
end

completion_cache = CompletionCache()

struct PkgCompletionProvider <: LineEdit.CompletionProvider end

function LineEdit.complete_line(c::PkgCompletionProvider, s)
    partial = REPL.beforecursor(s.input_buffer)
    full = LineEdit.input_string(s)
    ret, range, should_complete = completions(full, lastindex(partial))
    return ret, partial[range], should_complete
end

function complete_local_path(s, i1, i2)
    cmp = REPL.REPLCompletions.complete_path(s, i2)
    [REPL.REPLCompletions.completion_text(p) for p in cmp[1]], cmp[2], !isempty(cmp[1])
end

function complete_installed_package(s, i1, i2, project_opt)
    pkgs = project_opt ? API.__installed(PKGMODE_PROJECT) : API.__installed()
    pkgs = sort!(collect(keys(filter((p) -> p[2] != nothing, pkgs))))
    cmp = filter(cmd -> startswith(cmd, s), pkgs)
    return cmp, i1:i2, !isempty(cmp)
end

function complete_remote_package(s, i1, i2)
    cmp = String[]
    julia_version = VERSION
    for reg in Types.registries(;clone_default=false)
        data = Types.read_registry(joinpath(reg, "Registry.toml"))
        for (uuid, pkginfo) in data["packages"]
            name = pkginfo["name"]
            if startswith(name, s)
                compat_data = Operations.load_package_data_raw(
                    VersionSpec, joinpath(reg, pkginfo["path"], "Compat.toml"))
                supported_julia_versions = VersionSpec(VersionRange[])
                for (ver_range, compats) in compat_data
                    for (compat, v) in compats
                        if compat == "julia"
                            union!(supported_julia_versions, VersionSpec(v))
                        end
                    end
                end
                if VERSION in supported_julia_versions
                    push!(cmp, name)
                end
            end
        end
    end
    return cmp, i1:i2, !isempty(cmp)
end

function complete_argument(to_complete, i1, i2, lastcommand, project_opt
                           )::Tuple{Vector{String},UnitRange{Int},Bool}
    if lastcommand == CMD_HELP
        completions = filter(x->startswith(x,to_complete), completion_cache.canonical_names)
        return completions, i1:i2, !isempty(completions)
    elseif lastcommand in [CMD_STATUS, CMD_RM, CMD_UP, CMD_TEST, CMD_BUILD, CMD_FREE, CMD_PIN]
        return complete_installed_package(to_complete, i1, i2, project_opt)
    elseif lastcommand in [CMD_ADD, CMD_DEVELOP]
        if occursin(Base.Filesystem.path_separator_re, to_complete)
            return complete_local_path(to_complete, i1, i2)
        else
            rps = complete_remote_package(to_complete, i1, i2)
            lps = complete_local_path(to_complete, i1, i2)
            return vcat(rps[1], lps[1]), isempty(rps[1]) ? lps[2] : i1:i2, length(rps[1]) + length(lps[1]) > 0
        end
    end
    return String[], 0:-1, false
end

function completions(full, index)::Tuple{Vector{String},UnitRange{Int},Bool}
    pre = full[1:index]
    if isempty(pre)
        return completion_cache.commands, 0:-1, false
    end
    x = parse(pre; for_completions=true)
    if x === nothing # failed parse (invalid command name)
        return String[], 0:-1, false
    end
    (key::Symbol, to_complete::String, spec, proj::Bool) = x
    last = split(pre, ' ', keepempty=true)[end]
    offset = isempty(last) ? index+1 : last.offset+1
    if last != to_complete # require a space before completing next field
        return String[], 0:-1, false
    end
    if key == :arg
        return complete_argument(to_complete, offset, index, spec.kind, proj)
    end
    possible::Vector{String} =
        key == :meta ? completion_cache.meta_options :
        key == :cmd ? completion_cache.commands :
        key == :sub ? completion_cache.subcommands[spec] :
        key == :opt ? completion_cache.options[spec.kind] :
        String[]
    completions = filter(x->startswith(x,to_complete), possible)
    return completions, offset:index, !isempty(completions)
end

prev_project_file = nothing
prev_project_timestamp = nothing
prev_prefix = ""

function promptf()
    global prev_project_timestamp, prev_prefix, prev_project_file
    project_file = try
        Types.find_project_file()
    catch
        nothing
    end
    prefix = ""
    if project_file !== nothing
        if prev_project_file == project_file && prev_project_timestamp == mtime(project_file)
            prefix = prev_prefix
        else
            project = try
                Types.read_project(project_file)
            catch
                nothing
            end
            if project !== nothing
                proj_dir = ispath(project_file) ? realpath(project_file) : project_file
                proj_dir = dirname(proj_dir)
                projname = get(project, "name", nothing)
                if startswith(pwd(), proj_dir) && projname !== nothing
                    name = projname
                else
                    name = basename(proj_dir)
                end
                prefix = string("(", name, ") ")
                prev_prefix = prefix
                prev_project_timestamp = mtime(project_file)
                prev_project_file = project_file
            end
        end
    end
    return prefix * "pkg> "
end

# Set up the repl Pkg REPLMode
function create_mode(repl, main)
    pkg_mode = LineEdit.Prompt(promptf;
        prompt_prefix = Base.text_colors[:blue],
        prompt_suffix = "",
        complete = PkgCompletionProvider(),
        sticky = true)

    pkg_mode.repl = repl
    hp = main.hist
    hp.mode_mapping[:pkg] = pkg_mode
    pkg_mode.hist = hp

    search_prompt, skeymap = LineEdit.setup_search_keymap(hp)
    prefix_prompt, prefix_keymap = LineEdit.setup_prefix_keymap(hp, pkg_mode)

    pkg_mode.on_done = (s, buf, ok) -> begin
        ok || return REPL.transition(s, :abort)
        input = String(take!(buf))
        REPL.reset(repl)
        do_cmd(repl, input)
        REPL.prepare_next(repl)
        REPL.reset_state(s)
        s.current_mode.sticky || REPL.transition(s, main)
    end

    mk = REPL.mode_keymap(main)

    shell_mode = nothing
    for mode in Base.active_repl.interface.modes
        if mode isa LineEdit.Prompt
            mode.prompt == "shell> " && (shell_mode = mode)
        end
    end

    repl_keymap = Dict()
    if shell_mode != nothing
        repl_keymap[';'] = function (s,o...)
            if isempty(s) || position(LineEdit.buffer(s)) == 0
                buf = copy(LineEdit.buffer(s))
                LineEdit.transition(s, shell_mode) do
                    LineEdit.state(s, shell_mode).input_buffer = buf
                end
            else
                LineEdit.edit_insert(s, ';')
            end
        end
    end

    b = Dict{Any,Any}[
        skeymap, repl_keymap, mk, prefix_keymap, LineEdit.history_keymap,
        LineEdit.default_keymap, LineEdit.escape_defaults
    ]
    pkg_mode.keymap_dict = LineEdit.keymap(b)
    return pkg_mode
end

function repl_init(repl)
    main_mode = repl.interface.modes[1]
    pkg_mode = create_mode(repl, main_mode)
    push!(repl.interface.modes, pkg_mode)
    keymap = Dict{Any,Any}(
        ']' => function (s,args...)
            if isempty(s) || position(LineEdit.buffer(s)) == 0
                buf = copy(LineEdit.buffer(s))
                LineEdit.transition(s, pkg_mode) do
                    LineEdit.state(s, pkg_mode).input_buffer = buf
                end
            else
                LineEdit.edit_insert(s, ']')
            end
        end
    )
    main_mode.keymap_dict = LineEdit.keymap_merge(main_mode.keymap_dict, keymap)
    return
end

########
# SPEC #
########
command_declarations = [
#=
["registry"] => CommandDeclaration[
(
    CMD_REGISTRY_ADD,
    ["add"],
    do_registry_add!,
    (1=>Inf, identity, []),
    [],
    "Currently just a placeholder for a future command",
    nothing,
),
], #registry
=#

["package"] => CommandDeclaration[
(   CMD_TEST,
    ["test"],
    do_test!,
    (0=>Inf, parse_pkg, []),
    [
        ("coverage", OPT_SWITCH, :coverage => true),
    ],
    "run tests for packages",
    md"""

    test [opts] pkg[=uuid] ...

    opts: --coverage

Run the tests for package `pkg`. This is done by running the file `test/runtests.jl`
in the package directory. The option `--coverage` can be used to run the tests with
coverage enabled. The `startup.jl` file is disabled during testing unless
julia is started with `--startup-file=yes`.
    """,
),( CMD_HELP,
    ["help", "?"],
    nothing,
    (0=>Inf, identity, []),
    [],
    "show this message",
    md"""

    help

Display this message.

    help cmd ...

Display usage information for commands listed.

Available commands: `help`, `status`, `add`, `rm`, `up`, `preview`, `gc`, `test`, `build`, `free`, `pin`, `develop`.
    """,
),( CMD_INSTANTIATE,
    ["instantiate"],
    do_instantiate!,
    (0=>0, identity, []),
    [
        (["project", "p"], OPT_SWITCH, :manifest => false),
        (["manifest", "m"], OPT_SWITCH, :manifest => true),
    ],
    "downloads all the dependencies for the project",
    md"""
    instantiate
    instantiate [-m|--manifest]
    instantiate [-p|--project]

Download all the dependencies for the current project at the version given by the project's manifest.
If no manifest exists or the `--project` option is given, resolve and download the dependencies compatible with the project.
    """,
),( CMD_RM,
    ["remove", "rm"],
    do_rm!,
    (1=>Inf, parse_pkg, []),
    [
        (["project", "p"], OPT_SWITCH, :mode => PKGMODE_PROJECT),
        (["manifest", "m"], OPT_SWITCH, :mode => PKGMODE_MANIFEST),
    ],
    "remove packages from project or manifest",
    md"""

    rm [-p|--project] pkg[=uuid] ...

Remove package `pkg` from the project file. Since the name `pkg` can only
refer to one package in a project this is unambiguous, but you can specify
a `uuid` anyway, and the command is ignored, with a warning if package name
and UUID do not mactch. When a package is removed from the project file, it
may still remain in the manifest if it is required by some other package in
the project. Project mode operation is the default, so passing `-p` or
`--project` is optional unless it is preceded by the `-m` or `--manifest`
options at some earlier point.

    rm [-m|--manifest] pkg[=uuid] ...

Remove package `pkg` from the manifest file. If the name `pkg` refers to
multiple packages in the manifest, `uuid` disambiguates it. Removing a package
from the manifest forces the removal of all packages that depend on it, as well
as any no-longer-necessary manifest packages due to project package removals.
    """,
),( CMD_ADD,
    ["add"],
    do_add!,
    (1=>Inf, parse_pkg, [:add_or_dev => true, :valid => [VersionRange, Rev]]),
    [],
    "add packages to project",
    md"""

    add pkg[=uuid] [@version] [#rev] ...

Add package `pkg` to the current project file. If `pkg` could refer to
multiple different packages, specifying `uuid` allows you to disambiguate.
`@version` optionally allows specifying which versions of packages. Versions
may be specified by `@1`, `@1.2`, `@1.2.3`, allowing any version with a prefix
that matches, or ranges thereof, such as `@1.2-3.4.5`. A git-revision can be
specified by `#branch` or `#commit`.

If a local path is used as an argument to `add`, the path needs to be a git repository.
The project will then track that git repository just like if it is was tracking a remote repository online.

**Examples**
```
pkg> add Example
pkg> add Example@0.5
pkg> add Example#master
pkg> add Example#c37b675
pkg> add https://github.com/JuliaLang/Example.jl#master
pkg> add git@github.com:JuliaLang/Example.jl.git
pkg> add Example=7876af07-990d-54b4-ab0e-23690620f79a
```
    """,
),( CMD_DEVELOP,
    ["develop", "dev"],
    do_develop!,
    (1=>Inf, parse_pkg, [:add_or_dev => true, :valid => [VersionRange]]),
    [
        ("local", OPT_SWITCH, :shared => false),
        ("shared", OPT_SWITCH, :shared => true),
    ],
    "clone the full package repo locally for development",
    md"""
    develop [--shared|--local] pkg[=uuid] ...

Make a package available for development. If `pkg` is an existing local path that path will be recorded in
the manifest and used. Otherwise, a full git clone of `pkg` at rev `rev` is made. The location of the clone is
controlled by the `--shared` (default) and `--local` arguments. The `--shared` location defaults to
`~/.julia/dev`, but can be controlled with the `JULIA_PKG_DEVDIR` environment variable. When `--local` is given,
the clone is placed in a `dev` folder in the current project.
This operation is undone by `free`.

*Example*
```jl
pkg> develop Example
pkg> develop https://github.com/JuliaLang/Example.jl
pkg> develop ~/mypackages/Example
pkg> develop --local Example
```
    """,
),( CMD_FREE,
    ["free"],
    do_free!,
    (1=>Inf, parse_pkg, []),
    [],
    "undoes a `pin`, `develop`, or stops tracking a repo",
    md"""
    free pkg[=uuid] ...

Free a pinned package `pkg`, which allows it to be upgraded or downgraded again. If the package is checked out (see `help develop`) then this command
makes the package no longer being checked out.
    """,
),( CMD_PIN,
    ["pin"],
    do_pin!,
    (1=>Inf, parse_pkg, [:valid => [VersionRange]]),
    [],
    "pins the version of packages",
    md"""

    pin pkg[=uuid] ...

Pin packages to given versions, or the current version if no version is specified. A pinned package has its version fixed and will not be upgraded or downgraded.
A pinned package has the symbol `⚲` next to its version in the status list.
    """,
),( CMD_BUILD,
    ["build"],
    do_build!,
    (0=>Inf, parse_pkg, []),
    [],
    "run the build script for packages",
    md"""

    build pkg[=uuid] ...

Run the build script in `deps/build.jl` for each package in `pkg` and all of their dependencies in depth-first recursive order.
If no packages are given, runs the build scripts for all packages in the manifest.
The `startup.jl` file is disabled during building unless julia is started with `--startup-file=yes`.
    """,
),( CMD_RESOLVE,
    ["resolve"],
    do_resolve!,
    (0=>0, identity, []),
    [],
    "resolves to update the manifest from changes in dependencies of developed packages",
    md"""
    resolve

Resolve the project i.e. run package resolution and update the Manifest. This is useful in case the dependencies of developed
packages have changed causing the current Manifest to_indices be out of sync.
    """,
),( CMD_ACTIVATE,
    ["activate"],
    do_activate!,
    (0=>1, identity, []),
    [
        ("shared", OPT_SWITCH, :shared => true),
    ],
    "set the primary environment the package manager manipulates",
    md"""
    activate
    activate [--shared] path

Activate the environment at the given `path`, or the home project environment if no `path` is specified.
The active environment is the environment that is modified by executing package commands.
When the option `--shared` is given, `path` will be assumed to be a directory name and searched for in the
`environments` folders of the depots in the depot stack. In case no such environment exists in any of the depots,
it will be placed in the first depot of the stack.
    """ ,
),( CMD_UP,
    ["update", "up"],
    do_up!,
    (0=>Inf, parse_pkg, [:valid => [VersionRange]]),
    [
        (["project", "p"], OPT_SWITCH, :mode => PKGMODE_PROJECT),
        (["manifest", "m"], OPT_SWITCH, :mode => PKGMODE_MANIFEST),
        ("major", OPT_SWITCH, :level => UPLEVEL_MAJOR),
        ("minor", OPT_SWITCH, :level => UPLEVEL_MINOR),
        ("patch", OPT_SWITCH, :level => UPLEVEL_PATCH),
        ("fixed", OPT_SWITCH, :level => UPLEVEL_FIXED),
    ],
    "update packages in manifest",
    md"""

    up [-p|project]  [opts] pkg[=uuid] [@version] ...
    up [-m|manifest] [opts] pkg[=uuid] [@version] ...

    opts: --major | --minor | --patch | --fixed

Update the indicated package within the constraints of the indicated version
specifications. Versions may be specified by `@1`, `@1.2`, `@1.2.3`, allowing
any version with a prefix that matches, or ranges thereof, such as `@1.2-3.4.5`.
In `--project` mode, package specifications only match project packages, while
in `manifest` mode they match any manifest package. Bound level options force
the following packages to be upgraded only within the current major, minor,
patch version; if the `--fixed` upgrade level is given, then the following
packages will not be upgraded at all.
    """,
),( CMD_GENERATE,
    ["generate"],
    do_generate!,
    (1=>1, identity, []),
    [],
    "generate files for a new project",
    md"""

    generate pkgname

Create a project called `pkgname` in the current folder.
    """,
),( CMD_PRECOMPILE,
    ["precompile"],
    do_precompile!,
    (0=>0, identity, []),
    [],
    "precompile all the project dependencies",
    md"""
    precompile

Precompile all the dependencies of the project by running `import` on all of them in a new process.
The `startup.jl` file is disabled during precompilation unless julia is started with `--startup-file=yes`.
    """,
),( CMD_STATUS,
    ["status", "st"],
    do_status!,
    (0=>0, identity, []),
    [
        (["project", "p"], OPT_SWITCH, :mode => PKGMODE_PROJECT),
        (["manifest", "m"], OPT_SWITCH, :mode => PKGMODE_MANIFEST),
    ],
    "summarize contents of and changes to environment",
    md"""

    status
    status [-p|--project]
    status [-m|--manifest]

Show the status of the current environment. By default, the full contents of
the project file is summarized, showing what version each package is on and
how it has changed since the last git commit (if in a git repo), as well as
any changes to manifest packages not already listed. In `--project` mode, the
status of the project file is summarized. In `--manifest` mode the output also
includes the dependencies of explicitly added packages.
    """,
),( CMD_GC,
    ["gc"],
    do_gc!,
    (0=>0, identity, []),
    [],
    "garbage collect packages not used for a significant time",
    md"""

Deletes packages that cannot be reached from any existing environment.
    """,
),( # preview is not a regular command.
    # this is here so that preview appears as a registered command to users
    CMD_PREVIEW,
    ["preview"],
    nothing,
    (1=>Inf, identity, []),
    [],
    "previews a subsequent command without affecting the current state",
    md"""

    preview cmd

Runs the command `cmd` in preview mode. This is defined such that no side effects
will take place i.e. no packages are downloaded and neither the project nor manifest
is modified.
    """,
),
], #package
] #command_declarations

super_specs = SuperSpecs(command_declarations)
# cache things you need for completions
completion_cache.meta_options = sort(map(wrap_option, collect(keys(meta_option_specs))))
completion_cache.commands = sort(append!(collect(keys(super_specs)),
                                         collect(keys(super_specs["package"]))))
let names = String[]
    for (super, specs) in pairs(super_specs)
        super == "package" && continue # skip "package"
        for spec in unique(values(specs))
            push!(names, join([super, spec.canonical_name], "-"))
        end
    end
    for spec in unique(values(super_specs["package"]))
        push!(names, spec.canonical_name)
    end
    completion_cache.canonical_names = names
    sort!(completion_cache.canonical_names)
end
for (k, v) in pairs(super_specs)
    completion_cache.subcommands[k] = sort(collect(keys(v)))
    for spec in values(v)
        completion_cache.options[spec.kind] =
            sort(map(wrap_option, collect(keys(spec.option_specs))))
    end
end
# TODO remove this
command_specs = super_specs["package"]

const help = md"""

**Welcome to the Pkg REPL-mode**. To return to the `julia>` prompt, either press
backspace when the input line is empty or press Ctrl+C.

**Synopsis**

    pkg> cmd [opts] [args]

Multiple commands can be given on the same line by interleaving a `;` between the commands.

**Commands**
"""

for command in completion_cache.canonical_names
    spec = CommandSpec(command)
    push!(help.content, Markdown.parse("`$command`: $(spec.description)"))
end

end #module

max_stars_repo_name: jpsamaroo/julia

max_issues_repo_name: jpsamaroo/julia

max_forks_repo_name: jpsamaroo/julia


text:
using ReinforcementLearning

include("MDPtest.jl")
include("Gymtest.jl")

max_stars_repo_name: ozanarkancan/ReinforcementLearning

max_issues_repo_name: ozanarkancan/ReinforcementLearning

max_forks_repo_name: ozanarkancan/ReinforcementLearning


text:
using Test, OpenEXR, FileIO, Colors

@testset "Roundtrip" begin

rgba = rand(RGBA{Float16}, 256, 512)
fn = File{DataFormat{:EXR}}(tempname())
OpenEXR.save(fn, rgba)
try
    loaded_rgba = OpenEXR.load(fn)
    @test rgba == loaded_rgba
finally
    rm(fn.filename)
end

end


text:
is_u8_digit(u::UInt8) = u > 0x2f && u < 0x3a

# id = targ vector
# cv = char vector
# i = starting index in cv
# imax = max index in cv
# j = starting index in id
# jmax = max index in id
function fill_id!(id::Array{UInt8,1}, cv::Array{UInt8,1}, i::T, i_max::T, j::T, j_max::T) where T<:Integer
  o = one(T)
  while true
    c = getindex(cv, i)
    i = i+o
    i > i_max && break
    c < 0x2f && continue
    setindex!(id, c, j)
    j = j+o
    j > j_max && break
  end
  if j_max < T(15)
    id[j_max+1] = 0x2e
  end
  return i
end

function checkbuf!(buf::Array{UInt8,1}, nx::T1, T::Type) where T1<:Integer
  nb = Int64(nx)*sizeof(T)
  if lastindex(buf) > nb
    resize!(buf, nb)
  end
  return nb
end

function checkbuf!(buf::AbstractArray, nx::T) where {T<:Integer}
  if nx > lastindex(buf)
    resize!(buf, nx)
  end
end

function checkbuf_strict!(buf::AbstractArray, nx::T) where {T<:Integer}
  if nx != lastindex(buf)
    resize!(buf, nx)
  end
end

# ensures length(buf) is divisible by 8...needed to reinterpret as 64-bit type
function checkbuf_8!(buf::Array{UInt8,1}, n::Integer)
  if div(n, 8) == 0
    nx = n
  else
    nx = n + 8 - rem(n,8)
  end
  L = length(buf)
  if nx > L
    resize!(buf, nx)
  else
    r = rem(L, 8)
    if r > 0
      resize!(buf, L + 8 - r)
    end
  end
end

function fillx_i4!(x::AbstractArray, buf::Array{UInt8,1}, nx::Integer, os::Int64)
  j = os; i = 0
  while i < nx
    i += 1
    j += 1
    y = getindex(buf, i)
    x[j] = Int32(y >> 4)
    if i < nx
      j += 1
      x[j] = Int32((y << 4) >> 4)
    end
  end
  return nothing
end

function fillx_i8!(x::AbstractArray, buf::Array{UInt8,1}, nx::Integer, os::Int64)
  j = os; i = 0
  while i < nx
    i += 1
    j += 1
    x[j] = signed(buf[i])
  end
  return nothing
end

function fillx_i16_le!(x::AbstractArray, buf::Array{UInt8,1}, nx::Integer, os::Int64)
  y = zero(UInt16); j = os; i = 0
  while i < nx
    i += 1
    j += 1
    y  = UInt16(buf[2*i-1])
    y |= UInt16(buf[2*i]) << 8
    x[j] = signed(y)
  end
  return nothing
end

function fillx_i16_be!(x::AbstractArray, buf::Array{UInt8,1}, nx::Integer, os::Int64)
  y = zero(UInt16); j = os; i = 0
  while i < nx
    i += 1
    j += 1
    y  = UInt16(buf[2*i-1]) << 8
    y |= UInt16(buf[2*i])
    x[j] = signed(y)
  end
  return nothing
end

function fillx_i24_be!(x::AbstractArray, buf::Array{UInt8,1}, nx::Integer, os::Int64)
  y = zero(UInt32); j = os; i = 0
  while i < nx
    i += 1
    j += 1
    y  = UInt32(buf[3*i-2]) << 24
    y |= UInt32(buf[3*i-1]) << 16
    y |= UInt32(buf[3*i])   << 8
    x[j] = signed(y)        >> 8
  end
end

function fillx_i32_le!(x::AbstractArray, buf::Array{UInt8,1}, nx::Integer, os::Int64)
  y = zero(UInt32); j = os; i = 0
  while i < nx
    i += 1
    j += 1
    y = UInt32(buf[4*i-3])
    y |= UInt32(buf[4*i-2]) << 8
    y |= UInt32(buf[4*i-1]) << 16
    y |= UInt32(buf[4*i])   << 24
    x[j] = signed(y)
  end
  return nothing
end

function fillx_i32_be!(x::AbstractArray, buf::Array{UInt8,1}, nx::Integer, os::Int64)
  y = zero(UInt32); j = os; i = 0
  while i < nx
    i += 1
    j += 1
    y  = UInt32(buf[4*i-3]) << 24
    y |= UInt32(buf[4*i-2]) << 16
    y |= UInt32(buf[4*i-1]) << 8
    y |= UInt32(buf[4*i])
    x[j] = signed(y)
  end
  return nothing
end

function fillx_u32_be!(x::AbstractArray, buf::Array{UInt8,1}, nx::Integer, os::Int64)
  y = zero(UInt32); j = os; i = 0
  while i < nx
    i += 1
    j += 1
    y  = UInt32(buf[4*i-3]) << 24
    y |= UInt32(buf[4*i-2]) << 16
    y |= UInt32(buf[4*i-1]) << 8
    y |= UInt32(buf[4*i])
    x[j] = y
  end
  return nothing
end

function fillx_u32_le!(x::AbstractArray, buf::Array{UInt8,1}, nx::Integer, os::Int64)
  y = zero(UInt32); j = os; i = 0
  while i < nx
    i += 1
    j += 1
    y = UInt32(buf[4*i-3])
    y |= UInt32(buf[4*i-2]) << 8
    y |= UInt32(buf[4*i-1]) << 16
    y |= UInt32(buf[4*i])   << 24
    x[j] = y
  end
  return nothing
end


text:
#-----------------------------------------------------------------------------------
# Analyzer.Function;
#-----------------------------------------------------------------------------------
import ..Analyzer.Function: getstep, is_binding_name, is_special_expr, assertation_args,
                            AnalysisState, analyze, analyze!, analyze_args!

"""
`analyze(ex[, module]) -> PatternRoot`

Parses a julia code expression into a `PatternTree`, which
can be used in matching, destructuring and dispatch.

See [Language.md](../../Language.md) for an overview
on how to construct patterns.

This function also receives an module that will be
used in `evals`, for example in

```julia
analyze(:(:T{MyType}), MyModule)
```

`MyType` will be looked for in `MyModule`.
If no module is specified, `current_module` is used.

"""
analyze;

"""
`analyze!(ex, state::AnalysisState)`

Used by `analyze`, this creates a `PatternTree` from `ex`
and inserts them in the tree in `state`.

"""
analyze!;

"""
`analyze_args!(args, node, state)`

Analyzes the arguments of an expression (`args`) to
create the children of the `PatternNode` `node`.

Calls the slurp optimizations function (`optimize_slurps!`) when done.

"""
analyze_args!;

"""
`getstep(head::Symbol) -> PatternStep`

Get the `PatternStep` that should be used with
expressions of the given `head`.

| head     | step        |
|:---------|:------------|
| `:quote` | `QuoteStep` |
| `:block` | `BlockStep` |
| other    | `ArgsStep`  |

"""
getstep;

"""
`is_special_expr(ex) -> Bool`

Checks if `ex` is of the form `:X{ys...}` or `(*\\?){ys...}` and thus
represents a special pattern.

Related:
- `PatternCheck`

"""
is_special_expr;

"""
`assertation_args(expr_args) -> Vector`

This converts the one argument form of the a pattern
check to the two arguments form.

If `expr_args` is a vector of one element, that means
that a pattern of the form `:X{Y}` was passed, which is equivalent
to `:X{p,Y}` where `p` is a symbol. Adds one symbol to
the `expr_args`.

If `expr_args` is a vector of two elements, than a pattern
like `:X{P,Y}` was passed. Returns `expr_args` as is.

"""
assertation_args;

"""
`AnalysisState` keeps track of the current pattern tree
being matched, whether the literal mode is active or not,
and the module that is to be used in `eval`s.

"""
AnalysisState;

#-----------------------------------------------------------------------------------
# Analyzer.SlurpOptimizations;
#-----------------------------------------------------------------------------------
import ..Analyzer.SlurpOptimizations: optimize_slurps!

"""
`optimize_slurps!(node)`

Starting from the last, replaces the generic slurp algorithms
with faster ones, if possible.

"""
optimize_slurps!;


text: Dict{String, Any}("mismatch1" => Dict{String, Any}("value" => "aaa'''bbb", "type" => "string"), "lit_two_space" => Dict{String, Any}("value" => " ''two quotes'' ", "type" => "string"), "lit_one_space" => Dict{String, Any}("value" => " 'one quote' ", "type" => "string"), "lit_one" => Dict{String, Any}("value" => "'one quote'", "type" => "string"), "two" => Dict{String, Any}("value" => "\"\"two quotes\"\"", "type" => "string"), "one_space" => Dict{String, Any}("value" => " \"one quote\" ", "type" => "string"), "two_space" => Dict{String, Any}("value" => " \"\"two quotes\"\" ", "type" => "string"), "mismatch2" => Dict{String, Any}("value" => "aaa\"\"\"bbb", "type" => "string"), "one" => Dict{String, Any}("value" => "\"one quote\"", "type" => "string"), "lit_two" => Dict{String, Any}("value" => "''two quotes''", "type" => "string"))


text:
using CompScienceMeshes
using BEAST
using LinearAlgebra

trias = CompScienceMeshes.meshrectangle(1.0,1.0,0.05)
trias = CompScienceMeshes.meshcircle(1.0,0.5)
tetrs = CompScienceMeshes.tetmeshcuboid(1,1,1,0.2)

f = BEAST.ScalarTrace(x -> sin(pi*x[1]*x[2])*sin(pi*x[2]))
f2 = BEAST.ScalarTrace(x -> (-x[2]*sin(2*pi*(x[1]^2+x[2]^2))*sin(atan(x[2],x[1])), x[1]*sin(2*pi*(x[1]^2+x[2]^2))*sin(atan(x[2],x[1])), 0) )
g = BEAST.ScalarTrace(x -> (sin(pi*x[1])*cos(pi*x[2]), -cos(pi*x[1])*sin(pi*x[2]), 0 ))
g = BEAST.ScalarTrace(x -> (sin(pi*x[1])*cos(pi*x[2]), sin(pi*x[2])*cos(pi*x[2]), sin(pi*x[3])*cos(pi*x[2])))
g2 = BEAST.ScalarTrace(x -> ( sin(pi*x[1])*cos(pi*x[3]),  0, -cos(pi*x[1])*sin(pi*x[3])))

N = 4

h = Vector(undef, N)
errorL = Vector(undef, N)
errorBDM = Vector(undef, N)
errorRT = Vector(undef, N)

for n in 1:N

    h[n] = 0.5^(n)

    trias2 = CompScienceMeshes.meshrectangle(1.0,1.0, h[n])

    trias = CompScienceMeshes.meshcircle(1.0, h[n], 3)

    Z = BEAST.lagrangec0d1(trias, dirichlet=false)
    Y = BEAST.brezzidouglasmarini(trias)
    X = BEAST.raviartthomas(trias)

    u_exact = DofInterpolate(Z,f)
    b_exact = DofInterpolate(Y,f2)
    r_exact = DofInterpolate(X,f2)

    identity = BEAST.Identity()

    M = assemble(identity, Z, Z)
    b = assemble(f,Z)
    u_n = M \ b

    errorL[n] = sqrt((u_n-u_exact)'*M*(u_n-u_exact))

    M = assemble(identity, Y, Y)
    b = assemble(f2,Y)
    b_n = M \ b

    errorBDM[n] = sqrt((b_n-b_exact)'*M*(b_n-b_exact))

    M = assemble(identity, X, X)
    b = assemble(f2,X)
    r_n = M \ b

    errorRT[n] = sqrt((r_n-r_exact)'*M*(r_n-r_exact))

end

N = 15

delta = Vector(undef, N)
error = Vector(undef, N)

for i in 1:N

    delta[i] = 0.5-0.025*i

    tetrs =  tetmeshcuboid(1.0,1.0,1.0, delta[i])

    bndry = boundary(tetrs)
    faces = skeleton(tetrs, 2)

    bndry_faces = [sort(c) for c in cells(skeleton(bndry, 2))]
    function is_interior(faces)
        !(sort(faces) in bndry_faces)
    end

    interior_faces = submesh(is_interior, faces)

    W = BEAST.brezzidouglasmarini3d(tetrs, interior_faces)

    u_exact = DofInterpolate(W,g2)

    identity = BEAST.Identity()

    M = assemble(identity, W, W)

    b = assemble(g2,W)
    u_n = M \ b

    error[i] = real(sqrt((u_n-u_exact)'*M*(u_n-u_exact)))
end

N=15

delta2 = Vector(undef, N)
error2 = Vector(undef, N)

for i in 1:N

    delta2[i] =0.5-0.025*i

    tetrs =  tetmeshcuboid(1.0,1.0,1.0, delta2[i])

    bndry = boundary(tetrs)
    faces = skeleton(tetrs, 2)

    bndry_faces = [sort(c) for c in cells(skeleton(bndry, 2))]
    function is_interior(faces)
        !(sort(faces) in bndry_faces)
    end

    interior_faces = submesh(is_interior, faces)

    V = BEAST.nedelecd3d(tetrs, interior_faces)

    q_exact = DofInterpolate(V,g)

    identity = BEAST.Identity()

    M2 = assemble(identity, V, V)

    b2 = assemble(g,V)

    q_n = M2 \ b2

    error2[i] = real(sqrt((q_n-q_exact)'*M2*(q_n-q_exact)))
end

using Plots

p2 = plot(delta,error, label="BDM3D", title="L2-Error on [0,1]^3",xlabel="h", ylabel="Error", legend=:bottomright, xaxis=:log, yaxis=:log)
plot!(p2, delta2, error2, label="Nedelec Div", xlims=[0.075,0.55])

p1 = plot(h,real(errorL), label="Lagrange", title="L2-Error on [0,1]^2",xlabel="h", ylabel="Error", legend=:bottomright, xaxis=:log, yaxis=:log, xlims=[0.025,0.5])
plot!(p1,h,real(errorBDM),label="BDM")
plot!(p1,h,real(errorRT),label="Raviart-Thomas")

plot(p1,p2, layout=2)


text:
struct TikzFigure <: AbstractTikzFigure
	axes::Vector{AbstractTikzAxis}
end

function TikzFigure()
	return TikzFigure([EmptyTikzAxis()])
end

function EmptyTikzFigure()
	return TikzFigure()
end


text:
################################################################################
### FUNCTIONS FOR IMPORTING STANDARDIZED MODEL DATA
"""
    A,N,P,D,S,T,L,Sets,Pars = LoadSSCaseData(CaseDataDirectory=pwd())

# Arguments

CaseDataDirectory: file directory for supply chain case study data; defaults to current Julia directory if not specified
CustomLengths=false: (optional keyword arg) set true to use custom arc lengths from file; default, calculate arc lengths from node lon/lat
PrintSpacer="*"^50: (optional keyword arg) some text to be printed to the REPL as a spacer for outputs

# Returns

Data strucutures with inputs prepared for JuMP model
A - arc structure
N - node structure
P - product structure
D - demand data structure
S - supply data structure
T - technology data structure
L - technology site data structure
Sets - additional sets required for JuMP model
Pars - additional parameters required for JuMP model
```
Loads standardized data files for steady-state supply chains

Data files required are:
    arc_data.csv
    node_data.csv
    product_data.csv
    demand_data.csv
    supply_data.csv
    technology_data.csv
    techsite_data.csv

Files must be formatted as .csv (Comma Separated Values) delimited with the VeticalBar character (|). Formatting for each file is as follows, with columns ordered as indicated, containing:
    arc_data.csv:
        1. Arc ID: a unique string ID for the arc, prefereably of the form A01, A02, ...; no spaces allowed!
        2. Arc first node: a Node ID included in node_data.csv
        3. Arc second node: a Node ID included in node_data.csv
        4. Arc capacity: a number representing the capacity of the arc; units (tonne)
        5. Custom length (optional): A number representing the length of the arc; units: (km); used only if the CustomLengths parameter is set true; >=0
    node_data.csv:
        1. Node ID: a unique string ID for the node, prefereably of the form N01, N02, ...; no spaces allowed!
        2. Node Name: a string with detailed information about the node; spaces allowed
        3. Node longitude: A number representing the longitude of the node; e.g. Madison is -89.4012
        4. Node latitude: A number representing the latitude of the node; e.g. Madison is 43.0731
    product_data.csv:
        1. Product ID: a unique string ID for the product, prefereably of the form P01, P02, ...; no spaces allowed!
        2. Product name: a string with detailed information about the product; spaces allowed
        3. Transportation cost: the transport cost for the product; units: (USD.tonne^-1.km^-1); >0
    demand_data.csv:
        1. Demand ID: a unique string ID for the demand, prefereably of the form D01, D02, ...; no spaces allowed!
        2. Node: a Node ID included in node_data.csv
        3. Product: a Product ID included in product_data.csv
        4. Bid: a number representing the consumer bid for a product; units: (USD.tonne^-1); any real number
        5. Capacity: a number representing the maximum amount demanded; units (tonne); >0
        6. Notes: any notes about this demand instance; spaces allowed
    supply_data.csv:
        1. Supply ID: a unique string ID for the supply, prefereably of the form S01, S02, ...; no spaces allowed!
        2. Node: a Node ID included in node_data.csv
        3. Product: a Product ID included in product_data.csv
        4. Bid: a number representing the supplier bid for a product; units: (USD.tonne^-1); any real number
        5. Capacity: a number representing the maximum amount supplied; units (tonne); >0
        6. Notes: any notes about this supply instance; spaces allowed
    technology_data.csv:
        1. Tech ID: a unique string ID for the technology, prefereably of the form M01, M02, ...; no spaces allowed!
        2. Tech Outputs: a comma-delimited list of Product IDs included in product_data.csv; e.g., |P05,P06| (if single product no commas required)
        3. Tech Inputs: a comma-delimited list of Product IDs included in product_data.csv; e.g., |P01,P02,P04| (if single product no commas required)
        4. Output Yield: a comma-delimited list of yield parameters (>0) the same length as "Tech Outputs"; e.g., |0.4,0.3,0.6|
        5. Input Yield: a comma-delimited list of yield parameters (>0) the same length as "Tech Inputs"; e.g., |1.0,0.7,0.6| - one of these MUST be 1.0! see: 6. Reference Product
        6. Reference product: a Product ID included in product_data.csv; this is used as the basis for the technology, and it's yield coefficient in 5. Input Yield MUST be 1.0.
        7. Bid: a number representing the technology bid for a product; units: (USD.tonne^-1 of reference product); >0
        8. Capacity: a number representing the maximum amount of reference product processed; units (tonne); >0
        9. Name: a string with detailed information about the technology; spaces allowed
    techsite_data.csv:
        1. Tech location ID: a unique string ID for the technology, prefereably of the form L01, L02, ...; no spaces allowed!
        2. Tech ID: a Technology ID included in technology_data.csv
        3. Node ID: a Node ID included in node_data.csv

Note on Technologies and TechSites:
    Technologies are defined in technology_data.csv in a general form, and are not mapped onto the supply chain. The technology-node pairs in techsite_data.csv serve this function, allowing multiple copies of a technology to be placed at different nodes without defining them in technology_data.csv; i.e., L01|T01|N01 and L02|T01|N02 creates two "copies" of T01 at nodes N01 and N02, treated as separate entities in the model.
```
"""
function LoadSSCaseData(CaseDataDirectory=pwd(); CustomLengths=false, PrintSpacer="*"^50)
    ################################################################################
    ### DATA IMPORT
    node_data = readdlm(CaseDataDirectory*"/node_data.csv",'|',Any,comments=true)
    arc_data = readdlm(CaseDataDirectory*"/arc_data.csv",'|',Any,comments=true)
    product_data = readdlm(CaseDataDirectory*"/product_data.csv",'|',Any,comments=true)
    demand_data = readdlm(CaseDataDirectory*"/demand_data.csv",'|',Any,comments=true)
    supply_data = readdlm(CaseDataDirectory*"/supply_data.csv",'|',Any,comments=true)
    tech_data = readdlm(CaseDataDirectory*"/technology_data.csv",'|',Any,comments=true)
    techsite_data = readdlm(CaseDataDirectory*"/techsite_data.csv",'|',Any,comments=true)

    ################################################################################
    ### DATA SETUP
    # Node data parsing
    node_id = node_data[:, 1]
    [node_id[i] = string(node_id[i]) for i = 1:length(node_id)]
    node_alias = Dict(zip(node_id, node_data[:, 2])) # node name
    node_lon = Dict(zip(node_id, node_data[:, 3])) # longitude
    node_lat  = Dict(zip(node_id, node_data[:, 4])) # latitude

    # Arc data parsing
    arc_id = arc_data[:, 1]
    [arc_id[a] = string(arc_id[a]) for a = 1:length(arc_id)] # convert to string from substring
    arc_n = Dict(zip(arc_id, arc_data[:, 2])) # first node "n"
    arc_m  = Dict(zip(arc_id, arc_data[:, 3])) # second node "m"
    arc_cap  = Dict(zip(arc_id, arc_data[:, 4])) # arc capacity
    if CustomLengths # Only need to define this data if working with customized arc lengths
        arc_len = Dict(zip(arc_id, arc_data[:, 5])) # arc length; optional for distances
    else
        arc_len = Dict()
        for a in arc_id
            arc_len[a] = GreatCircle(node_lon[arc_n[a]], node_lat[arc_n[a]], node_lon[arc_m[a]], node_lat[arc_m[a]])
        end
    end

    # Extend set of arcs to ensure that both forward and backward arcs exist
    cardA = length(arc_id)
    for a = 1:cardA
        ai = a + cardA # shift index for added arc
        push!(arc_id, "AI"*lpad(a,ndigits(cardA),"0")) # add a label; reuse numbers, use "AI" prefix
        arc_n[arc_id[ai]] = arc_m[arc_id[a]] # flip arc at new position
        arc_m[arc_id[ai]] = arc_n[arc_id[a]] # flip arc at new position
        arc_cap[arc_id[ai]] = arc_cap[arc_id[a]] # same capacity
        arc_len[arc_id[ai]] = arc_len[arc_id[a]] # same length
    end

    # Product data parsing
    product_id = product_data[:, 1]
    [product_id[i] = String(product_id[i]) for i = 1:length(product_id)]
    product_alias = Dict(zip(product_id, product_data[:, 2])) # product name
    product_trspt = Dict(zip(product_id, product_data[:, 3])) # product transport cost

    # Demand data parsing
    dem_id = demand_data[:, 1]
    [dem_id[i] = string(dem_id[i]) for i = 1:length(dem_id)]
    dem_node  = Dict(zip(dem_id, demand_data[:, 2])) # demand node
    dem_prod  = Dict(zip(dem_id, demand_data[:, 3])) # demand product
    dem_bid   = Dict(zip(dem_id, demand_data[:, 4])) # demand bid
    dem_cap   = Dict(zip(dem_id, demand_data[:, 5])) # demand capacity

    # Supply data parsing
    sup_id = supply_data[:, 1]
    [sup_id[i] = string(sup_id[i]) for i = 1:length(sup_id)]
    sup_node  = Dict(zip(sup_id, supply_data[:, 2])) # supply node
    sup_prod  = Dict(zip(sup_id, supply_data[:, 3])) # supply product
    sup_bid   = Dict(zip(sup_id, supply_data[:, 4])) # supply bid
    sup_cap   = Dict(zip(sup_id, supply_data[:, 5])) # supply capacity

    # Technology transformation parsing
    tech_id = tech_data[:, 1]
    [tech_id[i] = string(tech_id[i]) for i = 1:length(tech_id)] # de-substring-ification
    tech_output = TextListFromCSV(tech_id, tech_data[:,2]) # products made
    tech_input = TextListFromCSV(tech_id, tech_data[:, 3]) # product consumed
    tech_output_yields = NumericListFromCSV(tech_id, tech_output, tech_data[:, 4]) # product yield factor
    tech_input_yields = NumericListFromCSV(tech_id, tech_input, tech_data[:, 5]) # reference yield factor
    tech_bid_ref = Dict(zip(tech_id, tech_data[:, 6])) # reference product for bids
    [tech_bid_ref[i] = string(tech_bid_ref[i]) for i in keys(tech_bid_ref)] # de-substring-ification
    tech_bid = Dict(zip(tech_id, tech_data[:, 7])) # bid value; interpreted as operating cost
    tech_cap = Dict(zip(tech_id, tech_data[:, 8])) # capacity value
    tech_defn = Dict(zip(tech_id, tech_data[:, 9])) # technology description

    # Technology site data
    techsite_id = techsite_data[:, 1]
    [techsite_id[i] = String(techsite_id[i]) for i = 1:length(techsite_id)]
    techsite_node = Dict(zip(techsite_id, techsite_data[:, 2])) # technology node
    techsite_tech = Dict(zip(techsite_id, techsite_data[:, 3])) # technology existing at specified site

    ################################################################################
    ### PRIMARY INDICES & ALIASES
    A = arc_id
    N = node_id
    M = node_id
    P = product_id
    Q = product_id
    D = dem_id
    S = sup_id
    T = tech_id
    L = techsite_id

    ################################################################################
    ### SECONDARY INDICES (INTERSECTIONS OF PRIMARY INDICES)

    # Truth table for available arcs; nodes N and M are connected by A
    AnM = Dict() # given node n, provides all nodes m in M connected by an arc a in A
    Ain = Dict() # given node n, provides all arcs a in A directed towards node n
    Aout = Dict() # given node n, provides all arcs a in A directed out of node n
    [AnM[n] = Vector{String}(undef,0) for n in N]
    [Ain[n] = Vector{String}(undef,0) for n in N]
    [Aout[n] = Vector{String}(undef,0) for n in N]
    for a in A
        push!(AnM[arc_n[a]], arc_m[a])
        push!(Ain[arc_m[a]],a)
        push!(Aout[arc_n[a]],a)
    end

    # Mapping set: technology T uses product Q to produce product P
    TPQ = []
    for t in tech_id
        for p in tech_output[t]
            for q in tech_input[t]
                push!(TPQ,(t,p,q)) # (prod,ref) pairs
            end
        end
    end
    TPQ = unique(TPQ) # remove duplicates

    # Mapping set: node n produces product p from technology t (truth table and index list)
    NPT = DictInit([N,P,T],false)
    NPTlist = []
    # Mapping set: node n consumes product q for technology t (truth table and index list)
    NQT = DictInit([N,Q,T],false)
    NQTlist = []
    for ts in techsite_id
        n = techsite_node[ts] # element
        t = techsite_tech[ts] # element
        peas = tech_output[t] # list of elements
        ques = tech_input[t] # list of elements
        for p in peas
            NPT[n,p,t] = true
            push!(NPTlist,(n,p,t))
        end
        for q in ques
            NQT[n,q,t] = true
            push!(NQTlist,(n,q,t))
        end
    end

    # given (n,p) (i.e, an output p in P) provide all associated t in T
    NPt = Dict()
    for n in node_id
        for p in product_id
            teas = [] # empty list of t indices
            for t in tech_id
                if NPT[n,p,t] # is true
                    push!(teas,t)
                end
            end
            NPt[n,p] = teas
        end
    end
    # given (n,q) (i.e, an input q in Q) provide all associated t in T
    NQt = Dict()
    for n in node_id
        for q in product_id
            teas = [] # empty list of t indices
            for t in tech_id
                if NQT[n,q,t] # is true
                    push!(teas,t)
                end
            end
            NQt[n,q] = teas
        end
    end

    ################################################################################
    ### CALCULATED PARAMETERS

    # Maximum demand by node and product
    dMAX = DictInit([N,P],0)
    for n in node_id
        for p in product_id
            for j in dem_id
                if dem_node[j] == n && dem_prod[j] == p
                    dMAX[n,p] += dem_cap[j]
                end
            end
        end
    end

    # Maximum supply by node and product
    sMAX = DictInit([N,P],0)
    for n in node_id
        for p in product_id
            for i in sup_id
                if sup_node[i] == n && sup_prod[i] == p
                    sMAX[n,p] += sup_cap[i]
                end
            end
        end
    end

    # yield of p from q in technology t
    α = Dict()
    for t in tech_id
        for p in tech_output[t] # list of products made by t
            for q in tech_input[t] # list of products consumed by t
                α[t,p,q] = tech_output_yields[t,p]/tech_input_yields[t,q]
            end
        end
    end

    # Maximum production levels
    ξgenMAX = DictInit([T,N,P],0)
    for ts in techsite_id # techsite produces (t,n) pairs
        for p in tech_output[techsite_tech[ts]] # list of output products from t
            ξgenMAX[techsite_tech[ts],techsite_node[ts],p] = tech_output_yields[techsite_tech[ts],p]*tech_cap[techsite_tech[ts]]
        end
    end

    ξconMAX = DictInit([T,N,Q],0)
    for ts in techsite_id # produces (t,n) pairs
        for q in tech_input[techsite_tech[ts]] # a list of product id's
            ξconMAX[techsite_tech[ts],techsite_node[ts],q] = tech_input_yields[techsite_tech[ts],q]*tech_cap[techsite_tech[ts]] # capacity for technology at ts
        end
    end

    # Maximum flow variable f
    fMAX = DictInit([A,P],0)
    for a in A
        for p in P
            fMAX[a,p] = arc_cap[a]
        end
    end

    ################################################################################
    ### SET UP CUSTOM STRUCTURES TO RETURN DATA

    # Structures for primary indices and data
    A = ArcDataStruct(arc_id,arc_n,arc_m,arc_cap,arc_len)
    N = NodeDataStruct(node_id,node_alias,node_lon,node_lat)
    P = ProductDataStruct(product_id,product_alias,product_trspt)
    D = DemandDataStruct(dem_id,dem_node,dem_prod,dem_bid,dem_cap)
    S = SupplyDataStruct(sup_id,sup_node,sup_prod,sup_bid,sup_cap)
    T = TechDataStruct(tech_id,tech_output,tech_input,tech_output_yields,tech_input_yields,tech_bid_ref,tech_bid,tech_cap,tech_defn)
    L = TechsiteDataStruct(techsite_id,techsite_node,techsite_tech)

    # Structures for secondary indices
    Sets = SetStruct(Ain,Aout,TPQ,NQT,NPt,NQt)

    # Structures for calculated parameters
    Pars = ParStruct(dMAX,sMAX,α,ξgenMAX,ξconMAX,fMAX)

    ################################################################################
    ### UPDATE USER
    println(PrintSpacer*"\nData Load Complete\n"*PrintSpacer)

    ################################################################################
    ### RETURN
    return(A,N,P,D,S,T,L,Sets,Pars);
    # e.g.: A,N,P,D,S,T,L,Sets,Pars = LoadSSCaseData("/Users/ptominac/Documents/Code/CoordinatedSupplyChains/TestCases/TestV36");
end


text:
mutable struct State <: AbstractState
  player::AbstractPlayer

  is_playing::Bool
  is_done::Bool

  can_hold::Bool
end

function State(cur_player::AbstractPlayer=Player())
  cur_is_playing = true
  cur_is_done = false
  can_hold = true

  cur_state = State(
    cur_player,
    cur_is_playing,
    cur_is_done,
    can_hold
  )

  cur_player.state = cur_state

  cur_state
end


text:
module Koma

#IMPORT PACKAGES
import Base.*, Base.+, Base.-, Base./, Base.vcat, Base.size,
       Base.copy, Base.Threads.@spawn, Base.Threads.@threads,
       Base.angle, Base.abs, Base.getproperty, Base.one, Base.zero
#General
using Pkg, Random, LinearAlgebra, FFTW, Images, Reexport
#Printing
using Scanf, Printf, ProgressMeter
#Datatypes
using Parameters, ArgCheck
#Simulation
using CUDA, Interpolations, Hwloc
#Reconstruction
using MRIReco
#IO
using FileIO, HDF5, MAT, JLD2
#GUI
using Blink, Interact, PlotlyJS, AssetRegistry
@reexport using PlotlyJS: savefig

global γ = 42.5774688e6; #Hz/T gyromagnetic constant for H1

#Hardware
include("datatypes/Scanner.jl")
#Sequence
include("datatypes/sequence/Grad.jl")
include("datatypes/sequence/RF.jl")
include("datatypes/sequence/ADC.jl")
include("simulation/KeyValuesCalculation.jl")
include("datatypes/Sequence.jl")
include("datatypes/sequence/Delay.jl")
include("sequences/PulseDesigner.jl")
include("io/Pulseq.jl")
#Phantom
include("datatypes/Phantom.jl")
include("io/JEMRIS.jl")
#Reconstruction
include("reconstruction/Recon.jl")
#Simulator
include("datatypes/simulation/Magnetization.jl")
include("simulation/TimeStepCalculation.jl")
include("simulation/other/DiffusionModel.jl")
# include("simulation/other/OffResonanceModel.jl")
include("simulation/TrapezoidalIntegration.jl")
include("simulation/SimulatorCore.jl")
include("io/ISMRMRD.jl")
#UI
include("ui/DisplayFunctions.jl")

#Main
export γ #gyro-magnetic ratio [Hz/T]
export Scanner, Sequence, Phantom
export Grad, RF, ADC, Delay
export Mag, dur
#Pulseq
export read_seq
#ISMRMRD
export rawSignalToISMRMRD
# Phantom
export brain_phantom2D, brain_phantom3D, read_phantom_jemris
#RF-related
export Spinor, Rx, Ry, Rz, Q, Un
#Secondary
export PulseDesigner, get_kspace, rotx, roty, rotz
# Display
export plot_seq, plot_grads_moments, plot_kspace, plot_phantom_map, plot_signal, plot_M0, plot_image
# Simulator
export simulate, simulate_slice_profile
#GUI
!Blink.AtomShell.isinstalled() && Blink.AtomShell.install()
include("KomaUI.jl")

export KomaUI

end


text:
#Illustrate how to set up certain constraint sets that are not found in other examples

using SetIntersectionProjection
using MAT
using LinearAlgebra, SparseArrays
using Statistics
using PyPlot
using JOLI

#define computational grid structure
mutable struct compgrid
  d :: Tuple
  n :: Tuple
end

#select working precision
TF=Float32 #Float32

#load escalator video) (Mablab files for compatibility with matlab only solvers for comparison...)
if isfile("escalator_data.mat") == true
  file     = matopen("escalator_data.mat")
  #file = matopen(joinpath(dirname(pathof(SetIntersectionProjection)), "../examples/Data/escalator_data.mat"))
else
  println("downloading escalator video from http://cvxr.com/tfocs/demos/rpca/escalator_data.mat")
  run(`wget http://cvxr.com/tfocs/demos/rpca/escalator_data.mat`)
  file     = matopen("escalator_data.mat")
  #error("download escalator video from http://cvxr.com/tfocs/demos/rpca/escalator_data.mat")
end

mtrue    = read(file, "X");
n1       = convert(Integer,read(file, "m"));
n2       = convert(Integer,read(file, "n"));
m_mat    = convert(Array{TF,2},mtrue);
m_tensor = convert(Array{TF,3},reshape(mtrue,n1,n2,Integer(200)));

#computational grid for the video
comp_grid  = compgrid((1f0,1f0,1f0),(size(m_tensor,1),size(m_tensor,2), size(m_tensor,3)));

comp_grid_time_slice = compgrid((1f0,1f0),(size(m_tensor,1),size(m_tensor,2)));

######################################################################
######## (anisotropic) total-variation on the time-coordinate ########

#the video has coordinates x-y-time, so the time corresponds to the 3rd coordinate
#which is z in the SetIntersectionProjection system (x-y-z)

#initialize constraints
constraint = Vector{SetIntersectionProjection.set_definitions}()
options = PARSDMM_options()

#l1 (total variation) constraints (in one direction)

#find a reasonable value for the l1-ball
(TD_OP, AtA_diag, dense, TD_n) = get_TD_operator(comp_grid,"D_z",options.FL);
TV_z = norm(TD_OP*vec(m_tensor),1)

m_min     = 0.0
m_max     = 0.025*TV_z
set_type  = "l1"
TD_OP     = "D_z"
app_mode  = ("tensor","")
custom_TD_OP = ([],false)
push!(constraint, set_definitions(set_type,TD_OP,m_min,m_max,app_mode,custom_TD_OP));

(P_sub,TD_OP,set_Prop) = setup_constraints(constraint,comp_grid,options.FL);
(TD_OP,AtA,l,y)        = PARSDMM_precompute_distribute(TD_OP,set_Prop,comp_grid,options);

@time (x,log_PARSDMM) = PARSDMM(vec(m_tensor),AtA,TD_OP,set_Prop,P_sub,comp_grid,options);
@time (x,log_PARSDMM) = PARSDMM(vec(m_tensor),AtA,TD_OP,set_Prop,P_sub,comp_grid,options);

m_proj = reshape(x,comp_grid.n);

figure();
for i=1:comp_grid.n[3]
    subplot(2,1,1);imshow(m_proj[:,:,i],cmap="gray");title("projected");
    subplot(2,1,2);imshow(m_tensor[:,:,i],cmap="gray");title("original");
    pause(0.01)
end

###############################################################################
######## (anisotropic) total-variation on the time-derivative          ########
########                     using JOLI operators                      ########

options = PARSDMM_options();

#TV operator per time-slice
(TV, AtA_diag, dense, TD_n) = get_TD_operator(comp_grid_time_slice,"TV",options.FL);
#time derivative over the time-slices
(D, AtA_diag, dense, TD_n)  =  get_TD_operator(comp_grid,"D_z",options.FL);

CustomOP_explicit_sparse = kron(TV, SparseMatrixCSC{TF}(LinearAlgebra.I, comp_grid.n[3]-1,comp_grid.n[3]-1))* D;

D  = joMatrix(D);
TV = joMatrix(TV);
CustomOP_JOLI = joKron(TV, joEye(comp_grid.n[3]-1,DDT=Float32,RDT=Float32))* D;

## Solve using JOLI ##

    #initialize constraints
    constraint = Vector{SetIntersectionProjection.set_definitions}();

    m_min     = 0.0
    m_max     = 0.1*norm(CustomOP_JOLI*vec(m_tensor),1)
    set_type  = "l1"
    TD_OP     = "identity"
    app_mode  = ("matrix","")
    custom_TD_OP = (CustomOP_JOLI,false)
    push!(constraint, set_definitions(set_type,TD_OP,m_min,m_max,app_mode,custom_TD_OP));

    (P_sub,TD_OP,set_Prop) = setup_constraints(constraint,comp_grid,options.FL);

    #set properties of custom operator
    set_Prop.AtA_diag[1]  = false
    set_Prop.dense[1]     = false
    set_Prop.banded[1]    = true

    (TD_OP,AtA,l,y)        = PARSDMM_precompute_distribute(TD_OP,set_Prop,comp_grid,options);

    @time (x_joli,log_PARSDMM) = PARSDMM(vec(m_tensor),AtA,TD_OP,set_Prop,P_sub,comp_grid,options);
    @time (x_joli,log_PARSDMM) = PARSDMM(vec(m_tensor),AtA,TD_OP,set_Prop,P_sub,comp_grid,options);

## solve using explicit sparse array ##
    constraint = Vector{SetIntersectionProjection.set_definitions}();

    m_min     = 0.0
    m_max     = 0.025*norm(CustomOP_explicit_sparse*vec(m_tensor),1)
    set_type  = "l1"
    TD_OP     = "identity"
    app_mode  = ("matrix","")
    custom_TD_OP = (CustomOP_explicit_sparse,false)
    push!(constraint, set_definitions(set_type,TD_OP,m_min,m_max,app_mode,custom_TD_OP));

    options=PARSDMM_options()
    (P_sub,TD_OP,set_Prop) = setup_constraints(constraint,comp_grid,options.FL);

    #set properties of custom operator
    set_Prop.AtA_diag[1]  = false
    set_Prop.dense[1]     = false
    set_Prop.banded[1]    = true

    (TD_OP,AtA,l,y)        = PARSDMM_precompute_distribute(TD_OP,set_Prop,comp_grid,options);

    @time (x_sp,log_PARSDMM) = PARSDMM(vec(m_tensor),AtA,TD_OP,set_Prop,P_sub,comp_grid,options);
    @time (x_sp,log_PARSDMM) = PARSDMM(vec(m_tensor),AtA,TD_OP,set_Prop,P_sub,comp_grid,options);

    m_proj = reshape(x_sp,comp_grid.n)

figure();
for i=1:comp_grid.n[3]
    subplot(2,1,1);imshow(m_proj[:,:,i],cmap="gray");title("projected");
    subplot(2,1,2);imshow(m_tensor[:,:,i],cmap="gray");title("original");
    pause(0.025)
end

###############################################################################
######## (anisotropic) total-variation on every subtracted time-slice #########

# every  time-slice should have a small TV like this: || TV(slice_2 -slice_1) ||_1 <= sigma, || TV(slice_3 -slice_1) ||_1 <= sigma, ...

#initialize constraints
# constraint = Vector{SetIntersectionProjection.set_definitions}()

# slice_ref = copy(m_tensor[:,:,1])

# m_shifted = similar(m_tensor)
# for i=1:comp_grid.n[3]
#     m_shifted[:,:,i] = m_tensor[:,:,i] - slice_ref
# end

# #find a reasonable value for the l1-ball
# (TD_OP, AtA_diag, dense, TD_n) = get_TD_operator(comp_grid,"D_z",options.FL)
# TV_z = norm(TD_OP*vec(m_shifted),1)

# m_min     = 0.0
# m_max     = 0.1*TV_z
# set_type  = "l1"
# TD_OP     = "TV"
# app_mode  = ("matrix","")
# custom_TD_OP = ([],false)
# push!(constraint, set_definitions(set_type,TD_OP,m_min,m_max,app_mode,custom_TD_OP))

# options=PARSDMM_options()
# (P_sub,TD_OP,set_Prop) = setup_constraints(constraint,comp_grid,options.FL)
# (TD_OP,AtA,l,y)        = PARSDMM_precompute_distribute(TD_OP,set_Prop,comp_grid,options)

# @time (x,log_PARSDMM) = PARSDMM(vec(m_shifted),AtA,TD_OP,set_Prop,P_sub,comp_grid,options);
# @time (x,log_PARSDMM) = PARSDMM(vec(m_shifted),AtA,TD_OP,set_Prop,P_sub,comp_grid,options);

# m_proj = reshape(x,comp_grid.n)
# for i=1:comp_grid.n[3]
#     m_proj[:,:,i] = m_proj[:,:,i] + slice_ref
# end

# figure();
# for i=1:comp_grid.n[3]
#     subplot(2,1,1);imshow(m_proj[:,:,i],cmap="gray");title("projected");
#     subplot(2,1,2);imshow(m_tensor[:,:,i],cmap="gray");title("original");
#     pause(0.025)
# end

###############################################################################
########################## custom dense JOLI matrix  ##########################

x = randn(Float32,10,20)
comp_grid  = compgrid((1f0,1f0),(10,20))

constraint = Vector{SetIntersectionProjection.set_definitions}()

options=PARSDMM_options()

#l1:
m_min     = 0.0f0
m_max     = 10.0f0
set_type  = "l1"
TD_OP     = "identity"
app_mode  = ("matrix","")
custom_TD_OP = (joMatrix(randn(options.FL, 133,prod(comp_grid.n))),false)
push!(constraint, set_definitions(set_type,TD_OP,m_min,m_max,app_mode,custom_TD_OP))

(P_sub,TD_OP,set_Prop) = setup_constraints(constraint,comp_grid,options.FL)

#set properties of custom operator
set_Prop.AtA_diag[1]  = false
set_Prop.dense[1]     = true
set_Prop.banded[1]    = false

options.adjust_rho             = false
options.adjust_feasibility_rho = false

(TD_OP,AtA,l,y)        = PARSDMM_precompute_distribute(TD_OP,set_Prop,comp_grid,options)

@time (x_proj,log_PARSDMM) = PARSDMM(vec(x),AtA,TD_OP,set_Prop,P_sub,comp_grid,options);
@time (x_proj,log_PARSDMM) = PARSDMM(vec(x),AtA,TD_OP,set_Prop,P_sub,comp_grid,options);

max_stars_repo_name: slimgroup/SetIntersectionProjection

max_issues_repo_name: slimgroup/SetIntersectionProjection

max_forks_repo_name: slimgroup/SetIntersectionProjection


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

module SubFoo2
thissub = "Foo1.SubFoo2"
end

max_stars_repo_name: TimoLarson/julia

max_issues_repo_name: TimoLarson/julia

max_forks_repo_name: TimoLarson/julia


text:
println("Compile libraries on main processor..."); flush(stdout)
include("imports.jl")
println("Finished loading libraries."); flush(stdout)

using Distributed
rmprocs(workers())
addprocs(20)
println("Compile libraries on workers ..."); flush(stdout)
@everywhere include("imports.jl")
println("Finished loading libraries."); flush(stdout)

markers = [:CD3z, :EOMES, :Perforin, :Granzyme_A, :Siglec7, :LAG3, :CD56, :CD57]
println("markers: ", markers); flush(stdout)

Ks = collect(2:9)
println("K: ", Ks)

skewtmix = [true, false]
println("skewtmix: ", skewtmix); flush(stdout)

# Jobs
sims = dict_list(Dict(:K => Ks, :marker => markers, :skewtmix => skewtmix))

# Combine results, plot DIC.
println("Compute DIC ..."); flush(stdout)
foreach(marker -> plot_dic(marker, Ks, skewtmix), markers)
foreach(marker -> plot_dic(marker, Ks, skewtmix, calibrate=true), markers)

# Send results to S3
println("Send results to s3 ..."); flush(stdout)
Util.s3sync(from="$(Info.resultsdir_datastudy)/$(simname)",
            to="$(Info.awsbucket_datastudy)/$(simname)",
            tags=`--exclude '*.nfs'`)


text:
function grashof(p)
    g = zeros(4)
    g[1] = p[1]+p[2]-p[3]-p[4]
    g[2] = p[2]-p[3]
    g[3] = p[3]-p[4]
    g[4] = p[4]-p[1]

    for i = 1:4
        g[i] <= 0.0 && (g[i] = 0.0)
    end

    return g
end

function sequence(θ2)
    n = length(θ2)
    offset = θ2[1]

    nvector = zeros(n)
    nvector[1]=0

    if π <= offset <= 2π
        for i=2:n
            if  π <= θ2[i] <= 2π
               nvector[i] = θ2[i] - offset
            else
               nvector[i] = θ2[i] + offset
            end
        end
    else
        for i=2:n
            nvector[i] = θ2[i]-offset
        end
    end

    g = zeros(n)

    for i = 1:n-1
        gi = nvector[i] - nvector[i+1]

        gi >= 0 && ( g[i] = gi )
    end

    nvector[n] >= 2π && ( g[n] = 2π )

    return g
end

function fourBarKinetics(p::Vector{Float64}, θ2_values::Vector{Float64})
    # p = [r1, r2, r3, r4, rcx, rcy θ0, x0, y0]
    # θ2_values = [θ21, ..., θ2n]
    θ1 = 0.0

    C = zeros( length(θ2_values),2 )

    i = 1
    for θ2 = θ2_values
        A1 = 2p[3] * (p[2] * cos(θ2) - p[1]*cos(θ1))
        B1 = 2p[3] * (p[2] * sin(θ2) - p[1]*sin(θ1))
        C1 = p[1]^2+p[2]^2+p[3]^2-p[4]^2- 2p[1]*p[2]*cos(θ2-θ1)

        Dis = B1^2+A1^2-C1^2

        if Dis < 0
            continue
        end

        θ3 = 2atan((-B1+sqrt(Dis))/(C1-A1))

        Cxr = p[2]*cos(θ2)+p[5]*cos(θ3)-p[6]*sin(θ3)
        Cyr = p[2]*sin(θ2)+p[5]*sin(θ3)+p[6]*cos(θ3)

        C[i,1] = Cxr*cos(p[7])-Cyr*sin(p[7])+p[8]
        C[i,2] = Cxr*sin(p[7])+Cyr*cos(p[7])+p[9]

        i += 1
    end

    # coupler
    return C
end

function quadraticError(X::Matrix{Float64}, Y::Matrix{Float64})
    # length(X) == length(Y)

    return sum( (X - Y) .^ 2 )
end

function objectiveFunction(p, precisionPts)
    # coupler values
    C = fourBarKinetics(p[1:9], p[10:end])

    # error
    return quadraticError(C, precisionPts)
end

function contraints(p)
    if length(p) < 10
        return grashof(p)
    end
    return [grashof(p); sequence(p[10:end])]
end

# no synchronized strategy
function getOptimizationProblem(precisionPts::Matrix{Float64})
    return 9 + size(precisionPts,1), # search space dimension
           p -> objectiveFunction(p, precisionPts),
           p -> contraints(p)
end

# not synchronized strategy pair points
function getOptimizationProblem(precisionPts::Matrix{Float64}, precisionPts2::Matrix{Float64})
    return 9 + size(precisionPts,1), # search space dimension
           p -> objectiveFunction(p, precisionPts) + objectiveFunction(p, precisionPts2),
           p -> 2contraints(p)
end

# synchronized strategy
function getOptimizationProblem(precisionPts::Matrix{Float64}, X0::Vector{Float64}, θ2::Vector{Float64})
    # X0 = [θ0, x0, y0]
    return 6, # search space dimension
           p -> objectiveFunction([p; X0; θ2], precisionPts),
           p -> contraints(p)
end


text:
tests = ["test-non-parametric",
         "test-parametric",
         "test-utils",
         "test-dependencies"]

for t in tests
    test_file = "$(t).jl"
    include(test_file)
end


text:
"""
Maps v to every possible coalition P(G)
"""
function valuemapping(G::Game)
    ((S, G.v(S)) for S in subsets(G.N))
end

"""
Breadth first test of convexity of a game,

For all S, T ∈ P(G):
v(S ∪ T) + v(S ∩ T) ≥ v(S) + v(T)
"""
function isconvex(G::Game)
    v = G.v
    coalitions = subsets(G.N)

    for (i, T) in enumerate(coalitions)
        for S in drop(coalitions, i)
            if v(S ∪ T) + v(S ∩ T) < v(S) + v(T)
                return false
        end
    end
    end

    return true
end