text:
type EasyMessage
  pts::Array{Float64,2}
  bws::Array{Float64,1}
end

type NBPMessage <: Singleton
  p::Dict{Int64,EasyMessage}
end
# type NBPMessage <: Singleton
#     Xi::Array{Int64,1}
#     p::Array{BallTreeDensity,1}
# end

type PotProd
    Xi::Int64
    prev::Array{Float64,2}
    product::Array{Float64,2}
    potentials::Array{BallTreeDensity,1}
end
type CliqGibbsMC
    prods::Array{PotProd,1}
end
type DebugCliqMCMC
    mcmc::Array{CliqGibbsMC,1}
    outmsg::NBPMessage
end

type UpReturnBPType
    upMsgs::NBPMessage #Dict{Int64, Array{Float64,2}}#
    dbgUp::DebugCliqMCMC
    IDvals::Dict{Int64, Array{Float64,2}}
end

type DownReturnBPType
    dwnMsg::NBPMessage
    dbgDwn::DebugCliqMCMC
    IDvals::Dict{Int64,Array{Float64,2}}
end

type ExploreTreeType
  fg::FactorGraph
  bt::BayesTree
  cliq::Graphs.ExVertex
  prnt::Any
  sendmsgs::Array{NBPMessage,1}
end

type MsgPassType
  fg::FactorGraph
  cliq::Graphs.ExVertex
  vid::Int
  msgs::Array{NBPMessage,1} #dwnMsgs
  N::Int
end

# type UpMsgPassType
#   fg::FactorGraph
#   cliq::Graphs.ExVertex
#   upMsgs::Array{NBPMessage,1}
#   N::Int
# end

#global pidx
pidx = 1
pidl = 1
pidA = 1
thxl = nprocs() > 4 ? floor(Int,nprocs()*0.333333333) : 1

# upploc to control processes done local to this machine and separated from other
# highly loaded processes. upploc() should be used for dispatching short and burst
# of high bottle neck computations. Use upp2() for general multiple dispatch.
function upploc()
    global pidl, thxl
    N = nprocs()
    pidl = (thxl > 1 ? ((pidl < thxl && pidl != 0 && thxl > 2) ? (pidl+1)%(thxl+1) : 2) : (N == 1 ? 1 : 2))
    return pidl
end

# upp2() may refer to processes on a different machine. Do not associate upploc()
# with processes on a separate computer -- this will be become more complicated when
# remote processes desire their own short fast 'local' burst computations. We
# don't want all the data traveling back and forth for shorter jobs
function upp2()
  global pidx, thxl
  N = nprocs()
  pidx = (N > 1 ? ((pidx < N && pidx != 0) ? (pidx+1)%(N+1) : thxl+1) : 1) #2 -- thxl+1
  return pidx
end

function uppA()
  global pidA
  N = nprocs()
  pidA = (N > 1 ? ((pidA < N && pidA != 0) ? (pidA+1)%(N+1) : 2) : 1) #2 -- thxl+1
  return pidA
end

function packFromIncomingDensities!(dens::Array{BallTreeDensity,1}, vertid::Int64, inmsgs::Array{NBPMessage,1})
    for m in inmsgs
        #@show Xi = keys(m.p)
        for idx in keys(m.p) #1:length(Xi)
            if idx == vertid #Xi[idx]
                pdi = m.p[vertid]
                push!(dens, kde!(pdi.pts, pdi.bws) ) #m.p[idx]
            end
            # TODO -- we can inprove speed of search for inner loop
        end
    end
    nothing
end

# add all potentials associated with this clique and vertid to dens
function packFromLocalPotentials!(fgl::FactorGraph, dens::Array{BallTreeDensity,1}, cliq::Graphs.ExVertex, vertid::Int64, N::Int64)
    for idfct in cliq.attributes["potentials"]
        for vertidx in idfct[2].attributes["data"].fncargvID #for vert in idfct[2].attributes["fnc"].Xi
            if vertidx == vertid
              p = findRelatedFromPotential(fgl, idfct[2], vertid, N)
              push!(dens, p)
            end
        end
        # for vert in idfct[2].attributes["data"].fnc.Xi #for vert in idfct[2].attributes["fnc"].Xi
        #     if vert.index == vertid
        #       p = findRelatedFromPotential(fgl, idfct[2], vertid, N)
        #       push!(dens, p)
        #     end
        # end
    end
    nothing
end

function cliqGibbs(fg::FactorGraph, cliq::Graphs.ExVertex, vertid::Int64, inmsgs::Array{NBPMessage,1}, N::Int=200)
    # several optimizations can be performed in this function TODO
    print("$(dlapi.getvertex(fg,vertid).attributes["label"]) ") # "$(fg.v[vertid].attributes["label"]) "
    #consolidate NBPMessages and potentials
    dens = Array{BallTreeDensity,1}()
    packFromIncomingDensities!(dens, vertid, inmsgs)
    packFromLocalPotentials!(fg, dens, cliq, vertid, N)
    # testing
    # end testing
    potprod = PotProd(vertid, getVal(dlapi.getvertex(fg,vertid)), Array{Float64,2}(), dens) # (fg.v[vertid])

    pGM = Array{Float64,2}()
    if length(dens) > 1
        Ndims = dens[1].bt.dims
        dummy = kde!(rand(Ndims,N),[1.0]);
        print("[$(length(dens)) prod. d$(Ndims),N$(N)],")
        pGM, = prodAppxMSGibbsS(dummy, dens, Union{}, Union{}, 8) #10
        #pGM, = remoteProdAppxMSGibbsS(dummy, dens, Union{}, Union{})
        # sum(abs(pGM))<1e-14 ? error("cliqGibbs -- nothing in pGM") : nothing
    elseif length(dens) == 1
        print("[direct]")
        pGM = reshape(dens[1].bt.centers[(dens[1].bt.dims*Npts(dens[1])+1):end], dens[1].bt.dims, Npts(dens[1]))  #N
    else
        pGM = Array{Float64,2}(0,1)
    end
    potprod.product = pGM
    print(", ")

    return pGM, potprod
end

function fmcmc!(fgl::FactorGraph, cliq::Graphs.ExVertex, fmsgs::Array{NBPMessage,1}, IDs::Array{Int64,1}, N::Int64, MCMCIter::Int)
    println("------------- functional mcmc ----------------$(cliq.attributes["label"])")
    # repeat several iterations of functional Gibbs sampling for fixed point convergence
    if length(IDs) == 1
        MCMCIter=1
    end
    mcmcdbg = Array{CliqGibbsMC,1}()

    for iter in 1:MCMCIter
        # iterate through each of the variables, KL-divergence tolerence would be nice test here
        print("#$(iter)\t -- ")
        dbg = CliqGibbsMC([])
        for vertid in IDs
          # we'd like to do this more pre-emptive and then just execute -- just point and skip up only msgs
            densPts, potprod = cliqGibbs(fgl, cliq, vertid, fmsgs, N) #cliqGibbs(fg, cliq, vertid, fmsgs, N)
            if size(densPts,1)>0
                updvert = dlapi.getvertex(fgl,vertid)
                setValKDE!(updvert, densPts) # fgl.v[vertid]
                # Go update the datalayer TODO -- excessive for general case
                dlapi.updatevertex!(fgl, updvert)
                # fgl.v[vertid].attributes["val"] = densPts
                push!(dbg.prods, potprod)
            end
        end
        push!(mcmcdbg, dbg)
        println("")
    end

    # populate dictionary for return NBPMessage in multiple dispatch
    # TODO -- change to EasyMessage dict
    d = Dict{Int64,Array{Float64,2}}()
    for vertid in IDs
        d[vertid] = getVal(dlapi.getvertex(fgl,vertid)) # fgl.v[vertid]
    end
    println("fmcmc! -- finished on $(cliq.attributes["label"])")

    return mcmcdbg, d
end

# function compOutMsgsProd(mpt::MsgPassType)
#     # TODO -- very very expensive function since all fg data is copied on remotecall
#     compIDVals = Array{Float64,2}()
#     compIDVals, = cliqGibbs(mpt.fg, mpt.cliq, mpt.vid, mpt.msgs, mpt.N)
#     if size(compIDVals,1) == 0
#         compIDVals = getVal(mpt.fg.v[mpt.vid])
#     end
#     p = kde!(compIDVals, "lcv")
#     bws = vec(getBW(p)[:,1])
#     return EasyMessage(compIDVals, bws)
# end
#
# # returns output NBPMessage, based on projection to IDs
# function upPrepOutMsg!(fgl::FactorGraph, cliq::Graphs.ExVertex,
#                   inMsgs::Array{NBPMessage,1}, IDs::Array{Int64,1}, N::Int)
#
#     print("Outgoing msg density on: ")
#     len = length(IDs)
#     m = NBPMessage(Dict{Int64,EasyMessage}()) #Int64[], BallTreeDensity[]
#   rr = Array{RemoteRef,1}()
#     i = 0
#     for vid in IDs
#         i+=1
#         mpt = MsgPassType(fgl, cliq, vid, inMsgs, N) # TODO -- should this be copied??
#         if true
#           push!(rr,remotecall(upploc() , compOutMsgsProd, mpt) )
#         else
#           outp = compOutMsgsProd(mpt)
#           m.p[vid] = outp
#         end
#     end
#     i = 0
#     for r in rr
#         i +=1
#         outp = fetch(r)
#         m.p[IDs[i]] = outp
#     end
#     println()
#
#     return m
# end

function upPrepOutMsg!(d::Dict{Int64,Array{Float64,2}}, IDs::Array{Int64,1})
  print("Outgoing msg density on: ")
  len = length(IDs)
  #outDens = Dict{Int64,BallTreeDensity}() # retired to new dictionary msg types
  m = NBPMessage(Dict{Int64,EasyMessage}()) #Int64[], BallTreeDensity[]

  for id in IDs
    p = kde!(d[id], "lcv")
    bws = vec(getBW(p)[:,1])
    m.p[id] = EasyMessage(d[id], bws)
  end
  return m
end

function upGibbsCliqueDensity(inp::ExploreTreeType, N::Int=200)
    print("up w $(length(inp.sendmsgs)) msgs")
    # Loval mcmc over belief functions
    # this is so slow! TODO Can be ignored once we have partial working
    # loclfg = nprocs() < 2 ? deepcopy(inp.fg) : inp.fg

    d = Union{}
    mcmcdbg = Union{}
    if false
      IDS = [inp.cliq.attributes["frontalIDs"];inp.cliq.attributes["conditIDs"]] #inp.cliq.attributes["frontalIDs"]
      mcmcdbg, d = fmcmc!(inp.fg, inp.cliq, inp.sendmsgs, IDS, N, 3)
    elseif true
      dummy, d = fmcmc!(inp.fg, inp.cliq, inp.sendmsgs, inp.cliq.attributes["directFrtlMsgIDs"], N, 1)
      if length(inp.cliq.attributes["msgskipIDs"]) > 0
        dummy, dd = fmcmc!(inp.fg, inp.cliq, inp.sendmsgs, inp.cliq.attributes["msgskipIDs"], N, 1)
        for md in dd d[md[1]] = md[2]; end
      end
      if length(inp.cliq.attributes["itervarIDs"]) > 0
        mcmcdbg, ddd = fmcmc!(inp.fg, inp.cliq, inp.sendmsgs, inp.cliq.attributes["itervarIDs"], N, 3)
        for md in ddd d[md[1]] = md[2]; end
      end
      # TODO -- do direct conditionals from msg also, before transits and iterations are done.
      if length(inp.cliq.attributes["directvarIDs"]) > 0
        dummy, dddd = fmcmc!(inp.fg, inp.cliq, inp.sendmsgs, inp.cliq.attributes["directvarIDs"], N, 1)
        for md in dddd d[md[1]] = md[2]; end
      end
    end

    #m = upPrepOutMsg!(inp.fg, inp.cliq, inp.sendmsgs, condids, N)
    m = upPrepOutMsg!(d, inp.cliq.attributes["conditIDs"])

    # Copy frontal variables back
    # for id in inp.cliq.attributes["frontalIDs"]
    #     inp.fg.v[id].attributes["val"] = loclfg.v[id].attributes["val"] # inp.
    # end
    # @show getVal(inp.fg.v[1])

    mdbg = DebugCliqMCMC(mcmcdbg, m)
    return UpReturnBPType(m, mdbg, d)
end
# else
#   rr = Array{RemoteRef,1}(4)
#   rr[1] = remotecall(upp2(), fmcmc!, inp.fg, inp.cliq, inp.sendmsgs, inp.cliq.attributes["directFrtlMsgIDs"], N, 1)
#   rr[2] = remotecall(upp2(), fmcmc!, inp.fg, inp.cliq, inp.sendmsgs, inp.cliq.attributes["msgskipIDs"], N, 1)
#   rr[3] = remotecall(upp2(), fmcmc!, inp.fg, inp.cliq, inp.sendmsgs, inp.cliq.attributes["itervarIDs"], N, 3)
#   rr[4] = remotecall(upp2(), fmcmc!, inp.fg, inp.cliq, inp.sendmsgs, inp.cliq.attributes["directvarIDs"], N, 1)
#   dummy, d = fetch(rr[1])
#   dummy, dd = fetch(rr[2])
#   mcmcdbg, ddd = fetch(rr[3])
#   dummy, dddd = fetch(rr[4])
#
#   for md in dd d[md[1]] = md[2]; end
#   for md in ddd d[md[1]] = md[2]; end
#   for md in dddd d[md[1]] = md[2]; end

function dwnPrepOutMsg(fg::FactorGraph, cliq::Graphs.ExVertex, dwnMsgs::Array{NBPMessage,1}, d::Dict{Int64, Array{Float64,2}})
    # pack all downcoming conditionals in a dictionary too.
    print("Outgoing msg density on: ")
    # cdwndict = Dict{Int64, BallTreeDensity}()
    # for cvid in cliq.attributes["conditIDs"] # root has no parent
    #     prtdwnmsg = dwnMsgs[1] # there is only one dwn incoming NBPMessage
    #     Xi = keys(prtdwnmsg)
    #     for i in 1:length(Xi) # prtdwnmsg.Xi
    #         if Xi[i] == cvid
    #             print("$(fg.v[cvid].attributes["label"]), ")
    #             p = prtdwnmsg.p[cvid]
    #             cdwndict[cvid] = kde!(p.pts, p.bws)
    #         end
    #     end
    # end
    # println()
    # allIDs = [cliq.attributes["frontalIDs"]; cliq.attributes["conditIDs"] ]
    # outDens = Array{BallTreeDensity,1}(length(allIDs))
    m = NBPMessage(Dict{Int64,EasyMessage}())
    i = 0
    for vid in cliq.attributes["frontalIDs"]
        outp = kde!(d[vid], "lcv") # need to find new down msg bandwidths
        # i+=1
        # outDens[i] = outp
        bws = vec((getBW(outp))[:,1])
        m.p[vid] = EasyMessage(  deepcopy(d[vid]) , bws  )
    end
    for cvid in cliq.attributes["conditIDs"]
        i+=1
        # TODO -- convert to points only since kde replace by rkhs in future
        # outDens[i] = cdwndict[cvid]
        m.p[cvid] = deepcopy(dwnMsgs[1].p[cvid]) # TODO -- maybe this can just be a union(,)
    end

    # NBPMessage(deepcopy(allIDs), deepcopy(outDens))
    return m
end

function downGibbsCliqueDensity(fg::FactorGraph, cliq::Graphs.ExVertex, dwnMsgs::Array{NBPMessage,1}, N::Int=200, MCMCIter::Int=3)
    print("dwn")
    mcmcdbg, d = fmcmc!(fg, cliq, dwnMsgs, cliq.attributes["frontalIDs"], N, MCMCIter)
    m = dwnPrepOutMsg(fg, cliq, dwnMsgs, d)

    mdbg = DebugCliqMCMC(mcmcdbg, m)
    return DownReturnBPType(m, mdbg, d)
end

function updateFGBT!(fg::FactorGraph, bt::BayesTree, cliqID::Int64, ddt::DownReturnBPType)
    # if dlapi.cgEnabled
    #   return nothing
    # end
    cliq = bt.cliques[cliqID]
    # cliq.attributes["debugDwn"] = deepcopy(ddt.dbgDwn) #inp.
    for dat in ddt.IDvals
      #TODO -- should become an update call
        updvert = dlapi.getvertex(fg,dat[1])
        setValKDE!(updvert, deepcopy(dat[2])) # TODO -- not sure if deepcopy is required
        # fg.v[dat[1]].attributes["val"] = deepcopy(dat[2]) # inp.
        dlapi.updatevertex!(fg, updvert)
    end
    nothing
end

function updateFGBT!(fg::FactorGraph, bt::BayesTree, cliqID::Int64, urt::UpReturnBPType)
    # if dlapi.cgEnabled
    #   return nothing
    # end
    cliq = bt.cliques[cliqID]
    # cliq.attributes["debug"] = deepcopy(urt.dbgUp) #inp.
    for dat in urt.IDvals
      updvert = dlapi.getvertex(fg,dat[1])
      setValKDE!(updvert, deepcopy(dat[2])) # (fg.v[dat[1]], ## TODO -- not sure if deepcopy is required
      # fg.v[dat[1]].attributes["val"] = deepcopy(dat[2]) # inp.
      dlapi.updatevertex!(fg, updvert)
    end
    println("updateFGBT! up -- finished updating $(cliq.attributes["label"])")
    nothing
end

# pass NBPMessages back down the tree -- pre order tree traversal
function downMsgPassingRecursive(inp::ExploreTreeType; N::Int=200)
    println("====================== Clique $(inp.cliq.attributes["label"]) =============================")

    mcmciter = inp.prnt != Union{} ? 3 : 0; # skip mcmc in root on dwn pass
    rDDT = downGibbsCliqueDensity(inp.fg, inp.cliq, inp.sendmsgs, N, mcmciter) #dwnMsg
    updateFGBT!(inp.fg, inp.bt, inp.cliq.index, rDDT)

    # rr = Array{RemoteRef,1}()
    pcs = procs()

    ddt=Union{}
    for child in out_neighbors(inp.cliq, inp.bt.bt)
        ett = ExploreTreeType(inp.fg, inp.bt, child, inp.cliq, [rDDT.dwnMsg])#inp.fg
        ddt = downMsgPassingRecursive( ett , N=N )
        # updateFG!(inp, ddt)
        #r = remotecall(upp2() , downMsgPassingRecursive, ett) #pidx
        #push!(rr, r)
        ##push!(ETT, ett)
        ##r = @spawn downMsgCliquePotentials( ett ) # loss of type information
    end
    # for r in rr
    #     rDDT = fetch(r)
    #     # calling updateFG here is obsolete and probably wrong at this point
    #     println("dwnMsgPR -- calling updateFGBT! on $(inp.cliq.attributes["label"])...")
    #     updateFGBT!(inp.fg, inp.bt, inp.cliq.index, rDDT) # this runs in the main thread (NBPMessage being passed back to proc1)
    # end

    # return modifications to factorgraph to calling process
    # TODO -- is this the right information to be passing down?
    return ddt
end

# post order tree traversal and build potential functions
function upMsgPassingRecursive(inp::ExploreTreeType; N::Int=200) #upmsgdict = Dict{Int64, Array{Float64,2}}()
    println("Start Clique $(inp.cliq.attributes["label"]) =============================")
    childMsgs = Array{NBPMessage,1}()

    len = length(out_neighbors(inp.cliq, inp.bt.bt))
    for child in out_neighbors(inp.cliq, inp.bt.bt)
        ett = ExploreTreeType(inp.fg, inp.bt, child, inp.cliq, NBPMessage[]) # ,Union{})
        println("upMsgRec -- calling new recursive on $(ett.cliq.attributes["label"])")
        # newmsgs = Dict{Int64, Array{Float64,2}}()
        newmsgs = upMsgPassingRecursive(  ett, N=N ) # newmsgs
        println("upMsgRec -- finished with $(ett.cliq.attributes["label"]), w $(keys(newmsgs.p)))")
          # Xi = Array{Int64,1}()
          # p = Array{BallTreeDensity,1}()
          # for umsg in newmsgs
          #     push!(Xi, umsg[1])
          #     push!(p, kde!(umsg[2], "lcv"))
          # end
          # push!(  childMsgs, NBPMessage(Xi, p))
        push!(  childMsgs, newmsgs )
    end

    println("====================== Clique $(inp.cliq.attributes["label"]) =============================")
    ett = ExploreTreeType(inp.fg, inp.bt, inp.cliq, Union{}, childMsgs)

    # @show "BEFORE", getVal(ett.fg.v[1])
    # upmsg = Dict{Int64, Array{Float64,2}}()
    urt = upGibbsCliqueDensity(ett, N) # upmsgdict
    updateFGBT!(inp.fg, inp.bt, inp.cliq.index, urt)
    # @show "AFTER", getVal(ett.fg.v[1])
    println("End Clique $(inp.cliq.attributes["label"]) =============================")
    urt.upMsgs
end

function downGibbsCliqueDensity(inp::ExploreTreeType, N::Int=200)
  println("=================== Iter Clique $(inp.cliq.attributes["label"]) ===========================")
  mcmciter = inp.prnt != Union{} ? 3 : 0
  return downGibbsCliqueDensity(inp.fg, inp.cliq, inp.sendmsgs, N, mcmciter)
end

function prepDwnPreOrderStack!(bt::BayesTree, parentStack::Array{Graphs.ExVertex,1})
  # dwn message passing function
  nodedata = Union{}
  tempStack = Array{Graphs.ExVertex,1}()
  push!(tempStack, parentStack[1])

  while ( length(tempStack) != 0 || nodedata != Union{} )
    if nodedata != Union{}
      for child in out_neighbors(nodedata, bt.bt) #nodedata.cliq, nodedata.bt.bt
          ## TODO -- don't yet have the NBPMessage results to propagate belief
          ## should only construct ett during processing
          push!(parentStack, child) #ett
          push!(tempStack, child) #ett
      end
      nodedata = Union{} # its over but we still need to complete computation in each of the leaves of the tree
    else
      nodedata = tempStack[1]
      deleteat!(tempStack, 1)
    end
  end
  nothing
end

function findVertsAssocCliq(fgl::FactorGraph, cliq::Graphs.ExVertex)

  IDS = [cliq.attributes["frontalIDs"];cliq.attributes["conditIDs"]] #inp.cliq.attributes["frontalIDs"]

  error("findVertsAssocCliq -- not completed yet")
  nothing
end

function partialExploreTreeType(pfg::FactorGraph, pbt::BayesTree, cliqCursor::Graphs.ExVertex, prnt, pmsgs::Array{NBPMessage,1})
    # println("starting pett")
    # TODO -- expand this to grab only partial subsection from the fg and bt data structures

    if length(pmsgs) < 1
      return ExploreTreeType(pfg, pbt, cliqCursor, prnt, NBPMessage[])
    else
      return ExploreTreeType(pfg, pbt, cliqCursor, prnt, pmsgs)
    end
    nothing
end

function dispatchNewDwnProc!(fg::FactorGraph, bt::BayesTree, parentStack::Array{Graphs.ExVertex,1}, stkcnt::Int64, refdict::Dict{Int64,RemoteRef}; N::Int=200)
  #ddt = visitNode(nodedata[2])
  #updateFG!(nodedata[2], ddt)
  cliq = parentStack[stkcnt]
  # while !haskey(cliq.attributes, "dwnremoteref") # nodedata.cliq
  while !haskey(refdict, cliq.index) # nodedata.cliq
    #println("Sleeping on lack of remoteref")
    sleep(0.25)
  end

  # rDDT = fetch(cliq.attributes["dwnremoteref"]) #nodedata.cliq
  # delete!(cliq.attributes, "dwnremoteref") # nodedata
  rDDT = fetch(refdict[cliq.index]) #nodedata.cliq
  delete!(refdict,cliq.index) # nodedata

  if rDDT != Union{}
    updateFGBT!(fg, bt, cliq.index, rDDT)
  end

  emptr = BayesTree(Union{}, 0, Dict{Int,Graphs.ExVertex}(), Dict{ASCIIString,Int64}());

  for child in out_neighbors(cliq, bt.bt) # nodedata.cliq, nodedata.bt.bt
      #haskey(child.attributes, "dwnremoteref") ? error("dispatchNewDwnProc! -- why you already have dwnremoteref?") : nothing
      haskey(refdict, child.index) ? error("dispatchNewDwnProc! -- why you already have dwnremoteref?") : nothing

      ett = partialExploreTreeType(fg, emptr, child, cliq, [rDDT.dwnMsg]) # bt
      #child.attributes["dwnremoteref"] = remotecall(upp2() , downGibbsCliqueDensity, ett, N) # pidxI[1]
      refdict[child.index] = remotecall(upp2() , downGibbsCliqueDensity, ett, N) # pidxI[1]
  end
  nothing
end

function processPreOrderStack!(fg::FactorGraph, bt::BayesTree, parentStack::Array{Graphs.ExVertex,1}, refdict::Dict{Int64,RemoteRef}; N::Int=200)
    # dwn message passing function for iterative tree exploration
    # @show length(parentStack)
    stkcnt = 0

    @sync begin
      while ( stkcnt < length(parentStack) ) # || nodedata != Union{}
          stkcnt += 1
          @async dispatchNewDwnProc!(fg, bt, parentStack, stkcnt, refdict, N=N) #pidxI,nodedata
      end
    end
    nothing
end

function downMsgPassingIterative!(startett::ExploreTreeType; N::Int=200)
  # this is where we launch the downward iteration process from
  parentStack = Array{Graphs.ExVertex,1}()
  refdict = Dict{Int64,RemoteRef}()

  # start at the given clique in the tree -- shouldn't have to be the root.
  # haskey(startett.cliq.attributes, "dwnremoteref") ? error("downMsgPassingIterative! -- why you already have dwnremoteref?") : nothing
  pett = partialExploreTreeType(startett.fg, startett.bt, startett.cliq,
                                        startett.prnt, startett.sendmsgs)
  # startett.cliq.attributes["dwnremoteref"] = remotecall(upp2(), downGibbsCliqueDensity, pett, N) #startett)
  refdict[startett.cliq.index] = remotecall(upp2(), downGibbsCliqueDensity, pett, N)

  push!(parentStack, startett.cliq ) # r

  prepDwnPreOrderStack!(startett.bt, parentStack)
  processPreOrderStack!(startett.fg, startett.bt, parentStack, refdict, N=N)

  println("dwnward leftovers, $(keys(refdict))")
  # println("dwnStk counter at end is $(stkcnt), length(parentStack) = $(length(parentStack))")
  nothing
end

function prepPostOrderUpPassStacks!(bt::BayesTree, parentStack::Array{Graphs.ExVertex,1}, childStack::Array{Graphs.ExVertex,1})
  # upward message passing preparation
  while ( length(parentStack) != 0 )
      #2.1 Pop a node from first stack and push it to second stack
      cliq = parentStack[end]
      deleteat!(parentStack, length(parentStack))
      push!(childStack, cliq )

      #2.2 Push left and right children of the popped node to first stack
      for child in out_neighbors(cliq, bt.bt) # nodedata.cliq, nodedata.bt.bt
          push!(parentStack, child )
      end
  end
  for child in childStack
    @show child.attributes["label"]
  end
  nothing
end

function asyncProcessPostStacks!(fgl::FactorGraph, bt::BayesTree, chldstk::Array{Graphs.ExVertex,1}, stkcnt::Int, refdict::Dict{Int64,RemoteRef};
                                  N::Int=200)
  # for up message passing
  cliq = chldstk[stkcnt]
  gomulti = true
  println("Start Clique $(cliq.attributes["label"]) =============================")
  childMsgs = Array{NBPMessage,1}()
  # println("asyncProcessPostStacks -- new async at stkcnt=$(stkcnt), cliq=$(cliq.attributes["label"]) has $(length(out_neighbors(cliq, bt.bt))) children")
  ur = Union{}
  for child in out_neighbors(cliq, bt.bt) #nodedata.cliq, nodedata.bt.bt
      println("asyncProcessPostStacks -- $(stkcnt), cliq=$(cliq.attributes["label"]), start on child $(child.attributes["label"]) haskey=$(haskey(child.attributes, "remoteref"))")

        #while !haskey(child.attributes, "remoteref")
        while !haskey(refdict, child.index)
          # println("Sleeping $(cliq.attributes["label"]) on lack of remoteref from $(child.attributes["label"])")
          sleep(0.25)
        end

      if gomulti
        #ur = fetch(child.attributes["remoteref"])
        ur = fetch(refdict[child.index])
      else
        ur = child.attributes["remoteref"]
      end
      updateFGBT!( fgl, bt, child.index, ur ) # deep copies happen in the update function
      #delete!(child.attributes, "remoteref")

      push!(childMsgs, ur.upMsgs)

  end
  println("====================== Clique $(cliq.attributes["label"]) =============================")
  #sleep(2.0) # delay sometimes makes it work -- we might have a race condition
  emptr = BayesTree(Union{}, 0, Dict{Int,Graphs.ExVertex}(), Dict{ASCIIString,Int64}());
  pett = partialExploreTreeType(fgl, emptr, cliq, Union{}, childMsgs) # bt   # parent cliq pointer is not needed here, fix Graphs.jl first

  if haskey(cliq.attributes, "remoteref")
      println("asyncProcessPostStacks! -- WHY YOU ALREADY HAVE REMOTEREF?")
  end

  newprocid = upp2()
  #println("asyncProcessPostStacks -- making remote call to $(newprocid) for $(pett.cliq.attributes["label"]) with $(length(pett.sendmsgs))")
  if gomulti
    #cliq.attributes["remoteref"] = remotecall(newprocid, upGibbsCliqueDensity, pett, N )
    refdict[cliq.index] = remotecall(newprocid, upGibbsCliqueDensity, pett, N )
  else
    cliq.attributes["remoteref"] = upGibbsCliqueDensity(pett, N)
  end

  # delete as late as possible, but could happen sooner
  for child in out_neighbors(cliq, bt.bt)
      # delete!(child.attributes, "remoteref")
      delete!(refdict, child.index)
  end

  println("End Clique $(cliq.attributes["label"]) =============================")
  nothing
end

      # Xi = Array{Int64,1}()
      # p = Array{BallTreeDensity,1}()
      # for umsg in ur.upMsgs
      #     push!(Xi, umsg[1])
      #     push!(p, kde!(umsg[2], "lcv"))
      # end
      # push!(  childMsgs, NBPMessage(Xi, p))

function processPostOrderStacks!(fg::FactorGraph, bt::BayesTree, childStack::Array{Graphs.ExVertex,1}, N::Int=200)
  # upward message passing function

  refdict = Dict{Int64,RemoteRef}()

  stkcnt = length(childStack)
  @sync begin
    while (stkcnt > 0) #length(childStack)
        #nodedata = childStack[end]
        #deleteat!(childStack, length(childStack))
        @async asyncProcessPostStacks!(fg, bt, childStack, stkcnt, refdict, N=N)
        stkcnt -= 1
    end
  end
  println("processPostOrderStacks! -- THIS ONLY HAPPENS AFTER SYNC")
  # we still need to fetch the root node computational output
  # println("processPostOrderStacks! -- going to fetch remoteref")
  # @show childStack[1].attributes["remoteref"]
  if true
    # ur = fetch(childStack[1].attributes["remoteref"])
    ur = fetch(refdict[childStack[1].index])
  else
    ur = childStack[1].attributes["remoteref"]
  end
  # delete!(childStack[1].attributes, "remoteref") # childStack[1].cliq
  delete!(refdict, childStack[1].index)

  println("upward leftovers, $(keys(refdict))")

  updateFGBT!(fg, bt, childStack[1].index, ur ) # nodedata
  nothing
end

function upMsgPassingIterative!(startett::ExploreTreeType; N::Int=200)
  #http://www.geeksforgeeks.org/iterative-postorder-traversal/
  # this is where we launch the downward iteration process from
  parentStack = Array{Graphs.ExVertex,1}()
  childStack = Array{Graphs.ExVertex,1}()
  #Loop while first stack is not empty
  push!(parentStack, startett.cliq )
  # Starting at the root means we have a top down view of the tree
  prepPostOrderUpPassStacks!(startett.bt, parentStack, childStack)
  # println("upMsgPassingIterative! -- okay lets work through the childStack")
  processPostOrderStacks!(startett.fg, startett.bt, childStack, N)
  nothing
end

function inferOverTree!(fgl::FactorGraph, bt::BayesTree; N::Int=200)
    println("Do inference over tree")
    # Profile.clear()
    cliq = bt.cliques[1]
    upMsgPassingIterative!(ExploreTreeType(fgl, bt, cliq, Union{}, NBPMessage[]),N=N);
    cliq = bt.cliques[1]
    downMsgPassingIterative!(ExploreTreeType(fgl, bt, cliq, Union{}, NBPMessage[]),N=N);
    nothing
end

function inferOverTreeR!(fgl::FactorGraph, bt::BayesTree; N::Int=200)
    println("Do inference over tree")
    # Profile.clear()
    cliq = bt.cliques[1]
    upMsgPassingRecursive(ExploreTreeType(fgl, bt, cliq, Union{}, NBPMessage[]), N=N);
    cliq = bt.cliques[1]
    downMsgPassingRecursive(ExploreTreeType(fgl, bt, cliq, Union{}, NBPMessage[]), N=N);
    nothing
end

### was downGibbsCliqueDensity
# if length(IDs) == 1
#     MCMCIter=1
# end
#
# mcmcdbg = Array{CliqGibbsMC,1}()
# for iter in 1:MCMCIter
#     dbg = CliqGibbsMC([])
#     # iterate through each of the frontal variables in the clique, KL-divergence tolerence would be better
#     print("#$(iter)\t -- ")
#     for vertid in IDs
#         densPts, potprod = cliqGibbs(fg, cliq, vertid, dwnMsgs, N)
#         if size(densPts,1)>0
#             fg.v[vertid].attributes["val"] = densPts
#             push!(dbg.prods, potprod)
#         end
#     end
#     push!(mcmcdbg, dbg)
#     println("")
# end

# populate dictionary for return NBPMessage in multiple dispatch
# d = Dict{Int64,Array{Float64,2}}()
# for vertid in IDs
#     d[vertid] = fg.v[vertid].attributes["val"]
# end
# print("Outgoing msg density on: ")
# outDens = Array{BallTreeDensity,1}(length(allIDs))
# rr = Array{RemoteRef,1}()
# # this can be in parallel
# for vid in allIDs #IDs
#     #pidx = upp(pidx, nprocs())
#     mpt = MsgPassType(fg, cliq, vid, dwnMsgs, N)
#     if false
#       push!(rr,remotecall(upp2() , compOutMsgsProd, mpt) )
#     else
#       outDens[i] = compOutMsgsProd(mpt)
#     end
# end
# count=0
# for r in rr
#   count +=1
#   outDens[count] = fetch(r)
# end
#
# println()
# #@show "outMsg",cliq.attributes["conditIDs"], size(outDens)
# m=NBPMessage(deepcopy(allIDs), deepcopy(outDens))


text:
# This file is a part of JuliaFEM.
# License is MIT: see https://github.com/JuliaFEM/Materials.jl/blob/master/LICENSE

module Utilities

using Tensors, ForwardDiff

export Symm2, Symm4
export delta, II, IT, IS, IA, IV, ID, isotropic_elasticity_tensor, isotropic_compliance_tensor
export lame, delame, debang, find_root

"""Symm2{T} is an alias for SymmetricTensor{2,3,T}."""
const Symm2{T} = SymmetricTensor{2,3,T}

"""Symm4{T} is an alias for SymmetricTensor{4,3,T}."""
const Symm4{T} = SymmetricTensor{4,3,T}

"""
    delta(i::Integer, j::Integer)

Kronecker delta, defined by delta(i, j) = (i == j) ? 1 : 0.
"""
delta(i::T, j::T) where T <: Integer = (i == j) ? one(T) : zero(T)

# TODO: We could probably remove the type argument, and just let the results be
# inferred as Symm4{Int64}, Symm4{Rational{Int64}} and similar. Simpler to use,
# and those behave correctly in calculations with types involving other reals
# such as Float64.
# Performance implications? Is the Julia compiler smart enough to optimize?
"""
    II(T::Type=Float64)

Rank-4 unit tensor, defined by II : A = A for any rank-2 tensor A.
"""
II(T::Type=Float64) = Symm4{T}((i,j,k,l) -> delta(i,k)*delta(j,l))

"""
    IT(T::Type=Float64)

Rank-4 unit tensor, defined by IT : A = transpose(A) for any rank-2 tensor A.
"""
IT(T::Type=Float64) = Symm4{T}((i,j,k,l) -> delta(i,l)*delta(j,k))

"""
    IS(T::Type=Float64)

Rank-4 unit tensor, symmetric. IS ≡ (1/2) (II + IT).
"""
IS(T::Type=Float64) = 1//2 * (II(T) + IT(T))

"""
    IA(T::Type=Float64)

Rank-4 unit tensor, screw-symmetric. IA ≡ (1/2) (II - IT).
"""
IA(T::Type=Float64) = 1//2 * (II(T) - IT(T))

"""
    IV(T::Type=Float64)

Rank-4 unit tensor, volumetric. IS ≡ (1/3) I ⊗ I, where I is the rank-2 unit tensor.
"""
IV(T::Type=Float64) = Symm4{T}((i,j,k,l) -> 1//3 * delta(i,j)*delta(k,l))

"""
    ID(T::Type=Float64)

Rank-4 unit tensor, deviatoric. ID ≡ IS - IV.
"""
ID(T::Type=Float64) = IS(T) - IV(T)

"""
    isotropic_elasticity_tensor(lambda::T, mu::T) where T <: Real

Compute the elasticity tensor C(i,j,k,l) (rank 4, symmetric) for an isotropic
material having the Lamé parameters `lambda` and `mu`.

If you have (E, nu) instead, use `lame` to get (lambda, mu).
"""
isotropic_elasticity_tensor(lambda::T, mu::T) where T <: Real = 3 * lambda * IV(T) + 2 * mu * IS(T)

# TODO: check: original expr from upstream/master:
#     g(i,j,k,l) = -lambda/(2.0*mu*(3.0*lambda + 2.0*mu))*delta(i,j)*delta(k,l) + 1.0/(4.0*mu)*(delta(i,k)*delta(j,l)+delta(i,l)*delta(j,k))
"""
    isotropic_compliance_tensor(lambda::T, mu::T) where T <: Real

Compute the compliance tensor S(i,j,k,l) (rank 4, symmetric) for an isotropic
material having the Lamé parameters `lambda` and `mu`.

If you have (E, nu) instead, use `lame` to get (lambda, mu).
"""
isotropic_compliance_tensor(lambda::T, mu::T) where T <: Real = -3 * lambda / (2*mu * (3*lambda + 2*mu)) * IV(T) + 1 / (2*mu) * IS(T)

"""
    lame(E::Real, nu::Real)

Convert elastic parameters (E, nu) of an isotropic material to Lamé parameters (lambda, mu).

See:
    https://en.wikipedia.org/wiki/Template:Elastic_moduli
"""
function lame(E::Real, nu::Real)
    lambda = E * nu / ((1 + nu) * (1 - 2 * nu))
    mu = E / (2 * (1 + nu))
    return lambda, mu
end

"""
    delame(lambda::Real, mu::Real)

Convert Lamé parameters (lambda, mu) of an isotropic material to elastic parameters (E, nu).

See:
    https://en.wikipedia.org/wiki/Template:Elastic_moduli
"""
function delame(lambda::Real, mu::Real)
    E = mu * (3 * lambda + 2 * mu) / (lambda + mu)
    nu = lambda / (2 * (lambda + mu))
    return E, nu
end

"""
    debang(f!::Function, ex=nothing)

Convert a mutating function into non-mutating form.

`f!` must be a two-argument mutating function, which writes the result into its
first argument. The result of `debang` is then `f`, a single-argument
non-mutating function that allocates and returns the result. Schematically,
`f!(out, x)` becomes `f(x)`.

When the type, size and shape of `out` is the same as those of `x`, it is enough
to supply just `f!`. When `f` is called, output will be allocated as `similar(x)`.

When the type, size and/or shape of `out` are different from those of `x`, then
an example instance of the correct type with the correct size and shape for the
output must be supplied, as debang's `ex` argument. When `f` is called, output
will be allocated as `similar(ex)`. The `ex` instance will be automatically kept
alive by the lexical closure of `f`.

# Note

While the type of `out` is known at compile time, the size and shape are
typically runtime properties, not encoded into the type. For example, arrays
have the number of dimensions as part of the type, but the length of each
dimension is only defined at run time, when an instance is created. This is why
the `ex` argument is needed.

# Etymology

By convention, mutating functions are marked with an exclamation mark, a.k.a.
bang. This function takes away the bang.
"""
function debang(f!::Function, ex=nothing)
    if ex === nothing
        function f(x)
            out = similar(x)
            f!(out, x)
            return out
        end
        return f
    else
        # We use a different name to make incremental compilation happy.
        function f_with_ex(x)
            out = similar(ex)
            f!(out, x)
            return out
        end
        return f_with_ex
    end
end

# This comes from the old viscoplastic.jl, and is currently unused.
# The original wording of the error message suggested this was planned to be used for "radial return".
"""A simple Newton solver for the vector x* such that f(x*) = 0.

The input `x` is the initial guess.

The default `dfdx=nothing` uses `ForwardDiff.jacobian` to compute the jacobian
automatically. In this case the output of `f` must be an `AbstractArray`.

`tol` is measured in the vector norm of the change in `x` between successive
iterations.
"""
function find_root(f::Function, x::AbstractVector{<:Real},
                   dfdx::Union{Function, Nothing}=nothing;
                   max_iter::Integer=50, tol::Real=1e-9)
    if dfdx === nothing
        dfdx = (x) -> ForwardDiff.jacobian(f, x)
    end
    for i=1:max_iter
        dx = -dfdx(x) \ f(x)
        x += dx
        if norm(dx) < tol
            return x
        end
    end
    error("No convergence!")
end

end

ext: jl


text:
"""
    GitLabPages(; assets::Vector{<:AbstractString}=String[]) -> GitLabPages

Add `GitLabPages` to a template's plugins to add [`Documenter`](@ref) support via GitLab
Pages, including automatic uploading of documentation from [`GitLabCI`](@ref). Also
adds appropriate badges to the README, and updates the `.gitignore` accordingly.

# Keyword Arguments
* `assets::Vector{<:AbstractString}=String[]`: Array of paths to Documenter asset files.
"""
struct GitLabPages <: Documenter
    gitignore::Vector{String}
    assets::Vector{String}

    function GitLabPages(; assets::Vector{<:AbstractString}=String[])
        for file in assets
            if !isfile(file)
                throw(ArgumentError("Asset file $(abspath(file)) does not exist"))
            end
        end
        # Windows Git recognizes these paths as well.
        new(["/docs/build/", "/docs/site/"], abspath.(assets))
    end
end

function badges(::GitLabPages, user::AbstractString, pkg_name::AbstractString)
    # We are only including a badge for `dev` documentation since versioned documentation
    # is not supported in GitLab pages yet.  See:
    # https://github.com/invenia/PkgTemplates.jl/pull/54
    return [
        format(Badge(
            "Dev",
            "https://img.shields.io/badge/docs-dev-blue.svg",
            "https://$user.gitlab.io/$pkg_name.jl/dev"
        )),
    ]
end

function gen_plugin(p::GitLabPages, t::Template, pkg_name::AbstractString)
    invoke(gen_plugin, Tuple{Documenter, Template, AbstractString}, p, t, pkg_name)
    return ["docs/"]
end

function interactive(::Type{GitLabPages})
    print("GitLabPages: Enter any Documenter asset files (separated by spaces) []: ")
    return GitLabPages(; assets=string.(split(readline())))
end


text:
function sage_maker(n::Int)
    println("a_1 = 1")
    for k=3:2:n
        println("a_$k = var('a_$k')")
    end
    print("f(x) = ")
    for k=1:2:n
        print("a_$k * x^$k")
        if k<n-1
            print(" + ")
        else
            println()
        end
    end
    println("y = f(f(x)).expand()")

    for k=3:2:n
        println("print y.coefficient(x^$k)")
    end
end


text:
module file

end

max_stars_repo_name: tmaone/xsh

max_issues_repo_name: turing-complete/xsh

max_forks_repo_name: turing-complete/xsh


text:
# (C) Nicholas Polson, James Scott, Jesse Windle, 2012-2019

# This file is part of BayesLogit.

# BayesLogit is free software: you can redistribute it and/or modify it under
# the terms of the GNU General Public License as published by the Free Software
# Foundation, either version 3 of the License, or (at your option) any later
# version.

# BayesLogit is distributed in the hope that it will be useful, but WITHOUT ANY
# WARRANTY without even the implied warranty of MERCHANTABILITY or FITNESS FOR
# A PARTICULAR PURPOSE.  See the GNU General Public License for more details.

# You should have received a copy of the GNU General Public License along with
# BayesLogit.  If not, see <https:#www.gnu.org/licenses/>.

#include "truncated_norm.h"
#include "simple_RNG_wrapper.h"
const RCHECK = 1000
const SQRT2PI = 2.50662827

function flat(rng::Distributions.AbstractRNG, a::Float64, b::Float64)
    return a + rand(rng) * (b - a)::Float64
end

function texpon_rate(rng::Distributions.AbstractRNG, left::Float64, rate::Float64)
    if (rate < 0)
        println("texpon_rate: rate < 0, return 0")
    end
    # return left - _log_c(rand(rng)) / rate
    return Distributions.rand(rng, Distributions.Exponential(1 / rate)) + left::Float64
end

function texpon_rate(
    rng::Distributions.AbstractRNG,
    left::Float64,
    right::Float64,
    rate::Float64,
)
    if (left == right)
        return left
    end
    if (left > right)
        println("texpon_rate: left > right, return 0.")
        return
    end
    if (rate < 0)
        println("texpon_rate: rate < 0, return 0.")
        return 0.0
    end
    b = 1 - _exp_c(rate * (left - right))
    y = 1 - b * Distributions.rand(rng)
    return (left - _log_c(y) / rate)::Float64
end

function alphastar(left::Float64)
    return 0.5 * (left + sqrt(left * left + 4))::Float64
end

function lowerbound(left::Float64)
    astar = alphastar(left)
    lbound = left + _exp_c(0.5 * +0.5 * left * (left - astar)) / astar
    return lbound::Float64
end

function tnorm(rng::Distributions.AbstractRNG, left::Float64)
    count = 1

    if (left < 0)  # Accept/Reject Normal
        while (true)
            ppsl = Random.randn(rng)
            if (ppsl > left)
                return ppsl
            end
            count += 1
            #check_R_interupt(count++)
            if (count > RCHECK * 1000)
                println("left < 0 count: %i\n", count)
            end
        end
    else  # Accept/Reject Exponential
        # return tnorm_tail(left) # Use Devroye.
        astar = alphastar(left)
        while (true)
            ppsl = texpon_rate(rng, left, astar)
            rho = _exp_c(-0.5 * (ppsl - astar) * (ppsl - astar))
            if (Distributions.rand(rng) < rho)
                return ppsl
            end
            count += 1
            #check_R_interupt(count++)
            if (count > RCHECK * 1000)
                println("left > 0 count: ", count)
            end
        end
    end
end

function tnorm(rng::Distributions.AbstractRNG, left::Float64, right::Float64)
    # The most difficult part of this algorithm is figuring out all the
    # various cases.  An outline is summarized in the Appendix.

    # Check input

    if (right < left)
        println("Warning: left: $left, right:$right.")
        println("tnorm: parameter problem.", 0.5 * (left + right))
    end

    count = 1

    if (left >= 0)
        lbound = lowerbound(left)
        if (right > lbound)  # Truncated Exponential.
            astar = alphastar(left)
            while (true)
                ppsl = texpon_rate(rng, left, right, astar)
                rho = _exp_c(-0.5 * (ppsl - astar) * (ppsl - astar))
                if (rand(rng) < rho)
                    return ppsl
                end
                if (count > RCHECK * 10)
                    println("left >= 0, right > lbound count: ", count)
                end
            end
        else
            while (true)
                ppsl = flat(rng, left, right)
                rho = _exp_c(0.5 * (left * left - ppsl * ppsl))
                if (rand(rng) < rho)
                    return ppsl
                end
                count += 1
                #check_R_interupt(count++)
                if (count > RCHECK * 10)
                    println("left >= 0, right <= lbound count: ", count)
                end
            end
        end
    elseif (right >= 0)
        if ((right - left) < SQRT2PI)
            while (true)
                ppsl = flat(rng, left, right)
                rho = _exp_c(-0.5 * ppsl * ppsl)
                if (rand(rng) < rho)
                    return ppsl
                end
                count += 1
                #check_R_interupt(count++)
                if (count > RCHECK * 10)
                    println("First, left < 0, right >= 0, count:", count)
                end
            end
        else
            while (true)
                ppsl = Random.randn(rng)
                if (left < ppsl && ppsl < right)
                    return ppsl
                end
                count += 1
                #check_R_interupt(count++)
                if (count > RCHECK * 10)
                    println("Second, left < 0, right > 0, count: ", count)
                end
            end
        end
    else
        return -1 * tnorm(rng, -1.0 * right, -1.0 * left)
    end
end

function tnorm(rng::Distributions.AbstractRNG, left::Float64, mu::Float64, sd::Float64)
    newleft = (left - mu) / sd
    return (mu + tnorm(rng, newleft) * sd)::Float64
end

function tnorm(
    rng::Distributions.AbstractRNG,
    left::Float64,
    right::Float64,
    mu::Float64,
    sd::Float64,
)
    if (left == right)
        return left
    end

    newleft = (left - mu) / sd
    newright = (right - mu) / sd

    # I want to check this here as well so we can see what the input was.
    # It may be more elegant to try and catch tdraw.
    if (newright < newleft)
        println("left, right, mu, sd: ", left, right, mu, sd)
        println("nleft, nright: ", newleft, newright)
        println("tnorm: parameter problem.", 0.5 * (left + right))
    end

    tdraw = tnorm(rng, newleft, newright)
    draw = mu + tdraw * sd

    # It may be the case that there is some numerical error and that the draw
    # ends up out of bounds.
    if (draw < left || draw > right)
        println("Error in tnorm: draw not in bounds.\n")
        println("left, right, mu, sd: ", left, right, mu, sd)
        println("nleft, nright, tdraw, draw: \n", newleft, newright, tdraw, draw)
        println("Aborting and returning average of left and right.\n", 0.5 * (left + right))
    end

    return draw::Float64
end

function rtinvchi2(rng::Distributions.AbstractRNG, scale::Float64, trunc::Float64)
    R = trunc / scale
    # double X = 0.0
    # # I need to consider using a different truncated normal sampler.
    # double E1 = r.expon_rate(1.0) double E2 = r.expon_rate(1.0)
    # while ( (E1*E1) > (2 * E2 / R)) {
    #   # printf("E %g %g %g %g\n", E1, E2, E1*E1, 2*E2/R)
    #   E1 = r.expon_rate(1.0) E2 = r.expon_rate(1.0)
    # }
    # # printf("E %g %g \n", E1, E2)
    # X = 1 + E1 * R
    # X = R / (X * X)
    # X = scale * X
    E = tnorm(rng, 1 / sqrt(R))
    X = scale / (E * E)
    return X
end


text:
module PerformanceProfilingHttpEndpointsTests

using PerformanceProfilingHttpEndpoints
using Test
using Serialization

import InteractiveUtils
import HTTP
import Profile
import PProf

const port = 13423
const t = @async PerformanceProfilingHttpEndpoints.serve_profiling_server(;port=port)
const url = "http://127.0.0.1:$port"

@testset "PerformanceProfilingHttpEndpoints.jl" begin

    @testset "CPU profiling" begin
        done = Threads.Atomic{Bool}(false)
        # Schedule some work that's known to be expensive, to profile it
        t = @async begin
            for _ in 1:200
                if done[] return end
                InteractiveUtils.peakflops()
                yield()  # yield to allow the tests to run
            end
        end

        req = HTTP.request("GET", "$url/profile?duration=3&pprof=false")
        @test req.status == 200
        @test length(req.body) > 0

        data, lidict = deserialize(IOBuffer(req.body))
        # Test that the profile seems like valid profile data
        @test data isa Vector{UInt64}
        @test lidict isa Dict{UInt64, Vector{Base.StackTraces.StackFrame}}

        @info "Finished tests, waiting for peakflops workload to finish."
        done[] = true
        wait(t)  # handle errors
    end

    @testset "Allocation profiling" begin
        done = Threads.Atomic{Bool}(false)
        # Schedule some work that's known to be expensive, to profile it
        t = @async begin
            for _ in 1:200
                if done[] return end
                global a = [[] for i in 1:1000]
                yield()  # yield to allow the tests to run
            end
        end

        req = HTTP.request("GET", "$url/allocs_profile?duration=3", retry=false, status_exception=false)
        if !(isdefined(Profile, :Allocs) && isdefined(PProf, :Allocs))
            # We should be tesing the Allocs profiling if we're on julia nightly.
            @assert VERSION < v"1.8.0-DEV.1346"
            @test req.status == 501  # not implemented
        else
            @test req.status == 200
            @test length(req.body) > 0

            data = read(IOBuffer(req.body), String)
            # Test that there's something here
            # TODO: actually parse the profile
            @test length(data) > 100

        end
        @info "Finished tests, waiting for workload to finish."
        done[] = true
        wait(t)  # handle errors
    end

    @testset "error handling" begin
        let res = HTTP.request("GET", "$url/profile", status_exception=false)
            @test 400 <= res.status < 500
            @test res.status != 404
            # Make sure we describe how to use the endpoint
            body = String(res.body)
            @test occursin("duration", body)
            @test occursin("delay", body)
        end

        if (isdefined(Profile, :Allocs) && isdefined(PProf, :Allocs))
            let res = HTTP.request("GET", "$url/allocs_profile", status_exception=false)
                @test 400 <= res.status < 500
                @test res.status != 404
                # Make sure we describe how to use the endpoint
                body = String(res.body)
                @test occursin("duration", body)
                @test occursin("sample_rate", body)
            end
        end
    end
end

end # module PerformanceProfilingHttpEndpointsTests

max_stars_repo_name: NHDaly/ServerIntrospectionEndpoints

max_issues_repo_name: NHDaly/ServerIntrospectionEndpoints

max_forks_repo_name: NHDaly/ServerIntrospectionEndpoints


text:
__precompile__(true)
module LightGraphs

using GZip
using DataStructures
using SimpleTraits

import Base: write, ==, <, *, ≈, convert, isless, issubset, union, intersect,
            reverse, reverse!, blkdiag, isassigned, getindex, setindex!, show,
            print, copy, in, sum, size, sparse, eltype, length, ndims, transpose,
            ctranspose, join, start, next, done, eltype, get, issymmetric, A_mul_B!,
            Pair, Tuple, zero
export
# Interface
AbstractGraph, AbstractDiGraph, AbstractEdge, AbstractEdgeInter,
Edge, Graph, DiGraph, vertices, edges, edgetype, nv, ne, src, dst,
is_directed, add_vertex!, add_edge!, rem_vertex!, rem_edge!,
has_vertex, has_edge, in_neighbors, out_neighbors,

# core
is_ordered, add_vertices!, indegree, outdegree, degree,
Δout, Δin, δout, δin, Δ, δ, degree_histogram,
neighbors, all_neighbors, common_neighbors,
has_self_loops, num_self_loops, density, squash, weights,

# decomposition
core_number, k_core, k_shell, k_crust, k_corona,

# distance
eccentricity, diameter, periphery, radius, center,

# distance between graphs
spectral_distance, edit_distance,

# edit path cost functions
MinkowskiCost, BoundedMinkowskiCost,

# operators
complement, reverse, reverse!, blkdiag, union, intersect,
difference, symmetric_difference,
join, tensor_product, cartesian_product, crosspath,
induced_subgraph, egonet,

# graph visit
AbstractGraphVisitor, TrivialGraphVisitor, LogGraphVisitor,
discover_vertex!, open_vertex!, close_vertex!,
examine_neighbor!, visited_vertices, traverse_graph!, traverse_graph_withlog,

# bfs
BreadthFirst, gdistances, gdistances!, bfs_tree, is_bipartite, bipartite_map,

# dfs
DepthFirst, is_cyclic, topological_sort_by_dfs, dfs_tree,

# random
randomwalk, saw, non_backtracking_randomwalk,

# connectivity
connected_components, strongly_connected_components, weakly_connected_components,
is_connected, is_strongly_connected, is_weakly_connected, period,
condensation, attracting_components, neighborhood, isgraphical,

# cycles
simplecycles_hadwick_james, maxsimplecycles, simplecycles, simplecycles_iter,
simplecyclescount, simplecycleslength,

# maximum_adjacency_visit
MaximumAdjacency, AbstractMASVisitor, mincut, maximum_adjacency_visit,

# a-star, dijkstra, bellman-ford, floyd-warshall
a_star, dijkstra_shortest_paths, bellman_ford_shortest_paths,
has_negative_edge_cycle, enumerate_paths, floyd_warshall_shortest_paths,
transitiveclosure!, transitiveclosure, yen_k_shortest_paths,
parallel_multisource_dijkstra_shortest_paths,

# centrality
betweenness_centrality, closeness_centrality, degree_centrality,
indegree_centrality, outdegree_centrality, katz_centrality, pagerank,
eigenvector_centrality,

parallel_betweenness_centrality, parallel_closeness_centrality,

# spectral
adjacency_matrix,laplacian_matrix, adjacency_spectrum, laplacian_spectrum,
CombinatorialAdjacency, non_backtracking_matrix, incidence_matrix,
nonbacktrack_embedding, Nonbacktracking,
contract,

# persistence
loadgraph, loadgraphs, savegraph, LGFormat,

# flow
maximum_flow, EdmondsKarpAlgorithm, DinicAlgorithm, BoykovKolmogorovAlgorithm, PushRelabelAlgorithm,
multiroute_flow, KishimotoAlgorithm, ExtendedMultirouteFlowAlgorithm,

# randgraphs
erdos_renyi, watts_strogatz, random_regular_graph, random_regular_digraph, random_configuration_model,
StochasticBlockModel, make_edgestream, nearbipartiteSBM, blockcounts, blockfractions,
stochastic_block_model, barabasi_albert, barabasi_albert!, static_fitness_model, static_scale_free,
kronecker,

#community
modularity, core_periphery_deg,
local_clustering,local_clustering_coefficient, global_clustering_coefficient, triangles,
label_propagation, maximal_cliques,

#generators
CompleteGraph, StarGraph, PathGraph, WheelGraph, CycleGraph,
CompleteBipartiteGraph, CompleteDiGraph, StarDiGraph, PathDiGraph, Grid,
WheelDiGraph, CycleDiGraph, BinaryTree, DoubleBinaryTree, RoachGraph, CliqueGraph,

#smallgraphs
smallgraph,

# Euclidean graphs
euclidean_graph,

#minimum_spanning_trees
kruskal_mst, prim_mst,

#biconnectivity and articulation points
articulation, biconnected_components

"""
    LightGraphs

An optimized graphs package.

Simple graphs (not multi- or hypergraphs) are represented in a memory- and
time-efficient manner with adjacency lists and edge sets. Both directed and
undirected graphs are supported via separate types, and conversion is available
from directed to undirected.

The project goal is to mirror the functionality of robust network and graph
analysis libraries such as NetworkX while being simpler to use and more
efficient than existing Julian graph libraries such as Graphs.jl. It is an
explicit design decision that any data not required for graph manipulation
(attributes and other information, for example) is expected to be stored
outside of the graph structure itself. Such data lends itself to storage in
more traditional and better-optimized mechanisms.

[Full documentation](http://codecov.io/github/JuliaGraphs/LightGraphs.jl) is available,
and tutorials are available at the
[JuliaGraphsTutorials repository](https://github.com/JuliaGraphs/JuliaGraphsTutorials).
"""
LightGraphs
include("interface.jl")
include("deprecations.jl")
include("core.jl")
    include("graphtypes/simplegraphs/SimpleGraphs.jl")
    const Graph = SimpleGraphs.SimpleGraph
    const DiGraph = SimpleGraphs.SimpleDiGraph
    const Edge = SimpleGraphs.SimpleEdge
    include("degeneracy.jl")
    include("digraph/transitivity.jl")
    include("digraph/cycles/johnson.jl")
    include("digraph/cycles/hadwick-james.jl")
        include("traversals/graphvisit.jl")
            include("traversals/bfs.jl")
            include("traversals/parallel_bfs.jl")
            include("traversals/dfs.jl")
            include("traversals/maxadjvisit.jl")
            include("traversals/randomwalks.jl")
        include("connectivity.jl")
        include("distance.jl")
        include("edit_distance.jl")
        include("shortestpaths/astar.jl")
        include("shortestpaths/bellman-ford.jl")
        include("shortestpaths/dijkstra.jl")
        include("shortestpaths/floyd-warshall.jl")
        include("shortestpaths/yen.jl")
        include("linalg/LinAlg.jl")
        include("operators.jl")
        include("persistence/common.jl")
            include("persistence/lg.jl")
        include("generators/staticgraphs.jl")
            include("generators/randgraphs.jl")
            include("generators/euclideangraphs.jl")
            include("generators/smallgraphs.jl")
        include("centrality/betweenness.jl")
        include("centrality/closeness.jl")
        include("centrality/degree.jl")
        include("centrality/katz.jl")
        include("centrality/pagerank.jl")
        include("centrality/eigenvector.jl")
        include("community/modularity.jl")
        include("community/label_propagation.jl")
        include("community/core-periphery.jl")
        include("community/clustering.jl")
        include("community/cliques.jl")
        include("flow/maximum_flow.jl")
            include("flow/edmonds_karp.jl")
            include("flow/dinic.jl")
            include("flow/boykov_kolmogorov.jl")
            include("flow/push_relabel.jl")
            include("flow/multiroute_flow.jl")
                include("flow/kishimoto.jl")
                include("flow/ext_multiroute_flow.jl")
        include("utils.jl")
        include("spanningtrees/kruskal.jl")
        include("spanningtrees/prim.jl")
        include("biconnectivity/articulation.jl")
        include("biconnectivity/biconnect.jl")

end # module


text:
#Write a function that returns the elements on odd positions in a list.

list = [1,2,3,45,"A","b","c"]

function return_odd(x)
    l = []
    for (i,j) in enumerate(x)
        if i % 2 == 1
            push!(l,j)
        end
    end
    return l
end

return_odd(list)

max_stars_repo_name: atott/Simple-Programming-Problems-Julia

max_issues_repo_name: atott/Simple-Programming-Problems-Julia

max_forks_repo_name: atott/Simple-Programming-Problems-Julia


text:
export ValueEntry, SparseArray

mutable struct ValueEntry{T}
    row_i::Int
    col_i::Int
    value::T

    ValueEntry{T}(row_i::Int, col_i::Int, value::T) where T = new(
        row_i,
        col_i,
        value
    )
end

mutable struct SparseArray{T}
    n_row::Int
    n_col::Int
    data::Vector{ValueEntry{T}}

    SparseArray{T}(n_row::Int, n_col::Int) where T = new(
        n_row,
        n_col,
        Vector{ValueEntry{T}}()
    )
end

Base.size(sa::SparseArray) = (sa.n_row, sa.n_col)

Base.length(sa::SparseArray) = length(sa.data)

Base.eltype(sa::SparseArray{T}) where {T} = T

function Base.show(io::IO, sa::SparseArray{T}) where T
    println(io, "SparseArray{$T}$(size(sa))[")
    for value in sa.data
        println(io, "\t[$(value.row_i), $(value.col_i)]: $(value.value)")
    end
    print(io, "]")
end

function check_boundary(sa::SparseArray, row_i::Int, col_i::Int)
    if row_i > sa.n_row || row_i < 1 return false end
    if col_i > sa.n_col || col_i < 1 return false end

    return true
end

function find(sa::SparseArray, row_i::Int, col_i::Int)
    for (i, value_entry) in enumerate(sa.data)
        if value_entry.row_i == row_i && value_entry.col_i == col_i
             return i
         end
    end

    return -1
end

function Base.push!(sa::SparseArray{T}, row_i::Int, col_i::Int, value::T) where T
    push!(sa.data, ValueEntry{T}(row_i, col_i, value))
end

function Base.setindex!(sa::SparseArray{T}, value::T, row_i::Int, col_i::Int) where T
    if !check_boundary(sa, row_i, col_i) throw(BoundsError()) end

    i = find(sa, row_i, col_i)
    if i != -1
        if value == 0
            deleteat!(sa.data, i)
            return
        end
        sa.data[i].value = value
    else
        if value == 0 return end
        push!(sa, row_i, col_i, value)
    end
end

function Base.getindex(sa::SparseArray{T}, row_i::Int, col_i::Int) where T
    if !check_boundary(sa, row_i, col_i) throw(BoundsError()) end

    i = find(sa, row_i, col_i)
    if i != -1 return sa.data[i].value end

    return zero(T)
end


text:
# Transformations of Julia ASTs containing ISPC fragments.

"""
Removes every `LineNumberNode` in the AST (eg. for clearer output).
"""
function strip_lineno(expr::Expr)
    args = strip_lineno(expr.args)
    newargs = map(strip_lineno, args)
    return Expr(expr.head, newargs...)
end

function strip_lineno(array::Vector{Any})
    filter(array) do obj
        !(isa(obj, LineNumberNode) || isa(obj, Expr) && (obj.head == :line))
    end
end

function strip_lineno(obj::Any)
    obj
end

"""
Replaces all calls to `Base.box()` with the unboxed value.
"""
strip_box(obj::Any) = obj
function strip_box(expr::Expr)
    if expr.head == :call
        # Unbox all values:
        if expr.args[1] == GlobalRef(Base, :box)
            # args[3] holds the boxed value:
            return strip_box(expr.args[3])
        end
    end
    args = map(strip_box, expr.args)
    return Expr(expr.head, args...)
end

"""
Returns the jump target if `stmt` is a branch, `nothing` otherwise.
"""
function branch_target(stmt)
    if isa(stmt, GotoNode)
        return stmt.label
    elseif isa(stmt, Expr)
        if stmt.head == :gotoifnot
            return stmt.args[2]
        end
    end
    return nothing
end

"""
Replaces all jump targets in `expr` with the associated values in `new_labels`.
"""
function replace_labels(expr, new_labels)
    if isa(expr, GotoNode)
        return GotoNode(new_labels[expr.label])
    end
    if isa(expr, Expr)
        if expr.head == :gotoifnot
            block, goto = expr.args
            return Expr(expr.head, replace_labels(block, new_labels), new_labels[goto])
        end
        return Expr(expr.head, [replace_labels(a, new_labels) for a in expr.args]...)
    end
    return expr
end

"""
Replaces jumps to labels with jumps to statement indices (aka line numbers or
program counters) and removes all `LabelNodes` from the AST. This transform
makes it easier to analyse branching patterns.
"""
function labels_to_lines(statements)
    labels = Dict()
    program = []
    for stmt in statements
        if isa(stmt, LabelNode)
            labels[stmt.label] = length(program) + 1
        else
            push!(program, stmt)
        end
    end
    newblock = Expr(:block, program...)
    replace_labels(newblock, labels).args
end

"""
Adds explicit labels for jumps to line/statement numbers. If the AST had been
transformed by `labels_to_lines()`, this turns it back into a form that Julia
can handle.
"""
function lines_to_labels(statements)
    targets = Set()
    program = []
    for stmt in statements
        target = branch_target(stmt)
        if target != nothing
            @assert 1 <= target <= length(statements)
            push!(targets, target)
        end
    end
    for (i, stmt) in enumerate(statements)
        if i in targets
            push!(program, LabelNode(i))
        end
        push!(program, stmt)
    end
    program
end

import Graphs: add_edge!, in_degree, out_degree, simple_graph, in_neighbors

function to_graph(statements, i=1, g=simple_graph(length(statements)))
    while i <= length(statements)
        if out_degree(i, g) > 0
            break
        end
        stmt = statements[i]
        target = branch_target(stmt)
        if isa(stmt, GotoNode)
            add_edge!(g, i, target)
            i = target
            continue
        end
        if (i < length(statements))
            add_edge!(g, i, i+1)
        end
        if target != nothing
            add_edge!(g, i, target)
            to_graph(statements, target, g)
        end
        i += 1
    end

    return g
end

function print_statements(io, statements)
    for (i, stmt) in enumerate(statements)
        println(io, "$i\t$stmt")
    end
end

function push_visited!(s::Set, items...)
    # -- debugging code goes here --
    for i in items
        Base.push!(s, i)
    end
end

"""
Try to lift patterns such as this:
    if condition
      do
         block
      while !!condition
    end

into a simpler form:
    while condition
       block
    end

"""
function raise_if_then_else(condition, ifthen, ifelse)
    if length(ifthen) == 1 && length(ifelse) == 0
        expr, = ifthen
        if isa(expr, Expr) && expr.head == :dowhile
            docondition, doblock = expr.args
            # Test that the conditions match via their string representation.
            # Not the most elegant way but the most maintainable that I've
            # found so far.
            # They should be logically identical but differ syntactically by
            # a double negation:
            expected = ("!((Base.box)(Base.Bool,(Base.not_int)($condition)))",
                        "!((top(!))($condition))")
            if string(docondition) in expected
                return Expr(:while, condition, doblock)
            end
        end
    end

    # Create a structured `if` block:
    if isempty(ifelse)
        return Expr(:if, condition, Expr(:block, ifthen...))
    else
        return Expr(:if, condition, Expr(:block, ifthen...),
                                    Expr(:block, ifelse...))
    end
end

"""
Transform unstructured control flow into structured statements.

We make a number of assumptions about the control flow:
- a backwards conditional jump always correspond
  to the end of a loop
- there are no unconditional backwards jumps.

So far these assumptions seem to hold for everything that Julia
generates.
"""
function raise_ast(statements, first=1, visited=Set(),
                    graph=to_graph(statements),
                    continue_to=nothing,
                    break_to=nothing)
    body = []
    i = first
    while i <= length(statements)
        stmt = statements[i]

        # println("@ $i, first: $first, visited: $visited")
        # println("continue: $continue_to, break: $break_to")

        # Any convergent flow?
        # =============================

        # Distinguish between forward and backward jumps to this
        # statement:
        antecedents = Set(in_neighbors(i, graph))
        incoming = filter(b -> b < i, antecedents)
        loopback = filter(b -> b > i, antecedents)

        if length(incoming) > 1
            # More than one branch converge onto this statement.
            # Don't enter until they have all been followed:
            if !(issubset(incoming, visited))
                # flow has not reconverged yet.
                # println("not converged at $i.")
                return body, i
            end
        end

        # println("enter")

        if !isempty(loopback)
            # A future statement points back here, that must be a
            # do-while loop. Make sure that the `continue_to` and
            # `break_to` arguments are set accordingly:
            cont, = loopback
            brk = cont + 1
            if (continue_to, break_to) != (cont, brk)
                # That's a new do-while block. Recurse with the new
                # break/continue arguments:
                # println("start do-while")
                stmt, tail = raise_ast(statements, i, visited, graph,
                                       cont, brk)
                push!(body, stmt...)

                if tail != nothing
                    # println("end do-while: continue $tail")
                    i = tail
                    continue
                else
                    # println("end do-while: return $brk")
                    return body, brk
                end
            else
                # We're already inside that do-while block.
            end
        end

        # Any divergent flow?
        # =============================

        if isa(stmt, GotoNode)
            # Unconditional branch.
            target = branch_target(stmt)
            push_visited!(visited, i)

            # If the branch target matches one of our `continue`
            # or `break` targets, insert the corresponding statement:
            if target == break_to
                push!(body, Expr(:break))
                return body, nothing # don't follow breaks, they take
                                     # us out of the current do-while

            elseif target == continue_to
                push!(body, Expr(:continue))
                return body, continue_to # follow that one to the
                                         # do-while loop check

            elseif target <= i
                print_statements(STDERR, statements)
                error("Unhandled backward goto: $i -> $target")

            else
                # Just follow the jump (and discard the
                # goto statement).
                i = target
                continue
            end
        end

        if isa(stmt, Expr) && stmt.head == :gotoifnot
            # Conditional branch: transform it to a structured
            # control flow statement.
            condition, target = stmt.args

            if target < i
                # This looks like the end of a do-while block...

                if target != first || i != continue_to
                    # ... but not of the one we're processing at
                    # the moment! Let the caller handle it:
                    # println("return to parent do-while at $i")
                    return body, i
                end

                # The body is formed by all previous statements
                # since the start of the current branch:
                push_visited!(visited, i)

                # We need to negate the condition to match do-while
                # semantics (the goto statements form a do-while-not)
                while_condition = :(!$condition)
                stmt = Expr(:dowhile, while_condition, Expr(:block, body...))
                return [stmt], break_to

            else
                # This is an `if-then-else` block.

                # Don't add the `if` statement itself to the set of
                # visited statements until all the branches have been
                # explored, otherwise the last branch to be followed
                # might swallow whatever comes after the `if` block
                # (eg. when the `else` branch is empty).

                # Follow the two branches independently until they
                # reconverge.
                visitedA = Set()
                ifthen, tailsA = raise_ast(statements, i+1, visitedA,
                                            graph, continue_to, break_to)

                visitedB = Set()
                ifelse, tailsB = raise_ast(statements, target, visitedB,
                                            graph, continue_to, break_to)

                # Merge the branch histories:
                push_visited!(visited, visitedA...)
                push_visited!(visited, visitedB...)
                push_visited!(visited, i)

                # println("IF at $i converged to $tailsA, $tailsB")

                # Build an `if` or `while` structured control flow:
                stmt = raise_if_then_else(condition, ifthen, ifelse)
                push!(body, stmt)

                # The tails should be all equal to each other or
                # `nothing`:
                tails = Set((tailsA, tailsB))
                delete!(tails, nothing)
                if length(tails) > 1
                    # The flow did not reconverge properly. This might be
                    # caused by a bug or by gotos that we don't know how
                    # to handle.
                    error("Invalid reconvergence at $i: $tailsA, $tailsB")
                elseif length(tails) == 1
                    # One of the branches has a non-void tail, follow up
                    # on that:
                    i, = tails
                    continue
                else
                    # Both tails are void.
                    return body, nothing
                end
            end
        end

        # Branchless statement:
        # =============================

        push!(visited, i)
        push!(body, stmt)
        i += 1
    end

    # We reached the end of the flow:
    return body, nothing
end

function meta_to_trees(expr::Any)
    expr
end

function meta_to_trees(expr::Expr)
    branches = []
    statements = expr.args
    for (range, identifier) in ispc_ranges(statements)
        # println("@ $range, $identifier")
        if identifier == nothing
            for stmt in statements[range]
                push!(branches, meta_to_trees(stmt))
            end
        else
            meta = statements[range.start]
            @assert meta.head == :meta
            meta_op = meta.args[3]
            meta_args = (meta.args[4:end]...)
            # println("$meta $meta_op, $meta_args")
            body = statements[(range.start+1):(range.stop-1)]
            tree = meta_to_trees(Expr(:block, body...))
            branch_expr = Expr(meta_op, meta_args..., tree)
            push!(branches, branch_expr)
        end
    end
    Expr(expr.head, branches...)
end

collect_foreach_indices(expr::Any) = expr
function collect_foreach_indices(expr::Expr)
    args = expr.args
    if expr.head == :foreach
        iters = []
        arrays, block = args
        # for arr in arrays
        #     if isa(arr, Expr) && arr.head == :quote
        #         push!(iters, arr.args[1])
        #     end
        # end
        block = collect_foreach_indices(block)
        statements = block.args
        filter!(statements) do expr
            if isa(expr, Expr) && expr.head == :(=)
                lhs, rhs = expr.args
                if isa(rhs, Expr) && rhs.head == :call
                    if rhs.args[1] == GlobalRef(ISPC, :foreachindex)
                        idx = eval(rhs.args[2])
                        push!(iters, arrays[idx] => lhs)
                        return false
                    end
                end
            end
            true
        end
        return Expr(:foreach, iters, Expr(:block, statements...))
    end
    args = [collect_foreach_indices(arg) for arg in args]
    Expr(expr.head, args...)
end

function resolve_topnodes(ast::Expr)
    # We are likely to encounter two types of getfield calls:
    # call(TopNode(getfield), GlobalRef, QuoteNode):
    #       accesses a function in a module
    # call(TopNode(getfield), GenSym, QuoteNode):
    #       accesses a field in a local object
    expr
end


text:
module TestTransformer

using Test, MLJModels
using Tables, CategoricalArrays, Random
using ScientificTypes
using StatsBase
using Statistics
using StableRNGs
stable_rng = StableRNGs.StableRNG(123)
using Dates: DateTime, Date, Time, Day, Hour

import MLJBase

#### FEATURE SELECTOR ####

@testset "Feat Selector" begin
    N = 100
    X = (Zn   = rand(N),
         Crim = rand(N),
         x3   = categorical(rand("YN", N)),
         x4   = categorical(rand("YN", N)))

    # Test feature selection with `features=Symbol[]`
    namesX   = Tables.schema(X).names |> collect
    selector = FeatureSelector()
    f,       = MLJBase.fit(selector, 1, X)
    @test f == namesX
    Xt = MLJBase.transform(selector, f, MLJBase.selectrows(X, 1:2))
    @test Set(Tables.schema(Xt).names) == Set(namesX)
    @test length(Xt.Zn) == 2

    # Test on selecting features if `features` keyword is defined
    selector = FeatureSelector(features=[:Zn, :Crim])
    f,       = MLJBase.fit(selector, 1, X)
    @test MLJBase.transform(selector, f, MLJBase.selectrows(X, 1:2)) ==
            MLJBase.select(X, 1:2, [:Zn, :Crim])

    # test on ignoring a feature, even if it's listed in the `features`
    selector.ignore = true
    f,   = MLJBase.fit(selector, 1, X)
    Xnew = MLJBase.transform(selector, f, X)
    @test MLJBase.transform(selector, f, MLJBase.selectrows(X, 1:2)) ==
         MLJBase.select(X, 1:2, [:x3, :x4])

    # test error about features selected or excluded in fit.
    selector = FeatureSelector(features=[:x1, :mickey_mouse])
    @test_throws(
        ArgumentError,
        MLJBase.fit(selector, 1, X)
    )
    selector.ignore = true
    @test_logs(
        (:warn, r"Excluding non-existent"),
        MLJBase.fit(selector, 1, X)
    )

    # features must be specified if ignore=true
    @test_throws ArgumentError FeatureSelector(ignore=true)

    # test logs for no features selected when using Bool-Callable function interface:
    selector = FeatureSelector(features= x-> x == (:x1))
   @test_throws(
        ArgumentError,
        MLJBase.fit(selector, 1, X)
    )
    selector.ignore = true
    selector.features = x-> x in [:Zn, :Crim, :x3, :x4]
     @test_throws(
        ArgumentError,
        MLJBase.fit(selector, 1, X)
    )

    # Test model Metadata
    infos = MLJBase.info_dict(selector)
    @test infos[:input_scitype]  == MLJBase.Table
    @test infos[:output_scitype] == MLJBase.Table
end

#  To be added with FeatureSelectorRule X = (n1=["a", "b", "a"], n2=["g", "g", "g"], n3=[7, 8, 9],
#               n4 =UInt8[3,5,10],  o1=[4.5, 3.6, 4.0], )
# MLJBase.schema(X)
# Xc = coerce(X,  :n1=>Multiclass, :n2=>Multiclass)

# t = Discretizer(features=[:o1, :n3, :n2, :n1])
# @test Xt.features == [:o1, :n3, :n2, :n1]
# @test Xt.is_ordinal == [true, false, false, false]
# @test Xt.A == [512 1 1 1; 1 2 1 2; 256 3 1 1]

#### UNIVARIATE DISCRETIZATION ####

@testset "U-Discr" begin
    v = randn(10000)
    t = UnivariateDiscretizer(n_classes=100);
    result, = MLJBase.fit(t, 1, v)
    w = MLJBase.transform(t, result, v)
    bad_values = filter(v - MLJBase.inverse_transform(t, result, w)) do x
        abs(x) > 0.05
    end
    @test length(bad_values)/length(v) < 0.06

    # scalars:
    @test MLJBase.transform(t, result, v[42]) == w[42]
    r =  MLJBase.inverse_transform(t, result, w)[43]
    @test MLJBase.inverse_transform(t, result, w[43]) ≈ r

    # test of permitted abuses of argument:
    @test MLJBase.inverse_transform(t, result, get(w[43])) ≈ r
    @test MLJBase.inverse_transform(t, result, map(get, w)) ≈
        MLJBase.inverse_transform(t, result, w)

    # all transformed vectors should have an identical pool (determined in
    # call to fit):
    v2 = v[1:3]
    w2 = MLJBase.transform(t, result, v2)
    @test levels(w2) == levels(w)

end

#### STANDARDIZER ####

@testset begin "standardization"

    # UnivariateStandardizer:
    stand = UnivariateStandardizer()
    f,    = MLJBase.fit(stand, 1, [0, 2, 4])
    @test round.(Int, MLJBase.transform(stand, f, [0,4,8])) == [-1.0,1.0,3.0]
    @test round.(Int, MLJBase.inverse_transform(stand, f, [-1, 1, 3])) ==
        [0, 4, 8]
    infos = MLJBase.info_dict(stand)

    N = 5
    rand_char = rand("abcefgh", N)
    while length(unique(rand_char)) < 2
        rand_char = rand("abcefgh", N)
    end
    X = (OverallQual  = rand(UInt8, N),
         GrLivArea    = rand(N),
         Neighborhood = categorical(rand_char, ordered=true),
         x1stFlrSF    = sample(1:10, N, replace=false),
         TotalBsmtSF  = rand(N))

    # introduce a field of type `Char`:
    x1 = categorical(map(Char, (X.OverallQual |> collect)))

    X = (x1=x1, x2=X[2], x3=X[3], x4=X[4], x5=X[5])

    stand = Standardizer()
    f,    = MLJBase.fit(stand, 1, X)
    Xnew  = MLJBase.transform(stand, f, X)

    # test inverse:
    XX = MLJBase.inverse_transform(stand, f, Xnew)
    @test MLJBase.schema(X) == MLJBase.schema(XX)
    @test XX.x1 == X.x1
    @test XX.x2 ≈ X.x2
    @test XX.x3 == X.x3
    @test XX.x4 == X.x4
    @test XX.x5 ≈ X.x5

    # test transformation:
    @test Xnew[1] == X[1]
    @test MLJBase.std(Xnew[2]) ≈ 1.0
    @test Xnew[3] == X[3]
    @test Xnew[4] == X[4]
    @test MLJBase.std(Xnew[5]) ≈ 1.0

    # test feature specification (ignore=false):
    stand.features = [:x1, :x5]
    f,   = MLJBase.fit(stand, 1, X)
    Xnew = MLJBase.transform(stand, f, X)
    @test issubset(Set(keys(f[3])), Set(Tables.schema(X).names[[5,]]))
    Xt = MLJBase.transform(stand, f, X)
    @test Xnew[1] == X[1]
    @test Xnew[2] == X[2]
    @test Xnew[3] == X[3]
    @test Xnew[4] == X[4]
    @test MLJBase.std(Xnew[5]) ≈ 1.0

    # test on ignoring a feature, even if it's listed in the `features`
    stand.ignore = true
    f,   = MLJBase.fit(stand, 1, X)
    Xnew = MLJBase.transform(stand, f, X)
    @test issubset(Set(keys(f[3])), Set(Tables.schema(X).names[[2,]]))
    Xt = MLJBase.transform(stand, f, X)
    @test Xnew[1] == X[1]
    @test MLJBase.std(Xnew[2]) ≈ 1.0
    @test Xnew[3] == X[3]
    @test Xnew[4] == X[4]
    @test Xnew[5] == X[5]

    # test warnings about features not encountered in fit or no
    # features need transforming:
    stand = Standardizer(features=[:x1, :mickey_mouse])
    @test_logs(
        (:warn, r"Some specified"),
        (:warn, r"No features"),
        MLJBase.fit(stand, 1, X)
    )
    stand.ignore = true
    @test_logs (:warn, r"Some specified") MLJBase.fit(stand, 1, X)

    # features must be specified if ignore=true
    @test_throws ArgumentError Standardizer(ignore=true)

    # test count, ordered_factor options:
    stand = Standardizer(features=[:x3, :x4], count=true, ordered_factor=true)
    f,   = MLJBase.fit(stand, 1, X)
    Xnew = MLJBase.transform(stand, f, X)
    @test issubset(Set(keys(f[3])), Set(Tables.schema(X).names[3:4,]))
    Xt = MLJBase.transform(stand, f, X)
    @test_throws Exception MLJBase.inverse_transform(stand, f, Xt)

    @test Xnew[1] == X[1]
    @test Xnew[2] == X[2]
    @test elscitype(X[3]) <: OrderedFactor
    @test elscitype(Xnew[3]) <: Continuous
    @test MLJBase.std(Xnew[3]) ≈ 1.0
    @test elscitype(X[4]) == Count
    @test elscitype(Xnew[4]) <: Continuous
    @test MLJBase.std(Xnew[4]) ≈ 1.0
    @test Xnew[5] == X[5]

    stand = Standardizer(features= x-> x == (:x2))
    f,    = MLJBase.fit(stand, 1, X)
    Xnew  = MLJBase.transform(stand, f, X)

    @test Xnew[1] == X[1]
    @test MLJBase.std(Xnew[2]) ≈ 1.0
    @test Xnew[3] == X[3]
    @test Xnew[4] == X[4]
    @test Xnew[5] == X[5]

    infos = MLJBase.info_dict(stand)

    @test infos[:name] == "Standardizer"
    @test infos[:input_scitype] ==
        Union{MLJBase.Table, AbstractVector{<:Continuous}}
    @test infos[:output_scitype] ==
        Union{MLJBase.Table, AbstractVector{<:Continuous}}

    # univariate case
    stand = Standardizer()
    f, _, _   = MLJBase.fit(stand, 1, [0, 2, 4])
    @test round.(Int, MLJBase.transform(stand, f, [0,4,8])) == [-1.0,1.0,3.0]
    @test [(MLJBase.fitted_params(stand, f).mean_and_std)...] ≈
        [2, MLJBase.std([0, 2, 4])]

end

### TIMETYPE TO CONTINUOUS

@testset "TimeTypeToContinuous" begin
    let dt = [Date(2018, 6, 15) + Day(i) for i=0:10],
        transformer = UnivariateTimeTypeToContinuous(; step=Day(1))
        fr, _, _ = MLJBase.fit(transformer, 1, dt)
        @test fr == (Date(2018, 6, 15), Day(1))
        dt_continuous = MLJBase.transform(transformer, fr, dt)
        @test all(dt_continuous .== Float64.(0:10))
    end

    let dt = [Date(2018, 6, 15) + Day(i) for i=0:10],
        transformer = UnivariateTimeTypeToContinuous()
        @test_logs(
            (:warn, "Cannot add `TimePeriod` `step` to `Date` `zero_time`. Converting `step` to `Day`."),
            MLJBase.fit(transformer, 1, dt)
        )
        fr, _, _ = MLJBase.fit(transformer, 1, dt)
        @test fr == (Date(2018, 6, 15), Day(1))
        dt_continuous = MLJBase.transform(transformer, fr, dt)
        @test all(dt_continuous .== Float64.(0:10))
    end

    let dt = [Time(0, 0, 0) + Hour(i) for i=0:3:30],
        transformer = UnivariateTimeTypeToContinuous(;
            step = Hour(1),
            zero_time = Time(7, 0, 0),
        )
        fr, _, _ = MLJBase.fit(transformer, 1, dt)
        @test fr == (Time(7, 0, 0), Hour(1))
        dt_continuous = MLJBase.transform(transformer, fr, dt)
        ex = collect(0:3:30) .% 24 .- 7.0
        diff = map(dt_continuous .- ex) do d
            mod(d, 24.0)
        end
        @test all(diff .≈ 0.0)
    end

    let dt = [Time(0, 0, 0) + Hour(i) for i=0:3:30],
        transformer = UnivariateTimeTypeToContinuous()
        fr, _, _ = MLJBase.fit(transformer, 1, dt)
        @test fr == (Time(0, 0, 0), Hour(24))
        dt_continuous = MLJBase.transform(transformer, fr, dt)
        ex = collect(0:3:30) .% 24 ./ 24
        diff = map(dt_continuous .- ex) do d
            mod(d, 1.0)
        end
        @test all(diff .≈ 0.0)
    end

    # test log messages
    let dt = [DateTime(2018, 6, 15) + Day(i) for i=0:10],
        step=Hour(1),
        zero_time=Date(2018, 6, 15),
        transformer = UnivariateTimeTypeToContinuous(;
            step=step,
            zero_time=zero_time,
        )
        @test_logs(
            (:warn, "Cannot add `TimePeriod` `step` to `Date` `zero_time`. Converting `zero_time` to `DateTime`."),
            UnivariateTimeTypeToContinuous(;
                step=step,
                zero_time=zero_time,
            )
        )
        fr, _, _ = MLJBase.fit(transformer, 1, dt)

        @test fr == (zero_time, step)
        dt_continuous = MLJBase.transform(transformer, fr, dt)
        @test all(dt_continuous .== Float64.(0:10).*24)
    end

    let dt = [Time(0, 0, 0) + Hour(i) for i=0:3:30],
        zero_time=Time(0, 0, 0),
        step=Day(1),
        transformer = UnivariateTimeTypeToContinuous(;
            step=step,
            zero_time=zero_time,
        )
        @test_logs(
            (:warn, "Cannot add `DatePeriod` `step` to `Time` `zero_time`. Converting `step` to `Hour`."),
            UnivariateTimeTypeToContinuous(;
                step=step,
                zero_time=zero_time,
            )
        )
        fr, _, _ = MLJBase.fit(transformer, 1, dt)

        @test fr == (zero_time, convert(Hour, step))
        dt_continuous = MLJBase.transform(transformer, fr, dt)
        ex = Float64.((0:3:30) .% 24)./24
        diff = map(dt_continuous .- ex) do d
            mod(d, 1.0)
        end
        @test all(diff .≈ 0.0)
    end

    let dt = [DateTime(2018, 6, 15) + Day(i) for i=0:10],
        step=Day(1),
        zero_time=Date(2018, 6, 15),
        transformer = UnivariateTimeTypeToContinuous(;
            step=step,
            zero_time=zero_time,
        )
        @test_logs(
            (:warn, "`Dates.Date` `zero_time` is not compatible with `Dates.DateTime` vector. Attempting to convert `zero_time`."),
            MLJBase.fit(transformer, 1, dt)
        )
        fr, _, _ = MLJBase.fit(transformer, 1, dt)

        @test fr == (zero_time, step)
        dt_continuous = MLJBase.transform(transformer, fr, dt)
        @test all(dt_continuous .== Float64.(0:10))
    end
end

#### UNIVARIATE BOX COX TRANSFORMER ####

@testset "U-boxcox" begin
    # create skewed non-negative vector with a zero value:
    Random.seed!(1551)
    v = abs.(randn(1000))
    v = v .- minimum(v)

    t  = UnivariateBoxCoxTransformer(shift=true)
    f, = MLJBase.fit(t, 2, v)

    e = v - MLJBase.inverse_transform(t, f, MLJBase.transform(t, f, v))
    @test sum(abs, e) <= 5000*eps()

    infos = MLJBase.info_dict(t)

    @test infos[:name] == "UnivariateBoxCoxTransformer"
    @test infos[:input_scitype] == AbstractVector{MLJBase.Continuous}
    @test infos[:output_scitype] == AbstractVector{MLJBase.Continuous}
end

#### ONE HOT ENCODER ####

@testset "One-Hot" begin
    X = (name   = categorical(["Ben", "John", "Mary", "John"], ordered=true),
         height = [1.85, 1.67, 1.5, 1.67],
         favourite_number = categorical([7, 5, 10, 5]),
         age    = [23, 23, 14, 23])

    t  = OneHotEncoder()
    f, _, report = @test_logs((:info, r"Spawning 3"),
                    (:info, r"Spawning 3"), MLJBase.fit(t, 1, X))

    Xt = MLJBase.transform(t, f, X)

    @test Xt.name__John == float.([false, true, false, true])
    @test Xt.height == X.height
    @test Xt.favourite_number__10 == float.([false, false, true, false])
    @test Xt.age == X.age
    @test MLJBase.schema(Xt).names == (:name__Ben, :name__John, :name__Mary,
                               :height, :favourite_number__5,
                               :favourite_number__7, :favourite_number__10,
                               :age)

    @test report.new_features == collect(MLJBase.schema(Xt).names)

    # test that *entire* pool of categoricals is used in fit, including
    # unseen levels:
    f, = @test_logs((:info, r"Spawning 3"), (:info, r"Spawning 3"),
                          MLJBase.fit(t, 1, MLJBase.selectrows(X,1:2)))
    Xtsmall = MLJBase.transform(t, f, X)
    @test Xt == Xtsmall

    # test that transform can be applied to subset of the data:
    @test MLJBase.transform(t, f, MLJBase.selectcols(X, [:name, :age])) ==
        MLJBase.selectcols(MLJBase.transform(t, f, X),
                           [:name__Ben, :name__John, :name__Mary, :age])

    # test ignore
    t = OneHotEncoder(features=[:name,], ignore=true)
    f, = MLJBase.fit(t, 0, X)
    Xt = MLJBase.transform(t, f, X)

    # test exclusion of ordered factors:
    t  = OneHotEncoder(ordered_factor=false)
    f, = MLJBase.fit(t, 0, X)
    Xt = MLJBase.transform(t, f, X)
    @test keys(Xt) == (:name, :height, :favourite_number__5,
                       :favourite_number__7, :favourite_number__10, :age)

    @test :name in Tables.schema(Xt).names
    @test :favourite_number__5 in Tables.schema(Xt).names
    @test MLJBase.schema(Xt).scitypes == (OrderedFactor{3}, Continuous,
                                          Continuous, Continuous,
                                          Continuous, Count)

    # test that one may not add new columns:
    X = (name = categorical(["Ben", "John", "Mary", "John"], ordered=true),
         height     = [1.85, 1.67, 1.5, 1.67],
         favourite_number = categorical([7, 5, 10, 5]),
         age        = [23, 23, 14, 23],
         gender     = categorical(['M', 'M', 'F', 'M']))
    @test_throws Exception MLJBase.transform(t, f, X)

    infos = MLJBase.info_dict(t)

    @test infos[:name] == "OneHotEncoder"
    @test infos[:input_scitype] == MLJBase.Table
    @test infos[:output_scitype] == MLJBase.Table
end

#### FILL IMPUTER ####'

@testset "UnivariateFillImputer" begin
    vpure = rand(stable_rng, 10)
    v = vcat([missing, ], vpure)
    filler = median(vpure)
    imp = MLJModels.UnivariateFillImputer()
    f, = MLJBase.fit(imp, 1, v)
    vnew = [missing, 1.0, missing, 2.0, 3.0]
    @test MLJBase.transform(imp, f, vnew) ≈ [filler, 1.0, filler, 2.0, 3.0]

    vpure = MLJBase.coerce(rand(stable_rng, "abc", 100), OrderedFactor);
    v = vcat([missing, ], vpure)
    filler = mode(vpure)
    imp = MLJModels.UnivariateFillImputer()
    f, = MLJBase.fit(imp, 1, v)
    vnew = vcat([missing, ], vpure[end-10:end], [missing, ])
    @test MLJBase.transform(imp, f, vnew) ==
        vcat([filler, ], vpure[end-10:end], [filler, ])

    vpure = rand(stable_rng, Int, 10)
    v = vcat([missing, ], vpure)
    filler = round(Int, median(vpure))
    imp = MLJModels.UnivariateFillImputer()
    f, = MLJBase.fit(imp, 1, v)
    vnew = [missing, 1, missing, 2, 3]
    @test MLJBase.transform(imp, f, vnew) == [filler, 1, filler, 2, 3]

    @test_throws Exception MLJBase.transform(imp, f, [missing, "1", "2"])

    @test_throws ArgumentError MLJBase.fit(imp, 1, [missing, "1", "2"])

end

@testset "FillImputer" begin
    X = (
        x = [missing,ones(10)...],
        y = [missing,ones(10)...],
        z = [missing,ones(10)...]
        )

    imp = FillImputer()
    f,  = MLJBase.fit(imp, 1, X)

    fp = MLJBase.fitted_params(imp, f)
    @test fp.features_seen_in_fit == [:x, :y, :z]
    @test fp.univariate_transformer == MLJModels.UnivariateFillImputer()
    @test fp.filler_given_feature[:x] ≈ 1.0
    @test fp.filler_given_feature[:x] ≈ 1.0
    @test fp.filler_given_feature[:x] ≈ 1.0

    Xnew = MLJBase.selectrows(X, 1:5)
    Xt  = MLJBase.transform(imp, f, Xnew)
    @test all(.!ismissing.(Xt.x))
    @test Xt.x isa Vector{Float64} # no missing
    @test all(Xt.x .== 1.0)

    imp = FillImputer(features=[:x,:y])
    f,  = MLJBase.fit(imp, 1, X)
    Xt = MLJBase.transform(imp, f, Xnew)
    @test all(Xt.x .== 1.0)
    @test all(Xt.y .== 1.0)
    @test ismissing(Xt.z[1])

    # adding a new feature not seen in fit:
    Xnew = (x = X.x, y=X.y, a=X.x)
    @test_throws ArgumentError  MLJBase.transform(imp, f, Xnew)

    # mixture of features:
    X = (x = categorical([missing, missing, missing, missing,
                          "Old", "Young", "Middle", "Young",
                          "Old", "Young", "Middle", "Young"]),
         y = [missing, ones(11)...],
         z = [missing, missing, 1,1,1,1,1,5,1,1,1,1],
         a = rand("abc", 12))

    imp = FillImputer()
    f, = MLJBase.fit(imp, 1, X)
    Xnew = MLJBase.selectrows(X, 1:4)
    Xt = MLJBase.transform(imp, f, Xnew)

    @test all(.!ismissing.(Xt.x))
    @test all(.!ismissing.(Xt.y))
    @test all(.!ismissing.(Xt.z))
    @test all(.!ismissing.(Xt.a))

    @test Xt.x[1] == mode(skipmissing(X.x))
    @test Xt.y[1] == 1
    @test Xt.z[1] == 1

    # user specifies a feature explicitly that's not supported:
    imp = FillImputer(features=[:x, :a]) # :a of Unknown scitype not supported
    @test_logs (:info, r"Feature a will not") MLJBase.fit(imp, 1, X)

end

#### CONTINUOUS ENCODER ####

@testset "Continuous encoder" begin

    X = (name  = categorical(["Ben", "John", "Mary", "John"], ordered=true),
         height = [1.85, 1.67, 1.5, 1.67],
         rubbish = ["a", "b", "c", "a"],
         favourite_number = categorical([7, 5, 10, 5]),
         age    = [23, 23, 14, 23])

    t  = ContinuousEncoder()
    f, _, _ = @test_logs((:info, r"Some.*dropped\:.*\:rubbish\]"),
                              MLJBase.fit(t, 1, X))

    Xt = MLJBase.transform(t, f, X)
    @test scitype(Xt) <: MLJBase.Table(MLJBase.Continuous)
    s = MLJBase.schema(Xt)
    @test s.names == (:name, :height, :favourite_number__5,
                      :favourite_number__7, :favourite_number__10, :age)

    t  = ContinuousEncoder(drop_last=true, one_hot_ordered_factors=true)
    f, _, r = MLJBase.fit(t, 0, X)
    Xt = MLJBase.transform(t, f, X)
    @test scitype(Xt) <: MLJBase.Table(MLJBase.Continuous)
    s = MLJBase.schema(Xt)
    @test s.names == (:name__Ben, :name__John, :height, :favourite_number__5,
                      :favourite_number__7, :age)

end

end
true


text:
# module Analysis
#
# using PyCall, Plots#; pyplot()
#
# ase_read = pyimport("ase.io")["read"]
# ase_write = pyimport("ase.io")["write"]
#
# export plotV, ase_read, ase_write
#
# function plotV(al :: Array{PyCall.PyObject,1}; hist=true)
#     Vl = []
#
#     for at in al
#         V = dot(at[:cell][3,:],
#         cross(at[:cell][1,:], at[:cell][2,:]))
#         push!(Vl, V)
#     end
#
#     if hist == true
#         histogram(Vl)
#     else
#         plot(Vl)
#     end
# end
#
# end


text:
include("./imports.jl")

@testset ExtendedTestSet "unet3D" begin
    @testset ExtendedTestSet "unet3D" begin
	x = rand(Float32, 96, 96, 96, 1, 1)
    model = unet3D(1, 1)
    x̂ = model(x)
    @test size(x) == size(x̂)
    end

    @testset ExtendedTestSet "unet3D" begin
	x = rand(Float32, 64, 64, 64, 3, 1)
    model = unet3D(3, 1)
    x̂ = model(x)
    @test size(x) != size(x̂)
    end

    @testset ExtendedTestSet "unet3D" begin
	x = rand(Float32, 64, 64, 64, 3, 1)
    model = unet3D(3, 1)
    x̂ = model(x)
    @test [size(x, 1), size(x, 2), size(x, 3)] == [size(x̂, 1), size(x̂, 2), size(x̂, 3)]
    end
end


text:
TOL = 0.5e-8

function secant(f::Function, x₀::Real, x₁::Real;
                ε::Real = TOL, iₘₐₓ::Int = 100)
    i = 0
    xᵢ, x₋ = x₁, x₀
    εᵢ = abs(xᵢ - x₋)
    while εᵢ >= ε && i < iₘₐₓ
        xᵢ, x₋ = xᵢ - (f(xᵢ) * (xᵢ - x₋))/ (f(xᵢ) - f(x₋)), xᵢ
        εᵢ = abs(xᵢ - x₋)
        i += 1
    end
    xᵢ, εᵢ, i
end