text:
if Sys.islinux()
    global PATH = pwd() * "/src/SamplePages/BrowserImages/"
elseif is_windows() # Do something windows-y here
    global PATH = pwd() * "/src\\SamplePages\\BrowserImages\\"
end

using Cairo, Gtk, Gtk.ShortNames, Graphics

export    DrawViewport, DrawContent, DrawShape, setcolor, drawNode,
          lastClipParent, inheritColor
# ======================================================================================
# These should go somewhere else.
# ======================================================================================
function inheritColor(node)
    while !isdefined(node.shape, :color) || length(node.shape.color) < 3
         node = node.parent
    end
    return node.shape.color
end
# ======================================================================================
function inheritFont(node)
    while !isdefined(node, :font) #|| length(node.font)
         node = node.parent
    end
    return node.font
end
# ======================================================================================
# Accept color values with or without alpha
# ======================================================================================
setcolor( ctx::Cairo.CairoContext, r, g, b) = set_source_rgb(ctx, r, g, b);
setcolor( ctx::Cairo.CairoContext, r, g, b, a) = set_source_rgba(ctx, r, g, b, a);
# setcolor( ctx::Cairo.CairoContext, color) = set_source_rgba(ctx, color...);
# setcolor( ctx::Cairo.CairoContext, node::Element) = set_source_rgba(ctx, node.shape.color...);
# setcolor( ctx::Cairo.CairoContext, shape::Draw) = set_source_rgba(ctx, shape.color...);

# ======================================================================================
# First draw all page elements (not controls) that flow and then draw "fixed" elements
# ======================================================================================
function DrawViewport(ctx::Cairo.CairoContext, document::Page, node::Element)
    DrawContent(ctx, document, node)
    DrawContent(ctx, document, document.fixed)
end
# ======================================================================================
# This takes a node and draws it along with all its children.
# ======================================================================================
# PROBLEM: This draws the children of a given node and not nesesarily the node itself.
#          At times, though, we don't want to draw all the children of a node but
#          specifically one node.
# ======================================================================================
function DrawContent(ctx::Cairo.CairoContext, document::Page, node::Element, clipPath=nothing)
      rows = node.rows
      border = get(node.shape.border,  Border(0,0,0,0,0,0, 0,[],[0,0,0,0]))
      padding = get(node.shape.padding, BoxOutline(0,0,0,0,0,0))
      Shape = node.shape

      if Shape.flags[Clip]
          clipPath = getBorderBox(Shape, border, padding)
      end

      if clipPath !== nothing
          rectangle(ctx, clipPath... )
          clip(ctx)
      end

  for row in rows
      for child in row.nodes
          if row.top < (Shape.top + Shape.height) # && !node.shape.flags[DisplayNone]
              drawNode(ctx, document, row, child.shape, child, clipPath)
          end
      end
  end

      # Scroll bars.......... TODO: fix
      if Shape.flags[IsVScroll]
          VScroller(ctx, document, node, Shape, clipPath)
      end
      if Shape.flags[Clip] #|| clipPath === nothing
         reset_clip(ctx)
         clipPath = nothing
      end
end
# ======================================================================================
# ======================================================================================
function drawNode(ctx, document, row, shape, node, clipPath)
             if isa(shape, TextLine)
                 DrawText(ctx, node, clipPath)
             else
                 DrawShape(ctx, node, shape, clipPath)
                 DrawContent(ctx, document, node, clipPath)     # Now draw children
             end
             # Draw box to be able to visualize text's area
             # if isa(node, Text)
             #     s = node.parent.shape
             #     setcolor(ctx, .3,.3,.3)
             #     set_line_width(ctx, 1)
             #     rectangle(ctx, s.left,s.top,s.width,s.height )
             #     stroke(ctx);
             # end
end
#...............................................................................
function lastClipParent(node)
    while node.parent !== node
        if node.shape.flags[Clip]
            return node
        end
        node = node.parent
    end
    return node
end

function drawNode(ctx, document, node)

    box = lastClipParent(node) #document.children[1].children[3];

    shape = node.shape
    WinShape = box.shape
    border = get(WinShape.border,  Border(0,0,0,0,0,0, 0,[],[0,0,0,0]))
    padding = get(WinShape.padding, BoxOutline(0,0,0,0,0,0))
    clipPath = getBorderBox(WinShape, border, padding)

    rectangle(ctx, clipPath... )
    clip(ctx)
                 # DrawText(ctx, row, shape)
             #else
                 DrawShape(ctx, node, shape, clipPath)
            if !isa(node, TextElement)
                 DrawContent(ctx, document, node, clipPath)     # Now draw children
            end
    reset_clip(ctx)

end

# linpat = cairo_pattern_create_linear(0, 0, 1, 1);
# pattern_add_color_stop_rgb(linpat, 0, 0, 0.3, 0.8);
# pattern_add_color_stop_rgb(linpat, 1, 0, 0.8, 0.3);

# radpat = cairo_pattern_create_radial(0.5, 0.5, 0.25, 0.5, 0.5, 0.75);
# pattern_add_color_stop_rgba(radpat, 0, 0, 0, 0, 1);
# pattern_add_color_stop_rgba(radpat, 0.5, 0, 0, 0, 0);

# set_source(cr, linpat);
# mask(cr, radpat);

# ======================================================================================
# http://www.nongnu.org/guile-cairo/docs/html/Patterns.html
# Draw a circle.
# TODO: remove node from function
# ======================================================================================
function DrawShape(ctx::CairoContext, node::Element, shape::Draw, clipPath)
    Cairo.save(ctx)
    path = GetPath(shape)
    # set_antialias(ctx,4)
    setPath(ctx::CairoContext, path)
    clip(ctx);
  if shape.flags[HasImage]
        DOM =  node.DOM
            if haskey(DOM, "image")
                imagePath = PATH * DOM["image"] # "Mountains.png"
            end
            BackgroundImage(ctx, path.wide, path.tall, shape.left, shape.top, imagePath)

  elseif  shape.flags[LinearGrad]
        linearGrad(ctx, path, shape.gradient)
  elseif  shape.flags[RadialGrad]
        radialGrad(ctx, path, shape.gradient)
  else
          # set_antialias(ctx,6)
          if isdefined(shape, :color) &&  length(shape.color) > 2
            setPath(ctx::CairoContext, path)
            setcolor(ctx, shape.color...)
            fill(ctx);
          end
  end
  restore(ctx)
  #reset_clip(ctx)
     thickness = setClipPath(ctx::CairoContext, path)
     clip(ctx)
          if isdefined(path.border, :color) && length(path.border.color) > 2
                setcolor( ctx, path.border.color...)
                # TODO: add clipping for the border path as well to clean up edges.
                #if clipPath !== nothing
                    # rectangle(ctx, clipPath... )
                    # clip(ctx)
                #end
                #clip_preserve()
                # save(ctx)
                        set_line_type(ctx, path.border.style)
                        set_line_width(ctx, thickness);
                        setborderPath(ctx::CairoContext, path)
                        stroke(ctx)
                # restore(ctx)

                # reset_clip(ctx)
          end
        reset_clip(ctx)
end
# ======================================================================================
# Temporary shortcut: draw the virticle scroll bar
# ======================================================================================
function VScroller(ctx::CairoContext, document::Page, node::Element, shape::Draw, clipPath)

    canvas = document.canvas
    ctx = getgc(canvas)
    winHeight = document.height # height(canvas)

        border = get(shape.border,  Border(0,0,0,0,0,0, 0,[],[0,0,0,0]))
        padding = get(shape.padding, BoxOutline(0,0,0,0,0,0))
        l,t,w,h = getBorderBox(shape, border, padding)
        h/node.scroll.contentHeight
        r, b = l+w-12, t+h-12

        realTop = abs(t)

        setcolor(ctx, .3,.3,.3, .3)
        rectangle(ctx,r,t,12,h )
        fill(ctx);
        #.............................................................
         height       = node.shape.height - node.shape.top    # Height of the viewport and of the scrollbarArea
         scrollHeight = node.scroll.contentHeight     # Height of the content
         scrollTop    = node.scroll.y      # Scrolled position of the content from the top
         #.............................................................
         scrollButHeight = height / (scrollHeight / height)
         y = scrollTop / (scrollHeight / height)
         #.............................................................
         #.............................................................

        setcolor(ctx, .4,.4,.4, 1)
        rectangle(ctx,r+1,t-y,10,scrollButHeight )
        fill(ctx);
end

#type
# ('m',1,2)
# ('m',1,2) M = moveto
# ('l',1,2) L = lineto                   H = horizontal lineto                 V = vertical lineto
# ('c',1,2) C = curveto                  S = smooth curveto
# ('q',1,2) Q = quadratic Bézier curve   T = smooth quadratic Bézier curveto
# ('a',1,2) A = elliptical Arc
# ('z',1,2) Z = closepath

    # close_path,
    # move_to, line_to, rel_line_to, rel_move_to,
    # arc, arc_negative,
    # curve_to, rel_curve_to,

function drawPath(ctx, path)
    startPoint = nothing
    for step in path
        if step[1] == 'm'
            startPoint = step[1]
            move_to(ctx, step[2], step[3])
        elseif step[1] == 'l'
            line_to(ctx, step[2], step[3])
        elseif step[1] == 'c'
            curve_to(ctx, )
        elseif step[1] == 'q'
            curve_to(ctx, )
        elseif step[1] == 'a'
            arc(ctx, )
        elseif step[1] == 'z'
            close_path(ctx, )
        end
    end
end

#  (from Cairo/src/cairo.jl):
#  function CairoImageSurface(img::Array{Uint32,2}, format::Integer; flipxy::Bool = true)
# CairoImageSurface(ary, fmt) copies the input array, but you can access the raw pixels
#  (R/W) by using the .data attribute of the surface.
# ======================================================================================
#
# CALLED FROM:
# ======================================================================================
#=
inline_png2 = b"\x89PNG\r\n\x1a\n\x00\x00\x00\rIHDR\x00\x00\x00d\x00\x00\x00d\x08\x02\x00\x00\x00\xff\x80\x02\x03\x00\x00\x00\x06bKGD\x00\xff\x00\xff\x00\xff\xa0\xbd\xa7\x93\x00\x00\x01=IDATx\x9c\xed\xdc\xc1i\x85@\x14@\xd1IH!\x96bi\x96f)\x96\x92E@!\x01\x7f\xeeF\xfdp\xce\xca\x9d\xc3\xe5\xb9{\xce\xc7\xb2,\x83\xff\xf9\xbc\xfb\x00\xefD\xac@\xac@\xac@\xac@\xac@\xac@\xac@\xac\xe0\xeb\xee\x03\xfc\xb1\xaec]\xc7\x18c\x9e\xc7<\xdf{\x96_LV V V V V V V V V V V V V V V V V V V V V V V V V V V V V V V V V V V V V\xf0j[\xf9gq\xf8J\xdbv<\\\xff\xf6\xd3\xfd\xe8\xe7\xc5\xdam\xdb\x11\xee2\xa7\xb1|\x86\xc1\xab\xc9\xba~m\x7f\x1f\xa8i\x1a\xd3t\xf5\xdbO=/\xd6\xba\x1e\xb1\xfca\xf1\xbe\xc4\n\xc4\n\xc4\n\xc4\n\xc4\n\xc4\n\xc4\n\xc4\n\xc4\n\xc4\n\xc4\n\xc4\n\xc4\n\xc4\n\xc4\n\xc4\n\xc4\n\xc4\n\xc4\n\xc4\n\xc4\n\xc4\n\xc4\n\xc4\n\xc4\n\xc4\n\xc4\n\xc4\n\xc4\n\xc4\n\xc4\n\xc4\n\xc4\n\xc4\n\xc4\n\xc4\n\x9ew\x1f\xfc\xf3\xae\x81\xdf\x99\xac@\xac@\xac@\xac@\xac@\xac@\xac@\xac@\xac\xe0\x1b\xaf\xef\x1c\xd4\x17\xfe\xcaX\x00\x00\x00\x00IEND\xaeB`\x82"
png_stream = IOBuffer();
write(png_stream,inline_png2);
seekstart(png_stream);

imagesurface = read_from_png(png_stream);
set_source_surface(cr, imagesurface, 0, 0);
paint(cr);
=#

mutable struct NQCircle
    border::Border
    radius::Float64
    left::Float64
    top::Float64
    wide::Float64
    tall::Float64
    NQCircle() = new()
end
mutable struct NQBox
    border::Border
    padding::BoxOutline
    left::Float64
    top::Float64
    wide::Float64
    tall::Float64
    right::Float64
    bottom::Float64
    NQBox() = new()
end

# ======================================================================================

# Draw the background image at 100% width and height.
# ======================================================================================
function BackgroundImage(ctx::CairoContext, wide::Float64, tall::Float64, l::Float64, t::Float64, path)

     image = read_from_png(path);
     w = image.width;  # w = (image.width/2);
     h = image.height; # h = (image.height/2);

     scale(ctx, wide/w, tall/h);
     translate(ctx,  (l*((w/wide)))-(l),  (t*(w/tall))-(t)); # 1029 >= w*2   l + l*(wide/w) + wide*(wide/w) =1014
     set_source_surface(ctx, image, l, t);
     paint(ctx);
     reset_transform(ctx)
end
# ======================================================================================
# Draw a linear gradient
# ======================================================================================
function linearGrad(ctx::CairoContext, path, gradient)
    pat = pattern_create_linear(path.left+0.0, path.top+180.0, path.left+180.0, path.top+0.0 );
    for i in 1:length(gradient)
            pattern_add_color_stop_rgba(pat, gradient[i] ...);
    end
    setPath(ctx, path)
    set_source(ctx, pat);
    fill(ctx);
    destroy(pat);
end
# ======================================================================================
# Draw a radial gradient.
# ======================================================================================
function radialGrad(ctx::CairoContext, path, gradient)
    offsetX, offsetY = 0, 0 #This can be added later to the second x,y coords
    # color, offset, start
    pat = pattern_create_radial(path.left,         path.top,         1,
                                path.left+offsetX, path.top+offsetY, path.wide*.5);
            pattern_add_color_stop_rgba(pat, 0, 0, 1, 0, 1);
            pattern_add_color_stop_rgba(pat, .5, 0, 0, 0, 1);
            pattern_add_color_stop_rgba(pat, 1, 0, 0, 1, 1);
            set_source(ctx, pat);
            setPath(ctx, path)
            fill(ctx);
            destroy(pat);
end
# ======================================================================================
# ======================================================================================
function GetPath(shape::NBox)
    b = NQBox()
    b.border  = get(shape.border,  Border(0,0,0,0,0,0, "solid",[],[0,0,0,0]))
    b.padding = get(shape.padding, BoxOutline(0,0,0,0,0,0))

        # b.left = shape.left   - b.padding.left   - (b.border.left *.5)
        # b.top  = shape.top    - b.padding.top    - (b.border.top *.5)
        # b.wide = shape.width  + b.padding.width  + (b.border.width)
        # b.tall = shape.height + b.padding.height + (b.border.height)

    b.left, b.top, b.wide, b.tall = getBorderBox(shape, b.border, b.padding)
    b.left  -= (b.border.left*.6)
    b.top   -= (b.border.top*.6)
    b.right  = b.left + b.wide
    b.bottom = b.top + b.tall
    return b
end

function GetPath(shape::Circle)
        c = NQCircle()
        border   = get(shape.border,  Border(0,0,0,0,0,0, "solid",[],[0,0,0,0]))
        c.border = border
        c.wide   = shape.radius*2
        c.tall   = shape.radius*2
        c.radius = shape.radius + (border.width*0.25)
        c.left   = shape.left + c.radius - (border.left*.5)
        c.top    = shape.top  + c.radius - (border.top*.5)
    return c
end
# ======================================================================================#
# Set circle path and fill with color
# ======================================================================================#
function setPath(ctx::CairoContext, shape::NQBox)
    if shape.border != nothing #!isnull(shape.border.radius) #(TR + BR + BL + TL) > 0
        radius = shape.border.radius
        TR = radius[1]
        BR = radius[2]
        BL = radius[3]
        TL = radius[4]
              rot   =   1.5707963    # 90 * degrees
          # TODO: see if curveTo() will work to simplify this.
                new_sub_path(ctx);
         		arc(ctx, shape.right - TR, shape.top    + TR, TR,     -rot,   0   );    # topRight
         		arc(ctx, shape.right - BR, shape.bottom - BR, BR,     0,      rot ); # bottomRight
         		arc(ctx, shape.left  + BL, shape.bottom - BL, BL,     rot,    pi  );   # bottomLeft
         		arc(ctx, shape.left  + TL, shape.top    + TL, TL,     pi,     -rot);      # topLeft
       	        close_path(ctx);
    else
        rectangle(ctx, shape.left, shape.top, shape.wide, shape.tall )
    end
end
function setPath(ctx, shape::NQCircle)
        move_to(ctx, shape.left, shape.top)
        arc(ctx, shape.left, shape.top, shape.wide*.5, 0, 2*pi);
end
# ======================================================================================
function setClipPath(ctx::CairoContext, shape::NQCircle)
    b  = shape.border.top #*.5
        move_to(ctx, shape.left + shape.radius, shape.top)
        arc(ctx, shape.left, shape.top, shape.radius+b, 0, 2*pi);
  return b
end

function setClipPath(ctx::CairoContext, shape::NQBox)
  bw  = shape.border.width*.5
  bh  = shape.border.height*.5
  borderWidth = max(shape.border.left, shape.border.top, shape.border.right,shape.border.bottom)

  if shape.border.radius != nothing # !isnull(shape.border.radius) #(TR + BR + BL + TL) > 0
      radius = shape.border.radius
      TR = radius[1]
      BR = radius[2]
      BL = radius[3]
      TL = radius[4]
            rot   =   1.5707963    # 90 * degrees
        # TODO: see if curveTo() will work to simplify this.
        borderWidth = max(shape.border.left, shape.border.top, shape.border.right,shape.border.bottom)
        line = (borderWidth/2)
        t = shape.top  #- (line - shape.border.top)
        l = shape.left  #- (line - shape.border.left)
        r = shape.right       #(shape.border.right - line)
        b = shape.bottom      #(shape.border.bottom - line)
            new_sub_path(ctx);
              arc(ctx, r - TR, t + TR, TR,     -rot,   0   );    # topRight
              arc(ctx, r - BR, b - BR, BR,     0,      rot ); # bottomRight
              arc(ctx, l + BL, b - BL, BL,     rot,    pi  );   # bottomLeft
              arc(ctx, l + TL, t + TL, TL,     pi,     -rot);      # topLeft
            close_path(ctx);
  else
      rectangle(ctx, shape.left, shape.top, shape.wide, shape.tall )
  end
  return borderWidth
end
# ======================================================================================
function setborderPath(ctx::CairoContext, shape::NQBox)

  # TODO: see if curveTo() will work to simplify this.
  borderWidth = max(shape.border.left,shape.border.top,shape.border.right,shape.border.bottom)
  line = (borderWidth/2)
  t = shape.top - (line - shape.border.top)
  l = shape.left - (line - shape.border.left)
  r = shape.right - (shape.border.right - line)
  b = shape.bottom - (shape.border.bottom - line)

    if shape.border.radius != nothing # !isnull(shape.border.radius) #(TR + BR + BL + TL) > 0
      radius = shape.border.radius
      # radius = border.radius,[0,0,0,0])
      TR = radius[1]
      BR = radius[2]
      BL = radius[3]
      TL = radius[4]
      rot   =   1.5707963    # 90 * degrees
              new_sub_path(ctx);
                arc(ctx, r - TR, t + TR, TR,     -rot,   0   );    # topRight
                arc(ctx, r - BR, b - BR, BR,     0,      rot ); # bottomRight
                arc(ctx, l + BL, b - BL, BL,     rot,    pi  );   # bottomLeft
                arc(ctx, l + TL, t + TL, TL,     pi,     -rot);      # topLeft
              close_path(ctx);
    else
        rectangle(ctx, l, t, r, b )
    end
end
function setborderPath(ctx::CairoContext, shape::NQCircle)
        move_to(ctx, shape.left + shape.radius, shape.top)
        arc(ctx, shape.left, shape.top, shape.radius, 0, 2*pi);
end


text:
using Gtk

function keypresswindow()
    tcount = 0
    txt = "Press a Key"
    win = GtkWindow("Keypress Test", 500, 30) |> (GtkFrame() |> ((vbox = GtkBox(:v)) |> (lab = GtkLabel(txt))))
    function keycall(w, event)
        ch = Char(event.keyval)
        tcount += 1
        set_gtk_property!(lab, :label, "You have typed $tcount chars including $ch this time")
    end
    signal_connect(keycall, win, "key-press-event")

    cond = Condition()
    endit(w) = notify(cond)
    signal_connect(endit, win, :destroy)
    showall(win)
    wait(cond)
end

keypresswindow()

ext: jl

max_stars_repo_name: ethansaxenian/RosettaDecode

max_issues_repo_name: ethansaxenian/RosettaDecode

max_forks_repo_name: ethansaxenian/RosettaDecode


text:
using KrylovMethods
using Base.Test

println("=== Testing bicgstb for real matrix === ")
A  = sprandn(100,100,.1) + 10*speye(100)
D  = diag(A)
Af(x) = A*x
M(x)  = D.\x
rhs = randn(100)

# test flag for early stopping
xt = bicgstb(A,rhs,tol=1e-6,maxIter=3,out=2,storeInterm=true)
@test xt[2]==-1

# test handling of zero rhs
xt = bicgstb(A,zeros(size(A,2)),tol=1e-6,maxIter=3,out=2)
@test  all(xt[1].==0)
@test xt[2]==-9

x0 = bicgstb(full(A),rhs,tol=1e-6)
x1 = bicgstb(A,rhs,tol=1e-6,out=1)
x2 = bicgstb(Af,rhs,tol=1e-6)
x3 = bicgstb(Af,rhs,tol=1e-6,maxIter=100,x=randn(size(rhs)))
x5 = bicgstb(Af,rhs,tol=1e-6,maxIter=100,M1=M)

@test norm(A*x0[1]-rhs)/norm(rhs) < 1e-6
@test norm(A*x1[1]-rhs)/norm(rhs) < 1e-6
@test norm(A*x2[1]-rhs)/norm(rhs) < 1e-6
@test norm(A*x3[1]-rhs)/norm(rhs) < 1e-6
@test norm(A*x5[1]-rhs)/norm(rhs) < 1e-6
@test norm(x2[1]-x1[1])/norm(x1[1]) < 1e-12
@test norm(x3[1]-x1[1])/norm(x1[1]) < 1e-5
@test norm(x0[1]-x1[1])/norm(x1[1]) < 1e-5

println("Testing bicgstb for complex matrix")
A  = sprandn(100,100,.1) + 10*speye(100) + im*(sprandn(100,100,.1) + 10*speye(100) )
D  = diag(A)
Af(x) = A*x
M(x)  = D.\x
rhs = randn(100) + 1im * randn(100)

x1 = bicgstb(A,rhs,tol=1e-6)
x2 = bicgstb(Af,rhs,tol=1e-6)
x3 = bicgstb(Af,rhs,tol=1e-6,maxIter=200,x=randn(size(rhs))+im*randn(size(rhs)))
x4 = bicgstb(Af,rhs,tol=1e-6,maxIter=200,M1=M)

@test norm(A*x1[1]-rhs)/norm(rhs) < 1e-6
@test norm(A*x2[1]-rhs)/norm(rhs) < 1e-6
@test norm(A*x3[1]-rhs)/norm(rhs) < 1e-6
@test norm(A*x4[1]-rhs)/norm(rhs) < 1e-6
@test norm(x2[1]-x1[1])/norm(x1[1]) < 1e-12
@test norm(x3[1]-x1[1])/norm(x1[1]) < 1e-5

println("=== BICGSTB: All tests passed ===")


text:
using StatsModels: StatsModels, AbstractTerm, FunctionTerm, term, terms, apply_schema, modelcols
using ShiftedArrays: lag
using DataFrames: DataFrame, groupby, transform
using Underscores: @_

struct GroupedLagTerm{T,G} <: AbstractTerm
    term::T
    nsteps::Int
    groups::G
end

groupedlag(t, n, g) = GroupedLagTerm(t, n, g)

GroupedLagTerm(term, nsteps) = GroupedLagTerm(term, nsteps, term(1))

StatsModels.terms(t::GroupedLagTerm) = (t.term, t.groups)

function StatsModels.apply_schema(t::FunctionTerm{typeof(groupedlag)},
                                  sch::StatsModels.Schema,
                                  Mod::Type)
    term_parsed, nsteps_parsed, groups_parsed = t.args_parsed
    nsteps = nsteps_parsed.n

    term = apply_schema(term_parsed, sch, Mod)
    groups = apply_schema(groups_parsed, sch, Mod)
    return GroupedLagTerm(term, nsteps, groups)
end

# apply_schema to internal Terms and check for proper types
function StatsModels.apply_schema(t::GroupedLagTerm,
                                  sch::StatsModels.Schema,
                                  Mod::Type)
    term = apply_schema(t.term, sch, Mod)
    groups = apply_schema(t.groups, sch, Mod)
    GroupedLagTerm(term, t.nsteps, groups)
end

function StatsModels.modelcols(ft::GroupedLagTerm, d::NamedTuple)
    column = modelcols(ft.term, d)
    group_var = StatsModels.termvars(ft.groups)[1]
    groups = d[group_var]
    df = DataFrame(column=column, groups=groups)
    @_ groupby(df, :groups) |>
        transform(__, :column => (x -> lag(x, ft.nsteps)) => :column_lagged) |>
        __.column_lagged
end

StatsModels.width(ft::GroupedLagTerm) = 1
StatsModels.coefnames(ft::GroupedLagTerm) = "lag(" .* StatsModels.coefnames(ft.term) .* ", $(ft.nsteps))"
# # names variables from the data that a LogTerm relies on
StatsModels.termvars(p::GroupedLagTerm) = StatsModels.termvars(p.term)


text:
module OceanographyCruises

using StaticArrays, Dates
using FieldMetadata, FieldDefaults
using PrettyTables
using Unitful
using Unitful: °
using Distances, TravelingSalesmanHeuristics
using RecipesBase, UnitfulRecipes

"""
    Station

Type containing `(lat,lon)` information.

(And optionally a `name` and a `date`).
"""
@default_kw struct Station <: FieldVector{2, Float64}
    lat::Float64   | NaN
    lon::Float64   | NaN
    name::String   | ""
    date::DateTime | Date(0)
end
pretty_data(st::Station) = [st.name (st.date == Date(0) ? "" : st.date) st.lat st.lon]
Base.show(io::IO, m::MIME"text/plain", st::Station) = println(io, string(st))
Base.show(io::IO, st::Station) = println(io, string(st))
function Base.string(st::Station)
    name = st.name == "" ? "Unnamed station " : "Station $(st.name) "
    date = st.date == Date(0) ? "" : "$(st.date) "
    return string(name, date, "($(latstring(st.lat)), $(lonstring(st.lon)))")
end
latstring(lat) = string(round(abs(lat)*10)/10, lat < 0 ? "S" : "N")
lonstring(lon) = string(round(abs(lon)*10)/10, lon < 0 ? "W" : "E")
# shift longitude for cruises that cross 0 to (-180,180)
shiftlon(lon; baselon=0) = mod(lon - baselon, 360) + baselon
shiftlon(st::Station; baselon=0) = Station(st.lat, shiftlon(st.lon, baselon=baselon), st.name, st.date)

"""
    CruiseTrack

Compact type containing cruise track information:
- cruise name
- stations
"""
@default_kw struct CruiseTrack
    name::String              | ""
    stations::Vector{Station} | Station[]
end
Base.length(ct::CruiseTrack) = length(ct.stations)
Base.isempty(ct::CruiseTrack) = isempty(ct.stations)
pretty_data(ct::CruiseTrack) = reduce(vcat, [pretty_data(st) for st in ct.stations])
function Base.show(io::IO, ::MIME"text/plain", ct::CruiseTrack)
    if isempty(ct)
        println("Empty cruise $(ct.name)")
    else
        println("Cruise $(ct.name)")
        pretty_table(pretty_data(ct), ["Station", "Date", "Lat", "Lon"])
    end
end
latitudes(ct::CruiseTrack) = [st.lat for st in ct.stations]
longitudes(ct::CruiseTrack) = [st.lon for st in ct.stations]
# shift longitude for cruises that cross 0 to (-180,180)
shiftlon(ct::CruiseTrack; baselon=0) = CruiseTrack(ct.name, shiftlon.(ct.stations, baselon=baselon))
function autoshift(ct::CruiseTrack)
    if any([0 ≤ st.lon < 90 for st in ct.stations]) && any([270 ≤ st.lon < 360 for st in ct.stations])
        shiftlon(ct, baselon=-180)
    else
        ct
    end
end

"""
    DepthProfile

A depth profile at a given station.
"""
@default_kw struct DepthProfile{V}
    station::Station        | Station()
    depths::Vector{Float64} | Float64[]
    values::Vector{V}       | Float64[]
    DepthProfile(st,d,v::Vector{V}) where {V} = (length(d) ≠ length(v)) ? error("`depths` and `values` must have same length") : new{V}(st,d,v)
end
Base.length(p::DepthProfile) = length(p.depths)
Base.isempty(p::DepthProfile) = isempty(p.depths)
pretty_data(p::DepthProfile) = [p.depths p.values]
pretty_data(p::DepthProfile{V}) where {V <: Quantity} = [p.depths ustrip.(p.values)]
function Base.show(io::IO, m::MIME"text/plain", p::DepthProfile)
    if isempty(p)
        println("Empty profile at ", string(p.station))
    else
        println("Depth profile at ", string(p.station))
        pretty_table(pretty_data(p), ["Depth", "Value"])
    end
end
function Base.show(io::IO, m::MIME"text/plain", p::DepthProfile{V}) where {V <: Quantity}
    if isempty(p)
        println("Empty profile at ", string(p.station))
    else
        println("Depth profile at ", string(p.station))
        pretty_table(pretty_data(p), ["Depth", "Value [$(unit(V))]"])
    end
end
Base.:*(pro::DepthProfile, q::Quantity) = DepthProfile(pro.station, pro.depths, pro.values .* q)
Base.:-(pro1::DepthProfile, pro2::DepthProfile) = DepthProfile(pro1.station, pro1.depths, pro1.values .- pro2.values)
Unitful.uconvert(u, pro::DepthProfile) = DepthProfile(pro.station, pro.depths, uconvert.(u, pro.values))
# shift longitude for cruises that cross 0 to (-180,180)
shiftlon(pro::DepthProfile; baselon=0) = DepthProfile(shiftlon(pro.station, baselon=baselon), pro.depths, pro.values)

"""
    Transect

A transect of depth profiles for a given tracer.
"""
@default_kw struct Transect{V}
    tracer::String                    | ""
    cruise::String                    | ""
    profiles::Vector{DepthProfile{V}} | DepthProfile{Float64}[]
end
Base.length(t::Transect) = length(t.profiles)
function Base.show(io::IO, m::MIME"text/plain", t::Transect)
    if isempty(t)
        println("Empty transect")
    else
        println("Transect of $(t.tracer)")
        show(io, m, CruiseTrack(t))
    end
end
CruiseTrack(t::Transect) = CruiseTrack(stations=[p.station for p in t.profiles], name=t.cruise)
Base.isempty(t::Transect) = isempty(t.profiles)
Base.:*(t::Transect, q::Quantity) = Transect(t.tracer, t.cruise, t.profiles .* q)
Base.:-(t1::Transect, t2::Transect) = Transect("$(t1.tracer)˟", t1.cruise, t1.profiles .- t2.profiles)
Unitful.uconvert(u, t::Transect) = Transect(t.tracer, t.cruise, uconvert.(u, t.profiles))
# shift longitude for cruises that cross 0 to (-180,180)
shiftlon(t::Transect; baselon=0) = Transect(t.tracer, t.cruise, shiftlon.(t.profiles, baselon=baselon))
function autoshift(t::Transect)
    if any([0 ≤ pro.station.lon < 90 for pro in t.profiles]) && any([270 ≤ pro.station.lon < 360 for pro in t.profiles])
        shiftlon(t, baselon=-180)
    else
        t
    end
end
latitudes(t::Transect) = [pro.station.lat for pro in t.profiles]
longitudes(t::Transect) = [pro.station.lon for pro in t.profiles]

"""
    Transects

A collection of transects for a given tracer.
"""
@default_kw struct Transects{V}
    tracer::String                 | ""
    cruises::Vector{String}        | String[]
    transects::Vector{Transect{V}} | Transect{Float64}[]
end
function Base.show(io::IO, m::MIME"text/plain", ts::Transects)
    println("Transects of $(ts.tracer)")
    print("(Cruises ")
    [print("$c, ") for c in ts.cruises[1:end-1]]
    println("and $(last(ts.cruises)).)")
end
Base.:*(ts::Transects, q::Quantity) = Transects(ts.tracer, ts.cruises, ts.transects .* q)
Base.:*(q::Quantity, ts::Transects) = ts * q
Base.:-(ts1::Transects, ts2::Transects) = Transects("$(ts1.tracer)˟", ts1.cruises, ts1.transects .- ts2.transects)
Unitful.uconvert(u, ts::Transects) = Transects(ts.tracer, ts.cruises, uconvert.(u, ts.transects ))
# shift longitude for cruises that cross 0 to (-180,180)
shiftlon(ts::Transects; baselon=0) = Transects(ts.tracer, ts.cruises, shiftlon.(t.transects, baselon=baselon))
autoshift(ts::Transects) = Transects(ts.tracer, ts.cruises, autoshift.(ts.transects))

import Base: sort, sortperm, getindex
getindex(ts::Transects, i::Int) = ts.transects[i]
"""
    sort(t::Transect)

Sorts the transect using a travelling salesman problem heuristic.
"""
sort(t::Transect; start=cruise_default_start(t)) = Transect(t.tracer, t.cruise, [t for t in t.profiles[sortperm(t; start=start)]])
sort(ct::CruiseTrack; start=cruise_default_start(ct)) = CruiseTrack(name=ct.name, stations=ct.stations[sortperm(ct; start=start)])
function sortperm(t::Union{CruiseTrack, Transect}; start=cruise_default_start(t))
    t = autoshift(t)
    n = length(t)
    lats = latitudes(t)
    lons = longitudes(t)
    pts = [lons lats]
    dist_mat = zeros(n+1, n+1)
    dist_mat[1:n,1:n] .= pairwise(Haversine(1), pts, dims=1)
    path, cost = solve_tsp(dist_mat)
    i = findall(path .== n+1)
    if length(i) == 1
        path = vcat(path[i[1]+1:end-1], path[1:i[1]-1])
    else
        path = path[2:end-1]
    end
    start == :south && pts[path[1],2] > pts[path[end],2] && reverse!(path)
    start == :west  && pts[path[1],1] > pts[path[end],1] && reverse!(path)
    return path
end
export sort, sortperm, getindex

function cruise_default_start(t)
    t = autoshift(t)
    extremalat = extrema(latitudes(t))
    extremalon = extrema(longitudes(t))
    Δlat = extremalat[2] - extremalat[1]
    Δlon = extremalon[2] - extremalon[1]
    Δlon > Δlat ? :west : :south
end

"""
    diff(t)

Computes the distance in km of each segment of the transect
"""
function Base.diff(t::Union{CruiseTrack,Transect})
    t = autoshift(t) # not sure this is needed since Haversine takes lat and lon?
    n = length(t)
    lats = latitudes(t)
    lons = longitudes(t)
    pts = [lons lats]
    return [Haversine(6371.0)(pts[i,:], pts[i+1,:]) for i in 1:n-1] * u"km"
end
export diff

function Base.range(departure::Station, arrival::Station; length::Int64, westmostlon=-180)
    lonstart = lonconvert(departure.lon, westmostlon)
    lonend = lonconvert(arrival.lon, westmostlon)
    lonstart - lonend >  180 && (lonend += 360)
    lonstart - lonend < -180 && (lonstart += 360)
    lats = range(departure.lat, arrival.lat, length=length)
    lons = range(lonstart, lonend, length=length)
    names = string.("$(departure.name) to $(arrival.name) ", 1:length)
    return [Station(lat=x[1], lon=lonconvert(x[2], westmostlon), name=x[3]) for x in zip(lats, lons, names)]
end

lonconvert(lon, westmostlon=-180) = mod(lon - westmostlon, 360) + westmostlon

export CruiseTrack, Station, DepthProfile, Transect, Transects
export latitudes, longitudes

Unitful.unit(t::Transect) = unit(t.profiles[1].values[1])

# nested functions
for f in (:maximum, :minimum)
    @eval begin
        import Base: $f
        """
            $($f)(t)

        Applies `$($f)` recursively.
        """
        $f(ts::Transects) = $f($f(t) for t in ts.transects)
        $f(t::Transect) = $f($f(pro) for pro in t.profiles)
        $f(pro::DepthProfile) = $f($f(v) for v in pro.values)
        export $f
    end
end

# recipe TODO split this file into a few different ones that make sens and include them all here
include("recipes.jl")

end # module


text:
# Autogenerated wrapper script for AFLplusplus_jll for aarch64-linux-gnu-cxx11
export afl_clang_fast, afl_clang_fast_cxx

using Clang_jll
using Glib_jll
using Pixman_jll
JLLWrappers.@generate_wrapper_header("AFLplusplus")
JLLWrappers.@declare_executable_product(afl_clang_fast)
JLLWrappers.@declare_executable_product(afl_clang_fast_cxx)
function __init__()
    JLLWrappers.@generate_init_header(Clang_jll, Glib_jll, Pixman_jll)
    JLLWrappers.@init_executable_product(
        afl_clang_fast,
        "bin/afl-clang-fast",
    )

    JLLWrappers.@init_executable_product(
        afl_clang_fast_cxx,
        "bin/afl-clang-fast++",
    )

    JLLWrappers.@generate_init_footer()
end  # __init__()


text:
using Base.Test
using QuantumOptics

@testset "phasespace" begin

srand(0)

D(op1::Operator, op2::Operator) = abs(tracedistance_nh(full(op1), full(op2)))

# Test quasi-probability functions
b = FockBasis(100)
alpha = complex(0.3, 0.7)
nfock = 3

X = [-2.1:1.5:2;]
Y = [0.13:1.4:3;]
psi_coherent = coherentstate(b, alpha)
rho_coherent = dm(psi_coherent)
psi_fock = fockstate(b, nfock)
rho_fock = dm(psi_fock)

Qpsi_coherent = qfunc(psi_coherent, X, Y)
Qrho_coherent = qfunc(rho_coherent, X, Y)
Qpsi_fock = qfunc(psi_fock, X, Y)
Qrho_fock = qfunc(rho_fock, X, Y)

Wpsi_coherent = wigner(psi_coherent, X, Y)
Wrho_coherent = wigner(rho_coherent, X, Y)
Wpsi_fock = wigner(psi_fock, X, Y)
Wrho_fock = wigner(rho_fock, X, Y)

laguerre3(x) = (-x^3+9x^2-18x+6)/6

for (i,x)=enumerate(X), (j,y)=enumerate(Y)
    beta = 1./sqrt(2)*complex(x, y)
    betastate = coherentstate(b, beta)

    q_coherent = 1/pi*exp(-abs2(alpha-beta))
    @test Qpsi_coherent[i, j] ≈ q_coherent
    @test Qrho_coherent[i, j] ≈ q_coherent
    @test qfunc(psi_coherent, beta) ≈ q_coherent
    @test qfunc(rho_coherent, beta) ≈ q_coherent
    @test qfunc(psi_coherent, x, y) ≈ q_coherent
    @test abs2(dagger(betastate) * psi_coherent)/pi ≈ q_coherent
    @test dagger(betastate) * rho_coherent * betastate/pi ≈ q_coherent

    q_fock = 1/pi*exp(-abs2(beta))*abs2(beta)^nfock/factorial(nfock)
    @test Qpsi_fock[i, j] ≈ q_fock
    @test Qrho_fock[i, j] ≈ q_fock
    @test qfunc(psi_fock, beta) ≈ q_fock
    @test qfunc(rho_fock, beta) ≈ q_fock
    @test abs2(dagger(betastate) * psi_fock)/pi ≈ q_fock
    @test dagger(betastate) * rho_fock * betastate/pi ≈ q_fock

    w_coherent = 1/pi*exp(-2*abs2(alpha-beta))
    @test Wpsi_coherent[i, j] ≈ w_coherent
    @test Wrho_coherent[i, j] ≈ w_coherent
    @test wigner(psi_coherent, beta) ≈ w_coherent
    @test wigner(rho_coherent, beta) ≈ w_coherent

    w_fock = 1/pi*(-1)^nfock*laguerre3(4*abs2(beta))*exp(-2*abs2(beta))
    @test Wpsi_fock[i, j] ≈ w_fock
    @test Wrho_fock[i, j] ≈ w_fock
    @test wigner(psi_fock, beta) ≈ w_fock
    @test wigner(rho_fock, beta) ≈ w_fock
end

# Test qfunc with rand operators
b = FockBasis(50)
psi = randstate(b)
rho = randoperator(b)
X = [-2.1:1.5:2;]
Y = [-0.5:.8:3;]

Qpsi = qfunc(psi, X, Y)
Qrho = qfunc(rho, X, Y)
for (i,x)=enumerate(X), (j,y)=enumerate(Y)
    c = complex(x, y)/sqrt(2)
    state = coherentstate(b, c)
    q_rho = dagger(state) * rho * state/pi
    q_psi = abs2(dagger(state) *psi)/pi
    @test 1e-14 > abs(Qpsi[i,j] - q_psi)
    @test 1e-14 > abs(Qrho[i,j] - q_rho)
    @test 1e-14 > abs(qfunc(psi, c) - q_psi)
    @test 1e-14 > abs(qfunc(rho, c) - q_rho)
end

end # testset


text:
"""
    elements, points = convexhull(points)

Compute the convex hull of a set of points `points` (of size `dim * npoint`). Return a
matrix of boundary elements `elements` (of size`dim * nelement`) and a restriction of the
original points to the boundary (size `dim * npoint_keep`).
"""
function convexhull(points)

    # Sizes
    dim, npoint = size(points)

    # Compute Delaunay triangulation
    D = Int.(delaunay(points))

    # Sort each column of d
    for i = 1:size(D, 2)
        sort!(@view D[:, i])
    end

    # Identify boundary elements. In 3D, those are facets not shared by two elements, in 2D
    # those are edges not shared by two facets
    if dim == 2
        combs = [[1, 2], [1, 3], [2, 3]]
    else
        combs = [[1, 2, 3], [1, 2, 4], [1, 3, 4], [2, 3, 4]]
    end
    A = [SVector{dim}(D[comb, i]) for i = 1:size(D, 2) for comb ∈ combs]
    sort!(A)

    # Indices of unique elements
    ikeep = fill(false, length(A))

    # Perform lookup for the first element
    lookup = true

    # Loop through facets or edges. Each one occurs exactly once or twice, and they are
    # ordered
    for i ∈ 1:length(A)-1
        if lookup
            # A[i] is different than A[i-1]
            if A[i] == A[i+1]
                # A[i] and A[i+1] is a double element, and should not be kept. Skip next
                # iteration
                lookup = false
            else
                # A[i] is unique, and is thus a boundary element
                ikeep[i] = true
            end
        else
            # A[i] is a copy of A[i-1], but A[i+1] is a new element. Perform next iteration
            lookup = true
        end
    end

    # Keep the last element if it is different from the one before
    ikeep[end] = lookup

    # Keep unique elements
    A = A[ikeep]

    # Order edges to form a connected wrapping in 2D case
    if dim == 2
        for i = 1:length(A)-1
            # Find next edge from endpoint of previous edge
            j = i + findfirst(e -> A[i][2] ∈ e, @view A[i+1:end])

            # Reorder
            tmp = A[i+1]
            A[i+1] = A[j]
            A[j] = tmp

            # Orient next edge correctly
            if A[i+1][2] == A[i][2]
                A[i+1] = SVector{2}(A[i+1][[2, 1]])
            end
        end
    end

    E = mapreduce(Vector, hcat, A)

    # Important: `unique` preserves order of edges in 2D case
    ikeep = unique(E)

    # Inverse point map
    ikeep_inv = fill(0, npoint)
    for i = 1:length(ikeep)
        ikeep_inv[ikeep[i]] = i
    end

    # Renumber points from 1 to nkeep
    boundary_elements = ikeep_inv[E]
    boundary_points = points[:, ikeep]

    boundary_elements, boundary_points
end

# a = rand(2, 100)
# e, p = convexhull(a)

# pl = scatter(a[1, :], a[2, :])

# scatter!(pl, p[1, :], p[2, :], color = :red)

# for i = 1:size(e, 2)
#     plot!(pl, p[1, e[:, i]], p[2, e[:, i]])
#     display(pl)
#     sleep(0.5)
# end


text:
# Mapping from token string identifiers to enumeration values as used in @K_str
#
# TODO: Unify Tokenize with this approach so we don't need to write these out
# in two places.

const _str_to_kind = let Ts = TzTokens
Dict([
"None"           =>  Ts.NONE
"EndMarker"      =>  Ts.ENDMARKER
"Comment"        =>  Ts.COMMENT
"Whitespace"     =>  Ts.WHITESPACE
"Identifier"     =>  Ts.IDENTIFIER
"@"              =>  Ts.AT_SIGN
","              =>  Ts.COMMA
";"              =>  Ts.SEMICOLON

"BEGIN_ERRORS"   =>  Ts.begin_errors
# Tokenization errors
"ErrorEofMultiComment"        => Ts.EOF_MULTICOMMENT
"ErrorEofChar"                => Ts.EOF_CHAR
"ErrorInvalidNumericConstant" => Ts.INVALID_NUMERIC_CONSTANT
"ErrorInvalidOperator"        => Ts.INVALID_OPERATOR
"ErrorInvalidInterpolationTerminator" => Ts.INVALID_INTERPOLATION_TERMINATOR
# Generic error
"error"          =>  Ts.ERROR
"END_ERRORS"     =>  Ts.end_errors

"BEGIN_KEYWORDS" => Ts.begin_keywords
"baremodule"  =>  Ts.BAREMODULE
"begin"       =>  Ts.BEGIN
"break"       =>  Ts.BREAK
"catch"       =>  Ts.CATCH
"const"       =>  Ts.CONST
"continue"    =>  Ts.CONTINUE
"do"          =>  Ts.DO
"else"        =>  Ts.ELSE
"elseif"      =>  Ts.ELSEIF
"end"         =>  Ts.END
"export"      =>  Ts.EXPORT
"finally"     =>  Ts.FINALLY
"for"         =>  Ts.FOR
"function"    =>  Ts.FUNCTION
"global"      =>  Ts.GLOBAL
"if"          =>  Ts.IF
"import"      =>  Ts.IMPORT
"let"         =>  Ts.LET
"local"       =>  Ts.LOCAL
"macro"       =>  Ts.MACRO
"module"      =>  Ts.MODULE
"quote"       =>  Ts.QUOTE
"return"      =>  Ts.RETURN
"struct"      =>  Ts.STRUCT
"try"         =>  Ts.TRY
"using"       =>  Ts.USING
"while"       =>  Ts.WHILE
# contextual keywords
"abstract"    =>  Ts.ABSTRACT
"as"          =>  Ts.AS
"doc"         =>  Ts.DOC
"mutable"     =>  Ts.MUTABLE
"outer"       =>  Ts.OUTER
"primitive"   =>  Ts.PRIMITIVE
"type"        =>  Ts.TYPE
"var"         =>  Ts.VAR
"END_KEYWORDS" => Ts.end_keywords

"BEGIN_CSTPARSER"    =>  Ts.begin_cstparser
"nothing"            =>  Ts.NOTHING
"NewlineWs"          =>  Ts.NEWLINE_WS
"END_CSTPARSER"      =>  Ts.end_cstparser

"BEGIN_LITERAL"  =>  Ts.begin_literal
"Integer"        =>  Ts.INTEGER
"BinInt"         =>  Ts.BIN_INT
"HexInt"         =>  Ts.HEX_INT
"OctInt"         =>  Ts.OCT_INT
"Float"          =>  Ts.FLOAT
"String"         =>  Ts.STRING
"Char"           =>  Ts.CHAR
"CmdString"      =>  Ts.CMD
"true"           =>  Ts.TRUE
"false"          =>  Ts.FALSE
"END_LITERAL"    =>  Ts.end_literal

"BEGIN_DELIMITERS"  => Ts.begin_delimiters
"["      =>  Ts.LSQUARE
"]"      =>  Ts.RSQUARE
"{"      =>  Ts.LBRACE
"}"      =>  Ts.RBRACE
"("      =>  Ts.LPAREN
")"      =>  Ts.RPAREN
"\""     =>  Ts.DQUOTE
"\"\"\"" =>  Ts.TRIPLE_DQUOTE
"`"      =>  Ts.BACKTICK
"```"    =>  Ts.TRIPLE_BACKTICK
"END_DELIMITERS"    => Ts.end_delimiters

"BEGIN_OPS"  => Ts.begin_ops
"..." => Ts.DDDOT

# Level 1
"BEGIN_ASSIGNMENTS"  => Ts.begin_assignments
"="     =>  Ts.EQ
"+="    =>  Ts.PLUS_EQ
"-="    =>  Ts.MINUS_EQ
"*="    =>  Ts.STAR_EQ
"/="    =>  Ts.FWD_SLASH_EQ
"//="   =>  Ts.FWDFWD_SLASH_EQ
"|="    =>  Ts.OR_EQ
"^="    =>  Ts.CIRCUMFLEX_EQ
"÷="    =>  Ts.DIVISION_EQ
"%="    =>  Ts.REM_EQ
"<<="   =>  Ts.LBITSHIFT_EQ
">>="   =>  Ts.RBITSHIFT_EQ
">>>="  =>  Ts.UNSIGNED_BITSHIFT_EQ
"\\="   =>  Ts.BACKSLASH_EQ
"&="    =>  Ts.AND_EQ
":="    =>  Ts.COLON_EQ
"~"     =>  Ts.APPROX
"\$="    =>  Ts.EX_OR_EQ
"⊻="    =>  Ts.XOR_EQ
"END_ASSIGNMENTS"    => Ts.end_assignments

"BEGIN_PAIRARROW"  => Ts.begin_pairarrow
"=>"    =>  Ts.PAIR_ARROW
"END_PAIRARROW"    => Ts.end_pairarrow

# Level 2
"BEGIN_CONDITIONAL"  => Ts.begin_conditional
"?"  =>  Ts.CONDITIONAL
"END_CONDITIONAL"    => Ts.end_conditional

# Level 3
"BEGIN_ARROW"  => Ts.begin_arrow
"-->"   =>  Ts.RIGHT_ARROW
"<--"   =>  Ts.LEFT_ARROW
"<-->"  =>  Ts.DOUBLE_ARROW
"←"     =>  Ts.LEFTWARDS_ARROW
"→"     =>  Ts.RIGHTWARDS_ARROW
"↔"     =>  Ts.LEFT_RIGHT_ARROW
"↚"     =>  Ts.LEFTWARDS_ARROW_WITH_STROKE
"↛"     =>  Ts.RIGHTWARDS_ARROW_WITH_STROKE
"↞"     =>  Ts.LEFTWARDS_TWO_HEADED_ARROW
"↠"     =>  Ts.RIGHTWARDS_TWO_HEADED_ARROW
"↢"     =>  Ts.LEFTWARDS_ARROW_WITH_TAIL
"↣"     =>  Ts.RIGHTWARDS_ARROW_WITH_TAIL
"↤"     =>  Ts.LEFTWARDS_ARROW_FROM_BAR
"↦"     =>  Ts.RIGHTWARDS_ARROW_FROM_BAR
"↮"     =>  Ts.LEFT_RIGHT_ARROW_WITH_STROKE
"⇎"     =>  Ts.LEFT_RIGHT_DOUBLE_ARROW_WITH_STROKE
"⇍"     =>  Ts.LEFTWARDS_DOUBLE_ARROW_WITH_STROKE
"⇏"     =>  Ts.RIGHTWARDS_DOUBLE_ARROW_WITH_STROKE
"⇐"     =>  Ts.LEFTWARDS_DOUBLE_ARROW
"⇒"     =>  Ts.RIGHTWARDS_DOUBLE_ARROW
"⇔"     =>  Ts.LEFT_RIGHT_DOUBLE_ARROW
"⇴"     =>  Ts.RIGHT_ARROW_WITH_SMALL_CIRCLE
"⇶"     =>  Ts.THREE_RIGHTWARDS_ARROWS
"⇷"     =>  Ts.LEFTWARDS_ARROW_WITH_VERTICAL_STROKE
"⇸"     =>  Ts.RIGHTWARDS_ARROW_WITH_VERTICAL_STROKE
"⇹"     =>  Ts.LEFT_RIGHT_ARROW_WITH_VERTICAL_STROKE
"⇺"     =>  Ts.LEFTWARDS_ARROW_WITH_DOUBLE_VERTICAL_STROKE
"⇻"     =>  Ts.RIGHTWARDS_ARROW_WITH_DOUBLE_VERTICAL_STROKE
"⇼"     =>  Ts.LEFT_RIGHT_ARROW_WITH_DOUBLE_VERTICAL_STROKE
"⇽"     =>  Ts.LEFTWARDS_OPEN_HEADED_ARROW
"⇾"     =>  Ts.RIGHTWARDS_OPEN_HEADED_ARROW
"⇿"     =>  Ts.LEFT_RIGHT_OPEN_HEADED_ARROW
"⟵"     =>  Ts.LONG_LEFTWARDS_ARROW
"⟶"     =>  Ts.LONG_RIGHTWARDS_ARROW
"⟷"     =>  Ts.LONG_LEFT_RIGHT_ARROW
"⟹"     =>  Ts.LONG_RIGHTWARDS_DOUBLE_ARROW
"⟺"     =>  Ts.LONG_LEFT_RIGHT_DOUBLE_ARROW
"⟻"     =>  Ts.LONG_LEFTWARDS_ARROW_FROM_BAR
"⟼"     =>  Ts.LONG_RIGHTWARDS_ARROW_FROM_BAR
"⟽"     =>  Ts.LONG_LEFTWARDS_DOUBLE_ARROW_FROM_BAR
"⟾"     =>  Ts.LONG_RIGHTWARDS_DOUBLE_ARROW_FROM_BAR
"⟿"     =>  Ts.LONG_RIGHTWARDS_SQUIGGLE_ARROW
"⤀"     =>  Ts.RIGHTWARDS_TWO_HEADED_ARROW_WITH_VERTICAL_STROKE
"⤁"     =>  Ts.RIGHTWARDS_TWO_HEADED_ARROW_WITH_DOUBLE_VERTICAL_STROKE
"⤂"     =>  Ts.LEFTWARDS_DOUBLE_ARROW_WITH_VERTICAL_STROKE
"⤃"     =>  Ts.RIGHTWARDS_DOUBLE_ARROW_WITH_VERTICAL_STROKE
"⤄"     =>  Ts.LEFT_RIGHT_DOUBLE_ARROW_WITH_VERTICAL_STROKE
"⤅"     =>  Ts.RIGHTWARDS_TWO_HEADED_ARROW_FROM_BAR
"⤆"     =>  Ts.LEFTWARDS_DOUBLE_ARROW_FROM_BAR
"⤇"     =>  Ts.RIGHTWARDS_DOUBLE_ARROW_FROM_BAR
"⤌"     =>  Ts.LEFTWARDS_DOUBLE_DASH_ARROW
"⤍"     =>  Ts.RIGHTWARDS_DOUBLE_DASH_ARROW
"⤎"     =>  Ts.LEFTWARDS_TRIPLE_DASH_ARROW
"⤏"     =>  Ts.RIGHTWARDS_TRIPLE_DASH_ARROW
"⤐"     =>  Ts.RIGHTWARDS_TWO_HEADED_TRIPLE_DASH_ARROW
"⤑"     =>  Ts.RIGHTWARDS_ARROW_WITH_DOTTED_STEM
"⤔"     =>  Ts.RIGHTWARDS_ARROW_WITH_TAIL_WITH_VERTICAL_STROKE
"⤕"     =>  Ts.RIGHTWARDS_ARROW_WITH_TAIL_WITH_DOUBLE_VERTICAL_STROKE
"⤖"     =>  Ts.RIGHTWARDS_TWO_HEADED_ARROW_WITH_TAIL
"⤗"     =>  Ts.RIGHTWARDS_TWO_HEADED_ARROW_WITH_TAIL_WITH_VERTICAL_STROKE
"⤘"     =>  Ts.RIGHTWARDS_TWO_HEADED_ARROW_WITH_TAIL_WITH_DOUBLE_VERTICAL_STROKE
"⤝"     =>  Ts.LEFTWARDS_ARROW_TO_BLACK_DIAMOND
"⤞"     =>  Ts.RIGHTWARDS_ARROW_TO_BLACK_DIAMOND
"⤟"     =>  Ts.LEFTWARDS_ARROW_FROM_BAR_TO_BLACK_DIAMOND
"⤠"     =>  Ts.RIGHTWARDS_ARROW_FROM_BAR_TO_BLACK_DIAMOND
"⥄"     =>  Ts.SHORT_RIGHTWARDS_ARROW_ABOVE_LEFTWARDS_ARROW
"⥅"     =>  Ts.RIGHTWARDS_ARROW_WITH_PLUS_BELOW
"⥆"     =>  Ts.LEFTWARDS_ARROW_WITH_PLUS_BELOW
"⥇"     =>  Ts.RIGHTWARDS_ARROW_THROUGH_X
"⥈"     =>  Ts.LEFT_RIGHT_ARROW_THROUGH_SMALL_CIRCLE
"⥊"     =>  Ts.LEFT_BARB_UP_RIGHT_BARB_DOWN_HARPOON
"⥋"     =>  Ts.LEFT_BARB_DOWN_RIGHT_BARB_UP_HARPOON
"⥎"     =>  Ts.LEFT_BARB_UP_RIGHT_BARB_UP_HARPOON
"⥐"     =>  Ts.LEFT_BARB_DOWN_RIGHT_BARB_DOWN_HARPOON
"⥒"     =>  Ts.LEFTWARDS_HARPOON_WITH_BARB_UP_TO_BAR
"⥓"     =>  Ts.RIGHTWARDS_HARPOON_WITH_BARB_UP_TO_BAR
"⥖"     =>  Ts.LEFTWARDS_HARPOON_WITH_BARB_DOWN_TO_BAR
"⥗"     =>  Ts.RIGHTWARDS_HARPOON_WITH_BARB_DOWN_TO_BAR
"⥚"     =>  Ts.LEFTWARDS_HARPOON_WITH_BARB_UP_FROM_BAR
"⥛"     =>  Ts.RIGHTWARDS_HARPOON_WITH_BARB_UP_FROM_BAR
"⥞"     =>  Ts.LEFTWARDS_HARPOON_WITH_BARB_DOWN_FROM_BAR
"⥟"     =>  Ts.RIGHTWARDS_HARPOON_WITH_BARB_DOWN_FROM_BAR
"⥢"     =>  Ts.LEFTWARDS_HARPOON_WITH_BARB_UP_ABOVE_LEFTWARDS_HARPOON_WITH_BARB_DOWN
"⥤"     =>  Ts.RIGHTWARDS_HARPOON_WITH_BARB_UP_ABOVE_RIGHTWARDS_HARPOON_WITH_BARB_DOWN
"⥦"     =>  Ts.LEFTWARDS_HARPOON_WITH_BARB_UP_ABOVE_RIGHTWARDS_HARPOON_WITH_BARB_UP
"⥧"     =>  Ts.LEFTWARDS_HARPOON_WITH_BARB_DOWN_ABOVE_RIGHTWARDS_HARPOON_WITH_BARB_DOWN
"⥨"     =>  Ts.RIGHTWARDS_HARPOON_WITH_BARB_UP_ABOVE_LEFTWARDS_HARPOON_WITH_BARB_UP
"⥩"     =>  Ts.RIGHTWARDS_HARPOON_WITH_BARB_DOWN_ABOVE_LEFTWARDS_HARPOON_WITH_BARB_DOWN
"⥪"     =>  Ts.LEFTWARDS_HARPOON_WITH_BARB_UP_ABOVE_LONG_DASH
"⥫"     =>  Ts.LEFTWARDS_HARPOON_WITH_BARB_DOWN_BELOW_LONG_DASH
"⥬"     =>  Ts.RIGHTWARDS_HARPOON_WITH_BARB_UP_ABOVE_LONG_DASH
"⥭"     =>  Ts.RIGHTWARDS_HARPOON_WITH_BARB_DOWN_BELOW_LONG_DASH
"⥰"     =>  Ts.RIGHT_DOUBLE_ARROW_WITH_ROUNDED_HEAD
"⧴"     =>  Ts.RULE_DELAYED
"⬱"     =>  Ts.THREE_LEFTWARDS_ARROWS
"⬰"     =>  Ts.LEFT_ARROW_WITH_SMALL_CIRCLE
"⬲"     =>  Ts.LEFT_ARROW_WITH_CIRCLED_PLUS
"⬳"     =>  Ts.LONG_LEFTWARDS_SQUIGGLE_ARROW
"⬴"     =>  Ts.LEFTWARDS_TWO_HEADED_ARROW_WITH_VERTICAL_STROKE
"⬵"     =>  Ts.LEFTWARDS_TWO_HEADED_ARROW_WITH_DOUBLE_VERTICAL_STROKE
"⬶"     =>  Ts.LEFTWARDS_TWO_HEADED_ARROW_FROM_BAR
"⬷"     =>  Ts.LEFTWARDS_TWO_HEADED_TRIPLE_DASH_ARROW
"⬸"     =>  Ts.LEFTWARDS_ARROW_WITH_DOTTED_STEM
"⬹"     =>  Ts.LEFTWARDS_ARROW_WITH_TAIL_WITH_VERTICAL_STROKE
"⬺"     =>  Ts.LEFTWARDS_ARROW_WITH_TAIL_WITH_DOUBLE_VERTICAL_STROKE
"⬻"     =>  Ts.LEFTWARDS_TWO_HEADED_ARROW_WITH_TAIL
"⬼"     =>  Ts.LEFTWARDS_TWO_HEADED_ARROW_WITH_TAIL_WITH_VERTICAL_STROKE
"⬽"     =>  Ts.LEFTWARDS_TWO_HEADED_ARROW_WITH_TAIL_WITH_DOUBLE_VERTICAL_STROKE
"⬾"     =>  Ts.LEFTWARDS_ARROW_THROUGH_X
"⬿"     =>  Ts.WAVE_ARROW_POINTING_DIRECTLY_LEFT
"⭀"     =>  Ts.EQUALS_SIGN_ABOVE_LEFTWARDS_ARROW
"⭁"     =>  Ts.REVERSE_TILDE_OPERATOR_ABOVE_LEFTWARDS_ARROW
"⭂"     =>  Ts.LEFTWARDS_ARROW_ABOVE_REVERSE_ALMOST_EQUAL_TO
"⭃"     =>  Ts.RIGHTWARDS_ARROW_THROUGH_GREATER_THAN
"⭄"     =>  Ts.RIGHTWARDS_ARROW_THROUGH_SUPERSET
"⭇"     =>  Ts.REVERSE_TILDE_OPERATOR_ABOVE_RIGHTWARDS_ARROW
"⭈"     =>  Ts.RIGHTWARDS_ARROW_ABOVE_REVERSE_ALMOST_EQUAL_TO
"⭉"     =>  Ts.TILDE_OPERATOR_ABOVE_LEFTWARDS_ARROW
"⭊"     =>  Ts.LEFTWARDS_ARROW_ABOVE_ALMOST_EQUAL_TO
"⭋"     =>  Ts.LEFTWARDS_ARROW_ABOVE_REVERSE_TILDE_OPERATOR
"⭌"     =>  Ts.RIGHTWARDS_ARROW_ABOVE_REVERSE_TILDE_OPERATOR
"￩"     =>  Ts.HALFWIDTH_LEFTWARDS_ARROW
"￫"     =>  Ts.HALFWIDTH_RIGHTWARDS_ARROW
"↻"     =>  Ts.CIRCLE_ARROW_RIGHT
"⇜"     =>  Ts.LEFT_SQUIGGLE_ARROW
"⇝"     =>  Ts.RIGHT_SQUIGGLE_ARROW
"↜"     =>  Ts.LEFT_WAVE_ARROW
"↝"     =>  Ts.RIGHT_WAVE_ARROW
"↩"     =>  Ts.LEFTWARDS_ARROW_WITH_HOOK
"↪"     =>  Ts.RIGHTWARDS_ARROW_WITH_HOOK
"↫"     =>  Ts.LOOP_ARROW_LEFT
"↬"     =>  Ts.LOOP_ARROW_RIGHT
"↼"     =>  Ts.LEFT_HARPOON_UP
"↽"     =>  Ts.LEFT_HARPOON_DOWN
"⇀"     =>  Ts.RIGHT_HARPOON_UP
"⇁"     =>  Ts.RIGHT_HARPOON_DOWN
"⇄"     =>  Ts.RIGHT_LEFT_ARROWS
"⇆"     =>  Ts.LEFT_RIGHT_ARROWS
"⇇"     =>  Ts.LEFT_LEFT_ARROWS
"⇉"     =>  Ts.RIGHT_RIGHT_ARROWS
"⇋"     =>  Ts.LEFT_RIGHT_HARPOONS
"⇌"     =>  Ts.RIGHT_LEFT_HARPOONS
"⇚"     =>  Ts.L_LEFT_ARROW
"⇛"     =>  Ts.R_RIGHT_ARROW
"⇠"     =>  Ts.LEFT_DASH_ARROW
"⇢"     =>  Ts.RIGHT_DASH_ARROW
"↷"     =>  Ts.CURVE_ARROW_RIGHT
"↶"     =>  Ts.CURVE_ARROW_LEFT
"↺"     =>  Ts.CIRCLE_ARROW_LEFT
"END_ARROW"    => Ts.end_arrow

# Level 4
"BEGIN_LAZYOR" => Ts.begin_lazyor
"||"  =>  Ts.LAZY_OR
"END_LAZYOR"   => Ts.end_lazyor

# Level 5
"BEGIN_LAZYAND" => Ts.begin_lazyand
"&&"  =>  Ts.LAZY_AND
"END_LAZYAND"   => Ts.end_lazyand

# Level 6
"BEGIN_COMPARISON" => Ts.begin_comparison
"<:"   =>  Ts.ISSUBTYPE
">:"   =>  Ts.ISSUPERTYPE
">"    =>  Ts.GREATER
"<"    =>  Ts.LESS
">="   =>  Ts.GREATER_EQ
"≥"    =>  Ts.GREATER_THAN_OR_EQUAL_TO
"<="   =>  Ts.LESS_EQ
"≤"    =>  Ts.LESS_THAN_OR_EQUAL_TO
"=="   =>  Ts.EQEQ
"==="  =>  Ts.EQEQEQ
"≡"    =>  Ts.IDENTICAL_TO
"!="   =>  Ts.NOT_EQ
"≠"    =>  Ts.NOT_EQUAL_TO
"!=="  =>  Ts.NOT_IS
"≢"    =>  Ts.NOT_IDENTICAL_TO
"∈"    =>  Ts.ELEMENT_OF
"in"   =>  Ts.IN
"isa"  =>  Ts.ISA
"∉"    =>  Ts.NOT_AN_ELEMENT_OF
"∋"    =>  Ts.CONTAINS_AS_MEMBER
"∌"    =>  Ts.DOES_NOT_CONTAIN_AS_MEMBER
"⊆"    =>  Ts.SUBSET_OF_OR_EQUAL_TO
"⊈"    =>  Ts.NEITHER_A_SUBSET_OF_NOR_EQUAL_TO
"⊂"    =>  Ts.SUBSET_OF
"⊄"    =>  Ts.NOT_A_SUBSET_OF
"⊊"    =>  Ts.SUBSET_OF_WITH_NOT_EQUAL_TO
"∝"    =>  Ts.PROPORTIONAL_TO
"∊"    =>  Ts.SMALL_ELEMENT_OF
"∍"    =>  Ts.SMALL_CONTAINS_AS_MEMBER
"∥"    =>  Ts.PARALLEL_TO
"∦"    =>  Ts.NOT_PARALLEL_TO
"∷"    =>  Ts.PROPORTION
"∺"    =>  Ts.GEOMETRIC_PROPORTION
"∻"    =>  Ts.HOMOTHETIC
"∽"    =>  Ts.REVERSED_TILDE
"∾"    =>  Ts.INVERTED_LAZY_S
"≁"    =>  Ts.NOT_TILDE
"≃"    =>  Ts.ASYMPTOTICALLY_EQUAL_TO
"≄"    =>  Ts.NOT_ASYMPTOTICALLY_EQUAL_TO
"≅"    =>  Ts.APPROXIMATELY_EQUAL_TO
"≆"    =>  Ts.APPROXIMATELY_BUT_NOT_ACTUALLY_EQUAL_TO
"≇"    =>  Ts.NEITHER_APPROXIMATELY_NOR_ACTUALLY_EQUAL_TO
"≈"    =>  Ts.ALMOST_EQUAL_TO
"≉"    =>  Ts.NOT_ALMOST_EQUAL_TO
"≊"    =>  Ts.ALMOST_EQUAL_OR_EQUAL_TO
"≋"    =>  Ts.TRIPLE_TILDE
"≌"    =>  Ts.ALL_EQUAL_TO
"≍"    =>  Ts.EQUIVALENT_TO
"≎"    =>  Ts.GEOMETRICALLY_EQUIVALENT_TO
"≐"    =>  Ts.APPROACHES_THE_LIMIT
"≑"    =>  Ts.GEOMETRICALLY_EQUAL_TO
"≒"    =>  Ts.APPROXIMATELY_EQUAL_TO_OR_THE_IMAGE_OF
"≓"    =>  Ts.IMAGE_OF_OR_APPROXIMATELY_EQUAL_TO
"≔"    =>  Ts.COLON_EQUALS
"≕"    =>  Ts.EQUALS_COLON
"≖"    =>  Ts.RING_IN_EQUAL_TO
"≗"    =>  Ts.RING_EQUAL_TO
"≘"    =>  Ts.CORRESPONDS_TO
"≙"    =>  Ts.ESTIMATES
"≚"    =>  Ts.EQUIANGULAR_TO
"≛"    =>  Ts.STAR_EQUALS
"≜"    =>  Ts.DELTA_EQUAL_TO
"≝"    =>  Ts.EQUAL_TO_BY_DEFINITION
"≞"    =>  Ts.MEASURED_BY
"≟"    =>  Ts.QUESTIONED_EQUAL_TO
"≣"    =>  Ts.STRICTLY_EQUIVALENT_TO
"≦"    =>  Ts.LESS_THAN_OVER_EQUAL_TO
"≧"    =>  Ts.GREATER_THAN_OVER_EQUAL_TO
"≨"    =>  Ts.LESS_THAN_BUT_NOT_EQUAL_TO
"≩"    =>  Ts.GREATER_THAN_BUT_NOT_EQUAL_TO
"≪"    =>  Ts.MUCH_LESS_THAN
"≫"    =>  Ts.MUCH_GREATER_THAN
"≬"    =>  Ts.BETWEEN
"≭"    =>  Ts.NOT_EQUIVALENT_TO
"≮"    =>  Ts.NOT_LESS_THAN
"≯"    =>  Ts.NOT_GREATER_THAN
"≰"    =>  Ts.NEITHER_LESS_THAN_NOR_EQUAL_TO
"≱"    =>  Ts.NEITHER_GREATER_THAN_NOR_EQUAL_TO
"≲"    =>  Ts.LESS_THAN_OR_EQUIVALENT_TO
"≳"    =>  Ts.GREATER_THAN_OR_EQUIVALENT_TO
"≴"    =>  Ts.NEITHER_LESS_THAN_NOR_EQUIVALENT_TO
"≵"    =>  Ts.NEITHER_GREATER_THAN_NOR_EQUIVALENT_TO
"≶"    =>  Ts.LESS_THAN_OR_GREATER_THAN
"≷"    =>  Ts.GREATER_THAN_OR_LESS_THAN
"≸"    =>  Ts.NEITHER_LESS_THAN_NOR_GREATER_THAN
"≹"    =>  Ts.NEITHER_GREATER_THAN_NOR_LESS_THAN
"≺"    =>  Ts.PRECEDES
"≻"    =>  Ts.SUCCEEDS
"≼"    =>  Ts.PRECEDES_OR_EQUAL_TO
"≽"    =>  Ts.SUCCEEDS_OR_EQUAL_TO
"≾"    =>  Ts.PRECEDES_OR_EQUIVALENT_TO
"≿"    =>  Ts.SUCCEEDS_OR_EQUIVALENT_TO
"⊀"    =>  Ts.DOES_NOT_PRECEDE
"⊁"    =>  Ts.DOES_NOT_SUCCEED
"⊃"    =>  Ts.SUPERSET_OF
"⊅"    =>  Ts.NOT_A_SUPERSET_OF
"⊇"    =>  Ts.SUPERSET_OF_OR_EQUAL_TO
"⊉"    =>  Ts.NEITHER_A_SUPERSET_OF_NOR_EQUAL_TO
"⊋"    =>  Ts.SUPERSET_OF_WITH_NOT_EQUAL_TO
"⊏"    =>  Ts.SQUARE_IMAGE_OF
"⊐"    =>  Ts.SQUARE_ORIGINAL_OF
"⊑"    =>  Ts.SQUARE_IMAGE_OF_OR_EQUAL_TO
"⊒"    =>  Ts.SQUARE_ORIGINAL_OF_OR_EQUAL_TO
"⊜"    =>  Ts.CIRCLED_EQUALS
"⊩"    =>  Ts.FORCES
"⊬"    =>  Ts.DOES_NOT_PROVE
"⊮"    =>  Ts.DOES_NOT_FORCE
"⊰"    =>  Ts.PRECEDES_UNDER_RELATION
"⊱"    =>  Ts.SUCCEEDS_UNDER_RELATION
"⊲"    =>  Ts.NORMAL_SUBGROUP_OF
"⊳"    =>  Ts.CONTAINS_AS_NORMAL_SUBGROUP
"⊴"    =>  Ts.NORMAL_SUBGROUP_OF_OR_EQUAL_TO
"⊵"    =>  Ts.CONTAINS_AS_NORMAL_SUBGROUP_OR_EQUAL_TO
"⊶"    =>  Ts.ORIGINAL_OF
"⊷"    =>  Ts.IMAGE_OF
"⋍"    =>  Ts.REVERSED_TILDE_EQUALS
"⋐"    =>  Ts.DOUBLE_SUBSET
"⋑"    =>  Ts.DOUBLE_SUPERSET
"⋕"    =>  Ts.EQUAL_AND_PARALLEL_TO
"⋖"    =>  Ts.LESS_THAN_WITH_DOT
"⋗"    =>  Ts.GREATER_THAN_WITH_DOT
"⋘"    =>  Ts.VERY_MUCH_LESS_THAN
"⋙"    =>  Ts.VERY_MUCH_GREATER_THAN
"⋚"    =>  Ts.LESS_THAN_EQUAL_TO_OR_GREATER_THAN
"⋛"    =>  Ts.GREATER_THAN_EQUAL_TO_OR_LESS_THAN
"⋜"    =>  Ts.EQUAL_TO_OR_LESS_THAN
"⋝"    =>  Ts.EQUAL_TO_OR_GREATER_THAN
"⋞"    =>  Ts.EQUAL_TO_OR_PRECEDES
"⋟"    =>  Ts.EQUAL_TO_OR_SUCCEEDS
"⋠"    =>  Ts.DOES_NOT_PRECEDE_OR_EQUAL
"⋡"    =>  Ts.DOES_NOT_SUCCEED_OR_EQUAL
"⋢"    =>  Ts.NOT_SQUARE_IMAGE_OF_OR_EQUAL_TO
"⋣"    =>  Ts.NOT_SQUARE_ORIGINAL_OF_OR_EQUAL_TO
"⋤"    =>  Ts.SQUARE_IMAGE_OF_OR_NOT_EQUAL_TO
"⋥"    =>  Ts.SQUARE_ORIGINAL_OF_OR_NOT_EQUAL_TO
"⋦"    =>  Ts.LESS_THAN_BUT_NOT_EQUIVALENT_TO
"⋧"    =>  Ts.GREATER_THAN_BUT_NOT_EQUIVALENT_TO
"⋨"    =>  Ts.PRECEDES_BUT_NOT_EQUIVALENT_TO
"⋩"    =>  Ts.SUCCEEDS_BUT_NOT_EQUIVALENT_TO
"⋪"    =>  Ts.NOT_NORMAL_SUBGROUP_OF
"⋫"    =>  Ts.DOES_NOT_CONTAIN_AS_NORMAL_SUBGROUP
"⋬"    =>  Ts.NOT_NORMAL_SUBGROUP_OF_OR_EQUAL_TO
"⋭"    =>  Ts.DOES_NOT_CONTAIN_AS_NORMAL_SUBGROUP_OR_EQUAL
"⋲"    =>  Ts.ELEMENT_OF_WITH_LONG_HORIZONTAL_STROKE
"⋳"    =>  Ts.ELEMENT_OF_WITH_VERTICAL_BAR_AT_END_OF_HORIZONTAL_STROKE
"⋴"    =>  Ts.SMALL_ELEMENT_OF_WITH_VERTICAL_BAR_AT_END_OF_HORIZONTAL_STROKE
"⋵"    =>  Ts.ELEMENT_OF_WITH_DOT_ABOVE
"⋶"    =>  Ts.ELEMENT_OF_WITH_OVERBAR
"⋷"    =>  Ts.SMALL_ELEMENT_OF_WITH_OVERBAR
"⋸"    =>  Ts.ELEMENT_OF_WITH_UNDERBAR
"⋹"    =>  Ts.ELEMENT_OF_WITH_TWO_HORIZONTAL_STROKES
"⋺"    =>  Ts.CONTAINS_WITH_LONG_HORIZONTAL_STROKE
"⋻"    =>  Ts.CONTAINS_WITH_VERTICAL_BAR_AT_END_OF_HORIZONTAL_STROKE
"⋼"    =>  Ts.SMALL_CONTAINS_WITH_VERTICAL_BAR_AT_END_OF_HORIZONTAL_STROKE
"⋽"    =>  Ts.CONTAINS_WITH_OVERBAR
"⋾"    =>  Ts.SMALL_CONTAINS_WITH_OVERBAR
"⋿"    =>  Ts.Z_NOTATION_BAG_MEMBERSHIP
"⟈"    =>  Ts.REVERSE_SOLIDUS_PRECEDING_SUBSET
"⟉"    =>  Ts.SUPERSET_PRECEDING_SOLIDUS
"⟒"    =>  Ts.ELEMENT_OF_OPENING_UPWARDS
"⦷"    =>  Ts.CIRCLED_PARALLEL
"⧀"    =>  Ts.CIRCLED_LESS_THAN
"⧁"    =>  Ts.CIRCLED_GREATER_THAN
"⧡"    =>  Ts.INCREASES_AS
"⧣"    =>  Ts.EQUALS_SIGN_AND_SLANTED_PARALLEL
"⧤"    =>  Ts.EQUALS_SIGN_AND_SLANTED_PARALLEL_WITH_TILDE_ABOVE
"⧥"    =>  Ts.IDENTICAL_TO_AND_SLANTED_PARALLEL
"⩦"    =>  Ts.EQUALS_SIGN_WITH_DOT_BELOW
"⩧"    =>  Ts.IDENTICAL_WITH_DOT_ABOVE
"⩪"    =>  Ts.TILDE_OPERATOR_WITH_DOT_ABOVE
"⩫"    =>  Ts.TILDE_OPERATOR_WITH_RISING_DOTS
"⩬"    =>  Ts.SIMILAR_MINUS_SIMILAR
"⩭"    =>  Ts.CONGRUENT_WITH_DOT_ABOVE
"⩮"    =>  Ts.EQUALS_WITH_ASTERISK
"⩯"    =>  Ts.ALMOST_EQUAL_TO_WITH_CIRCUMFLEX_ACCENT
"⩰"    =>  Ts.APPROXIMATELY_EQUAL_OR_EQUAL_TO
"⩱"    =>  Ts.EQUALS_SIGN_ABOVE_PLUS_SIGN
"⩲"    =>  Ts.PLUS_SIGN_ABOVE_EQUALS_SIGN
"⩳"    =>  Ts.EQUALS_SIGN_ABOVE_TILDE_OPERATOR
"⩴"    =>  Ts.DOUBLE_COLON_EQUAL
"⩵"    =>  Ts.TWO_CONSECUTIVE_EQUALS_SIGNS
"⩶"    =>  Ts.THREE_CONSECUTIVE_EQUALS_SIGNS
"⩷"    =>  Ts.EQUALS_SIGN_WITH_TWO_DOTS_ABOVE_AND_TWO_DOTS_BELOW
"⩸"    =>  Ts.EQUIVALENT_WITH_FOUR_DOTS_ABOVE
"⩹"    =>  Ts.LESS_THAN_WITH_CIRCLE_INSIDE
"⩺"    =>  Ts.GREATER_THAN_WITH_CIRCLE_INSIDE
"⩻"    =>  Ts.LESS_THAN_WITH_QUESTION_MARK_ABOVE
"⩼"    =>  Ts.GREATER_THAN_WITH_QUESTION_MARK_ABOVE
"⩽"    =>  Ts.LESS_THAN_OR_SLANTED_EQUAL_TO
"⩾"    =>  Ts.GREATER_THAN_OR_SLANTED_EQUAL_TO
"⩿"    =>  Ts.LESS_THAN_OR_SLANTED_EQUAL_TO_WITH_DOT_INSIDE
"⪀"    =>  Ts.GREATER_THAN_OR_SLANTED_EQUAL_TO_WITH_DOT_INSIDE
"⪁"    =>  Ts.LESS_THAN_OR_SLANTED_EQUAL_TO_WITH_DOT_ABOVE
"⪂"    =>  Ts.GREATER_THAN_OR_SLANTED_EQUAL_TO_WITH_DOT_ABOVE
"⪃"    =>  Ts.LESS_THAN_OR_SLANTED_EQUAL_TO_WITH_DOT_ABOVE_RIGHT
"⪄"    =>  Ts.GREATER_THAN_OR_SLANTED_EQUAL_TO_WITH_DOT_ABOVE_LEFT
"⪅"    =>  Ts.LESS_THAN_OR_APPROXIMATE
"⪆"    =>  Ts.GREATER_THAN_OR_APPROXIMATE
"⪇"    =>  Ts.LESS_THAN_AND_SINGLE_LINE_NOT_EQUAL_TO
"⪈"    =>  Ts.GREATER_THAN_AND_SINGLE_LINE_NOT_EQUAL_TO
"⪉"    =>  Ts.LESS_THAN_AND_NOT_APPROXIMATE
"⪊"    =>  Ts.GREATER_THAN_AND_NOT_APPROXIMATE
"⪋"    =>  Ts.LESS_THAN_ABOVE_DOUBLE_LINE_EQUAL_ABOVE_GREATER_THAN
"⪌"    =>  Ts.GREATER_THAN_ABOVE_DOUBLE_LINE_EQUAL_ABOVE_LESS_THAN
"⪍"    =>  Ts.LESS_THAN_ABOVE_SIMILAR_OR_EQUAL
"⪎"    =>  Ts.GREATER_THAN_ABOVE_SIMILAR_OR_EQUAL
"⪏"    =>  Ts.LESS_THAN_ABOVE_SIMILAR_ABOVE_GREATER_THAN
"⪐"    =>  Ts.GREATER_THAN_ABOVE_SIMILAR_ABOVE_LESS_THAN
"⪑"    =>  Ts.LESS_THAN_ABOVE_GREATER_THAN_ABOVE_DOUBLE_LINE_EQUAL
"⪒"    =>  Ts.GREATER_THAN_ABOVE_LESS_THAN_ABOVE_DOUBLE_LINE_EQUAL
"⪓"    =>  Ts.LESS_THAN_ABOVE_SLANTED_EQUAL_ABOVE_GREATER_THAN_ABOVE_SLANTED_EQUAL
"⪔"    =>  Ts.GREATER_THAN_ABOVE_SLANTED_EQUAL_ABOVE_LESS_THAN_ABOVE_SLANTED_EQUAL
"⪕"    =>  Ts.SLANTED_EQUAL_TO_OR_LESS_THAN
"⪖"    =>  Ts.SLANTED_EQUAL_TO_OR_GREATER_THAN
"⪗"    =>  Ts.SLANTED_EQUAL_TO_OR_LESS_THAN_WITH_DOT_INSIDE
"⪘"    =>  Ts.SLANTED_EQUAL_TO_OR_GREATER_THAN_WITH_DOT_INSIDE
"⪙"    =>  Ts.DOUBLE_LINE_EQUAL_TO_OR_LESS_THAN
"⪚"    =>  Ts.DOUBLE_LINE_EQUAL_TO_OR_GREATER_THAN
"⪛"    =>  Ts.DOUBLE_LINE_SLANTED_EQUAL_TO_OR_LESS_THAN
"⪜"    =>  Ts.DOUBLE_LINE_SLANTED_EQUAL_TO_OR_GREATER_THAN
"⪝"    =>  Ts.SIMILAR_OR_LESS_THAN
"⪞"    =>  Ts.SIMILAR_OR_GREATER_THAN
"⪟"    =>  Ts.SIMILAR_ABOVE_LESS_THAN_ABOVE_EQUALS_SIGN
"⪠"    =>  Ts.SIMILAR_ABOVE_GREATER_THAN_ABOVE_EQUALS_SIGN
"⪡"    =>  Ts.DOUBLE_NESTED_LESS_THAN
"⪢"    =>  Ts.DOUBLE_NESTED_GREATER_THAN
"⪣"    =>  Ts.DOUBLE_NESTED_LESS_THAN_WITH_UNDERBAR
"⪤"    =>  Ts.GREATER_THAN_OVERLAPPING_LESS_THAN
"⪥"    =>  Ts.GREATER_THAN_BESIDE_LESS_THAN
"⪦"    =>  Ts.LESS_THAN_CLOSED_BY_CURVE
"⪧"    =>  Ts.GREATER_THAN_CLOSED_BY_CURVE
"⪨"    =>  Ts.LESS_THAN_CLOSED_BY_CURVE_ABOVE_SLANTED_EQUAL
"⪩"    =>  Ts.GREATER_THAN_CLOSED_BY_CURVE_ABOVE_SLANTED_EQUAL
"⪪"    =>  Ts.SMALLER_THAN
"⪫"    =>  Ts.LARGER_THAN
"⪬"    =>  Ts.SMALLER_THAN_OR_EQUAL_TO
"⪭"    =>  Ts.LARGER_THAN_OR_EQUAL_TO
"⪮"    =>  Ts.EQUALS_SIGN_WITH_BUMPY_ABOVE
"⪯"    =>  Ts.PRECEDES_ABOVE_SINGLE_LINE_EQUALS_SIGN
"⪰"    =>  Ts.SUCCEEDS_ABOVE_SINGLE_LINE_EQUALS_SIGN
"⪱"    =>  Ts.PRECEDES_ABOVE_SINGLE_LINE_NOT_EQUAL_TO
"⪲"    =>  Ts.SUCCEEDS_ABOVE_SINGLE_LINE_NOT_EQUAL_TO
"⪳"    =>  Ts.PRECEDES_ABOVE_EQUALS_SIGN
"⪴"    =>  Ts.SUCCEEDS_ABOVE_EQUALS_SIGN
"⪵"    =>  Ts.PRECEDES_ABOVE_NOT_EQUAL_TO
"⪶"    =>  Ts.SUCCEEDS_ABOVE_NOT_EQUAL_TO
"⪷"    =>  Ts.PRECEDES_ABOVE_ALMOST_EQUAL_TO
"⪸"    =>  Ts.SUCCEEDS_ABOVE_ALMOST_EQUAL_TO
"⪹"    =>  Ts.PRECEDES_ABOVE_NOT_ALMOST_EQUAL_TO
"⪺"    =>  Ts.SUCCEEDS_ABOVE_NOT_ALMOST_EQUAL_TO
"⪻"    =>  Ts.DOUBLE_PRECEDES
"⪼"    =>  Ts.DOUBLE_SUCCEEDS
"⪽"    =>  Ts.SUBSET_WITH_DOT
"⪾"    =>  Ts.SUPERSET_WITH_DOT
"⪿"    =>  Ts.SUBSET_WITH_PLUS_SIGN_BELOW
"⫀"    =>  Ts.SUPERSET_WITH_PLUS_SIGN_BELOW
"⫁"    =>  Ts.SUBSET_WITH_MULTIPLICATION_SIGN_BELOW
"⫂"    =>  Ts.SUPERSET_WITH_MULTIPLICATION_SIGN_BELOW
"⫃"    =>  Ts.SUBSET_OF_OR_EQUAL_TO_WITH_DOT_ABOVE
"⫄"    =>  Ts.SUPERSET_OF_OR_EQUAL_TO_WITH_DOT_ABOVE
"⫅"    =>  Ts.SUBSET_OF_ABOVE_EQUALS_SIGN
"⫆"    =>  Ts.SUPERSET_OF_ABOVE_EQUALS_SIGN
"⫇"    =>  Ts.SUBSET_OF_ABOVE_TILDE_OPERATOR
"⫈"    =>  Ts.SUPERSET_OF_ABOVE_TILDE_OPERATOR
"⫉"    =>  Ts.SUBSET_OF_ABOVE_ALMOST_EQUAL_TO
"⫊"    =>  Ts.SUPERSET_OF_ABOVE_ALMOST_EQUAL_TO
"⫋"    =>  Ts.SUBSET_OF_ABOVE_NOT_EQUAL_TO
"⫌"    =>  Ts.SUPERSET_OF_ABOVE_NOT_EQUAL_TO
"⫍"    =>  Ts.SQUARE_LEFT_OPEN_BOX_OPERATOR
"⫎"    =>  Ts.SQUARE_RIGHT_OPEN_BOX_OPERATOR
"⫏"    =>  Ts.CLOSED_SUBSET
"⫐"    =>  Ts.CLOSED_SUPERSET
"⫑"    =>  Ts.CLOSED_SUBSET_OR_EQUAL_TO
"⫒"    =>  Ts.CLOSED_SUPERSET_OR_EQUAL_TO
"⫓"    =>  Ts.SUBSET_ABOVE_SUPERSET
"⫔"    =>  Ts.SUPERSET_ABOVE_SUBSET
"⫕"    =>  Ts.SUBSET_ABOVE_SUBSET
"⫖"    =>  Ts.SUPERSET_ABOVE_SUPERSET
"⫗"    =>  Ts.SUPERSET_BESIDE_SUBSET
"⫘"    =>  Ts.SUPERSET_BESIDE_AND_JOINED_BY_DASH_WITH_SUBSET
"⫙"    =>  Ts.ELEMENT_OF_OPENING_DOWNWARDS
"⫷"    =>  Ts.TRIPLE_NESTED_LESS_THAN
"⫸"    =>  Ts.TRIPLE_NESTED_GREATER_THAN
"⫹"    =>  Ts.DOUBLE_LINE_SLANTED_LESS_THAN_OR_EQUAL_TO
"⫺"    =>  Ts.DOUBLE_LINE_SLANTED_GREATER_THAN_OR_EQUAL_TO
"⊢"    =>  Ts.RIGHT_TACK
"⊣"    =>  Ts.LEFT_TACK
# ⫪,⫫ see https://github.com/JuliaLang/julia/issues/39350
"⫪"    =>  Ts.DOUBLE_DOWN_TACK
"⫫"    =>  Ts.DOUBLE_UP_TACK
"⟂"    =>  Ts.PERP
"END_COMPARISON" => Ts.end_comparison

# Level 7
"BEGIN_PIPE"  => Ts.begin_pipe
"<|"  =>  Ts.LPIPE
"|>"  =>  Ts.RPIPE
"END_PIPE"    => Ts.end_pipe

# Level 8
"BEGIN_COLON"  => Ts.begin_colon
":"   =>  Ts.COLON
".."  =>  Ts.DDOT
"…"   =>  Ts.LDOTS
"⁝"   =>  Ts.TRICOLON
"⋮"   =>  Ts.VDOTS
"⋱"   =>  Ts.DDOTS
"⋰"   =>  Ts.ADOTS
"⋯"   =>  Ts.CDOTS
"END_COLON"    => Ts.end_colon

# Level 9
"BEGIN_PLUS"  => Ts.begin_plus
"\$"  =>  Ts.EX_OR
"+"   =>  Ts.PLUS
"-"   =>  Ts.MINUS
"++"  =>  Ts.PLUSPLUS
"⊕"   =>  Ts.CIRCLED_PLUS
"⊖"   =>  Ts.CIRCLED_MINUS
"⊞"   =>  Ts.SQUARED_PLUS
"⊟"   =>  Ts.SQUARED_MINUS
"|"   =>  Ts.OR
"∪"   =>  Ts.UNION
"∨"   =>  Ts.LOGICAL_OR
"⊔"   =>  Ts.SQUARE_CUP
"±"   =>  Ts.PLUS_MINUS_SIGN
"∓"   =>  Ts.MINUS_OR_PLUS_SIGN
"∔"   =>  Ts.DOT_PLUS
"∸"   =>  Ts.DOT_MINUS
"≂"   =>  Ts.MINUS_TILDE
"≏"   =>  Ts.DIFFERENCE_BETWEEN
"⊎"   =>  Ts.MULTISET_UNION
"⊻"   =>  Ts.XOR
"⊽"   =>  Ts.NOR
"⋎"   =>  Ts.CURLY_LOGICAL_OR
"⋓"   =>  Ts.DOUBLE_UNION
"⧺"   =>  Ts.DOUBLE_PLUS
"⧻"   =>  Ts.TRIPLE_PLUS
"⨈"   =>  Ts.TWO_LOGICAL_OR_OPERATOR
"⨢"   =>  Ts.PLUS_SIGN_WITH_SMALL_CIRCLE_ABOVE
"⨣"   =>  Ts.PLUS_SIGN_WITH_CIRCUMFLEX_ACCENT_ABOVE
"⨤"   =>  Ts.PLUS_SIGN_WITH_TILDE_ABOVE
"⨥"   =>  Ts.PLUS_SIGN_WITH_DOT_BELOW
"⨦"   =>  Ts.PLUS_SIGN_WITH_TILDE_BELOW
"⨧"   =>  Ts.PLUS_SIGN_WITH_SUBSCRIPT_TWO
"⨨"   =>  Ts.PLUS_SIGN_WITH_BLACK_TRIANGLE
"⨩"   =>  Ts.MINUS_SIGN_WITH_COMMA_ABOVE
"⨪"   =>  Ts.MINUS_SIGN_WITH_DOT_BELOW
"⨫"   =>  Ts.MINUS_SIGN_WITH_FALLING_DOTS
"⨬"   =>  Ts.MINUS_SIGN_WITH_RISING_DOTS
"⨭"   =>  Ts.PLUS_SIGN_IN_LEFT_HALF_CIRCLE
"⨮"   =>  Ts.PLUS_SIGN_IN_RIGHT_HALF_CIRCLE
"⨹"   =>  Ts.PLUS_SIGN_IN_TRIANGLE
"⨺"   =>  Ts.MINUS_SIGN_IN_TRIANGLE
"⩁"   =>  Ts.UNION_WITH_MINUS_SIGN
"⩂"   =>  Ts.UNION_WITH_OVERBAR
"⩅"   =>  Ts.UNION_WITH_LOGICAL_OR
"⩊"   =>  Ts.UNION_BESIDE_AND_JOINED_WITH_UNION
"⩌"   =>  Ts.CLOSED_UNION_WITH_SERIFS
"⩏"   =>  Ts.DOUBLE_SQUARE_UNION
"⩐"   =>  Ts.CLOSED_UNION_WITH_SERIFS_AND_SMASH_PRODUCT
"⩒"   =>  Ts.LOGICAL_OR_WITH_DOT_ABOVE
"⩔"   =>  Ts.DOUBLE_LOGICAL_OR
"⩖"   =>  Ts.TWO_INTERSECTING_LOGICAL_OR
"⩗"   =>  Ts.SLOPING_LARGE_OR
"⩛"   =>  Ts.LOGICAL_OR_WITH_MIDDLE_STEM
"⩝"   =>  Ts.LOGICAL_OR_WITH_HORIZONTAL_DASH
"⩡"   =>  Ts.SMALL_VEE_WITH_UNDERBAR
"⩢"   =>  Ts.LOGICAL_OR_WITH_DOUBLE_OVERBAR
"⩣"   =>  Ts.LOGICAL_OR_WITH_DOUBLE_UNDERBAR
"¦"   =>  Ts.BROKEN_BAR
"END_PLUS"    => Ts.end_plus

# Level 10
"BEGIN_BITSHIFTS"  => Ts.begin_bitshifts
"<<"   =>  Ts.LBITSHIFT
">>"   =>  Ts.RBITSHIFT
">>>"  =>  Ts.UNSIGNED_BITSHIFT
"END_BITSHIFTS"    => Ts.end_bitshifts

# Level 11
"BEGIN_TIMES"  => Ts.begin_times
"*"   =>  Ts.STAR
"/"   =>  Ts.FWD_SLASH
"÷"   =>  Ts.DIVISION_SIGN
"%"   =>  Ts.REM
"⋅"   =>  Ts.UNICODE_DOT
"∘"   =>  Ts.RING_OPERATOR
"×"   =>  Ts.MULTIPLICATION_SIGN
"\\"  =>  Ts.BACKSLASH
"&"   =>  Ts.AND
"∩"   =>  Ts.INTERSECTION
"∧"   =>  Ts.LOGICAL_AND
"⊗"   =>  Ts.CIRCLED_TIMES
"⊘"   =>  Ts.CIRCLED_DIVISION_SLASH
"⊙"   =>  Ts.CIRCLED_DOT_OPERATOR
"⊚"   =>  Ts.CIRCLED_RING_OPERATOR
"⊛"   =>  Ts.CIRCLED_ASTERISK_OPERATOR
"⊠"   =>  Ts.SQUARED_TIMES
"⊡"   =>  Ts.SQUARED_DOT_OPERATOR
"⊓"   =>  Ts.SQUARE_CAP
"∗"   =>  Ts.ASTERISK_OPERATOR
"∙"   =>  Ts.BULLET_OPERATOR
"∤"   =>  Ts.DOES_NOT_DIVIDE
"⅋"   =>  Ts.TURNED_AMPERSAND
"≀"   =>  Ts.WREATH_PRODUCT
"⊼"   =>  Ts.NAND
"⋄"   =>  Ts.DIAMOND_OPERATOR
"⋆"   =>  Ts.STAR_OPERATOR
"⋇"   =>  Ts.DIVISION_TIMES
"⋉"   =>  Ts.LEFT_NORMAL_FACTOR_SEMIDIRECT_PRODUCT
"⋊"   =>  Ts.RIGHT_NORMAL_FACTOR_SEMIDIRECT_PRODUCT
"⋋"   =>  Ts.LEFT_SEMIDIRECT_PRODUCT
"⋌"   =>  Ts.RIGHT_SEMIDIRECT_PRODUCT
"⋏"   =>  Ts.CURLY_LOGICAL_AND
"⋒"   =>  Ts.DOUBLE_INTERSECTION
"⟑"   =>  Ts.AND_WITH_DOT
"⦸"   =>  Ts.CIRCLED_REVERSE_SOLIDUS
"⦼"   =>  Ts.CIRCLED_ANTICLOCKWISE_ROTATED_DIVISION_SIGN
"⦾"   =>  Ts.CIRCLED_WHITE_BULLET
"⦿"   =>  Ts.CIRCLED_BULLET
"⧶"   =>  Ts.SOLIDUS_WITH_OVERBAR
"⧷"   =>  Ts.REVERSE_SOLIDUS_WITH_HORIZONTAL_STROKE
"⨇"   =>  Ts.TWO_LOGICAL_AND_OPERATOR
"⨰"   =>  Ts.MULTIPLICATION_SIGN_WITH_DOT_ABOVE
"⨱"   =>  Ts.MULTIPLICATION_SIGN_WITH_UNDERBAR
"⨲"   =>  Ts.SEMIDIRECT_PRODUCT_WITH_BOTTOM_CLOSED
"⨳"   =>  Ts.SMASH_PRODUCT
"⨴"   =>  Ts.MULTIPLICATION_SIGN_IN_LEFT_HALF_CIRCLE
"⨵"   =>  Ts.MULTIPLICATION_SIGN_IN_RIGHT_HALF_CIRCLE
"⨶"   =>  Ts.CIRCLED_MULTIPLICATION_SIGN_WITH_CIRCUMFLEX_ACCENT
"⨷"   =>  Ts.MULTIPLICATION_SIGN_IN_DOUBLE_CIRCLE
"⨸"   =>  Ts.CIRCLED_DIVISION_SIGN
"⨻"   =>  Ts.MULTIPLICATION_SIGN_IN_TRIANGLE
"⨼"   =>  Ts.INTERIOR_PRODUCT
"⨽"   =>  Ts.RIGHTHAND_INTERIOR_PRODUCT
"⩀"   =>  Ts.INTERSECTION_WITH_DOT
"⩃"   =>  Ts.INTERSECTION_WITH_OVERBAR
"⩄"   =>  Ts.INTERSECTION_WITH_LOGICAL_AND
"⩋"   =>  Ts.INTERSECTION_BESIDE_AND_JOINED_WITH_INTERSECTION
"⩍"   =>  Ts.CLOSED_INTERSECTION_WITH_SERIFS
"⩎"   =>  Ts.DOUBLE_SQUARE_INTERSECTION
"⩑"   =>  Ts.LOGICAL_AND_WITH_DOT_ABOVE
"⩓"   =>  Ts.DOUBLE_LOGICAL_AND
"⩕"   =>  Ts.TWO_INTERSECTING_LOGICAL_AND
"⩘"   =>  Ts.SLOPING_LARGE_AND
"⩚"   =>  Ts.LOGICAL_AND_WITH_MIDDLE_STEM
"⩜"   =>  Ts.LOGICAL_AND_WITH_HORIZONTAL_DASH
"⩞"   =>  Ts.LOGICAL_AND_WITH_DOUBLE_OVERBAR
"⩟"   =>  Ts.LOGICAL_AND_WITH_UNDERBAR
"⩠"   =>  Ts.LOGICAL_AND_WITH_DOUBLE_UNDERBAR
"⫛"   =>  Ts.TRANSVERSAL_INTERSECTION
"⊍"   =>  Ts.MULTISET_MULTIPLICATION
"▷"   =>  Ts.WHITE_RIGHT_POINTING_TRIANGLE
"⨝"   =>  Ts.JOIN
"⟕"   =>  Ts.LEFT_OUTER_JOIN
"⟖"   =>  Ts.RIGHT_OUTER_JOIN
"⟗"   =>  Ts.FULL_OUTER_JOIN
"⌿"   =>  Ts.NOT_SLASH
"⨟"   =>  Ts.BB_SEMI
"END_TIMES"    => Ts.end_times

# Level 12
"BEGIN_RATIONAL"  => Ts.begin_rational
"//"  =>  Ts.FWDFWD_SLASH
"END_RATIONAL"    => Ts.end_rational

# Level 13
"BEGIN_POWER"  => Ts.begin_power
"^"  =>  Ts.CIRCUMFLEX_ACCENT
"↑"  =>  Ts.UPWARDS_ARROW
"↓"  =>  Ts.DOWNWARDS_ARROW
"⇵"  =>  Ts.DOWNWARDS_ARROW_LEFTWARDS_OF_UPWARDS_ARROW
"⟰"  =>  Ts.UPWARDS_QUADRUPLE_ARROW
"⟱"  =>  Ts.DOWNWARDS_QUADRUPLE_ARROW
"⤈"  =>  Ts.DOWNWARDS_ARROW_WITH_HORIZONTAL_STROKE
"⤉"  =>  Ts.UPWARDS_ARROW_WITH_HORIZONTAL_STROKE
"⤊"  =>  Ts.UPWARDS_TRIPLE_ARROW
"⤋"  =>  Ts.DOWNWARDS_TRIPLE_ARROW
"⤒"  =>  Ts.UPWARDS_ARROW_TO_BAR
"⤓"  =>  Ts.DOWNWARDS_ARROW_TO_BAR
"⥉"  =>  Ts.UPWARDS_TWO_HEADED_ARROW_FROM_SMALL_CIRCLE
"⥌"  =>  Ts.UP_BARB_RIGHT_DOWN_BARB_LEFT_HARPOON
"⥍"  =>  Ts.UP_BARB_LEFT_DOWN_BARB_RIGHT_HARPOON
"⥏"  =>  Ts.UP_BARB_RIGHT_DOWN_BARB_RIGHT_HARPOON
"⥑"  =>  Ts.UP_BARB_LEFT_DOWN_BARB_LEFT_HARPOON
"⥔"  =>  Ts.UPWARDS_HARPOON_WITH_BARB_RIGHT_TO_BAR
"⥕"  =>  Ts.DOWNWARDS_HARPOON_WITH_BARB_RIGHT_TO_BAR
"⥘"  =>  Ts.UPWARDS_HARPOON_WITH_BARB_LEFT_TO_BAR
"⥙"  =>  Ts.DOWNWARDS_HARPOON_WITH_BARB_LEFT_TO_BAR
"⥜"  =>  Ts.UPWARDS_HARPOON_WITH_BARB_RIGHT_FROM_BAR
"⥝"  =>  Ts.DOWNWARDS_HARPOON_WITH_BARB_RIGHT_FROM_BAR
"⥠"  =>  Ts.UPWARDS_HARPOON_WITH_BARB_LEFT_FROM_BAR
"⥡"  =>  Ts.DOWNWARDS_HARPOON_WITH_BARB_LEFT_FROM_BAR
"⥣"  =>  Ts.UPWARDS_HARPOON_WITH_BARB_LEFT_BESIDE_UPWARDS_HARPOON_WITH_BARB_RIGHT
"⥥"  =>  Ts.DOWNWARDS_HARPOON_WITH_BARB_LEFT_BESIDE_DOWNWARDS_HARPOON_WITH_BARB_RIGHT
"⥮"  =>  Ts.UPWARDS_HARPOON_WITH_BARB_LEFT_BESIDE_DOWNWARDS_HARPOON_WITH_BARB_RIGHT
"⥯"  =>  Ts.DOWNWARDS_HARPOON_WITH_BARB_LEFT_BESIDE_UPWARDS_HARPOON_WITH_BARB_RIGHT
"￪"  =>  Ts.HALFWIDTH_UPWARDS_ARROW
"￬"  =>  Ts.HALFWIDTH_DOWNWARDS_ARROW
"END_POWER"    => Ts.end_power

# Level 14
"BEGIN_DECL"  => Ts.begin_decl
"::"  =>  Ts.DECLARATION
"END_DECL"    => Ts.end_decl

# Level 15
"BEGIN_WHERE"  => Ts.begin_where
"where"  =>  Ts.WHERE
"END_WHERE"    => Ts.end_where

# Level 16
"BEGIN_DOT"  => Ts.begin_dot
"."  =>  Ts.DOT
"END_DOT"    => Ts.end_dot

"!"   =>  Ts.NOT
"'"   =>  Ts.PRIME
".'"  =>  Ts.TRANSPOSE
"->"  =>  Ts.ANON_FUNC

"BEGIN_UNICODE_OPS" => Ts.begin_unicode_ops
"¬"  =>  Ts.NOT_SIGN
"√"  =>  Ts.SQUARE_ROOT
"∛"  =>  Ts.CUBE_ROOT
"∜"  =>  Ts.QUAD_ROOT
"END_UNICODE_OPS"   => Ts.end_unicode_ops

"END_OPS" => Ts.end_ops

"BEGIN_PARSER_TOKENS" => Ts.begin_parser_tokens

"TOMBSTONE"          =>  Ts.TOMBSTONE

# Macro names are modelled as a special kind of identifier because the
# @ may not be attached to the macro name in the source (or may not be
# associated with a token at all in the case of implied macro calls
# like CORE_DOC_MACRO_NAME)
"BEGIN_MACRO_NAMES" => Ts.begin_macro_names
"MacroName"          =>  Ts.MACRO_NAME                  # A macro name identifier
"@."                 =>  Ts.DOT_MACRO_NAME              # The macro name of @.
"StringMacroName"    =>  Ts.STRING_MACRO_NAME           # macname"some_str"
"CmdMacroName"       =>  Ts.CMD_MACRO_NAME              # macname`some_str`
"core_@doc"          =>  Ts.CORE_DOC_MACRO_NAME         # Core.@doc
"core_@cmd"          =>  Ts.CORE_CMD_MACRO_NAME         # Core.@cmd
"core_@int128_str"   =>  Ts.CORE_INT128_STR_MACRO_NAME  # Core.@int128_str
"core_@uint128_str"  =>  Ts.CORE_UINT128_STR_MACRO_NAME # Core.@uint128_str
"core_@big_str"      =>  Ts.CORE_BIG_STR_MACRO_NAME     # Core.@big_str
"END_MACRO_NAMES"    =>  Ts.end_macro_names
"END_PARSER_TOKENS"  =>  Ts.end_parser_tokens

# Our custom syntax tokens
"BEGIN_SYNTAX_KINDS" => Ts.begin_syntax_kinds
"block"                =>  Ts.BLOCK
"call"                 =>  Ts.CALL
"comparison"           =>  Ts.COMPARISON
"curly"                =>  Ts.CURLY
"inert"                =>  Ts.INERT
"string"               =>  Ts.STRING_INTERP
"macrocall"            =>  Ts.MACROCALL
"kw"                   =>  Ts.KW              # the = in f(a=1)
"parameters"           =>  Ts.PARAMETERS      # the list after ; in f(; a=1)
"toplevel"             =>  Ts.TOPLEVEL
"tuple"                =>  Ts.TUPLE
"ref"                  =>  Ts.REF
"vect"                 =>  Ts.VECT
"braces"               =>  Ts.BRACES
"bracescat"            =>  Ts.BRACESCAT
"hcat"                 =>  Ts.HCAT
"vcat"                 =>  Ts.VCAT
"ncat"                 =>  Ts.NCAT
"typed_hcat"           =>  Ts.TYPED_HCAT
"typed_vcat"           =>  Ts.TYPED_VCAT
"typed_ncat"           =>  Ts.TYPED_NCAT
"row"                  =>  Ts.ROW
"nrow"                 =>  Ts.NROW
"generator"            =>  Ts.GENERATOR
"filter"               =>  Ts.FILTER
"flatten"              =>  Ts.FLATTEN
"comprehension"        =>  Ts.COMPREHENSION
"typed_comprehension"  =>  Ts.TYPED_COMPREHENSION
"END_SYNTAX_KINDS" => Ts.end_syntax_kinds

])
end

# Mapping from kinds to their unique string representation, if it exists
const _kind_to_str_unique =
    Dict{Kind,String}(k=>string(s) for (k,s) in TzTokens.UNICODE_OPS_REVERSE)
for kw in split("""
        ( [ { } ] ) @ , ; " \"\"\" ` ```

        baremodule begin break catch const
        continue do else elseif end export finally for
        function global if import let local
        macro module quote return struct try type using while

        as abstract doc mutable outer primitive type var

        block call comparison curly string inert macrocall kw parameters
        toplevel tuple ref vect braces bracescat hcat
        vcat ncat typed_hcat typed_vcat typed_ncat row nrow generator
        filter flatten comprehension typed_comprehension

        error nothing true false None
    """)
    _kind_to_str_unique[_str_to_kind[kw]] = kw
end

const _kind_to_str = Dict(s=>k for (k,s) in _str_to_kind)


text:
using POMDPModels
using POMDPs
using POMDPModelTools
using StaticArrays
using Random
using DiscreteValueIteration
using GridInterpolations
using LocalFunctionApproximation
using LocalApproximationValueIteration
using POMDPLinter: show_requirements, get_requirements
using Test

@testset "all" begin

    println("Testing Requirements")
    gifa = LocalGIFunctionApproximator(RectangleGrid([0.0, 1.0], [0.0, 1.0]))
    @test_skip @requirements_info LocalApproximationValueIterationSolver(gifa) SimpleGridWorld()
    show_requirements(get_requirements(POMDPs.solve, (LocalApproximationValueIterationSolver(gifa), SimpleGridWorld())))

    @testset "integration" begin
        include("runtests_versus_discrete_vi.jl")
    end
end


text:
ENV["NN_DEBUG"] = true # or change DEBUG = true in NearestNeighbors.jl file

using NearestNeighbors

const data = [[2, 3] [5, 4] [9, 6] [4, 7] [8, 1] [7, 2]]

NearestNeighbors.reset_stats()
const kdtree = KDTree(Float64.(data))
const indexpoint = [9,2]
idx, dist = knn(kdtree, indexpoint, 1)
println("Wikipedia example: The nearest neighbor to $indexpoint is ",
    "$(data[1:2, idx[1]]) at distance $(dist).")
NearestNeighbors.print_stats()

NearestNeighbors.reset_stats()
const cubedis = rand(3, 1000)
const kdcubetree = KDTree(cubedis)
const rand3point = rand(3, 1)
idx, dist = knn(kdcubetree, rand3point, 1)
println("\n\n1000 cube points: The point $rand3point is closest to the point $(cubedis[1:3, idx[1]])",
    " at distance $(dist[1]).")
NearestNeighbors.print_stats()

NearestNeighbors.reset_stats()
const cubedis2 = rand(3, 500000)
const kdcubetree2 = KDTree(cubedis2)
const rand3point2 = rand(3, 1)
idx, dist = knn(kdcubetree2, rand3point2, 1)
println("\n\nExtra: The point $rand3point2 is closest to the point $(cubedis2[1:3, idx[1]]) out of $(size(cubedis2)[2]) points,",
    " at distance $(dist[1]).")
NearestNeighbors.print_stats()


text:
module TestPkg

using AWS

@service S3

end


text:
using Documenter, Zygote

makedocs(
  sitename="Zygote",
  analytics = "UA-36890222-9",
  pages = [
        "Home" => "index.md",
        "Custom Adjoints" => "adjoints.md",
        "Profiling" => "profiling.md",
        "Internals" => "internals.md"])

deploydocs(
    repo = "github.com/FluxML/Zygote.jl.git",
)


text:
mutable struct DeJongMaximinModel <: AbstractModel
    settings::Settings
    constraints::Vector{Constraint}
    obj::DeJongMaximinObjective
    output::Output
    DeJongMaximinModel(settings, constraints, obj, output) =
        new(settings, constraints, obj, output)
    DeJongMaximinModel() = new(Settings(), Constraint[], DeJongMaximinObjective(), Output())
end


text:
# This file is a part of MJDSigGen, licensed under the MIT License (MIT).

function read_fields(setup::SigGenSetup)
    config_dir = dirname(setup.config_name)
    field_data = readdlm(joinpath(config_dir, setup.field_name), comments=true)
    wpot_data = readdlm(joinpath(config_dir, setup.wp_name), comments=true)

    Nr::Int = setup.xtal_radius / setup.xtal_grid + 1
    Nz::Int = setup.xtal_length / setup.xtal_grid + 1
    N = Nz * Nr

    @assert size(field_data, 1) == size(wpot_data, 1) == N

    E_pot = copy(reshape(view(field_data, :, 3), (Nz, Nr)))
    W_pot = copy(reshape(view(wpot_data, :, 3), (Nz, Nr)))

    E_abs = copy(reshape(view(field_data, :, 4), (Nz, Nr)))
    E_r = copy(reshape(view(field_data, :, 5), (Nz, Nr)))
    E_z = copy(reshape(view(field_data, :, 6), (Nz, Nr)))

    return E_pot, W_pot, E_abs, E_r, E_z
end

cyl2cart(r, ϕ, z) = (r * cos(ϕ), r * sin(ϕ), z)

function cart2cyl(x, y, z)
    r = √(x^2 + y^2)
    if x == 0
        ϕ = (y > 0) ? π/2 : -π/2
    else
        ϕ = (x < 0) ? atan(y/x) + π : atan(y/x)
    end
    return r, ϕ, z
end

function coll_effects_off!(setup::SigGenSetup)
    setup.energy            = 0
	setup.charge_cloud_size = 0
	setup.use_diffusion     = 0
	setup.use_acceleration  = 0
	setup.use_repulsion     = 0
	return setup
end

function with_coll_effects!(f::Function, setup::SigGenSetup, E::Real, ch_cld_size::Real)
	try
		setup.energy            = E
		setup.charge_cloud_size = ch_cld_size
		setup.use_diffusion     = 1
		setup.use_acceleration  = 1
		setup.use_repulsion     = 1

		return f()
	finally
		coll_effects_off!(setup)
	end
end

getδτ(charge_size::Real, speed::Real) = charge_size / speed

getδτ(setup::SigGenSetup) = getδτ(setup.final_charge_size, setup.final_vel)

function getδτ!(setup::SigGenSetup, pos::NTuple{3, Real})
    get_signal!(setup, pos)
    return getδτ(setup)
end


text:
# Examples
println("Running examples:")
# examples = readdir()
examples = ["ising.jl",
            "parameterestimation.jl",
            "polynomial.jl",
            "sat.jl",
            "cancer.jl",
            "symmetry.jl",
            "nball.jl",
            "geometry.jl",
            ]
for example in examples
  println(" * $example")
  try
    include(example)
  catch e
    println("Example $example failed with error:\n $e")
  end
end


text:
@testset "polyagammapoisson" begin
    # TODO add more basic tests
    d = PolyaGammaPoisson(2, 3.0, 3.0)
    @test length(d) == 2
end


text:
using Oceananigans.OutputReaders: FieldTimeSeries
using Statistics

import Oceananigans.Fields: interior
import Oceananigans.Grids: topology, interior_parent_indices

function variance(field)
    field_mean = mean(interior(field))
    variance = mean(f_ijk -> (f_ijk - field_mean)^2, interior(field))
    return variance
end

#=
function Base.iterate(field_time_series::FieldTimeSeries, state)
    if state >= length(field_time_series.times)
        return nothing
    else
        return field_time_series[state+1], state+1
    end
end
=#

mean_variance(field_time_series) = mean((variance(field_time_series[i]) for i = 1:length(field_time_series.times)))

#####
##### Normalization functionality for forward map output
#####

abstract type AbstractNormalization end

struct IdentityNormalization <: AbstractNormalization end

IdentityNormalization(field_time_series) = IdentityNormalization()

struct ZScore{T} <: AbstractNormalization
    μ :: T
    σ :: T
end

"""
    normalize!(field, normalization)

Normalize `field` using `normalization`.
"""
normalize!(field, ::IdentityNormalization) = nothing

function normalize!(field, normalization::ZScore)
    field .-= normalization.μ

    if normalization.σ != 0
        field ./= normalization.σ
    else
        @warn "Field data seems to be all zeros -- just saying."
    end

    return nothing
end

#=
"Returns a view of `f` that excludes halo points."
@inline interior(f::FieldTimeSeries{X, Y, Z}) where {X, Y, Z} =
    view(parent(f.data),
         interior_parent_indices(X, topology(f, 1), f.grid.Nx, f.grid.Hx),
         interior_parent_indices(Y, topology(f, 2), f.grid.Ny, f.grid.Hy),
         interior_parent_indices(Z, topology(f, 3), f.grid.Nz, f.grid.Hz),
         :)
=#

"""
    ZScore(field_time_series::FieldTimeSeries)

Return the `ZScore` normalization of a `FieldTimeSeries` after computing
its mean and its variance.
"""
function ZScore(field_time_series::FieldTimeSeries)
    μ = mean(interior(field_time_series))
    σ = sqrt(mean_variance(field_time_series))

    return ZScore(μ, σ)
end


text:
# Fast fitting for exact observations

# Expert loglik
# function _exact_expert_ll_pos_list(Y, model)
#     return [[hcat([expert_ll_pos.(model[j, k], fill(0.0, length(Y[:, j])), Y[:, j], Y[:, j], fill(Inf, length(Y[:, j]))) for k in 1:size(model)[2]]...) for j in 1:size(model)[1]]...]
# end

# function _exact_expert_ll_list(Y, model)
#     return [[hcat([expert_ll.(model[j, k], fill(0.0, length(Y[:, j])), Y[:, j], Y[:, j], fill(Inf, length(Y[:, j]))) for k in 1:size(model)[2]]...) for j in 1:size(model)[1]]...]
# end

# function loglik_exact(Y, gate, model)

#     # By dimension, then by component
#     # expert_ll_pos_dim_comp = expert_ll_pos_list(Y, model)
#     # expert_tn_pos_dim_comp = expert_tn_pos_list(Y, model)
#     # expert_tn_bar_pos_dim_comp = expert_tn_bar_pos_list(Y, model)

#     # expert_ll_dim_comp = expert_ll_list(Y, model)
#     # expert_tn_dim_comp = expert_tn_list(Y, model)
#     # expert_tn_bar_dim_comp = expert_tn_bar_list(Y, model)

#     expert_ll_pos_dim_comp = _exact_expert_ll_pos_list(Y, model)
#     expert_ll_dim_comp = _exact_expert_ll_list(Y, model)

#     # Aggregate by dimension
#     expert_ll_pos_comp = loglik_aggre_dim(expert_ll_pos_dim_comp)
#     # expert_tn_pos_comp = loglik_aggre_dim(expert_tn_pos_dim_comp)
#     # expert_tn_bar_pos_comp = loglik_aggre_dim(expert_tn_bar_pos_dim_comp)

#     expert_ll_comp = loglik_aggre_dim(expert_ll_dim_comp)
#     # expert_tn_comp = loglik_aggre_dim(expert_tn_dim_comp)
#     # expert_tn_bar_comp = loglik_aggre_dim(expert_tn_bar_dim_comp)

#     # Adding the gating function
#     gate_expert_ll_pos_comp = loglik_aggre_gate_dim(gate, expert_ll_pos_comp)
#     # gate_expert_tn_pos_comp = loglik_aggre_gate_dim(gate, expert_tn_pos_comp)
#     # gate_expert_tn_bar_pos_comp = gate + log1mexp.(expert_tn_pos_comp)
#     # gate_expert_tn_bar_pos_comp_k = gate + expert_tn_bar_pos_comp

#     gate_expert_ll_comp = loglik_aggre_gate_dim(gate, expert_ll_comp)
#     # gate_expert_tn_comp = loglik_aggre_gate_dim(gate, expert_tn_comp)
#     # gate_expert_tn_bar_comp = gate + log1mexp.(expert_tn_comp)
#     # gate_expert_tn_bar_comp_k = gate + log1mexp.(expert_tn_bar_comp)
#     # gate_expert_tn_bar_comp_z_lat = loglik_aggre_gate_dim(gate, expert_tn_bar_comp)

#     # Aggregate by component
#     gate_expert_ll_pos = loglik_aggre_gate_dim_comp(gate_expert_ll_pos_comp)
#     # gate_expert_tn_pos = loglik_aggre_gate_dim_comp(gate_expert_tn_pos_comp)
#     # gate_expert_tn_bar_pos = loglik_aggre_gate_dim_comp(gate_expert_tn_bar_pos_comp)
#     # gate_expert_tn_bar_pos_k = loglik_aggre_gate_dim_comp(gate_expert_tn_bar_pos_comp_k)

#     gate_expert_ll = loglik_aggre_gate_dim_comp(gate_expert_ll_comp)
#     # gate_expert_tn = loglik_aggre_gate_dim_comp(gate_expert_tn_comp)
#     # gate_expert_tn_bar = loglik_aggre_gate_dim_comp(gate_expert_tn_bar_comp)
#     # gate_expert_tn_bar_k = loglik_aggre_gate_dim_comp(gate_expert_tn_bar_comp_k)
#     # gate_expert_tn_bar_z_lat = loglik_aggre_gate_dim_comp(gate_expert_tn_bar_comp_z_lat)

#     # Normalize by tn & tn_bar
#     # norm_gate_expert_ll_pos = gate_expert_ll_pos - gate_expert_tn_pos
#     # norm_gate_expert_ll = gate_expert_ll - gate_expert_tn
#     norm_gate_expert_ll = gate_expert_ll #  - gate_expert_tn_bar_k

#     # Sum over all observations
#     ll = sum(norm_gate_expert_ll)

#     return (expert_ll_pos_dim_comp = expert_ll_pos_dim_comp,
#             # expert_tn_pos_dim_comp = expert_tn_pos_dim_comp,
#             # expert_tn_bar_pos_dim_comp = expert_tn_bar_pos_dim_comp,

#             expert_ll_dim_comp = expert_ll_dim_comp,
#             # expert_tn_dim_comp = expert_tn_dim_comp,
#             # expert_tn_bar_dim_comp = expert_tn_bar_dim_comp,

#             expert_ll_pos_comp = expert_ll_pos_comp,
#             # expert_tn_pos_comp = expert_tn_pos_comp,
#             # expert_tn_bar_pos_comp = expert_tn_bar_pos_comp,

#             expert_ll_comp = expert_ll_comp,
#             # expert_tn_comp = expert_tn_comp,
#             # expert_tn_bar_comp = expert_tn_bar_comp,

#             gate_expert_ll_pos_comp = gate_expert_ll_pos_comp,
#             # gate_expert_tn_pos_comp = gate_expert_tn_pos_comp,
#             # gate_expert_tn_bar_pos_comp = gate_expert_tn_bar_pos_comp,
#             # gate_expert_tn_bar_pos_comp_k = gate_expert_tn_bar_pos_comp_k,

#             gate_expert_ll_comp = gate_expert_ll_comp,
#             # gate_expert_tn_comp = gate_expert_tn_comp,
#             # gate_expert_tn_bar_comp = gate_expert_tn_bar_comp,
#             # gate_expert_tn_bar_comp_k = gate_expert_tn_bar_comp_k,
#             # gate_expert_tn_bar_comp_z_lat = gate_expert_tn_bar_comp_z_lat,

#             gate_expert_ll_pos = gate_expert_ll_pos,
#             # gate_expert_tn_pos = gate_expert_tn_pos,
#             # gate_expert_tn_bar_pos = gate_expert_tn_bar_pos,
#             # gate_expert_tn_bar_pos_k = gate_expert_tn_bar_pos_k,

#             gate_expert_ll = gate_expert_ll,
#             # gate_expert_tn = gate_expert_tn,
#             # gate_expert_tn_bar = gate_expert_tn_bar,
#             # gate_expert_tn_bar_k = gate_expert_tn_bar_k,
#             # gate_expert_tn_bar_z_lat = gate_expert_tn_bar_z_lat,

#             # norm_gate_expert_ll_pos = norm_gate_expert_ll_pos,
#             norm_gate_expert_ll = norm_gate_expert_ll,

#             ll = ll
#         )
# end

# function fit_exact(Y, X, α_init, model;
#                     penalty = true, pen_α = 5.0, pen_params = nothing,
#                     ϵ = 1e-03, α_iter_max = 5.0, ecm_iter_max = 200,
#                     grad_jump = true, grad_seq = nothing,
#                     print_steps = true)
#     # Make variables accessible within the scope of `let`
#     let α_em, gate_em, model_em, ll_em_list, ll_em, ll_em_np, ll_em_old, ll_em_np_old, iter, z_e_obs, z_e_lat, k_e, params_old
#         # Initial loglik
#         gate_init = LogitGating(α_init, X)
#         ll_np_list = loglik_exact(Y, gate_init, model)
#         ll_init_np = ll_np_list.ll
#         ll_penalty = penalty ? (penalty_α(α_init, pen_α) + penalty_params(model, pen_params)) : 0.0
#         ll_init = ll_init_np + ll_penalty

#         if print_steps
#             @info("Initial loglik: $(ll_init_np) (no penalty), $(ll_init) (with penalty)")
#         end

#         # start em
#         α_em = copy(α_init)
#         model_em = copy(model)
#         gate_em = LogitGating(α_em, X)
#         ll_em_list = loglik_exact(Y, gate_em, model_em)
#         ll_em_np = ll_em_list.ll
#         ll_em = ll_init
#         ll_em_old = -Inf
#         iter = 0

#         while (ll_em - ll_em_old > ϵ) & (iter < ecm_iter_max)
#             # Update counter and loglikelihood
#             iter = iter + 1
#             ll_em_np_old = ll_em_np
#             ll_em_old = ll_em

#             # E-Step
#             z_e_obs = exp.(ll_em_list.gate_expert_ll_comp .- ll_em_list.gate_expert_ll)
#             z_e_lat = fill(1/size(model)[2], size(X)[1], size(model)[2])
#             # k_e = EM_E_k(ll_em_list.gate_expert_tn)
#             k_e = fill(0.0, size(X)[1])

#             # M-Step: α
#             ll_em_temp = ll_em
#             α_em = EM_M_α(X, α_em, z_e_obs, z_e_lat, k_e, α_iter_max = α_iter_max, penalty = penalty, pen_α = pen_α)
#             gate_em = LogitGating(α_em, X)
#             ll_em_list = loglik_exact(Y, gate_em, model_em)
#             ll_em_np = ll_em_list.ll
#             ll_em_penalty = penalty ? (penalty_α(α_em, pen_α) + penalty_params(model_em, pen_params)) : 0.0
#             ll_em = ll_em_np + ll_em_penalty

#             s = ll_em - ll_em_temp > 0 ? "+" : "-"
#             pct = abs((ll_em - ll_em_temp) / ll_em_temp) * 100
#             if print_steps
#                 @info("Iteration $(iter), updating α: $(ll_em_old) ->  $(ll_em), ( $(s) $(pct) % )")
#             end
#             ll_em_temp = ll_em

#             # M-Step: component distributions
#             for j in 1:size(model)[1] # by dimension
#             # for j in 1:1
#                 for k in 1:size(model)[2] # by component

#                     params_old = params(model_em[j,k])

#                     model_em[j,k] = EM_M_expert_exact(model_em[j,k],
#                                                 Y[:, j],
#                                                 ll_em_list.expert_ll_pos_dim_comp[j][:,k],
#                                                 vec(z_e_obs[:,k]),
#                                                 penalty = penalty, pen_pararms_jk = pen_params[j][k])

#                     ll_em_list = loglik_exact(Y, gate_em, model_em)
#                     ll_em_np = ll_em_list.ll
#                     ll_em_penalty = penalty ? (penalty_α(α_em, pen_α) + penalty_params(model_em, pen_params)) : 0.0
#                     ll_em = ll_em_np + ll_em_penalty

#                     if print_steps
#                         s = ll_em - ll_em_temp > 0 ? "+" : "-"
#                         pct = abs((ll_em - ll_em_temp) / ll_em_temp) * 100
#                         @info("Iteration $(iter), updating model[$j, $k]: $(ll_em_temp) ->  $(ll_em), ( $(s) $(pct) % )")
#                         if s=="-" @info("Intended update of params: $(params_old) ->  $(params(model_em[j,k]))") end
#                     end
#                     ll_em_temp = ll_em
#                 end
#             end

#             α_em = α_em
#             model_em = model_em
#             gate_em = LogitGating(α_em, X)
#             ll_em_list = loglik_exact(Y, gate_em, model_em)
#             ll_em_np = ll_em_list.ll
#             ll_em_penalty = penalty ? (penalty_α(α_em, pen_α) + penalty_params(model_em, pen_params)) : 0.0
#             ll_em = ll_em_np + ll_em_penalty
#         end

#         converge = (ll_em - ll_em_old > ϵ) ? false : true
#         AIC = -2.0*ll_em_np + 2*(_count_α(α_em) + _count_params(model_em))
#         BIC = -2.0*ll_em_np + log(size(Y)[1])*(_count_α(α_em) + _count_params(model_em))

#         return (α_fit = α_em, model_fit = model_em,
#                 converge = converge, iter = iter,
#                 ll_np = ll_em_np, ll = ll_em,
#                 AIC = AIC, BIC = BIC)

#     end

# end

function loglik_exact(Y, gate, model)

    # By dimension, then by component
    expert_ll_dim_comp = expert_ll_list_exact(Y, model)

    # Aggregate by dimension
    expert_ll_comp = loglik_aggre_dim(expert_ll_dim_comp)

    # Adding the gating function
    gate_expert_ll_comp = loglik_aggre_gate_dim(gate, expert_ll_comp)

    # Aggregate by component
    gate_expert_ll = loglik_aggre_gate_dim_comp(gate_expert_ll_comp)

    # Normalize by tn & tn_bar
    norm_gate_expert_ll = gate_expert_ll

    # Sum over all observations
    ll = sum(norm_gate_expert_ll)

    return (expert_ll_dim_comp = expert_ll_dim_comp,
            expert_ll_comp = expert_ll_comp,
            gate_expert_ll_comp = gate_expert_ll_comp,
            gate_expert_ll = gate_expert_ll,
            norm_gate_expert_ll = norm_gate_expert_ll,
            ll = ll
        )
end

function fit_exact(Y, X, α_init, model;
                    exposure = nothing,
                    penalty = true, pen_α = 5.0, pen_params = nothing,
                    ϵ = 1e-03, α_iter_max = 5.0, ecm_iter_max = 200,
                    grad_jump = true, grad_seq = nothing,
                    print_steps = true)
    # Make variables accessible within the scope of `let`
    let α_em, gate_em, model_em, model_em_expo, ll_em_list, ll_em, ll_em_np, ll_em_old, ll_em_np_old, iter, z_e_obs, z_e_lat, k_e, params_old
        # Initial loglik
        gate_init = LogitGating(α_init, X)
        model_expo = exposurize_model(model, exposure = exposure) # exposurize
        ll_np_list = loglik_exact(Y, gate_init, model_expo)
        ll_init_np = ll_np_list.ll
        ll_penalty = penalty ? (penalty_α(α_init, pen_α) + penalty_params(model, pen_params)) : 0.0
        ll_init = ll_init_np + ll_penalty

        if print_steps
            @info("Initial loglik: $(ll_init_np) (no penalty), $(ll_init) (with penalty)")
        end

        # start em
        α_em = copy(α_init)
        model_em = copy(model)
        model_em_expo = exposurize_model(model_em, exposure = exposure)
        gate_em = LogitGating(α_em, X)
        ll_em_list = loglik_exact(Y, gate_em, model_em_expo)
        ll_em_np = ll_em_list.ll
        ll_em = ll_init
        ll_em_old = -Inf
        iter = 0

        while (ll_em - ll_em_old > ϵ) & (iter < ecm_iter_max)
            # Update counter and loglikelihood
            iter = iter + 1
            ll_em_np_old = ll_em_np
            ll_em_old = ll_em

            # E-Step
            z_e_obs = exp.(ll_em_list.gate_expert_ll_comp .- ll_em_list.gate_expert_ll)
            z_e_lat = fill(1/size(model)[2], size(X)[1], size(model)[2])
            # k_e = EM_E_k(ll_em_list.gate_expert_tn)
            k_e = fill(0.0, size(X)[1])

            # M-Step: α
            ll_em_temp = ll_em
            α_em = EM_M_α(X, α_em, z_e_obs, z_e_lat, k_e, α_iter_max = α_iter_max, penalty = penalty, pen_α = pen_α)
            gate_em = LogitGating(α_em, X)
            ll_em_list = loglik_exact(Y, gate_em, model_em_expo)
            ll_em_np = ll_em_list.ll
            ll_em_penalty = penalty ? (penalty_α(α_em, pen_α) + penalty_params(model_em, pen_params)) : 0.0
            ll_em = ll_em_np + ll_em_penalty

            s = ll_em - ll_em_temp > 0 ? "+" : "-"
            pct = abs((ll_em - ll_em_temp) / ll_em_temp) * 100
            if print_steps
                @info("Iteration $(iter), updating α: $(ll_em_old) ->  $(ll_em), ( $(s) $(pct) % )")
            end
            ll_em_temp = ll_em

            # M-Step: component distributions
            for j in 1:size(model)[1] # by dimension
            # for j in 1:1
                for k in 1:size(model)[2] # by component

                    params_old = params(model_em[j,k])

                    model_em[j,k] = EM_M_expert_exact(model_em[j,k],
                                                Y[:, j], exposure,
                                                # ll_em_list.expert_ll_pos_dim_comp[j][:,k],
                                                # ll_em_list.expert_ll_dim_comp[j, k, :],
                                                vec(z_e_obs[:,k]),
                                                penalty = penalty, pen_pararms_jk = pen_params[j][k])

                    model_em_expo = exposurize_model(model_em, exposure = exposure)
                    ll_em_list = loglik_exact(Y, gate_em, model_em_expo)
                    ll_em_np = ll_em_list.ll
                    ll_em_penalty = penalty ? (penalty_α(α_em, pen_α) + penalty_params(model_em, pen_params)) : 0.0
                    ll_em = ll_em_np + ll_em_penalty

                    if print_steps
                        s = ll_em - ll_em_temp > 0 ? "+" : "-"
                        pct = abs((ll_em - ll_em_temp) / ll_em_temp) * 100
                        @info("Iteration $(iter), updating model[$j, $k]: $(ll_em_temp) ->  $(ll_em), ( $(s) $(pct) % )")
                        if s=="-" @info("Intended update of params: $(params_old) ->  $(params(model_em[j,k]))") end
                    end
                    ll_em_temp = ll_em
                end
            end

            α_em = α_em
            model_em = model_em
            model_em_expo = exposurize_model(model_em, exposure = exposure)
            gate_em = LogitGating(α_em, X)
            ll_em_list = loglik_exact(Y, gate_em, model_em_expo)
            ll_em_np = ll_em_list.ll
            ll_em_penalty = penalty ? (penalty_α(α_em, pen_α) + penalty_params(model_em, pen_params)) : 0.0
            ll_em = ll_em_np + ll_em_penalty
        end

        converge = (ll_em - ll_em_old > ϵ) ? false : true
        AIC = -2.0*ll_em_np + 2*(_count_α(α_em) + _count_params(model_em))
        BIC = -2.0*ll_em_np + log(size(Y)[1])*(_count_α(α_em) + _count_params(model_em))

        return (α_fit = α_em, model_fit = model_em,
                converge = converge, iter = iter,
                ll_np = ll_em_np, ll = ll_em,
                AIC = AIC, BIC = BIC)

    end

end

max_stars_repo_name: sparktseung/JuliaLRMoE

max_issues_repo_name: sparktseung/JuliaLRMoE

max_forks_repo_name: sparktseung/JuliaLRMoE