text:
"""
    set_root!(tree::BnBTree, node_info::NamedTuple)

Set the root node information based on the `node_info` which needs to include the same fields as the `Node` struct given
to the [`initialize`](@ref) method. (Besides the `std` field which is set by Bonobo automatically)

# Example
If your node structure is the following:
```julia
mutable struct MIPNode <: AbstractNode
    std :: BnBNode
    lbs :: Vector{Float64}
    ubs :: Vector{Float64}
    status :: MOI.TerminationStatusCode
end
```

then you can call the function with this syntax:

```julia
Bonobo.set_root!(tree, (
    lbs = fill(-Inf, length(x)),
    ubs = fill(Inf, length(x)),
    status = MOI.OPTIMIZE_NOT_CALLED
))
```
"""
function set_root!(tree::BnBTree, node_info::NamedTuple)
    add_node!(tree, node_info)
end

"""
    add_node!(tree::BnBTree{Node}, node_info::NamedTuple)

Add a new node to the tree using the `node_info`. For information on that see [`set_root!`](@ref).
"""
function add_node!(tree::BnBTree{Node}, node_info::NamedTuple) where Node <: AbstractNode
    node_id = tree.num_nodes + 1
    node = create_node(Node, node_id, node_info)
    tree.nodes[node_id] = node
    tree.num_nodes += 1
end

"""
    create_node(Node, node_id::Int, node_info::NamedTuple)

Creates a node of type `Node` with id `node_id` and the named tuple `node_info`.
For information on that see [`set_root!`](@ref).
"""
function create_node(Node, node_id::Int, node_info::NamedTuple)
    bnb_node = structfromnt(BnBNode, (id = node_id, lb = -Inf, ub = Inf))
    bnb_nt = (std = bnb_node,)
    node_nt = merge(bnb_nt, node_info)
    return structfromnt(Node, node_nt)
end

"""
    get_next_node(tree::BnBTree, travese_strategy::BFS)

Get the next node of the tree which shall be evaluted next by [`evaluate_node!`](@ref).
If you want to implement your own traversing strategy check out [`AbstractTraverseStrategy`](@ref).
"""
function get_next_node(tree::BnBTree, travese_strategy::BFS)
    _, node = peek(tree.nodes)
    return node
end

function Base.isless(n1::AbstractNode, n2::AbstractNode)
    if n1.lb < n2.lb
        return true
    elseif n1.lb == n2.lb
        return n1.id < n2.id
    end
    return false
end

"""
    evaluate_node!(tree, node)

Evaluate the current node and return the lower and upper bound of that node.
"""
function evaluate_node! end


text:
#####
##### Computing board statistics
#####

# All state statistics and are expressed with respect to the current player

@kwdef mutable struct StateActionStats
  P     :: Option{Float64} = nothing  # Probability given by `think`
  Pmem  :: Option{Float64} = nothing  # Recorded π component in memory
  Pmcts :: Option{Float64} = nothing  # Percentage of MCTS visits
  Qmcts :: Option{Float64} = nothing
  UCT   :: Option{Float64} = nothing
  Pnet  :: Option{Float64} = nothing
  Qnet  :: Option{Float64} = nothing
end

mutable struct StateStats{Action}
  Nmcts :: Option{Int}
  Nmem  :: Option{Int}
  Vmem  :: Option{Float64}
  Vnet  :: Option{Float64}
  actions :: Vector{Tuple{Action, StateActionStats}} # Sorted by P
  function StateStats(actions)
    actions_stats = [(a, StateActionStats()) for a in actions]
    new{eltype(actions)}(nothing, nothing, nothing, nothing, actions_stats)
  end
end

player_reward(state, white_reward) =
  GI.white_playing(state) ? r : GI.symmetric_reward(white_reward)

function evaluate_vnet(oracle::MCTS.Oracle, state)
  r = GI.white_reward(state)
  if !isnothing(r)
    return player_reward(state, r)
  else
    cboard = GI.canonical_board(state)
    return MCTS.evaluate(oracle, cboard)[2]
  end
end

function evaluate_qnet(oracle::MCTS.Oracle, state, action)
  @assert !GI.game_terminated(state)
  next = copy(state)
  GI.play!(next, action)
  q = evaluate_vnet(oracle, next)
  pswitch = (GI.white_playing(state) != GI.white_playing(next))
  return pswitch ? GI.symmetric_reward(q) : q
end

player_oracle(p) = nothing
player_oracle(p::MctsPlayer) = p.mcts.oracle
player_oracle(p::NetworkPlayer) = p.network

function state_statistics(state, player, memory=nothing)
  @assert !GI.game_terminated(state)
  cboard = GI.canonical_board(state)
  # Make the player think
  actions, π = think(player, state, exp.turn)
  report = StateStats(actions)
  for i in eachindex(actions)
    report.actions[i][2].P = π[i]
  end
  # Collect MCTS Statistics
  if isa(player, MctsPlayer) && haskey(player.mcts.tree, cboard)
    mcts = player.mcts
    info = mcts.tree[cboard]
    ucts = MCTS.uct_scores(info, mcts.cpuct, 0., nothing)
    report.Nmcts = MCTS.Ntot(info)
    for (i, a) in enumerate(actions)
      astats = info.stats[i]
      arep = report.actions[i][2]
      arep.Pmcts = astats.N / max(1, report.Nmcts)
      arep.Qmcts = astats.N > 0 ? astats.W / astats.N : 0.
      arep.UCT = ucts[i]
    end
  end
  # Collect memory statistics
  if !isnothing(memory)
    mem = merge_by_board(get(memory))
    relevant = findall((ex -> ex.b == cboard), mem)
    if !isempty(relevant)
      @assert length(relevant) == 1
      e = mem[relevant[1]]
      report.Nmem = e.n
      report.Vmem = e.z
      for i in eachindex(actions)
        report.actions[i][2].Pmem = e.π[i]
      end
    end
  end
  # Collect network statistics
  oracle = player_oracle(player)
  if isa(oracle, AbstractNetwork)
    Pnet, Vnet = MCTS.evaluate(oracle, cboard)
    report.Vnet = Vnet
    for i in eachindex(actions)
      arep = report.actions[i][2]
      arep.Pnet = Pnet[i]
      arep.Qnet = evaluate_qnet(oracle, state, actions[i])
    end
  end
  # Sort the actions from best to worst
  sortby = ((a, arep),) -> arep.P
  sort!(report.actions, rev=true, by=sortby)
  return report
end

#####
##### Displaying board statistics
#####

function print_state_statistics(::Type{G}, stats::StateStats) where G
  prob   = Log.ColType(nothing, x -> fmt(".1f", 100 * x) * "%")
  val    = Log.ColType(nothing, x -> fmt("+.2f", x))
  bigint = Log.ColType(nothing, n -> format(ceil(Int, n), commas=true))
  alabel = Log.ColType(nothing, identity)
  btable = Log.Table([
    ("Nmcts", bigint, r -> r.Nmcts),
    ("Nmem",  bigint, r -> r.Nmem),
    ("Vmem",  val,    r -> r.Vmem),
    ("Vnet",  val,    r -> r.Vnet)],
    header_style=Log.BOLD)
  atable = Log.Table([
    ("",      alabel, r -> GI.action_string(G, r[1])),
    ("",      prob,   r -> r[2].P),
    ("Pmcts", prob,   r -> r[2].Pmcts),
    ("Pnet",  prob,   r -> r[2].Pnet),
    ("UCT",   val,    r -> r[2].UCT),
    ("Pmem",  prob,   r -> r[2].Pmem),
    ("Qmcts", val,    r -> r[2].Qmcts),
    ("Qnet",  val,    r -> r[2].Qnet)],
    header_style=Log.BOLD)
  logger = Logger()
  if !all(isnothing, [stats.Nmcts, stats.Nmem, stats.Vmem, stats.Vnet])
    Log.table(logger, btable, [stats])
    Log.sep(logger)
  end
  Log.table(logger, atable, stats.actions)
  Log.sep(logger)
end

# Return the stats
function compute_and_print_state_statistics(exp)
  if !GI.game_terminated(exp.state)
    stats = state_statistics(exp.state, exp.player, exp.memory)
    print_state_statistics(GameType(exp), stats)
    return stats
  else
    return nothing
  end
end

#####
##### Interactive exploration of the environment
#####

mutable struct Explorer{Game}
  state :: Game
  history :: Stack{Game}
  player :: AbstractPlayer{Game}
  memory :: Option{MemoryBuffer}
  turn :: Int
  function Explorer(player::AbstractPlayer, state=nothing; memory=nothing)
    Game = GameType(player)
    isnothing(state) && (state = Game())
    history = Stack{Game}()
    new{Game}(state, history, player, memory, 0)
  end
end

GameType(::Explorer{Game}) where Game = Game

function Explorer(env::Env, state=nothing; arena_mode=false)
  Game = GameType(env)
  isnothing(state) && (state = Game())
  mcts_params = arena_mode ? env.params.arena.mcts : env.params.self_play.mcts
  player = MctsPlayer(env.bestnn, mcts_params)
  return Explorer(player, state, memory=env.memory)
end

function restart!(exp::Explorer)
  reset_player!(exp.player)
  empty!(exp.history)
  exp.state = GameType(exp)()
  exp.turn = 0
end

save_state!(exp::Explorer) = push!(exp.history, copy(exp.state))

# Return true if the command is valid, false otherwise
function interpret!(exp::Explorer, stats, cmd, args=[])
  if cmd == "go"
    st = GI.read_state(GameType(exp))
    if !isnothing(st)
      save_state!(exp)
      exp.state = st
      exp.turn = 0
      return true
    end
  elseif cmd == "restart"
    restart!(exp)
    return true
  elseif cmd == "undo"
    if !isempty(exp.history)
      exp.state = pop!(exp.history)
      exp.turn -= 1
      return true
    end
  elseif cmd == "do"
    GI.game_terminated(exp.state) && return false
    if length(args) == 0
      a = stats.actions[1][1]
    else
      length(args) == 1 || return false
      a = GI.parse_action(exp.state, args[1])
      isnothing(a) && return false
      a ∈ GI.available_actions(exp.state) || return false
    end
    save_state!(exp)
    GI.play!(exp.state, a)
    exp.turn += 1
    return true
  elseif cmd == "flip"
    save_state!(exp)
    exp.state = GI.random_symmetric_state(exp.state)
    return true
  elseif cmd == "explore"
    isa(exp.player, MctsPlayer) || (return false)
    try
      if isempty(args)
        n = exp.player.niters
      else
        n = parse(Int, args[1])
      end
      MCTS.explore!(exp.player.mcts, exp.state, n)
      return true
    catch e
      isa(e, ArgumentError) && (return false)
      rethrow(e)
    end
  end
  return false
end

function explore(exp::Explorer)
  while true
    # Print the state
    GI.print_state(exp.state)
    stats = compute_and_print_state_statistics(exp)
    # Interpret command
    while true
      print("> ")
      inp = readline() |> lowercase |> split
      isempty(inp) && return
      cmd = inp[1]
      args = inp[2:end]
      interpret!(exp, stats, cmd, args) && break
    end
    println("")
  end
end

ext: jl


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

using DocStringExtensions
using DiffEqBase, Distributions, MacroTools
using RecursiveArrayTools, ModelingToolkit
using Parameters, Distributions, Optim, Requires
using Distances, DocStringExtensions, Random, StanSample

STANDARD_PROB_GENERATOR(prob,p) = remake(prob;u0=eltype(p).(prob.u0),p=p)
STANDARD_PROB_GENERATOR(prob::EnsembleProblem,p) = EnsembleProblem(remake(prob.prob;u0=eltype(p).(prob.prob.u0),p=p))

include("stan_string.jl")
include("stan_inference.jl")

export stan_inference
end # module


text:
#!/usr/bin/env julia

import GR

srand(0)
xd = -2 + 4 * rand(100)
yd = -2 + 4 * rand(100)
zd = [xd[i] * exp(-xd[i]^2 - yd[i]^2) for i = 1:100]

GR.setviewport(0.1, 0.95, 0.1, 0.95)
GR.setwindow(-2, 2, -2, 2)
GR.setspace(-0.5, 0.5, 0, 90)
GR.setmarkersize(1)
GR.setmarkertype(GR.MARKERTYPE_SOLID_CIRCLE)
GR.setcharheight(0.024)
GR.settextalign(2, 0)
GR.settextfontprec(3, 0)

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

GR.updatews()


text:
function classicaltoys()

	ds=18   #6 days, 3 observed states
	da=2
	dtheta=2

	pi1=kron(hcat(zeros(6),eye(6,5)),rsm(3,3))
	pi2=kron(hcat(zeros(6),eye(6,5)),rsm(3,3))
	pi1[16,:]=[vec(RustModels.rsm(1,8));zeros(10)]
	pi1[17,:]=pi1[16,:]
	pi1[18,:]=pi1[16,:]
	pi2[16,:]=pi1[16,:]
	pi2[17,:]=pi1[16,:]
	pi2[18,:]=pi1[16,:]
	pis=[pi::Array{Float64,2} for pi=(pi1,pi2)]
	spis=[sparse(pi)::SparseMatrixCSC{Float64,Int} for pi=(pi1,pi2)]

	dk=ds

	beta=.95
	mu=fill(1/ds*da,ds,da)

	aa=rand(dk,da,dtheta)
	bb=zeros(dk,da)
	dirm=rustmodel(aa,bb,mu,beta,pis)
	sirm=rustmodel(aa,bb,mu,beta,spis)
	dfrm=rustmodel(aa,bb,mu,beta,spis,16)
	sfrm=rustmodel(aa,bb,mu,beta,spis,16)

	dirm,sirm,dfrm,sfrm
end


text:
module TestKeys

using Test
using LazyGroupBy

@testset begin
    @test keys.(grouped(isodd, [0, 7, 3, 1, 5, 9, 4, 3, 0, 5])) ==
          Dict(false => [1, 7, 9], true => [2, 3, 4, 5, 6, 8, 10])
end

end  # module


text:
@testset "Events/Attribute List Declarations" begin
    evaluate(s) = collect(E.events(L.State(IOBuffer(s))))

    E = Events
    L = Events.Lexical

    DC = Events.DataContent
    ME = Events.MarkupError

    ADs = Events.AttributeDeclarations
    AD  = Events.AttributeDeclaration

    CData    = Events.StringType
    Entity   = Events.EntityType
    Entities = Events.EntitiesType
    IDRef    = Events.IDRefType
    IDRefs   = Events.IDRefsType
    ID       = Events.IDType
    NmToken  = Events.NameTokenType
    NmTokens = Events.NameTokensType

    Enumerated = Events.EnumeratedType
    Notations  = Events.EnumeratedNotationType

    DefaultValue = Events.DefaultValue
    Implied      = Events.Implied
    Required     = Events.Required

    @testset "Events/Attribute List Declarations (Positive ... empty attribute list)" begin
        # Verify that the element name is handled properly.
        #
        @test evaluate("<!ATTLIST e>")   == [ ADs(false, "e", [ ], L.Location("a buffer", -1)) ]
        @test evaluate("<!ATTLIST efg>") == [ ADs(false, "efg", [ ], L.Location("a buffer", -1)) ]
        @test evaluate("<!ATTLIST é>")   == [ ADs(false, "é", [ ], L.Location("a buffer", -1)) ]
        @test evaluate("<!ATTLIST eé>")  == [ ADs(false, "eé", [ ], L.Location("a buffer", -1)) ]
        @test evaluate("<!ATTLIST éf>")  == [ ADs(false, "éf", [ ], L.Location("a buffer", -1)) ]
        @test evaluate("<!ATTLIST eéf>") == [ ADs(false, "eéf", [ ], L.Location("a buffer", -1)) ]

        # Make sure that if there are no attribute declarations, the white space following the element name is properly
        # discarded.
        #
        @test evaluate("<!ATTLIST e >")       == [ ADs(false, "e", [ ], L.Location("a buffer", -1)) ]
        @test evaluate("<!ATTLIST e\n>")      == [ ADs(false, "e", [ ], L.Location("a buffer", -1)) ]
        @test evaluate("<!ATTLIST e\t>")      == [ ADs(false, "e", [ ], L.Location("a buffer", -1)) ]
        @test evaluate("<!ATTLIST e\t\n >")   == [ ADs(false, "e", [ ], L.Location("a buffer", -1)) ]
    end

    @testset "Events/Attribute List Declarations, CDATA (Positive)" begin
        # Verify that the element name is handled properly. Just use a CDATA/#REQUIRED model.
        #
        events = evaluate("<!ATTLIST e a CDATA #REQUIRED>")
        @test events == [ ADs(false, "e", [ AD(false, "a", CData(), Required()) ], L.Location("a buffer", -1)) ]

        events = evaluate("<!ATTLIST e abc CDATA #REQUIRED>")
        @test events == [ ADs(false, "e", [ AD(false, "abc", CData(), Required()) ], L.Location("a buffer", -1)) ]

        events = evaluate("<!ATTLIST e é CDATA #REQUIRED>")
        @test events == [ ADs(false, "e", [ AD(false, "é", CData(), Required()) ], L.Location("a buffer", -1)) ]

        events = evaluate("<!ATTLIST e aé CDATA #REQUIRED>")
        @test events == [ ADs(false, "e", [ AD(false, "aé", CData(), Required()) ], L.Location("a buffer", -1)) ]

        events = evaluate("<!ATTLIST e éb CDATA #REQUIRED>")
        @test events == [ ADs(false, "e", [ AD(false, "éb", CData(), Required()) ], L.Location("a buffer", -1)) ]

        events = evaluate("<!ATTLIST e aéb CDATA #REQUIRED>")
        @test events == [ ADs(false, "e", [ AD(false, "aéb", CData(), Required()) ], L.Location("a buffer", -1)) ]

        # Verify that the CDATA model is handled properly. Use #REQUIRED for now. This is mostly a matter of verifying
        # that surrounding white space is discarded.
        #
        events = evaluate("<!ATTLIST e a CDATA #REQUIRED>")
        @test events == [ ADs(false, "e", [ AD(false, "a", CData(), Required()) ], L.Location("a buffer", -1)) ]

        events = evaluate("<!ATTLIST e a\nCDATA\n#REQUIRED>")
        @test events == [ ADs(false, "e", [ AD(false, "a", CData(), Required()) ], L.Location("a buffer", -1)) ]

        events = evaluate("<!ATTLIST e a\tCDATA\t#REQUIRED>")
        @test events == [ ADs(false, "e", [ AD(false, "a", CData(), Required()) ], L.Location("a buffer", -1)) ]

        events = evaluate("<!ATTLIST e a\n\tCDATA\n\t#REQUIRED>")
        @test events == [ ADs(false, "e", [ AD(false, "a", CData(), Required()) ], L.Location("a buffer", -1)) ]
    end

    @testset "Events/Attribute List Declarations, ENTITY (Positive)" begin
        # Verify that the ENTITY model is handled properly. Use #REQUIRED for now. This is mostly a matter of verifying
        # that surrounding white space is discarded.
        #
        events = evaluate("<!ATTLIST e a ENTITY #REQUIRED>")
        @test events == [ ADs(false, "e", [ AD(false, "a", Entity(), Required()) ], L.Location("a buffer", -1)) ]

        events = evaluate("<!ATTLIST e a\nENTITY\n#REQUIRED>")
        @test events == [ ADs(false, "e", [ AD(false, "a", Entity(), Required()) ], L.Location("a buffer", -1)) ]

        events = evaluate("<!ATTLIST e a\tENTITY\t#REQUIRED>")
        @test events == [ ADs(false, "e", [ AD(false, "a", Entity(), Required()) ], L.Location("a buffer", -1)) ]

        events = evaluate("<!ATTLIST e a\n\tENTITY\n\t#REQUIRED>")
        @test events == [ ADs(false, "e", [ AD(false, "a", Entity(), Required()) ], L.Location("a buffer", -1)) ]
    end

    @testset "Events/Attribute List Declarations, ENTITIES (Positive)" begin
        # Verify that the ENTITIES model is handled properly. Use #REQUIRED for now. This is mostly a matter of
        # verifying that surrounding white space is discarded.
        #
        events = evaluate("<!ATTLIST e a ENTITIES #REQUIRED>")
        @test events == [ ADs(false, "e", [ AD(false, "a", Entities(), Required()) ], L.Location("a buffer", -1)) ]

        events = evaluate("<!ATTLIST e a\nENTITIES\n#REQUIRED>")
        @test events == [ ADs(false, "e", [ AD(false, "a", Entities(), Required()) ], L.Location("a buffer", -1)) ]

        events = evaluate("<!ATTLIST e a\tENTITIES\t#REQUIRED>")
        @test events == [ ADs(false, "e", [ AD(false, "a", Entities(), Required()) ], L.Location("a buffer", -1)) ]

        events = evaluate("<!ATTLIST e a\n\tENTITIES\n\t#REQUIRED>")
        @test events == [ ADs(false, "e", [ AD(false, "a", Entities(), Required()) ], L.Location("a buffer", -1)) ]
    end

    @testset "Events/Attribute List Declarations, IDREF (Positive)" begin
        # Verify that the IDREF model is handled properly. Use #REQUIRED for now. This is mostly a matter of verifying
        # that surrounding white space is discarded.
        #
        events = evaluate("<!ATTLIST e a IDREF #REQUIRED>")
        @test events == [ ADs(false, "e", [ AD(false, "a", IDRef(), Required()) ], L.Location("a buffer", -1)) ]

        events = evaluate("<!ATTLIST e a\nIDREF\n#REQUIRED>")
        @test events == [ ADs(false, "e", [ AD(false, "a", IDRef(), Required()) ], L.Location("a buffer", -1)) ]

        events = evaluate("<!ATTLIST e a\tIDREF\t#REQUIRED>")
        @test events == [ ADs(false, "e", [ AD(false, "a", IDRef(), Required()) ], L.Location("a buffer", -1)) ]

        events = evaluate("<!ATTLIST e a\n\tIDREF\n\t#REQUIRED>")
        @test events == [ ADs(false, "e", [ AD(false, "a", IDRef(), Required()) ], L.Location("a buffer", -1)) ]
    end

    @testset "Events/Attribute List Declarations, IDREFS (Positive)" begin
        # Verify that the IDREFS model is handled properly. Use #REQUIRED for now. This is mostly a matter of verifying
        # that surrounding white space is discarded.
        #
        events = evaluate("<!ATTLIST e a IDREFS #REQUIRED>")
        @test events == [ ADs(false, "e", [ AD(false, "a", IDRefs(), Required()) ], L.Location("a buffer", -1)) ]

        events = evaluate("<!ATTLIST e a\nIDREFS\n#REQUIRED>")
        @test events == [ ADs(false, "e", [ AD(false, "a", IDRefs(), Required()) ], L.Location("a buffer", -1)) ]

        events = evaluate("<!ATTLIST e a\tIDREFS\t#REQUIRED>")
        @test events == [ ADs(false, "e", [ AD(false, "a", IDRefs(), Required()) ], L.Location("a buffer", -1)) ]

        events = evaluate("<!ATTLIST e a\n\tIDREFS\n\t#REQUIRED>")
        @test events == [ ADs(false, "e", [ AD(false, "a", IDRefs(), Required()) ], L.Location("a buffer", -1)) ]
    end

    @testset "Events/Attribute List Declarations, ID (Positive)" begin
        # Verify that the ID model is handled properly. Use #REQUIRED for now. This is mostly a matter of verifying
        # that surrounding white space is discarded.
        #
        events = evaluate("<!ATTLIST e a ID #REQUIRED>")
        @test events == [ ADs(false, "e", [ AD(false, "a", ID(), Required()) ], L.Location("a buffer", -1)) ]

        events = evaluate("<!ATTLIST e a\nID\n#REQUIRED>")
        @test events == [ ADs(false, "e", [ AD(false, "a", ID(), Required()) ], L.Location("a buffer", -1)) ]

        events = evaluate("<!ATTLIST e a\tID\t#REQUIRED>")
        @test events == [ ADs(false, "e", [ AD(false, "a", ID(), Required()) ], L.Location("a buffer", -1)) ]

        events = evaluate("<!ATTLIST e a\n\tID\n\t#REQUIRED>")
        @test events == [ ADs(false, "e", [ AD(false, "a", ID(), Required()) ], L.Location("a buffer", -1)) ]
    end

    @testset "Events/Attribute List Declarations, NMTOKEN (Positive)" begin
        # Verify that the NMTOKEN model is handled properly. Use #REQUIRED for now. This is mostly a matter of verifying
        # that surrounding white space is discarded.
        #
        events = evaluate("<!ATTLIST e a NMTOKEN #REQUIRED>")
        @test events == [ ADs(false, "e", [ AD(false, "a", NmToken(), Required()) ], L.Location("a buffer", -1)) ]

        events = evaluate("<!ATTLIST e a\nNMTOKEN\n#REQUIRED>")
        @test events == [ ADs(false, "e", [ AD(false, "a", NmToken(), Required()) ], L.Location("a buffer", -1)) ]

        events = evaluate("<!ATTLIST e a\tNMTOKEN\t#REQUIRED>")
        @test events == [ ADs(false, "e", [ AD(false, "a", NmToken(), Required()) ], L.Location("a buffer", -1)) ]

        events = evaluate("<!ATTLIST e a\n\tNMTOKEN\n\t#REQUIRED>")
        @test events == [ ADs(false, "e", [ AD(false, "a", NmToken(), Required()) ], L.Location("a buffer", -1)) ]
    end

    @testset "Events/Attribute List Declarations, NMTOKENS (Positive)" begin
        # Verify that the NMTOKENS model is handled properly. Use #REQUIRED for now. This is mostly a matter of verifying
        # that surrounding white space is discarded.
        #
        events = evaluate("<!ATTLIST e a NMTOKENS #REQUIRED>")
        @test events == [ ADs(false, "e", [ AD(false, "a", NmTokens(), Required()) ], L.Location("a buffer", -1)) ]

        events = evaluate("<!ATTLIST e a\nNMTOKENS\n#REQUIRED>")
        @test events == [ ADs(false, "e", [ AD(false, "a", NmTokens(), Required()) ], L.Location("a buffer", -1)) ]

        events = evaluate("<!ATTLIST e a\tNMTOKENS\t#REQUIRED>")
        @test events == [ ADs(false, "e", [ AD(false, "a", NmTokens(), Required()) ], L.Location("a buffer", -1)) ]

        events = evaluate("<!ATTLIST e a\n\tNMTOKENS\n\t#REQUIRED>")
        @test events == [ ADs(false, "e", [ AD(false, "a", NmTokens(), Required()) ], L.Location("a buffer", -1)) ]
    end

    @testset "Events/Attribute List Declarations, enumerated type (Positive)" begin
        # Verify that the enumerated type is handled properly. Use #REQUIRED for now. First check that basic grouping is
        # handled.
        #
        events = evaluate("<!ATTLIST e a (b) #REQUIRED>")
        @test events == [ ADs(false, "e", [ AD(false, "a", Enumerated([ "b" ]), Required()) ], L.Location("a buffer", -1)) ]

        events = evaluate("<!ATTLIST e a (b|c) #REQUIRED>")
        @test events == [ ADs(false, "e", [ AD(false, "a", Enumerated([ "b", "c" ]), Required()) ], L.Location("a buffer", -1)) ]

        events = evaluate("<!ATTLIST e a (b|c|d) #REQUIRED>")
        @test events == [ ADs(false, "e", [ AD(false, "a", Enumerated([ "b", "c", "d" ]), Required()) ],
                              L.Location("a buffer", -1)) ]

        # Verify that white space in the enumeration is handled properly.
        #
        events = evaluate("<!ATTLIST e a ( b |c|d) #REQUIRED>")
        @test events == [ ADs(false, "e", [ AD(false, "a", Enumerated([ "b", "c", "d" ]), Required()) ],
                              L.Location("a buffer", -1)) ]

        events = evaluate("<!ATTLIST e a (b |c|d) #REQUIRED>")
        @test events == [ ADs(false, "e", [ AD(false, "a", Enumerated([ "b", "c", "d" ]), Required()) ],
                              L.Location("a buffer", -1)) ]

        events = evaluate("<!ATTLIST e a ( b|c|d) #REQUIRED>")
        @test events == [ ADs(false, "e", [ AD(false, "a", Enumerated([ "b", "c", "d" ]), Required()) ],
                              L.Location("a buffer", -1)) ]

        events = evaluate("<!ATTLIST e a (b|c |d) #REQUIRED>")
        @test events == [ ADs(false, "e", [ AD(false, "a", Enumerated([ "b", "c", "d" ]), Required()) ],
                              L.Location("a buffer", -1)) ]

        events = evaluate("<!ATTLIST e a (b| c|d) #REQUIRED>")
        @test events == [ ADs(false, "e", [ AD(false, "a", Enumerated([ "b", "c", "d" ]), Required()) ],
                              L.Location("a buffer", -1)) ]

        events = evaluate("<!ATTLIST e a (\n\nb|\t\nc\n\t|\t\td\t\t) #REQUIRED>")
        @test events == [ ADs(false, "e", [ AD(false, "a", Enumerated([ "b", "c", "d" ]), Required()) ],
                              L.Location("a buffer", -1)) ]

        # Verify that various NMTOKENs in the enumeration are handled properly.
        #
        events = evaluate("<!ATTLIST e a (0|1|2|33|444|5555) #REQUIRED>")
        @test events == [ ADs(false, "e", [ AD(false, "a", Enumerated([ "0", "1", "2", "33", "444", "5555" ]), Required()) ],
                              L.Location("a buffer", -1)) ]

        events = evaluate("<!ATTLIST e a (:a|\uc0|\ud8|\uf8) #REQUIRED>")
        @test events == [ ADs(false, "e", [ AD(false, "a", Enumerated([ ":a", "\uc0", "\ud8", "\uf8" ]), Required()) ],
                              L.Location("a buffer", -1)) ]

        events = evaluate("<!ATTLIST e a (-|--|.|\ub7|\u0300) #REQUIRED>")
        @test events == [ ADs(false, "e", [ AD(false, "a", Enumerated([ "-", "--", ".", "\ub7", "\u0300" ]), Required()) ],
                              L.Location("a buffer", -1)) ]
    end

    @testset "Events/Attribute List Declarations, enumerated NOTATIONs type (Positive)" begin
        # Verify that the enumerated NOTATIONs type is handled properly. Use #REQUIRED for now. First check that basic
        # grouping is handled.
        #
        events = evaluate("<!ATTLIST e a NOTATION (b) #REQUIRED>")
        @test events == [ ADs(false, "e", [ AD(false, "a", Notations([ "b" ]), Required()) ], L.Location("a buffer", -1)) ]

        events = evaluate("<!ATTLIST e a NOTATION (b|c) #REQUIRED>")
        @test events == [ ADs(false, "e", [ AD(false, "a", Notations([ "b", "c" ]), Required()) ], L.Location("a buffer", -1)) ]

        events = evaluate("<!ATTLIST e a NOTATION (b|c|d) #REQUIRED>")
        @test events == [ ADs(false, "e", [ AD(false, "a", Notations([ "b", "c", "d" ]), Required()) ],
                              L.Location("a buffer", -1)) ]

        # Verify that white space in the enumeration is handled properly.
        #
        events = evaluate("<!ATTLIST e a NOTATION ( b |c|d) #REQUIRED>")
        @test events == [ ADs(false, "e", [ AD(false, "a", Notations([ "b", "c", "d" ]), Required()) ],
                              L.Location("a buffer", -1)) ]

        events = evaluate("<!ATTLIST e a NOTATION (b |c|d) #REQUIRED>")
        @test events == [ ADs(false, "e", [ AD(false, "a", Notations([ "b", "c", "d" ]), Required()) ],
                              L.Location("a buffer", -1)) ]

        events = evaluate("<!ATTLIST e a NOTATION ( b|c|d) #REQUIRED>")
        @test events == [ ADs(false, "e", [ AD(false, "a", Notations([ "b", "c", "d" ]), Required()) ],
                              L.Location("a buffer", -1)) ]

        events = evaluate("<!ATTLIST e a NOTATION (b|c |d) #REQUIRED>")
        @test events == [ ADs(false, "e", [ AD(false, "a", Notations([ "b", "c", "d" ]), Required()) ],
                              L.Location("a buffer", -1)) ]

        events = evaluate("<!ATTLIST e a NOTATION (b| c|d) #REQUIRED>")
        @test events == [ ADs(false, "e", [ AD(false, "a", Notations([ "b", "c", "d" ]), Required()) ],
                              L.Location("a buffer", -1)) ]

        events = evaluate("<!ATTLIST e a NOTATION (\n\nb|\t\nc\n\t|\t\td\t\t) #REQUIRED>")
        @test events == [ ADs(false, "e", [ AD(false, "a", Notations([ "b", "c", "d" ]), Required()) ],
                              L.Location("a buffer", -1)) ]

        # Verify that various NMTOKENs in the enumeration are handled properly.
        #
        events = evaluate("<!ATTLIST e a NOTATION (a0|b1|c2|d33|e444|f5555) #REQUIRED>")
        @test events == [ ADs(false, "e", [ AD(false, "a", Notations([ "a0", "b1", "c2", "d33", "e444", "f5555" ]), Required()) ],
                              L.Location("a buffer", -1)) ]

        events = evaluate("<!ATTLIST e a NOTATION (a:a|b\uc0|c\ud8|d\uf8) #REQUIRED>")
        @test events == [ ADs(false, "e", [ AD(false, "a", Notations([ "a:a", "b\uc0", "c\ud8", "d\uf8" ]), Required()) ],
                              L.Location("a buffer", -1)) ]

        events = evaluate("<!ATTLIST e a NOTATION (a-|b--|c.|d\ub7|e\u0300) #REQUIRED>")
        @test events == [ ADs(false, "e", [ AD(false, "a", Notations([ "a-", "b--", "c.", "d\ub7", "e\u0300" ]), Required()) ],
                              L.Location("a buffer", -1)) ]
    end

    @testset "Events/Attribute List Declarations, #REQUIRED (Positive)" begin
        # #REQUIRED has been tested enough earlier. This is just to indicate that I haven't forgotten it.
        #
    end

    @testset "Events/Attribute List Declarations, #IMPLIED (Positive)" begin
        # Verify that #IMPLIED is handled properly. This is mostly a matter of verifying that the surrounding white
        # space is discarded.
        #
        events = evaluate("<!ATTLIST e a CDATA #IMPLIED>")
        @test events == [ ADs(false, "e", [ AD(false, "a", CData(), Implied()) ], L.Location("a buffer", -1)) ]

        events = evaluate("<!ATTLIST e a CDATA\n#IMPLIED\n>")
        @test events == [ ADs(false, "e", [ AD(false, "a", CData(), Implied()) ], L.Location("a buffer", -1)) ]

        events = evaluate("<!ATTLIST e a CDATA\t#IMPLIED\t>")
        @test events == [ ADs(false, "e", [ AD(false, "a", CData(), Implied()) ], L.Location("a buffer", -1)) ]

        events = evaluate("<!ATTLIST e a CDATA\t\n#IMPLIED\n\t>")
        @test events == [ ADs(false, "e", [ AD(false, "a", CData(), Implied()) ], L.Location("a buffer", -1)) ]
    end

    @testset "Events/Attribute List Declarations, default value (Positive)" begin
        # Verify that a default attribute value is handled properly.
        #
        events = evaluate("<!ATTLIST e a CDATA \"\">")
        @test events == [ ADs(false, "e", [ AD(false, "a", CData(), DefaultValue(false, [ ])) ], L.Location("a buffer", -1)) ]

        events = evaluate("<!ATTLIST e a CDATA \" \">")
        @test events == [ ADs(false, "e",
                              [ AD(false, "a", CData(),
                                   DefaultValue(false, [ DC(" ", true, L.Location("a buffer", -1))])) ],
                              L.Location("a buffer", -1)) ]

        events = evaluate("<!ATTLIST e a CDATA \"Hello, World!\">")
        @test events == [ ADs(false, "e",
                              [ AD(false, "a", CData(),
                                   DefaultValue(false, [ DC("Hello", false, L.Location("a buffer", -1)),
                                                         DC(",", false, L.Location("a buffer", -1)),
                                                         DC(" ", true, L.Location("a buffer", -1)),
                                                         DC("World!", false, L.Location("a buffer", -1))])) ],
                              L.Location("a buffer", -1)) ]

        # Verify that surrounding white space is discarded.
        #
        events = evaluate("<!ATTLIST e a CDATA \"value\">")
        @test events == [ ADs(false, "e",
                              [ AD(false, "a", CData(),
                                   DefaultValue(false, [ DC("value", false, L.Location("a buffer", -1))])) ],
                              L.Location("a buffer", -1)) ]

        events = evaluate("<!ATTLIST e a CDATA\n\"value\"\n>")
        @test events == [ ADs(false, "e",
                              [ AD(false, "a", CData(),
                                   DefaultValue(false, [ DC("value", false, L.Location("a buffer", -1))])) ],
                              L.Location("a buffer", -1)) ]

        events = evaluate("<!ATTLIST e a CDATA\t\"value\"\t>")
        @test events == [ ADs(false, "e",
                              [ AD(false, "a", CData(),
                                   DefaultValue(false, [ DC("value", false, L.Location("a buffer", -1))])) ],
                              L.Location("a buffer", -1)) ]

        events = evaluate("<!ATTLIST e a CDATA\n\t\"value\"\t\n>")
        @test events == [ ADs(false, "e",
                              [ AD(false, "a", CData(),
                                   DefaultValue(false, [ DC("value", false, L.Location("a buffer", -1))])) ],
                              L.Location("a buffer", -1)) ]

        # Verify that #FIXED is handled properly. This is mostly a matter of checking that surrounding white space is
        # discarded.
        #
        events = evaluate("<!ATTLIST e a CDATA #FIXED \"value\">")
        @test events == [ ADs(false, "e",
                              [ AD(false, "a", CData(),
                                   DefaultValue(true, [ DC("value", false, L.Location("a buffer", -1))])) ],
                              L.Location("a buffer", -1)) ]

        events = evaluate("<!ATTLIST e a CDATA\n#FIXED\n\"value\">")
        @test events == [ ADs(false, "e",
                              [ AD(false, "a", CData(),
                                   DefaultValue(true, [ DC("value", false, L.Location("a buffer", -1))])) ],
                              L.Location("a buffer", -1)) ]

        events = evaluate("<!ATTLIST e a CDATA\t#FIXED\t\"value\">")
        @test events == [ ADs(false, "e",
                              [ AD(false, "a", CData(),
                                   DefaultValue(true, [ DC("value", false, L.Location("a buffer", -1))])) ],
                              L.Location("a buffer", -1)) ]

        events = evaluate("<!ATTLIST e a CDATA\n\t#FIXED\t\n\"value\">")
        @test events == [ ADs(false, "e",
                              [ AD(false, "a", CData(),
                                   DefaultValue(true, [ DC("value", false, L.Location("a buffer", -1))])) ],
                              L.Location("a buffer", -1)) ]
    end

    @testset "Events/Attribute List Declarations (Negative ... missing element name)" begin
        events = evaluate("<!ATTLIST")
        @test events == [ ME("ERROR: White space is required following the 'ATTLIST' keyword.", L.Location("a buffer", -1)),
                          ME("ERROR: Expecting an element name.", L.Location("a buffer", -1)) ]

        events = evaluate("<!ATTLIST ")
        @test events == [ ME("ERROR: Expecting an element name.", L.Location("a buffer", -1)) ]

        events = evaluate("<!ATTLIST >")
        @test events == [ ME("ERROR: Expecting an element name.", L.Location("a buffer", -1)),
                          DC(">", false, L.Location("a buffer", -1)) ]
    end

    @testset "Events/Attribute List Declarations (Negative ... missing TAGC)" begin
        events = evaluate("<!ATTLIST e")
        @test events == [ ME("ERROR: Expecting '>' to end an attribute list declaration.", L.Location("a buffer", -1)),
                          ADs(true, "e", [ ], L.Location("a buffer", -1)) ]

        events = evaluate("<!ATTLIST e ")
        @test events == [ ME("ERROR: Expecting '>' to end an attribute list declaration.", L.Location("a buffer", -1)),
                          ADs(true, "e", [ ], L.Location("a buffer", -1)) ]

        events = evaluate("<!ATTLIST e a CDATA #IMPLIED")
        @test events == [ ME("ERROR: Expecting '>' to end an attribute list declaration.", L.Location("a buffer", -1)),
                          ADs(true, "e", [ AD(false, "a", CData(), Implied()) ], L.Location("a buffer", -1)) ]
    end

    @testset "Events/Attribute List Declarations (Negative ... missing attribute name)" begin
        # This is tricky, because in the absence of an attribute name, any of the attribute types will be interpreted as
        # the attribute name, and the error will occur later. So we'll use an enumerated attribute type, which starts
        # with '('.
        #
        events = evaluate("<!ATTLIST e (a) #IMPLIED>")
        @test events == [ ME("ERROR: Expecting '>' to end an attribute list declaration.", L.Location("a buffer", -1)),
                          ADs(true, "e", [ ], L.Location("a buffer", -1)),
                          DC("(", false, L.Location("a buffer", -1)),
                          DC("a", false, L.Location("a buffer", -1)),
                          DC(")", false, L.Location("a buffer", -1)),
                          DC(" ", true, L.Location("a buffer", -1)),
                          DC("#IMPLIED", false, L.Location("a buffer", -1)),
                          DC(">", false, L.Location("a buffer", -1)) ]

        events = evaluate("<!ATTLIST e(a) #IMPLIED>")
        @test events == [ ME("ERROR: Expecting '>' to end an attribute list declaration.", L.Location("a buffer", -1)),
                          ADs(true, "e", [ ], L.Location("a buffer", -1)),
                          DC("(", false, L.Location("a buffer", -1)),
                          DC("a", false, L.Location("a buffer", -1)),
                          DC(")", false, L.Location("a buffer", -1)),
                          DC(" ", true, L.Location("a buffer", -1)),
                          DC("#IMPLIED", false, L.Location("a buffer", -1)),
                          DC(">", false, L.Location("a buffer", -1)) ]
    end

    @testset "Events/Attribute List Declarations (Negative ... missing/invalid attribute type)" begin
        events = evaluate("<!ATTLIST e a #IMPLIED>")
        @test events == [ ME("ERROR: Expecting an attribute type.", L.Location("a buffer", -1)),
                          ADs(false, "e", [ AD(false, "a", Main.Events.StringType(), Implied()) ], L.Location("a buffer", -1)) ]

        # The attribute type is misspelled here. I purposefully chose something other than CDATA, to nail down the
        # recovery for the attribute type ... i.e., the fallback to CDATA.
        #
        events = evaluate("<!ATTLIST e a ENTITIE #IMPLIED>")
        @test events == [ ME("ERROR: Expecting an attribute type.", L.Location("a buffer", -1)),
                          ME("ERROR: Expecting a quoted attribute value.", L.Location("a buffer", -1)),
                          ME("ERROR: Expecting '>' to end an attribute list declaration.", L.Location("a buffer", -1)),
                          ADs(true, "e", [ AD(true, "a", CData(), DefaultValue(false, [ ])) ], L.Location("a buffer", -1)),
                          DC("ENTITIE", false, L.Location("a buffer", -1)),
                          DC(" ", true, L.Location("a buffer", -1)),
                          DC("#IMPLIED", false, L.Location("a buffer", -1)),
                          DC(">", false, L.Location("a buffer", -1)) ]
    end

    @testset "Events/Attribute List Declarations (Negative ... NOTATION missing TAGO for group)" begin
        events = evaluate("<!ATTLIST e a NOTATION a | b) #IMPLIED>")
        @test events == [  ME("ERROR: Expecting '(' to open an enumerated notation attribute value.", L.Location("a buffer", -1)),
                           ADs(false, "e", [ AD(false, "a", Notations([ "a", "b" ]), Implied()) ], L.Location("a buffer", -1)) ]

    end

    @testset "Events/Attribute List Declarations (Negative ... NOTATION missing white space)" begin
        events = evaluate("<!ATTLIST e a NOTATION(a | b) #IMPLIED>")
        @test events == [  ME("ERROR: White space is required following the 'NOTATION' keyword.", L.Location("a buffer", -1)),
                           ADs(false, "e", [ AD(false, "a", Notations([ "a", "b" ]), Implied()) ], L.Location("a buffer", -1)) ]

    end

    @testset "Events/Attribute List Declarations (Negative ... NOTATION missing TAGC for group)" begin
        events = evaluate("<!ATTLIST e a NOTATION (a | b #IMPLIED>")
        @test events == [  ME("ERROR: Expecting '|' or ')'.", L.Location("a buffer", -1)),
                           ADs(true, "e", [ AD(true, "a", Notations([ "a", "b" ]), Implied()) ], L.Location("a buffer", -1)) ]

    end

    @testset "Events/Attribute List Declarations (Negative ... missing TAGC for group)" begin
        events = evaluate("<!ATTLIST e a (a | b #IMPLIED>")
        @test events == [  ME("ERROR: Expecting '|' or ')'.", L.Location("a buffer", -1)),
                           ADs(true, "e", [ AD(true, "a", Enumerated([ "a", "b" ]), Implied()) ], L.Location("a buffer", -1)) ]

    end

    @testset "Events/Attribute List Declarations (Negative ... invalid group)" begin
        events = evaluate("<!ATTLIST e a (| b) #IMPLIED>")
        @test events == [  ME("ERROR: Expecting a NMTOKEN for an enumerated attribute value.", L.Location("a buffer", -1)),
                           ADs(true, "e", [ AD(true, "a", Enumerated([ "b" ]), Implied()) ], L.Location("a buffer", -1)) ]

        events = evaluate("<!ATTLIST e a NOTATION (| b) #IMPLIED>")
        @test events == [  ME("ERROR: Expecting a name for an enumerated attribute value.", L.Location("a buffer", -1)),
                           ADs(true, "e", [ AD(true, "a", Notations([ "b" ]), Implied()) ], L.Location("a buffer", -1)) ]

        events = evaluate("<!ATTLIST e a (< | b) #IMPLIED>")
        @test events == [  ME("ERROR: Expecting a NMTOKEN for an enumerated attribute value.", L.Location("a buffer", -1)),
                           ADs(true, "e", [ AD(true, "a", Enumerated([ "b" ]), Implied()) ], L.Location("a buffer", -1)) ]

        events = evaluate("<!ATTLIST e a NOTATION (< | b) #IMPLIED>")
        @test events == [  ME("ERROR: Expecting a name for an enumerated attribute value.", L.Location("a buffer", -1)),
                           ADs(true, "e", [ AD(true, "a", Notations([ "b" ]), Implied()) ], L.Location("a buffer", -1)) ]

        events = evaluate("<!ATTLIST e a (a b) #IMPLIED>")
        @test events == [  ME("ERROR: Expecting '|' between items in an enumerated attribute value.", L.Location("a buffer", -1)),
                           ADs(true, "e", [ AD(true, "a", Enumerated([ "a", "b" ]), Implied()) ], L.Location("a buffer", -1)) ]

        events = evaluate("<!ATTLIST e a NOTATION (a b) #IMPLIED>")
        @test events == [  ME("ERROR: Expecting '|' between items in an enumerated attribute value.", L.Location("a buffer", -1)),
                           ADs(true, "e", [ AD(true, "a", Notations([ "a", "b" ]), Implied()) ], L.Location("a buffer", -1)) ]

        events = evaluate("<!ATTLIST e a (a | b] #IMPLIED>")
        @test events == [ ME("ERROR: Expecting '|' or ')'.", L.Location("a buffer", -1)),
                          ME("ERROR: Expecting a quoted attribute value.", L.Location("a buffer", -1)),
                          ME("ERROR: Expecting '>' to end an attribute list declaration.", L.Location("a buffer", -1)),
                          ADs(true, "e", [ AD(true, "a", Enumerated([ "a", "b" ]), DefaultValue(false, [ ])) ],
                              L.Location("a buffer", -1)),
                          DC("]", false, L.Location("a buffer", -1)),
                          DC(" ", true, L.Location("a buffer", -1)),
                          DC("#IMPLIED", false, L.Location("a buffer", -1)),
                          DC(">", false, L.Location("a buffer", -1)) ]

        events = evaluate("<!ATTLIST e a NOTATION (a | b] #IMPLIED>")
        @test events == [ ME("ERROR: Expecting '|' or ')'.", L.Location("a buffer", -1)),
                          ME("ERROR: Expecting a quoted attribute value.", L.Location("a buffer", -1)),
                          ME("ERROR: Expecting '>' to end an attribute list declaration.", L.Location("a buffer", -1)),
                          ADs(true, "e", [ AD(true, "a", Notations([ "a", "b" ]), DefaultValue(false, [ ])) ],
                              L.Location("a buffer", -1)),
                          DC("]", false, L.Location("a buffer", -1)),
                          DC(" ", true, L.Location("a buffer", -1)),
                          DC("#IMPLIED", false, L.Location("a buffer", -1)),
                          DC(">", false, L.Location("a buffer", -1)) ]
    end
end

max_stars_repo_name: jlegare/NativeXML

max_issues_repo_name: jlegare/NativeXML

max_forks_repo_name: jlegare/NativeXML


text:
#=
Let's load some packages
=#

using OneHot, CairoMakie, Random, BenchmarkTools
nothing #hide

#=
Setup the size of the examples we'll look at
=#

q = 20
N = 200
M = 400
nothing #hide

#=
Benchmark normal matrix multiply.
This doesn't exploit OneHot structure of `X`.
=#

@benchmark A * X setup=(A=randn(M,q); X=Array(OneHotArray(rand(1:q, N), q)))

#=
This package implements efficient matrix multiply by a `OneHotArray`.
=#

@benchmark A * X setup=(A=randn(M,q); X=OneHotArray(rand(1:q, N), q))

#=
That's about 20x faster!
We can also check that these two codes are computing the same thing.
=#

A = randn(M, q)
X = OneHotArray(rand(1:q, N), q)
A * X ≈ A * Array(X)


text:
# Create individual specs

# Plot constants
global const _plot_width  = 450
global const _plot_height = 410
global const _slider_height = 90

##
## Primary spec functions
##

function _spec_for_item(m::Model, comp_name::Symbol, item_name::Symbol; interactive::Bool=true)
    dims = dim_names(m, comp_name, item_name)
    if length(dims) > 2
        # Drop references to singleton dimensions
        dims = tuple([dim for dim in dims if dim_count(m, dim) != 1]...)
    end

    # Control flow logic selects the correct plot type based on dimensions
    # and dataframe fields

    ##
    ## No Plot Clases
    ##

    # if there are no dimensions we show the values in the label in the menu
    if length(dims) == 0
        paths = _get_all_paths(m)
        comp_path = paths[comp_name];
        value = m[comp_path, item_name] === nothing ? m[comp_name, item_name] : m[comp_path, item_name]
        name = "$comp_name : $item_name = $value"
        spec = createspec_singlevalue(name)

    # we do not support over two indexed dimensions right now
    elseif length(dims) > 2
        @warn("$comp_name.$item_name has > 2 indexed dimensions, not yet implemented in explorer")
        name = "$comp_name : $item_name (CANNOT DISPLAY)"
        spec = createspec_singlevalue(name)

    ##
    ## Plot Cases
    ##

    else
        name = "$comp_name : $item_name"
        df = getdataframe(m, comp_name, item_name)
        dffields = map(string, names(df))         # convert to string once before creating specs

        # Time is a Dimension - line plots
        if "time" in dffields

            # need to reorder the df to have 'time' as the first dimension
            ti = findfirst(isequal("time"), dffields)
            if ti != 1
                fields1, fields2 = dffields[1:ti-1], dffields[ti+1:end]
                dffields = ["time", fields1..., fields2...]
                df = df[:, [Symbol(name) for name in dffields]]
            end

            if length(dffields) > 2
                spec = createspec_multilineplot(name, df, dffields, dims, interactive=interactive)
            else
                spec = createspec_lineplot(name, df, dffields, interactive=interactive)
            end

        # No Time Dimension and First Dim is a Number Type - scatter plots
        elseif eltype(df[!, 1]) <: Number
            if length(dffields) > 2
                spec = createspec_multiscatterplot(name, df, dffields)
            else
                spec = createspec_scatterplot(name, df, dffields)
            end

        # No Time Dimension and First Dim is Not a Number Type - bar plots
        else
            if length(dffields) > 2
                spec = createspec_multibarplot(name, df, dffields)
            else
                spec = createspec_barplot(name, df, dffields)
            end
        end
    end

    return spec

end

function _spec_for_sim_item(sim_inst::SimulationInstance, comp_name::Symbol, item_name::Symbol, results::DataFrame; model_index::Int = 1, interactive::Bool=true)

    # Control flow logic selects the correct plot type based on dimensions
    # and dataframe fields
    m = sim_inst.models[model_index]
    dims = dim_names(m, comp_name, item_name)
    if length(dims) > 2
        # Drop references to singleton dimensions
        dims = tuple([dim for dim in dims if dim_count(m, dim) != 1]...)
    end

    dffields = map(string, names(results))         # convert to string once before creating specs

    name = "$comp_name : $item_name"

    if length(dims) == 0 # histogram
        spec = createspec_histogram(name, results, dffields; interactive = interactive)
    elseif length(dims) > 2
        @warn("$name has > 2 indexed dimensions, not yet implemented in explorer")
        name = "$comp_name : $item_name (CANNOT DISPLAY)"
        spec = createspec_singlevalue(name)
    else

        # check if there are too many dimensions to map and if so, error
        if length(dffields) > 4
            error()

        # a 'time' field necessitates a trumpet plot
        elseif dffields[1] == "time"
            if length(dffields) > 3
                spec =createspec_multitrumpet(name, results, dffields; interactive = interactive)
            else
                spec = createspec_trumpet(name, results, dffields; interactive = interactive)
            end
        #otherwise we are dealing with layered histograms
        else
            spec = createspec_multihistogram(name,results, dffields; interactive = interactive)
        end
    end

    return spec

end

##
## Methods for explore(m::model)
##

function createspec_lineplot(name, df, dffields; interactive::Bool=true)
    interactive ? createspec_lineplot_interactive(name, df, dffields) : createspec_lineplot_static(name, df, dffields)
end

function createspec_lineplot_interactive(name, df, dffields)
    datapart = getdatapart(df, dffields, :line) #returns JSONtext type
    spec = Dict(
        "name"  => name,
        "type" => "line",
        "VLspec" => Dict(
            "\$schema" => "https://vega.github.io/schema/vega-lite/v3.json",
            "description" => "plot for a specific component variable pair",
            "data"=> Dict("values" => datapart),
            "vconcat" => [
                Dict(
                    "title" => "$name",
                    # "transform" => [Dict("filter" => Dict("selection" => "brush"))],
                    "width" => _plot_width,
                    "height" => _plot_height,
                    "mark" => Dict("type" => "line", "point" => true),
                    "encoding" => Dict(
                        "x" => Dict(
                            "field" => dffields[1],
                            "type" => "temporal",
                            "timeUnit" => "utcyear",
                            "axis" => Dict("title"=> ""),
                            "scale" => Dict("domain" => Dict("selection" => "brush"))
                        ),
                        "y" => Dict(
                            "field" => dffields[2],
                            "type" => "quantitative"
                        )
                    )
                ), Dict(
                    "title" => "INTERACTIVE PLOT",
                    "width" => _plot_width,
                    "height" => _slider_height,
                    "mark" => Dict("type" => "line", "point" => true),
                    "selection" => Dict("brush" => Dict("type" => "interval", "encodings" => ["x"])),
                    "encoding" => Dict(
                        "x" => Dict(
                            "field" => dffields[1],
                            "type" => "temporal",
                            "timeUnit" => "utcyear"
                        ),
                        "y" => Dict(
                            "field" => dffields[2],
                            "type" => "quantitative",
                            "axis" => Dict("tickCount" => 3, "grid" => false
                            )
                        )
                    )
                )
            ]
        )
    )
    return spec
end

function createspec_lineplot_static(name, df, dffields)
    datapart = getdatapart(df, dffields, :line) #returns JSONtext type
    spec = Dict(
        "name"  => name,
        "type" => "line",
        "VLspec" => Dict(
            "\$schema" => "https://vega.github.io/schema/vega-lite/v3.json",
            "description" => "plot for a specific component variable pair",
            "title" => name,
            "data"=> Dict("values" => datapart),
            "mark" => Dict("type" => "line"),
            "encoding" => Dict(
                "x" => Dict(
                    "field" => dffields[1],
                    "type" => "temporal",
                    "timeUnit" => "utcyear"
                ),
                "y" => Dict(
                    "field" => dffields[2],
                    "type" => "quantitative"
                )
            ),
            "width" => _plot_width,
            "height" => _plot_height
        )
    )
    return spec
end

function createspec_multilineplot(name, df, dffields, multidims; interactive::Bool=true)
    strmultidims = [String(dim) for dim in multidims]
    interactive ? createspec_multilineplot_interactive(name, df, dffields, strmultidims) : createspec_multilineplot_static(name, df, dffields, strmultidims)
end

function createspec_multilineplot_interactive(name, df, dffields, strmultidims)
    datapart = getdatapart(df, dffields, :multiline) #returns JSONtext type
    spec = Dict(
        "name"  => name,
        "VLspec" => Dict(
            "\$schema" => "https://vega.github.io/schema/vega-lite/v3.json",
            "description" => "plot for a specific component variable pair",
            "data"  => Dict("values" => datapart),
            "vconcat" => [
                Dict(
                    "title" => "$name",
                    # "transform" => [Dict("filter" => Dict("selection" => "brush"))],
                    "mark" => Dict("type" => "line", "point" => true),
                    "encoding" => Dict(
                        "x"     => Dict(
                            "field" => dffields[1],
                            "type" => "temporal",
                            "timeUnit" => "utcyear",
                            "axis" => Dict("title"=> ""),
                            "scale" => Dict("domain" => Dict("selection" => "brush"))
                            ),
                        "y"     => Dict(
                            "field" => dffields[3],
                            "type" => "quantitative"
                            ),
                        "color" => Dict("field" => strmultidims[findfirst(strmultidims .!= "time")],
                                        "type" => "nominal",
                            "scale" => Dict("scheme" => "category20"))
                    ),
                    "width"  => _plot_width,
                    "height" => _plot_height
                ), Dict(
                    "title" => "INTERACTIVE PLOT",
                    "width" => _plot_width,
                    "height" => _slider_height,
                    "mark" => Dict("type" => "line", "point" => true),
                    "selection" => Dict("brush" => Dict("type" => "interval", "encodings" => ["x"])),
                    "encoding" => Dict(
                        "x" => Dict(
                            "field" => dffields[1],
                            "type" => "temporal",
                            "timeUnit" => "utcyear"
                        ),
                        "y" => Dict(
                            "field" => dffields[3],
                            "type" => "quantitative",
                            "axis" => Dict("tickCount" => 3, "grid" => false)
                        ),
                        "color" => Dict("field" => strmultidims[findfirst(strmultidims .!= "time")],
                                        "type" => "nominal",
                            "scale" => Dict("scheme" => "category20")
                        )
                    )
                )
            ]
        ),
    )
    return spec
end

function createspec_multilineplot_static(name, df, dffields, strmultidims)
    datapart = getdatapart(df, dffields, :multiline) #returns JSONtext type
    spec = Dict(
        "name"  => name,
        "VLspec" => Dict(
            "\$schema" => "https://vega.github.io/schema/vega-lite/v3.json",
            "description" => "plot for a specific component variable pair",
            "title" => name,
            "data"  => Dict("values" => datapart),

            "mark" => Dict("type" => "line"),
            "encoding" => Dict(
                "x"     => Dict(
                    "field" => dffields[1],
                    "type" => "temporal",
                    "timeUnit" => "utcyear"
                    ),
                "y"     => Dict(
                    "field" => dffields[3],
                    "type" => "quantitative"
                    ),
                "color" => Dict("field" => strmultidims[findfirst(strmultidims .!= "time")], "type" => "nominal",
                    "scale" => Dict("scheme" => "category20"))
            ),
            "width"  => _plot_width,
            "height" => _plot_height
        ),
    )
    return spec
end

function createspec_barplot(name, df, dffields)
    datapart = getdatapart(df, dffields, :bar) #returns JSONtext type
    spec = Dict(
        "name"  => name,
        "type" => "bar",
        "VLspec" => Dict(
            "\$schema" => "https://vega.github.io/schema/vega-lite/v3.json",
            "description" => "plot for a specific component variable pair",
            "title" => name,
            "data"=> Dict("values" => datapart),
            "mark" => "bar",
            "encoding" => Dict(
                "x" => Dict("field" => dffields[1], "type" => "ordinal"),
                "y" => Dict("field" => dffields[2], "type" => "quantitative" )
                ),
            "width"  => _plot_width,
            "height" => _plot_height
        )
    )
    return spec
end

function createspec_multibarplot(name, df, dffields)
    datapart = getdatapart(df, dffields, :multibar)
    spec = Dict(
        "name"  => name,
        "type" => "bar",
        "VLspec" => Dict(
            "\$schema" => "https://vega.github.io/schema/vega-lite/v3.json",
            "description" => "plot for a specific component variable pair",
            "title" => name,
            "data"=> Dict("values" => datapart),
            "mark" => "bar",
            "encoding" => Dict(
                "x" => Dict("field" => dffields[1], "type" => "ordinal"),
                "y" => Dict("field" => dffields[3], "type" => "quantitative" ),
                "color" => Dict("field" => dffields[2], "type" => "nominal",
                            "scale" => Dict("scheme" => "category20"))
                ),
            "width"  => _plot_width,
            "height" => _plot_height
        )
    )
    return spec
end

function createspec_scatterplot(name, df, dffields)
    datapart = getdatapart(df, dffields, :scatter) #returns JSONtext type
    spec = Dict(
        "name"  => name,
        "type" => "point",
        "VLspec" => Dict(
            "\$schema" => "https://vega.github.io/schema/vega-lite/v3.json",
            "description" => "plot for a specific component variable pair",
            "title" => name,
            "data"=> Dict("values" => datapart),
            "mark" => "point",
            "encoding" => Dict(
                "x" => Dict("field" => dffields[1], "type" => "quantitative"),
                "y" => Dict("field" => dffields[2], "type" => "quantitative" )
                ),
            "width"  => _plot_width,
            "height" => _plot_height
        )
    )
    return spec
end

function createspec_multiscatterplot(name, df, dffields)
    datapart = getdatapart(df, dffields, :multiscatter)
    spec = Dict(
        "name"  => name,
        "type" => "point",
        "VLspec" => Dict(
            "\$schema" => "https://vega.github.io/schema/vega-lite/v3.json",
            "description" => "plot for a specific component variable pair",
            "title" => name,
            "data"=> Dict("values" => datapart),
            "mark" => "point",
            "encoding" => Dict(
                "x" => Dict("field" => dffields[1], "type" => "quantitative"),
                "y" => Dict("field" => dffields[3], "type" => "quantitative" ),
                "color" => Dict("field" => dffields[2], "type" => "nominal",
                            "scale" => Dict("scheme" => "category20"))
                ),
            "width"  => _plot_width,
            "height" => _plot_height
        )
    )
    return spec

end

function createspec_singlevalue(name)

    datapart = [];
    spec = Dict(
        "name" => name,
        "type" => "singlevalue",
        "VLspec" => Dict()
    )
    return spec
end

##
## Methods for explore(sim_inst::SimulationInstance)
##

function createspec_trumpet(name, df, dffields; interactive::Bool=true)
    df_reduced = trumpet_df_reduce(df, :trumpet) #reduce the dataframe down to only the data needed for max, min, and mean lines
    interactive ? createspec_trumpet_interactive(name, df_reduced, dffields) : createspec_trumpet_static(name, df_reduced, dffields)
end

# https://vega.github.io/vega-lite/examples/layer_line_errorband_ci.html
function createspec_trumpet_static(name, df, dffields)

    datapart = getdatapart(df, dffields, :trumpet) #returns JSONtext type
    spec = Dict(
        "name"  => name,
        "VLspec" => Dict(
            "\$schema" => "https://vega.github.io/schema/vega-lite/v3.json",
            "description" => "plot for a specific component variable pair",
            "title" => name,
            "data"=> Dict("values" => datapart),
            "encoding" => Dict(
                "x"     => Dict(
                    "field" => dffields[1],
                    "type" => "temporal",
                    "timeUnit" => "utcyear",
                )
            ),
            "layer" => [
                Dict(
                    "mark" => "line",
                    "encoding" => Dict(
                        "y" => Dict(
                            "aggregate" => "mean",
                            "field" => dffields[2],
                            "type" => "quantitative"
                        )
                    )
                ),
                Dict(
                    "mark" => "area",
                    "encoding" => Dict(
                        "y" => Dict(
                            "aggregate" => "max",
                            "field" => dffields[2],
                            "type" => "quantitative",
                            "title" => "$(dffields[2])"
                        ),
                        "y2" => Dict(
                            "aggregate" => "min",
                            "field" => dffields[2]
                        ),
                        "opacity" => Dict(
                            "value" => 0.5
                        )
                    )
                )

            ]
        ),
        "width"  => _plot_width,
        "height" => _plot_height
    )
    return spec
end

function createspec_trumpet_interactive(name, df, dffields)

    datapart = getdatapart(df, dffields, :trumpet) #returns JSONtext type
    spec = Dict(
        "name"  => name,
        "VLspec" => Dict(
            "\$schema" => "https://vega.github.io/schema/vega-lite/v3.json",
            "description" => "plot for a specific component variable pair",
            "data"=> Dict("values" => datapart),
            "vconcat" => [
                Dict(
                    "title" => "$name",
                    "width" => _plot_width,
                    "height" => _plot_height,
                    "encoding" => Dict(
                        "x"     => Dict(
                            "field" => dffields[1],
                            "type" => "temporal",
                            "timeUnit" => "utcyear",
                            "scale" => Dict("domain" => Dict("selection" => "brush"))
                        )
                    ),
                    "layer" => [
                        Dict(
                            "mark" => "line",
                            "encoding" => Dict(
                                "y" => Dict(
                                    "aggregate" => "mean",
                                    "field" => dffields[2],
                                    "type" => "quantitative"
                                )
                            )
                        ),
                        Dict(
                            "mark" => "area",
                            "encoding" => Dict(
                                "y" => Dict(
                                    "aggregate" => "max",
                                    "field" => dffields[2],
                                    "type" => "quantitative",
                                    "title" => "$(dffields[2])"
                                ),
                                "y2" => Dict(
                                    "aggregate" => "min",
                                    "field" => dffields[2]
                                ),
                                "opacity" => Dict(
                                    "value" => 0.5
                                )
                            )
                        )

                    ]
                ),
                Dict(
                    "title" => "INTERACTIVE PLOT",
                    "width" => _plot_width,
                    "height" => _slider_height,
                    "encoding" => Dict(
                        "x"     => Dict(
                            "field" => dffields[1],
                            "type" => "temporal",
                            "timeUnit" => "utcyear"
                        )
                    ),

                    "layer" => [
                        Dict(
                            "mark" => "line",
                            "encoding" => Dict(
                                "y" => Dict(
                                    "aggregate" => "mean",
                                    "field" => dffields[2],
                                    "type" => "quantitative"
                                )
                            )
                        ),
                        Dict(
                            "mark" => "area",
                            "selection" => Dict("brush" => Dict("type" => "interval", "encodings" => ["x"])),
                            "encoding" => Dict(
                                "y" => Dict(
                                    "aggregate" => "max",
                                    "field" => dffields[2],
                                    "type" => "quantitative",
                                    "title" => "$(dffields[2])"
                                ),
                                "y2" => Dict(
                                    "aggregate" => "min",
                                    "field" => dffields[2]
                                ),
                                "opacity" => Dict(
                                    "value" => 0.5
                                )
                            )
                        )

                    ]
                )
            ]
        )
    )
    return spec
end

function createspec_multitrumpet(name, df, dffields; interactive::Bool = true)
    df_reduced = trumpet_df_reduce(df, :multitrumpet) #reduce the dataframe down to only the data needed for max, min, and mean lines
    interactive ? createspec_multitrumpet_interactive(name, df, dffields) : createspec_multitrumpet_static(name, df, dffields)
end

function createspec_multitrumpet_interactive(name, df, dffields)
    datapart = getdatapart(df, dffields, :multitrumpet) #returns JSONtext type
    spec = Dict(
        "name"  => name,
        "VLspec" => Dict(
            "\$schema" => "https://vega.github.io/schema/vega-lite/v3.json",
            "description" => "plot for a specific component variable pair",
            "title" => "$name (use top plot for interactive selection)",
            "data"=> Dict("values" => datapart),
            "vconcat" => [

            # summary graphic of all mean lines
            Dict(
                "mark" => "line",
                "width"  => _plot_width,
                "height" => _slider_height,
                "selection" => Dict("brush" => Dict("type" => "interval", "encodings" => ["x"])),
                "encoding" => Dict(
                    "x" => Dict(
                        "field" => dffields[1],
                        "type" => "temporal",
                        "timeUnit" => "utcyear"
                    ),
                    "y" => Dict(
                        "aggregate" => "mean",
                        "field" => dffields[3],
                        "type" => "quantitative",
                        "title" => "Mean $(dffields[3])"
                    ),
                    "color" => Dict("field" => dffields[2], "type" => "nominal",
                    "scale" => Dict("scheme" => "category20"))
                )
            ),

            # faceted rows
            Dict(
                "facet" => Dict("row" => Dict("field" => dffields[2], "type" => "nominal")),
                "spec" => Dict(
                        Dict(
                            "width" => _plot_width,
                            "height" => _plot_height / 2,
                            "encoding" => Dict(
                                "x"     => Dict(
                                    "field" => dffields[1],
                                    "type" => "temporal",
                                    "timeUnit" => "utcyear",
                                    "scale" => Dict("domain" => Dict("selection" => "brush"))
                                ),
                                "color" => Dict("field" => dffields[2], "type" => "nominal",
                                "scale" => Dict("scheme" => "category20"))
                            ),
                            "layer" => [
                                Dict(
                                    "mark" => "line",
                                    "encoding" => Dict(
                                        "y" => Dict(
                                            "aggregate" => "mean",
                                            "field" => dffields[3],
                                            "type" => "quantitative"
                                        ),
                                        "color" => Dict("field" => dffields[2], "type" => "nominal",
                                        "scale" => Dict("scheme" => "category20"))
                                    )
                                ),
                                Dict(
                                    "mark" => "area",
                                    "encoding" => Dict(
                                        "y" => Dict(
                                            "aggregate" => "max",
                                            "field" => dffields[3],
                                            "type" => "quantitative",
                                            "title" => "Mean $(dffields[3]) with Max/Min"
                                        ),
                                        "y2" => Dict(
                                            "aggregate" => "min",
                                            "field" => dffields[3]
                                        ),
                                        "opacity" => Dict(
                                            "value" => 0.5
                                        ),
                                        "color" => Dict("field" => dffields[2], "type" => "nominal",
                                        "scale" => Dict("scheme" => "category20"))
                                    )
                                )
                            ]
                        )
                )
            )
            ]
        )
    )
    return spec
end

function createspec_multitrumpet_static(name, df, dffields)
    datapart = getdatapart(df, dffields, :multitrumpet) #returns JSONtext type
    spec = Dict(
        "name"  => name,
        "VLspec" => Dict(
            "\$schema" => "https://vega.github.io/schema/vega-lite/v3.json",
            "description" => "plot for a specific component variable pair",
            "title" => name,
            "data" => Dict("values" => datapart),
            "vconcat" => [

            # summary graphic of all mean lines
            Dict(
                "width"  => _plot_width,
                "height" => _slider_height,
                "mark" => "line",
                "encoding" => Dict(
                    "x" => Dict(
                        "field" => dffields[1],
                        "type" => "temporal",
                        "timeUnit" => "utcyear"
                    ),
                    "y" => Dict(
                        "aggregate" => "mean",
                        "field" => dffields[3],
                        "type" => "quantitative",
                        "title" => "Mean $(dffields[3])"
                    ),
                    "color" => Dict("field" => dffields[2], "type" => "nominal",
                    "scale" => Dict("scheme" => "category20"))
                )
            ),
            # faceted rows
            Dict(
                "facet" => Dict("row" => Dict("field" => dffields[2], "type" => "nominal")),
                "spec" => Dict(
                    "width"  => _plot_width,
                    "height" => _plot_height / 2,
                    "encoding" => Dict(
                        "x"     => Dict(
                            "field" => dffields[1],
                            "type" => "temporal",
                            "timeUnit" => "utcyear"
                        ),
                        "color" => Dict("field" => dffields[2], "type" => "nominal",
                        "scale" => Dict("scheme" => "category20"))
                    ),
                    "layer" => [
                        Dict(
                            "mark" => "line",
                            "encoding" => Dict(
                                "y" => Dict(
                                    "aggregate" => "mean",
                                    "field" => dffields[3],
                                    "type" => "quantitative"
                                ),
                                "color" => Dict("field" => dffields[2], "type" => "nominal",
                                "scale" => Dict("scheme" => "category20"))
                            )
                        ),
                        Dict(
                            "mark" => "area",
                            "encoding" => Dict(
                                "y" => Dict(
                                    "aggregate" => "max",
                                    "field" => dffields[3],
                                    "type" => "quantitative",
                                    "title" => "Mean $(dffields[3]) with Min/Max"
                                ),
                                "y2" => Dict(
                                    "aggregate" => "min",
                                    "field" => dffields[3]
                                ),
                                "opacity" => Dict(
                                    "value" => 0.5
                                ),
                                "color" => Dict("field" => dffields[2], "type" => "nominal",
                                "scale" => Dict("scheme" => "category20"))
                            )
                        )
                    ]
                )
            )
            ]
        )
    )
    return spec
end

function createspec_histogram(name, df, dffields; interactive::Bool = true)
    interactive ? createspec_histogram_interactive(name, df, dffields) : createspec_histogram_static(name, df, dffields)
end

function createspec_histogram_static(name, df, dffields)
    datapart = getdatapart(df, dffields, :histogram) #returns JSONtext type
    spec = Dict(
        "name"  => name,
        "type" => "histogram",
        "VLspec" => Dict(
            "\$schema" => "https://vega.github.io/schema/vega-lite/v3.json",
            "description" => "plot for a specific component variable pair",
            "title" => name,
            "data"=> Dict("values" => datapart),
            "mark" => Dict("type" => "bar"),
            "encoding" => Dict(
                "x" => Dict(
                    "field" => dffields[1],
                    "type" => "quantitative",
                    "bin" => Dict("maxbins" => 15)
                ),
                "y" => Dict(
                    "aggregate" => "count",
                    "type" => "quantitative",
                    "title" => "count"
                )
            ),
            "width" => _plot_width,
            "height" => _plot_height
        )
    )
    return spec
end

function createspec_histogram_interactive(name, df, dffields) # for now the same as static version
    createspec_histogram_static(name, df, dffields)
end

function createspec_multihistogram(name, df, dffields; interactive::Bool = true)
    interactive ? createspec_multihistogram_interactive(name, df, dffields) : createspec_multihistogram_static(name, df, dffields)
end

function createspec_multihistogram_static(name, df, dffields)
    datapart = getdatapart(df, dffields, :multihistogram) #returns JSONtext type
    spec = Dict(
        "name"  => name,
        "VLspec" => Dict(
            "\$schema" => "https://vega.github.io/schema/vega-lite/v3.json",
            "description" => "plot for a specific component variable pair",
            "title" => name,
            "data"  => Dict("values" => datapart),
            "vconcat" => [
                # layered histogram of all
                Dict(
                    "mark" => Dict("type" => "bar"),
                    "width"  => _plot_width,
                    "height" => _slider_height,
                    "encoding" => Dict(
                        "x"     => Dict(
                            "field" => dffields[2],
                            "type" => "quantitative",
                            "bin" => Dict("maxbins" => 15)
                            ),
                        "y"     => Dict(
                            "field" => dffields[3],
                            "aggregate" => "count",
                            "type" => "quantitative",
                            "stack" => nothing
                            ),
                        "color" => Dict("field" => dffields[1], "type" => "nominal",
                            "scale" => Dict("scheme" => "category20")),
                        "opacity" => Dict("value" => 0.7)
                    )
                ),

                # faceted rows
                Dict(
                    "facet" => Dict("row" => Dict("field" => dffields[1], "type" => "nominal")),
                    "spec" => Dict(
                        Dict(
                            "mark" => Dict("type" => "bar"),
                            "width"  => _plot_width,
                            "height" => _plot_height / 2,
                            "encoding" => Dict(
                                "x"     => Dict(
                                    "field" => dffields[2],
                                    "type" => "quantitative",
                                    "bin" => Dict("maxbins" => 15)
                                    ),
                                "y"     => Dict(
                                    "field" => dffields[3],
                                    "aggregate" => "count",
                                    "type" => "quantitative",
                                    "stack" => true
                                    ),
                                "color" => Dict("field" => dffields[1], "type" => "nominal",
                                "scale" => Dict("scheme" => "category20"))
                            )
                        )
                    )
                )
            ]
        )
    )
    return spec
end

function createspec_multihistogram_interactive(name, df, dffields)
    datapart = getdatapart(df, dffields, :multihistogram) #returns JSONtext type
    spec = Dict(
        "name"  => name,
        "VLspec" => Dict(
            "\$schema" => "https://vega.github.io/schema/vega-lite/v3.json",
            "description" => "plot for a specific component variable pair",
            "title" => "$name (use top plot for interactive selection)",
            "data"  => Dict("values" => datapart),
            "vconcat" => [

                # layered histogram of all
                Dict(
                    "mark" => Dict("type" => "bar"),
                    "width"  => _plot_width,
                    "height" => _slider_height,
                    "selection" => Dict("brush" => Dict("type" => "interval", "encodings" => ["x"])),
                    "encoding" => Dict(
                        "x"     => Dict(
                            "field" => dffields[2],
                            "type" => "quantitative",
                            "bin" => Dict("maxbins" => 15)
                        ),
                        "y"     => Dict(
                            "field" => dffields[3],
                            "aggregate" => "count",
                            "type" => "quantitative",
                            "stack" => nothing
                        ),
                        "color" => Dict("field" => dffields[1], "type" => "nominal",
                            "scale" => Dict("scheme" => "category20")),
                        "opacity" => Dict("value" => 0.7)
                    )
                ),

                # faceted rows
                Dict(
                    "facet" => Dict("row" => Dict("field" => dffields[1], "type" => "nominal")),
                    "spec" => Dict(
                        Dict(
                            "mark" => Dict("type" => "bar"),
                            "width"  => _plot_width,
                            "height" => _plot_height / 2,
                            "encoding" => Dict(
                                "x"     => Dict(
                                    "field" => dffields[2],
                                    "type" => "quantitative",
                                    "bin" => Dict("maxbins" => 15),
                                    "scale" => Dict("domain" => Dict("selection" => "brush"))
                                    ),
                                "y"     => Dict(
                                    "field" => dffields[3],
                                    "aggregate" => "count",
                                    "type" => "quantitative"
                                    ),
                                "color" => Dict("field" => dffields[1], "type" => "nominal",
                                "scale" => Dict("scheme" => "category20"))
                            )
                        )
                    )
                )
            ]
        )
    )
    return spec
end

##
## Helpers
##

function dataframe_or_scalar(m::Model, comp_name::Symbol, item_name::Symbol)
    dims = dim_names(m, comp_name, item_name)
    return length(dims) > 0 ? getdataframe(m, comp_name, item_name) : m[comp_name, item_name]
end

function trumpet_df_reduce(df, plottype::Symbol)

    if plottype == :trumpet
        col_index = 2
    else
        col_index = 3
    end

    col = names(df)[col_index]
    groupby_keys = []
    for i = 1:length(names(df))
        i != col_index && push!(groupby_keys,  names(df)[i])
    end

    gd = DataFrames.groupby(df, groupby_keys)

    df_new = rename!(combine(gd, col => maximum), Symbol(col, :_maximum) => col)
    append!(df_new, rename!(combine(gd, col => minimum), Symbol(col, :_minimum) => col))
    append!(df_new, rename!(combine(gd, col => mean), Symbol(col, :_mean) => col))

    if plottype == :trumpet
        reorder_cols = [groupby_keys[1], col, groupby_keys[2:end]...]
    else
        reorder_cols = [groupby_keys[1:2]..., col, groupby_keys[3:end]...]
    end

    return df_new[:, reorder_cols]
end


text:
# defined in Makie
Base.@deprecate_binding Quaternionf0 Quaternionf
Base.@deprecate_binding RGBf0 RGBf
Base.@deprecate_binding RGBAf0 RGBAf

# defined in GeometryBasics and reexported by Makie
export FRect, FRect2D, IRect2D, FRect3D, IRect3D, Rect3D, IRect, FRect, Rect2D
export Vec4f0, Vec3f0, Vec2f0, Point4f0, Point3f0, Point2f0


text:
ENV["XLSXWRITER_PATH"] = "C:\\Users\\matthew.heath\\repos\\XlsxWriter.py"

using SHA
using XlsxWriter
using Test

broadcast(rm, filter(f->endswith(f, ".xlsx"), readdir()));

filehash(fn) = open(fn) do f  bytes2hex(sha2_256(f))  end

@testset "XlsxWriter.jl" begin
	@test include("array_formula.jl")
	@test include("autofilter.jl")
	@test include("cell_indentation.jl")
	@test include("chart.jl")
	@test include("chart_area.jl")
	@test include("chart_bar.jl")
	@test include("chart_clustered.jl")
	@test include("chart_column.jl")
	@test include("chart_combined.jl")
	@test include("chart_data_table.jl")
	@test include("chart_data_tools.jl")
	@test include("chart_date_axis.jl")
	@test include("chart_doughnut.jl")
	@test include("chart_gradient.jl")
	@test include("chart_line.jl")
	@test include("chart_pareto.jl")
	@test include("chart_pattern.jl")
	@test include("chart_pie.jl")
	@test include("chart_radar.jl")
	@test include("chart_scatter.jl")
	@test include("chart_secondary_axis.jl")
	@test include("chart_stock.jl")
	@test include("chart_styles.jl")
	@test include("chartsheet.jl")
	@test include("comments1.jl")
	@test include("comments2.jl")
	@test include("conditional_format.jl")
	@test include("demo.jl")
	@test include("in_memory.jl")
end

println("All this actually tests for is that the code ran and produced .xlsx files.\nWhich is better than nothing.\nThe output is not necessarily correct.")


text:
function CholeskyL(A::Array)

  #A = the matrix of coefficients 'A' must be squared

    m,n =size(A)

    if m != n
        error("Matrix must be squared")
    end

    # System of equations
    L=zeros(m,m)

    L[1,1]=A[1,1]^0.5

    for k=2:m

    for i=1:k-1
       L[k,i]=(A[k,i]-(L[i,1:i-1]'*L[k,1:i-1]))/(L[i,i])

    end
          L[k,k]=(A[k,k]-(L[k,1:k-1]'*L[k,1:k-1]))^0.5
    end

    return L

end


text:
using EDClust
using Test
using SafeTestsets
using Random

@testset "EDClust.jl" begin

end


text:
using LogExpFunctions, Test

include("basicfuns.jl")


text:
# Autogenerated wrapper script for SwiftShader_jll for x86_64-w64-mingw32
export libvulkan

JLLWrappers.@generate_wrapper_header("SwiftShader")
JLLWrappers.@declare_library_product(libvulkan, "vk_swiftshader.dll")
function __init__()
    JLLWrappers.@generate_init_header()
    JLLWrappers.@init_library_product(
        libvulkan,
        "bin\\vk_swiftshader.dll",
        RTLD_LAZY | RTLD_DEEPBIND,
    )

    JLLWrappers.@generate_init_footer()
end  # __init__()