about summary refs log tree commit diff stats
diff options
context:
space:
mode:
authorbptato <nincsnevem662@gmail.com>2021-03-12 20:52:44 +0100
committerbptato <nincsnevem662@gmail.com>2021-03-12 20:53:18 +0100
commit6084e104d5c3868196c9e2d3748c6627cf983470 (patch)
treef5103ffd0b33049e74b44f7a91448a22e3c8ef74
parent424f52e1f2eff84ee23a79b38e7932c2918351ed (diff)
downloadchawan-6084e104d5c3868196c9e2d3748c6627cf983470.tar.gz
Replaced parser, readline, etc...
-rw-r--r--.gitignore2
-rw-r--r--Makefile4
-rw-r--r--as5
-rw-r--r--buffer.nim48
-rw-r--r--config343
-rw-r--r--config.nim118
-rw-r--r--default.css0
-rw-r--r--display.nim122
-rw-r--r--dom.nim346
-rw-r--r--entity.json2233
-rw-r--r--enums.nim24
-rw-r--r--htmlelement.nim368
-rw-r--r--main.nim10
-rw-r--r--parser.nim865
-rw-r--r--radixtree.nim153
-rw-r--r--style.nim0
-rw-r--r--twtio.nim267
-rw-r--r--twtstr.nim99
18 files changed, 3824 insertions, 1183 deletions
diff --git a/.gitignore b/.gitignore
index 7dab9430..6197c0e1 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,4 +1,2 @@
 a
 twt
-dtwt
-twt_opt
diff --git a/Makefile b/Makefile
index 9d231e4a..74872b8a 100644
--- a/Makefile
+++ b/Makefile
@@ -1,9 +1,9 @@
 debug:
-	nim compile -d:ssl -o:dtwt main.nim
+	nim compile -d:ssl -o:twt main.nim
 release:
 	nim compile -d:release -d:ssl -o:twt main.nim
 release_opt:
-	nim compile -d:danger -d:ssl -o:twt_opt main.nim
+	nim compile -d:danger -d:ssl -o:twt main.nim
 clean:
 	rm ./twt ./dtwt ./twt_opt
 all: debug release release_opt
diff --git a/as b/as
new file mode 100644
index 00000000..1b43b788
--- /dev/null
+++ b/as
@@ -0,0 +1,5 @@
+<html>
+<body>
+Hello?
+</body>
+</html>
diff --git a/buffer.nim b/buffer.nim
index 1897cdc4..cb67c417 100644
--- a/buffer.nim
+++ b/buffer.nim
@@ -6,10 +6,8 @@ import tables
 import strutils
 import unicode
 
-import fusion/htmlparser/xmltree
-
 import termattrs
-import htmlelement
+import dom
 import twtio
 import enums
 import twtstr
@@ -21,21 +19,19 @@ type
     rawtext*: seq[string]
     title*: string
     hovertext*: string
-    htmlsource*: XmlNode
     width*: int
     height*: int
     cursorx*: int
     cursory*: int
-    cursorchar*: int
     xend*: int
     fromx*: int
     fromy*: int
-    nodes*: seq[HtmlNode]
-    links*: seq[HtmlNode]
-    clickables*: seq[HtmlNode]
+    nodes*: seq[Node]
+    links*: seq[Node]
+    clickables*: seq[Node]
     elements*: seq[HtmlElement]
     idelements*: Table[string, HtmlElement]
-    selectedlink*: HtmlNode
+    selectedlink*: Node
     printwrite*: bool
     attrs*: TermAttributes
     document*: Document
@@ -52,13 +48,11 @@ func lastLine*(buffer: Buffer): int =
 func lastVisibleLine*(buffer: Buffer): int =
   return min(buffer.fromy + buffer.height - 1, buffer.lastLine())
 
-func realCurrentLineLength*(buffer: Buffer): int =
-  return mk_wcswidth_cjk(buffer.rawtext[buffer.cursory])
-
 func currentLineLength*(buffer: Buffer): int =
-  return buffer.rawtext[buffer.cursory].runeLen()
+  return buffer.rawtext[buffer.cursory].width()
 
 func atPercentOf*(buffer: Buffer): int =
+  if buffer.fmttext.len == 0: return 100
   return (100 * (buffer.cursory + 1)) div (buffer.lastLine() + 1)
 
 func fmtBetween*(buffer: Buffer, sx: int, sy: int, ex: int, ey: int): string =
@@ -75,10 +69,10 @@ func fmtBetween*(buffer: Buffer, sx: int, sy: int, ex: int, ey: int): string =
 func visibleText*(buffer: Buffer): string = 
   return buffer.fmttext[buffer.fromy..buffer.lastVisibleLine()].join("\n")
 
-func lastNode*(buffer: Buffer): HtmlNode =
+func lastNode*(buffer: Buffer): Node =
   return buffer.nodes[^1]
 
-func cursorOnNode*(buffer: Buffer, node: HtmlNode): bool =
+func cursorOnNode*(buffer: Buffer, node: Node): bool =
   if node.y == node.ey and node.y == buffer.cursory:
     return buffer.cursorx >= node.x and buffer.cursorx < node.ex
   else:
@@ -103,17 +97,18 @@ func getElementById*(buffer: Buffer, id: string): HtmlElement =
     return buffer.idelements[id]
   return nil
 
-proc findSelectedNode*(buffer: Buffer): Option[HtmlNode] =
+proc findSelectedNode*(buffer: Buffer): Option[Node] =
   for node in buffer.nodes:
     if node.getFmtLen() > 0 and node.displayed():
       if buffer.cursory >= node.y and buffer.cursory <= node.y + node.height and buffer.cursorx >= node.x and buffer.cursorx <= node.x + node.width:
         return some(node)
-  return none(HtmlNode)
+  return none(Node)
 
-proc addNode*(buffer: Buffer, htmlNode: HtmlNode) =
+proc addNode*(buffer: Buffer, htmlNode: Node) =
   buffer.nodes.add(htmlNode)
 
-  if htmlNode.isTextNode() and htmlNode.parentElement != nil and htmlNode.parentElement.islink:
+  #TODO
+  if htmlNode.isTextNode() and htmlNode.parentElement != nil and HtmlElement(htmlNode.parentElement).islink:
     buffer.links.add(htmlNode)
 
   if htmlNode.isElemNode():
@@ -123,7 +118,8 @@ proc addNode*(buffer: Buffer, htmlNode: HtmlNode) =
         buffer.clickables.add(htmlNode)
     else: discard
   elif htmlNode.isTextNode():
-    if htmlNode.parentElement != nil and htmlNode.parentElement.islink:
+    #TODO
+    if htmlNode.parentElement != nil and HtmlElement(htmlNode.parentElement).islink:
       let anchor = htmlNode.ancestor(TAG_A)
       assert(anchor != nil)
       buffer.clickables.add(anchor)
@@ -216,9 +212,9 @@ proc cursorUp*(buffer: Buffer): bool =
     if buffer.cursorx > buffer.currentLineLength():
       if buffer.cursorx == 0:
         buffer.xend = buffer.cursorx
-      buffer.cursorx = buffer.currentLineLength()
+      buffer.cursorx = max(buffer.currentLineLength() - 1, 0)
     elif buffer.xend > 0:
-      buffer.cursorx = min(buffer.currentLineLength(), buffer.xend)
+      buffer.cursorx = min(buffer.currentLineLength() - 1, buffer.xend)
     if buffer.cursory < buffer.fromy:
       dec buffer.fromy
       return true
@@ -246,7 +242,7 @@ proc cursorLineEnd*(buffer: Buffer) =
   buffer.cursorx = buffer.currentLineLength() - 1
   buffer.xend = buffer.cursorx
 
-iterator revnodes*(buffer: Buffer): HtmlNode {.inline.} =
+iterator revnodes*(buffer: Buffer): Node {.inline.} =
   var i = buffer.nodes.len - 1
   while i >= 0:
     yield buffer.nodes[i]
@@ -303,7 +299,7 @@ proc cursorPrevWord*(buffer: Buffer): bool =
       x = buffer.rawtext[y].runeLen() - 1
   return buffer.cursorTo(x, y)
 
-iterator revclickables*(buffer: Buffer): HtmlNode {.inline.} =
+iterator revclickables*(buffer: Buffer): Node {.inline.} =
   var i = buffer.clickables.len - 1
   while i >= 0:
     yield buffer.clickables[i]
@@ -419,7 +415,7 @@ proc checkLinkSelection*(buffer: Buffer): bool =
       buffer.selectedlink.fmttext = buffer.selectedlink.getFmtText()
       buffer.selectedlink = nil
       buffer.hovertext = ""
-      var stack: seq[HtmlNode]
+      var stack: seq[Node]
       stack.add(anchor)
       while stack.len > 0:
         let elem = stack.pop()
@@ -433,7 +429,7 @@ proc checkLinkSelection*(buffer: Buffer): bool =
       assert(anchor != nil)
       anchor.selected = true
       buffer.hovertext = HtmlAnchorElement(anchor).href
-      var stack: seq[HtmlNode]
+      var stack: seq[Node]
       stack.add(anchor)
       while stack.len > 0:
         let elem = stack.pop()
diff --git a/config b/config
index a40c4e16..99397288 100644
--- a/config
+++ b/config
@@ -45,6 +45,7 @@ lemap C-m LINED_SUBMIT
 lemap C-j LINED_SUBMIT
 lemap C-h LINED_BACKSPACE
 lemap C-? LINED_BACKSPACE
+lemap C-d LINED_DELETE
 lemap C-c LINED_CANCEL
 lemap \eb LINED_PREV_WORD
 lemap \ef LINED_NEXT_WORD
@@ -54,3 +55,345 @@ lemap C-u LINED_CLEAR
 lemap C-k LINED_KILL
 lemap C-w LINED_KILL_WORD
 lemap C-v LINED_ESC
+lemap C-g LINED_COMPOSE_TOGGLE
+
+#compose keybindings
+
+#european
+comp a: ä
+comp o: ö
+comp u: ü
+comp u" ű
+comp o" ő
+
+comp a' á
+comp e' é
+comp i' í
+comp o' ó
+comp u' ú
+
+comp sS ß
+comp n~ ñ
+
+#hiragana
+comp a あ
+comp i い
+comp u う
+comp e え
+comp o お
+
+comp la ぁ
+comp li ぃ
+comp lu ぅ
+comp le ぇ
+comp lo ぉ
+
+comp ka か
+comp ki き
+comp ku く
+comp ke け
+comp ko こ
+
+comp kka っか
+comp kki っき
+comp kku っく
+comp kke っけ
+comp kko っこ
+
+comp sa さ
+comp shi し
+comp su す
+comp se せ
+comp so そ
+
+comp ssa っさ
+comp sshi っし
+comp ssu っす
+comp sse っせ
+comp sso っそ
+
+comp ta た
+comp chi ち
+comp tsu つ
+comp te て
+comp to と
+
+comp tta った
+comp tchi っち
+comp ttsu っつ
+comp tte って
+comp tto っと
+
+comp na な
+comp ni に
+comp nu ぬ
+comp ne ね
+comp no の
+
+comp ha は
+comp hi ひ
+comp fu ふ
+comp he へ
+comp ho ほ
+
+comp ma ま
+comp mi み
+comp mu む
+comp me め
+comp mo も
+
+comp ya や
+comp yu ゆ
+comp yo よ
+
+comp ra ら
+comp ri り
+comp ru る
+comp re れ
+comp ro ろ
+
+comp wa わ
+comp wi ゐ
+comp we ゑ
+comp wo を
+
+comp ga が
+comp gi ぎ
+comp gu ぐ
+comp ge げ
+comp go ご
+
+comp za ざ
+comp ji じ
+comp zu ず
+comp ze ぜ
+comp zo ぞ
+
+comp da だ
+comp dji ぢ
+comp dzu づ
+comp de で
+comp do ど
+
+comp ba ば
+comp bi び
+comp bu ぶ
+comp be べ
+comp bo ぼ
+
+comp pa ぱ
+comp pi ぴ
+comp pu ぷ
+comp pe ぺ
+comp po ぽ
+
+comp n ん
+
+comp kya きゃ
+comp kyu きゅ
+comp kyo きょ
+
+comp sha しゃ
+comp shu しゅ
+comp sho しょ
+
+comp cha ちゃ
+comp chu ちゅ
+comp cho ちょ
+
+comp nya にゃ
+comp nyu にゅ
+comp nyo にょ
+
+comp hya ひゃ
+comp hyu ひゅ
+comp hyo ひょ
+
+comp mya みゃ
+comp myu みゅ
+comp myo みょ
+
+comp rya りゃ
+comp ryu りゅ
+comp ryo りょ
+
+comp gya ぎゃ
+comp gyu ぎゅ
+comp gyo ぎょ
+
+comp ja じゃ
+comp ju じゅ
+comp jo じょ
+
+comp bya びゃ
+comp byu びゅ
+comp byo びょ
+
+comp pya ぴゃ
+comp pyu ぴゅ
+comp pyo ぴょ
+
+comp kwa くゎ
+comp gwa ぐゎ
+
+#katakana
+comp A ア
+comp I イ
+comp U ウ
+comp E エ
+comp O オ
+
+comp LA ォ
+comp LI ィ
+comp LU ゥ
+comp LE ェ
+comp LO ォ
+
+comp KA か
+comp KI き
+comp KU く
+comp KE け
+comp KO こ
+
+comp KKA ッカ
+comp KKI ッキ
+comp KKU ック
+comp KKE ッケ
+comp KKO ッコ
+
+comp SA サ
+comp SHI シ
+comp SU ス
+comp SE セ
+comp SO ソ
+
+comp SSA ッサ
+comp SSHI ッシ
+comp SSU ッス
+comp SSE ッセ
+comp SSO ッソ
+
+comp TA タ
+comp CHI チ
+comp TSU ツ
+comp TE テ
+comp TO ト
+
+comp TTA ッタ
+comp TCHI ッチ
+comp TTSU ッツ
+comp TTE ッテ
+comp TTO ッと
+
+comp NA ナ
+comp NI ニ
+comp NU ヌ
+comp NE ネ
+comp NO ノ
+
+comp HA ハ
+comp HI ヒ
+comp FU フ
+comp HE ヘ
+comp HO ホ
+
+comp MA マ
+comp MI ミ
+comp MU ム
+comp ME メ
+comp MO モ
+
+comp YA ヤ
+comp YU ユ
+comp YO ヨ
+
+comp RA ラ
+comp RI リ
+comp RU ル
+comp RE レ
+comp RO ロ
+
+comp WA ワ
+comp WI ヰ
+comp WE ヱ
+comp WO ヲ
+
+comp GA ガ
+comp GI ギ
+comp GU グ
+comp GE ゲ
+comp GO ゴ
+
+comp ZA ザ
+comp JI ジ
+comp ZU ズ
+comp ZE ゼ
+comp ZO ゾ
+
+comp DA ダ
+comp DJI ヂ
+comp DZU ヅ
+comp DE デ
+comp DO ド
+
+comp BA バ
+comp BI ビ
+comp BU ブ
+comp BE ベ
+comp BO ボ
+
+comp PA パ
+comp PI ピ
+comp PU プ
+comp PE ペ
+comp PO ポ
+
+comp N ン
+comp xc ー
+
+comp KYA キャ
+comp KYU キュ
+comp KYO キョ
+
+comp SHA シャ
+comp SHU シュ
+comp SHO ショ
+
+comp CHA チャ
+comp CHU チュ
+comp CHO チョ
+
+comp NYA ニャ
+comp NYU ニュ
+comp NYO ニョ
+
+comp HYA ヒャ
+comp HYU ヒュ
+comp HYO ヒョ
+
+comp MYA ミャ
+comp MYU ミュ
+comp MYO ミョ
+
+comp RYA リャ
+comp RYU リュ
+comp RYO リョ
+
+comp GYA ギャ
+comp GYU ギュ
+comp GYO ギョ
+
+comp JA ジャ
+comp JU ジュ
+comp JO ジョ
+
+comp BYA ビャ
+comp BYU ビュ
+comp BYO ビョ
+
+comp PYA ピャ
+comp PYU ピュ
+comp PYO ピョ
+
+comp KWA クヮ
+comp GWA グヮ
diff --git a/config.nim b/config.nim
index 0e7aa92b..6d185a1d 100644
--- a/config.nim
+++ b/config.nim
@@ -1,8 +1,8 @@
 import tables
 import strutils
-import macros
 
 import twtstr
+import radixtree
 
 type
   TwtAction* =
@@ -25,15 +25,21 @@ type
     ACTION_CURSOR_TOP, ACTION_CURSOR_MIDDLE, ACTION_CURSOR_BOTTOM,
     ACTION_CENTER_LINE, ACTION_LINE_INFO,
     ACTION_LINED_SUBMIT, ACTION_LINED_CANCEL,
-    ACTION_LINED_BACKSPACE, ACTION_LINED_CLEAR, ACTION_LINED_KILL, ACTION_LINED_KILL_WORD,
+    ACTION_LINED_BACKSPACE, ACTION_LINED_DELETE,
+    ACTION_LINED_CLEAR, ACTION_LINED_KILL, ACTION_LINED_KILL_WORD,
     ACTION_LINED_BACK, ACTION_LINED_FORWARD,
     ACTION_LINED_PREV_WORD, ACTION_LINED_NEXT_WORD,
+    ACTION_LINED_COMPOSE_TOGGLE, ACTION_LINED_COMPOSE_ON, ACTION_LINED_COMPOSE_OFF
     ACTION_LINED_ESC
 
-var normalActionRemap*: Table[string, TwtAction]
-var linedActionRemap*: Table[string, TwtAction]
+  ActionMap = Table[string, TwtAction]
+  ComposeMap = RadixTree[string]
 
-proc getRealKey(key: string): string =
+var normalActionRemap*: ActionMap
+var linedActionRemap*: ActionMap
+var composeRemap*: ComposeMap
+
+func getRealKey(key: string): string =
   var realk: string
   var currchar: char
   var control = 0
@@ -64,9 +70,9 @@ proc getRealKey(key: string): string =
     realk &= 'C'
   return realk
 
-proc constructActionTable*(origTable: var Table[string, TwtAction]): Table[string, TwtAction] =
-  var newTable: Table[string, TwtAction]
-  var strs = newSeq[string](0)
+func constructActionTable*(origTable: ActionMap): ActionMap =
+  var newTable: ActionMap
+  var strs: seq[string]
   for k in origTable.keys:
     let realk = getRealKey(k)
     var teststr = ""
@@ -74,7 +80,7 @@ proc constructActionTable*(origTable: var Table[string, TwtAction]): Table[strin
       teststr &= c
       strs.add(teststr)
 
-  for k, v in origTable.mpairs:
+  for k, v in origTable:
     let realk = getRealKey(k)
     var teststr = ""
     for c in realk:
@@ -84,84 +90,50 @@ proc constructActionTable*(origTable: var Table[string, TwtAction]): Table[strin
     newTable[realk] = v
   return newTable
 
-macro staticReadKeymap(): untyped =
-  var config = staticRead"config"
-  var normalActionMap: Table[string, TwtAction]
-  var linedActionMap: Table[string, TwtAction]
+proc parseConfigLine(line: string, nmap: var ActionMap, lemap: var ActionMap,
+                     compose: var ComposeMap) =
+  if line.len == 0 or line[0] == '#':
+    return
+  let cmd = line.split(' ')
+  if cmd.len == 3:
+    if cmd[0] == "nmap":
+      nmap[getRealKey(cmd[1])] = parseEnum[TwtAction]("ACTION_" & cmd[2])
+    elif cmd[0] == "lemap":
+      lemap[getRealKey(cmd[1])] = parseEnum[TwtAction]("ACTION_" & cmd[2])
+    elif cmd[0] == "comp":
+      compose[getRealKey(cmd[1])] = cmd[2]
+
+proc staticReadKeymap(): (ActionMap, ActionMap, ComposeMap) =
+  let config = staticRead"config"
+  var nmap: ActionMap
+  var lemap: ActionMap
+  var compose = newRadixTree[string]()
   for line in config.split('\n'):
-    if line.len == 0 or line[0] == '#':
-      continue
-    let cmd = line.split(' ')
-    if cmd.len == 3:
-      if cmd[0] == "nmap":
-        normalActionMap[getRealKey(cmd[1])] = parseEnum[TwtAction]("ACTION_" & cmd[2])
-      elif cmd[0] == "lemap":
-        linedActionMap[getRealKey(cmd[1])] = parseEnum[TwtAction]("ACTION_" & cmd[2])
-
-  normalActionMap = constructActionTable(normalActionMap)
-  linedActionMap = constructActionTable(linedActionMap)
-
-  let normalActionConstr = nnkTableConstr.newTree()
-  for k, v in normalActionMap:
-    let colonExpr = nnkExprColonExpr.newTree()
-    colonExpr.add(newLit(k))
-    colonExpr.add(newLit(v))
-    normalActionConstr.add(colonExpr)
+    parseConfigLine(line, nmap, lemap, compose)
 
-  let normalActionAsgn = nnkAsgn.newTree()
-  normalActionAsgn.add(ident("normalActionRemap"))
-  normalActionAsgn.add(newCall(ident("toTable"), normalActionConstr))
+  nmap = constructActionTable(nmap)
+  lemap = constructActionTable(lemap)
+  return (nmap, lemap, compose)
 
-  let linedActionConstr = nnkTableConstr.newTree()
-  for k, v in linedActionMap:
-    let colonExpr = nnkExprColonExpr.newTree()
-    colonExpr.add(newLit(k))
-    colonExpr.add(newLit(v))
-    linedActionConstr.add(colonExpr)
+const (normalActionMap, linedActionMap, composeMap) = staticReadKeymap()
 
-  let linedActionAsgn = nnkAsgn.newTree()
-  linedActionAsgn.add(ident("linedActionRemap"))
-  linedActionAsgn.add(newCall(ident("toTable"), linedActionConstr))
-  result = newStmtList()
-  result.add(normalActionAsgn)
-
-staticReadKeymap()
+normalActionRemap = normalActionMap
+linedActionRemap = linedActionMap
+composeRemap = composeMap
 
 proc readConfig*(filename: string): bool =
   var f: File
   let status = f.open(filename, fmRead)
-  var normalActionMap: Table[string, TwtAction]
-  var linedActionMap: Table[string, TwtAction]
+  var nmap: ActionMap
+  var lemap: ActionMap
+  var compose = newRadixTree[string]()
   if status:
     var line: TaintedString
     while f.readLine(line):
-      if line.string.len == 0 or line.string[0] == '#':
-        continue
-      let cmd = line.split(' ')
-      if cmd.len == 3:
-        if cmd[0] == "nmap":
-          normalActionMap[getRealKey(cmd[1])] = parseEnum[TwtAction]("ACTION_" & cmd[2])
-        elif cmd[0] == "lemap":
-          linedActionMap[getRealKey(cmd[1])] = parseEnum[TwtAction]("ACTION_" & cmd[2])
+      parseConfigLine(line, nmap, lemap, compose)
 
     normalActionRemap = constructActionTable(normalActionMap)
     linedActionRemap = constructActionTable(linedActionMap)
     return true
   else:
     return false
-
-proc parseKeymap*(keymap: string) =
-  var normalActionMap: Table[string, TwtAction]
-  var linedActionMap: Table[string, TwtAction]
-  for line in keymap.split('\n'):
-    if line.len == 0 or line[0] == '#':
-      continue
-    let cmd = line.split(' ')
-    if cmd.len == 3:
-      if cmd[0] == "nmap":
-        normalActionMap[getRealKey(cmd[1])] = parseEnum[TwtAction]("ACTION_" & cmd[2])
-      elif cmd[0] == "lemap":
-        linedActionMap[getRealKey(cmd[1])] = parseEnum[TwtAction]("ACTION_" & cmd[2])
-
-  normalActionRemap = constructActionTable(normalActionMap)
-  linedActionRemap = constructActionTable(linedActionMap)
diff --git a/default.css b/default.css
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/default.css
diff --git a/display.nim b/display.nim
index 4bf15921..ff5e63f1 100644
--- a/display.nim
+++ b/display.nim
@@ -4,11 +4,9 @@ import uri
 import strutils
 import unicode
 
-import fusion/htmlparser/xmltree
-
 import buffer
 import termattrs
-import htmlelement
+import dom
 import twtstr
 import twtio
 import config
@@ -29,7 +27,7 @@ type
     lastwidth: int
     fmtline: string
     rawline: string
-    centerqueue: seq[HtmlNode]
+    centerqueue: seq[Node]
     centerlen: int
     blanklines: int
     blankspaces: int
@@ -39,7 +37,7 @@ type
     listval: int
 
 func newRenderState(): RenderState =
-  return RenderState()
+  return RenderState(blanklines: 1)
 
 proc write(state: var RenderState, s: string) =
   state.fmtline &= s
@@ -69,20 +67,21 @@ proc addSpaces(buffer: Buffer, state: var RenderState, n: int) =
   state.write(' '.repeat(n))
   state.x += n
 
-proc writeWrappedText(buffer: Buffer, state: var RenderState, node: HtmlNode) =
+proc writeWrappedText(buffer: Buffer, state: var RenderState, node: Node) =
   state.lastwidth = 0
   var n = 0
   var fmtword = ""
   var rawword = ""
   var prevl = false
-  for w in node.fmttext:
+  let fmttext = node.getFmtText()
+  for w in fmttext:
     if w.len > 0 and w[0] == '\e':
       fmtword &= w
       continue
 
     for r in w.runes:
       if r == Rune(' '):
-        if rawword[0] == ' ' and prevl: #first byte can't fool comparison to ascii
+        if rawword.len > 0 and rawword[0] == ' ' and prevl: #first byte can't fool comparison to ascii
           fmtword = fmtword.substr(1)
           rawword = rawword.substr(1)
           state.x -= 1
@@ -91,15 +90,21 @@ proc writeWrappedText(buffer: Buffer, state: var RenderState, node: HtmlNode) =
         fmtword = ""
         rawword = ""
 
-      fmtword &= r
-      rawword &= r
+      if r == Rune('\n'):
+        state.write(fmtword, rawword)
+        buffer.flushLine(state)
+        rawword = ""
+        fmtword = ""
+      else:
+        fmtword &= r
+        rawword &= r
 
-      state.x += mk_wcwidth_cjk(r)
+      state.x += r.width()
 
       if state.x >= buffer.width:
         state.lastwidth = max(state.lastwidth, state.x)
         buffer.flushLine(state)
-        state.x = mk_wcswidth_cjk(rawword)
+        state.x = rawword.width()
         prevl = true
       else:
         state.lastwidth = max(state.lastwidth, state.x)
@@ -108,20 +113,21 @@ proc writeWrappedText(buffer: Buffer, state: var RenderState, node: HtmlNode) =
 
   state.write(fmtword, rawword)
   if prevl:
-    state.x += mk_wcswidth_cjk(rawword)
+    state.x += rawword.width()
     prevl = false
 
   state.lastwidth = max(state.lastwidth, state.x)
 
-proc preAlignNode(buffer: Buffer, node: HtmlNode, state: var RenderState) =
+proc preAlignNode(buffer: Buffer, node: Node, state: var RenderState) =
   let elem = node.nodeAttr()
-  if state.rawline.len > 0 and node.openblock and state.blanklines == 0:
+  if state.rawline.len > 0 and node.firstNode() and state.blanklines == 0:
     buffer.flushLine(state)
 
-  if node.openblock:
-    while state.blanklines < max(elem.margin, elem.margintop):
+  if node.firstNode():
+    while state.blanklines < max(node.parentElement.margin, node.parentElement.margintop):
       buffer.flushLine(state)
-    state.indent += elem.indent
+    if elem.parentNode.nodeType == ELEMENT_NODE:
+      state.indent += elem.parentElement.indent
 
   if state.rawline.len > 0 and state.blanklines == 0 and node.displayed():
     buffer.addSpaces(state, state.nextspaces)
@@ -134,7 +140,8 @@ proc preAlignNode(buffer: Buffer, node: HtmlNode, state: var RenderState) =
     state.centerlen = 0
   
   if node.isElemNode() and elem.display == DISPLAY_LIST_ITEM and state.indent > 0:
-    buffer.flushLine(state)
+    if state.blanklines == 0:
+      buffer.flushLine(state)
     var listchar = ""
     case elem.parentElement.tagType
     of TAG_UL:
@@ -149,7 +156,7 @@ proc preAlignNode(buffer: Buffer, node: HtmlNode, state: var RenderState) =
     state.x += listchar.runeLen()
     buffer.addSpaces(state, 1)
 
-proc postAlignNode(buffer: Buffer, node: HtmlNode, state: var RenderState) =
+proc postAlignNode(buffer: Buffer, node: Node, state: var RenderState) =
   let elem = node.nodeAttr()
 
   if node.getRawLen() > 0:
@@ -158,25 +165,32 @@ proc postAlignNode(buffer: Buffer, node: HtmlNode, state: var RenderState) =
 
   if state.rawline.len > 0 and state.blanklines == 0:
     state.nextspaces += max(elem.margin, elem.marginright)
-    if node.closeblock and (node.isTextNode() or elem.childNodes.len == 0):
-      buffer.flushLine(state)
+    #if node.lastNode() and (node.isTextNode() or elem.childNodes.len == 0):
+    #  buffer.flushLine(state)
 
-  if node.closeblock:
-    while state.blanklines < max(elem.margin, elem.marginbottom):
+  if node.lastNode():
+    while state.blanklines < max(node.parentElement.margin, node.parentElement.marginbottom):
       buffer.flushLine(state)
-    if node.isElemNode():
-      state.indent -= elem.indent
+    if elem.parentElement != nil:
+      state.indent -= elem.parentElement.indent
 
-  if elem.tagType == TAG_BR and not node.openblock:
+  if elem.tagType == TAG_BR and not node.firstNode():
     buffer.flushLine(state)
 
-proc renderNode(buffer: Buffer, node: HtmlNode, state: var RenderState) =
-  if node.isDocument():
+  if elem.display == DISPLAY_LIST_ITEM and node.lastNode():
+    buffer.flushLine(state)
+
+proc renderNode(buffer: Buffer, node: Node, state: var RenderState) =
+  if not (node.nodeType in {ELEMENT_NODE, TEXT_NODE}):
+    return
+  if node.parentNode.nodeType != ELEMENT_NODE:
     return
   let elem = node.nodeAttr()
+  if elem.tagType in {TAG_SCRIPT, TAG_STYLE, TAG_NOSCRIPT}:
+    return
   if elem.tagType == TAG_TITLE:
     if node.isTextNode():
-      buffer.title = node.rawtext
+      buffer.title = node.getRawText()
     return
   else: discard
   if elem.hidden: return
@@ -184,7 +198,7 @@ proc renderNode(buffer: Buffer, node: HtmlNode, state: var RenderState) =
   if not state.docenter:
     if elem.centered:
       state.centerqueue.add(node)
-      if node.closeblock or elem.tagType == TAG_BR:
+      if node.lastNode() or elem.tagType == TAG_BR:
         state.docenter = true
         state.centerlen = 0
         for node in state.centerqueue:
@@ -218,54 +232,12 @@ proc renderNode(buffer: Buffer, node: HtmlNode, state: var RenderState) =
 
   buffer.postAlignNode(node, state)
 
-iterator revItems*(n: XmlNode): XmlNode {.inline.} =
-  var i = n.len - 1
-  while i >= 0:
-    if n[i].kind != xnComment:
-      yield n[i]
-    i -= 1
-
-type
-  XmlHtmlNode* = ref XmlHtmlNodeObj
-  XmlHtmlNodeObj = object
-    xml*: XmlNode
-    html*: HtmlNode
-
 proc setLastHtmlLine(buffer: Buffer, state: var RenderState) =
   if state.rawline.len != 0:
     buffer.flushLine(state)
 
 proc renderHtml*(buffer: Buffer) =
-  var stack: seq[XmlHtmlNode]
-  let first = XmlHtmlNode(xml: buffer.htmlSource,
-                         html: getHtmlNode(buffer.htmlSource, buffer.document))
-  stack.add(first)
-
-  var state = newRenderState()
-  while stack.len > 0:
-    let currElem = stack.pop()
-    buffer.addNode(currElem.html)
-    buffer.renderNode(currElem.html, state)
-    if currElem.xml.len > 0:
-      var last = false
-      for item in currElem.xml.revItems:
-        let child = XmlHtmlNode(xml: item,
-                                html: getHtmlNode(item, currElem.html))
-        stack.add(child)
-        currElem.html.childNodes.add(child.html)
-        if not last and not child.html.hidden:
-          last = true
-          if HtmlElement(currElem.html).display == DISPLAY_BLOCK:
-            eprint "elem", HtmlElement(currElem.html).tagType, "close @", child.html.nodeAttr().tagType
-            stack[^1].html.closeblock = true
-      if last:
-        eprint "elem", HtmlElement(currElem.html).tagType, "open @", stack[^1].html.nodeAttr().tagType
-        if HtmlElement(currElem.html).display == DISPLAY_BLOCK:
-          stack[^1].html.openblock = true
-  buffer.setLastHtmlLine(state)
-
-proc nrenderHtml*(buffer: Buffer) =
-  var stack: seq[HtmlNode]
+  var stack: seq[Node]
   let first = buffer.document
   stack.add(first)
 
@@ -372,7 +344,7 @@ proc inputLoop(attrs: TermAttributes, buffer: Buffer): bool =
         buffer.setLocation(parseUri(url))
         return true
     of ACTION_LINE_INFO:
-      statusMsg("line " & $buffer.cursory & "/" & $buffer.lastLine() & " col " & $buffer.cursorx & "/" & $buffer.realCurrentLineLength(), buffer.width)
+      statusMsg("line " & $buffer.cursory & "/" & $buffer.lastLine() & " col " & $(buffer.cursorx + 1) & "/" & $buffer.currentLineLength(), buffer.width)
       nostatus = true
     of ACTION_FEED_NEXT:
       feedNext = true
diff --git a/dom.nim b/dom.nim
new file mode 100644
index 00000000..b018e0f2
--- /dev/null
+++ b/dom.nim
@@ -0,0 +1,346 @@
+import terminal
+import uri
+import unicode
+import strutils
+import tables
+
+import twtstr
+import twtio
+import enums
+
+type
+  EventTarget* = ref EventTargetObj
+  EventTargetObj = object of RootObj
+
+  Node* = ref NodeObj
+  NodeObj = object of EventTargetObj
+    nodeType*: NodeType
+    childNodes*: seq[Node]
+    firstChild*: Node
+    isConnected*: bool
+    lastChild*: Node
+    nextSibling*: Node
+    previousSibling*: Node
+    parentNode*: Node
+    parentElement*: Element
+    ownerDocument*: Document
+
+    rawtext*: string
+    fmttext*: seq[string]
+    x*: int
+    y*: int
+    ex*: int
+    ey*: int
+    width*: int
+    height*: int
+    hidden*: bool
+
+  Attr* = ref AttrObj
+  AttrObj = object of NodeObj
+    namespaceURI*: string
+    prefix*: string
+    localName*: string
+    name*: string
+    value*: string
+    ownerElement*: Element
+
+  Document* = ref DocumentObj
+  DocumentObj = object of NodeObj
+    location*: Uri
+
+  CharacterData* = ref CharacterDataObj
+  CharacterDataObj = object of NodeObj
+    data*: string
+    length*: int
+
+  Text* = ref TextObj
+  TextObj = object of CharacterDataObj
+    wholeText*: string
+
+  Comment* = ref CommentObj
+  CommentObj = object of CharacterDataObj
+
+  Element* = ref ElementObj
+  ElementObj = object of NodeObj
+    namespaceURI*: string
+    prefix*: string
+    localName*: string
+    tagName*: string
+    tagType*: TagType
+
+    id*: string
+    classList*: seq[string]
+    attributes*: Table[string, Attr]
+
+    margintop*: int
+    marginbottom*: int
+    marginleft*: int
+    marginright*: int
+    margin*: int
+    centered*: bool
+    display*: DisplayType
+    innerText*: string
+    bold*: bool
+    italic*: bool
+    underscore*: bool
+    islink*: bool
+    selected*: bool
+    numChildNodes*: int
+    indent*: int
+
+  HTMLElement* = ref HTMLElementObj
+  HTMLElementObj = object of ElementObj
+
+  HTMLInputElement* = ref HTMLInputElementObj
+  HTMLInputElementObj = object of HTMLElementObj
+    itype*: InputType
+    autofocus*: bool
+    required*: bool
+    value*: string
+    size*: int
+
+  HTMLAnchorElement* = ref HTMLAnchorElementObj
+  HTMLAnchorElementObj = object of HTMLElementObj
+    href*: string
+
+  HTMLSelectElement* = ref HTMLSelectElementObj
+  HTMLSelectElementObj = object of HTMLElementObj
+    name*: string
+    value*: string
+    valueSet*: bool
+
+  HTMLOptionElement* = ref HTMLOptionElementObj
+  HTMLOptionElementObj = object of HTMLElementObj
+    value*: string
+  
+  HTMLHeadingElement* = ref HTMLHeadingElementObj
+  HTMLHeadingElementObj = object of HTMLElementObj
+    rank*: uint16
+
+  HTMLBRElement* = ref HTMLBRElementObj
+  HTMLBRElementObj = object of HTMLElementObj
+
+
+func getTagTypeMap(): Table[string, TagType] =
+  for i in low(TagType) .. high(TagType):
+    let enumname = $TagType(i)
+    let tagname = enumname.split('_')[1..^1].join("_").tolower()
+    result[tagname] = TagType(i)
+
+func getInputTypeMap(): Table[string, InputType] =
+  for i in low(InputType) .. high(InputType):
+    let enumname = $InputType(i)
+    let tagname = enumname.split('_')[1..^1].join("_").tolower()
+    result[tagname] = InputType(i)
+
+const tagTypeMap = getTagTypeMap()
+const inputTypeMap = getInputTypeMap()
+
+func tagType*(s: string): TagType =
+  if tagTypeMap.hasKey(s):
+    return tagTypeMap[s]
+  else:
+    return TAG_UNKNOWN
+
+func inputType*(s: string): InputType =
+  if inputTypeMap.hasKey(s):
+    return inputTypeMap[s]
+  else:
+    return INPUT_UNKNOWN
+
+#TODO
+func nodeAttr*(node: Node): HtmlElement =
+  case node.nodeType
+  of TEXT_NODE: return HtmlElement(node.parentElement)
+  of ELEMENT_NODE: return HtmlElement(node)
+  else: assert(false)
+
+func displayed*(node: Node): bool =
+  return node.rawtext.len > 0 and node.nodeAttr().display != DISPLAY_NONE
+
+func isTextNode*(node: Node): bool =
+  return node.nodeType == TEXT_NODE
+
+func isElemNode*(node: Node): bool =
+  return node.nodeType == ELEMENT_NODE
+
+func isComment*(node: Node): bool =
+  return node.nodeType == COMMENT_NODE
+
+func isCData*(node: Node): bool =
+  return node.nodeType == CDATA_SECTION_NODE
+
+func isDocument*(node: Node): bool =
+  return node.nodeType == DOCUMENT_NODE
+
+func getFmtLen*(htmlNode: Node): int =
+  return htmlNode.fmttext.join().runeLen()
+
+func getRawLen*(htmlNode: Node): int =
+  return htmlNode.rawtext.runeLen()
+
+func firstNode*(htmlNode: Node): bool =
+  return htmlNode.parentElement != nil and htmlNode.parentElement.childNodes[0] == htmlNode
+
+func lastNode*(htmlNode: Node): bool =
+  return htmlNode.parentElement != nil and htmlNode.parentElement.childNodes[^1] == htmlNode
+
+func toInputType*(str: string): InputType =
+  case str
+  of "button": INPUT_BUTTON
+  of "checkbox": INPUT_CHECKBOX
+  of "color": INPUT_COLOR
+  of "date": INPUT_DATE
+  of "datetime_local": INPUT_DATETIME_LOCAL
+  of "email": INPUT_EMAIL
+  of "file": INPUT_FILE
+  of "hidden": INPUT_HIDDEN
+  of "image": INPUT_IMAGE
+  of "month": INPUT_MONTH
+  of "number": INPUT_NUMBER
+  of "password": INPUT_PASSWORD
+  of "radio": INPUT_RADIO
+  of "range": INPUT_RANGE
+  of "reset": INPUT_RESET
+  of "search": INPUT_SEARCH
+  of "submit": INPUT_SUBMIT
+  of "tel": INPUT_TEL
+  of "text": INPUT_TEXT
+  of "time": INPUT_TIME
+  of "url": INPUT_URL
+  of "week": INPUT_WEEK
+  else: INPUT_UNKNOWN
+
+func toInputSize*(str: string): int =
+  if str.len == 0:
+    return 20
+  for c in str:
+    if not c.isDigit:
+      return 20
+  return str.parseInt()
+
+func getFmtInput(inputElement: HtmlInputElement): seq[string] =
+  case inputElement.itype
+  of INPUT_TEXT, INPUT_SEARCH:
+    let valueFit = fitValueToSize(inputElement.value, inputElement.size)
+    return valueFit.ansiStyle(styleUnderscore).ansiReset().buttonFmt()
+  of INPUT_SUBMIT:
+    return inputElement.value.buttonFmt()
+  else: discard
+
+func getRawInput(inputElement: HtmlInputElement): string =
+  case inputElement.itype
+  of INPUT_TEXT, INPUT_SEARCH:
+    return inputElement.value.fitValueToSize(inputElement.size).buttonRaw()
+  of INPUT_SUBMIT:
+    return inputElement.value.buttonRaw()
+  else: discard
+
+#TODO
+func ancestor*(htmlNode: Node, tagType: TagType): HtmlElement =
+  result = HtmlElement(htmlNode.parentElement)
+  while result != nil and result.tagType != tagType:
+    result = HtmlElement(result.parentElement)
+
+func displayWhitespace*(htmlElem: HtmlElement): bool =
+  return htmlElem.display == DISPLAY_INLINE or htmlElem.display == DISPLAY_INLINE_BLOCK
+
+proc getRawText*(htmlNode: Node): string =
+  if htmlNode.isElemNode():
+    case HtmlElement(htmlNode).tagType
+    of TAG_INPUT: return HtmlInputElement(htmlNode).getRawInput()
+    else: return ""
+  elif htmlNode.isTextNode():
+    let chardata = CharacterData(htmlNode)
+    if htmlNode.parentElement != nil and htmlNode.parentElement.tagType != TAG_PRE:
+      result = chardata.data.remove("\n")
+      if unicode.strip(result).runeLen() > 0:
+        if htmlNode.nodeAttr().display != DISPLAY_INLINE:
+          if htmlNode.previousSibling == nil or htmlNode.previousSibling.nodeAttr().displayWhitespace():
+            result = unicode.strip(result, true, false)
+          if htmlNode.nextSibling == nil or htmlNode.nextSibling.nodeAttr().displayWhitespace():
+            result = unicode.strip(result, false, true)
+      else:
+        result = ""
+    else:
+      result = unicode.strip(chardata.data)
+    if htmlNode.parentElement != nil and htmlNode.parentElement.tagType == TAG_OPTION:
+      result = result.buttonRaw()
+  else:
+    assert(false)
+
+func getFmtText*(htmlNode: Node): seq[string] =
+  if htmlNode.isElemNode():
+    case HtmlElement(htmlNode).tagType
+    of TAG_INPUT: return HtmlInputElement(htmlNode).getFmtInput()
+    else: return @[]
+  elif htmlNode.isTextNode():
+    let chardata = CharacterData(htmlNode)
+    result &= chardata.data
+    if htmlNode.parentElement != nil:
+      #TODO
+      if HtmlElement(htmlNode.parentElement).islink:
+        result = result.ansiFgColor(fgBlue).ansiReset()
+        let anchor = htmlNode.ancestor(TAG_A)
+        if anchor != nil and anchor.selected:
+          result = result.ansiStyle(styleUnderscore).ansiReset()
+
+      if htmlNode.parentElement.tagType == TAG_OPTION:
+        result = result.ansiFgColor(fgRed).ansiReset()
+
+      #TODO
+      if HtmlElement(htmlNode.parentElement).bold:
+        result = result.ansiStyle(styleBright).ansiReset()
+      if HtmlElement(htmlNode.parentElement).italic:
+        result = result.ansiStyle(styleItalic).ansiReset()
+      if HtmlElement(htmlNode.parentElement).underscore:
+        result = result.ansiStyle(styleUnderscore).ansiReset()
+    else:
+      assert(false, "Uhhhh I'm pretty sure we should have parent elements for text nodes?" & htmlNode.rawtext)
+  else:
+    assert(false)
+
+func newDocument*(): Document =
+  new(result)
+  result.nodeType = DOCUMENT_NODE
+
+func newText*(): Text =
+  new(result)
+  result.nodeType = TEXT_NODE
+
+func newComment*(): Comment =
+  new(result)
+  result.nodeType = COMMENT_NODE
+
+func newHtmlElement*(tagType: TagType): HTMLElement =
+  case tagType
+  of TAG_INPUT:
+    result = new(HTMLInputElement)
+  of TAG_A:
+    result = new(HTMLAnchorElement)
+  of TAG_SELECT:
+    result = new(HTMLSelectElement)
+  of TAG_OPTION:
+    result = new(HTMLOptionElement)
+  of TAG_H1, TAG_H2, TAG_H3, TAG_H4, TAG_H5, TAG_H6:
+    result = new(HTMLHeadingElement)
+  of TAG_BR:
+    result = new(HTMLBRElement)
+  else:
+    new(result)
+
+  result.nodeType = ELEMENT_NODE
+  result.tagType = tagType
+
+func newAttr*(parent: Element, key: string, value: string): Attr =
+  new(result)
+  result.nodeType = ATTRIBUTE_NODE
+  result.ownerElement = parent
+  result.name = key
+  result.value = value
+
+func getAttrValue*(element: Element, s: string): string =
+  let attr = element.attributes.getOrDefault(s, nil)
+  if attr != nil:
+    return attr.value
+  return ""
diff --git a/entity.json b/entity.json
new file mode 100644
index 00000000..557170b4
--- /dev/null
+++ b/entity.json
@@ -0,0 +1,2233 @@
+{
+  "&AElig": { "codepoints": [198], "characters": "\u00C6" },
+  "&AElig;": { "codepoints": [198], "characters": "\u00C6" },
+  "&AMP": { "codepoints": [38], "characters": "\u0026" },
+  "&AMP;": { "codepoints": [38], "characters": "\u0026" },
+  "&Aacute": { "codepoints": [193], "characters": "\u00C1" },
+  "&Aacute;": { "codepoints": [193], "characters": "\u00C1" },
+  "&Abreve;": { "codepoints": [258], "characters": "\u0102" },
+  "&Acirc": { "codepoints": [194], "characters": "\u00C2" },
+  "&Acirc;": { "codepoints": [194], "characters": "\u00C2" },
+  "&Acy;": { "codepoints": [1040], "characters": "\u0410" },
+  "&Afr;": { "codepoints": [120068], "characters": "\uD835\uDD04" },
+  "&Agrave": { "codepoints": [192], "characters": "\u00C0" },
+  "&Agrave;": { "codepoints": [192], "characters": "\u00C0" },
+  "&Alpha;": { "codepoints": [913], "characters": "\u0391" },
+  "&Amacr;": { "codepoints": [256], "characters": "\u0100" },
+  "&And;": { "codepoints": [10835], "characters": "\u2A53" },
+  "&Aogon;": { "codepoints": [260], "characters": "\u0104" },
+  "&Aopf;": { "codepoints": [120120], "characters": "\uD835\uDD38" },
+  "&ApplyFunction;": { "codepoints": [8289], "characters": "\u2061" },
+  "&Aring": { "codepoints": [197], "characters": "\u00C5" },
+  "&Aring;": { "codepoints": [197], "characters": "\u00C5" },
+  "&Ascr;": { "codepoints": [119964], "characters": "\uD835\uDC9C" },
+  "&Assign;": { "codepoints": [8788], "characters": "\u2254" },
+  "&Atilde": { "codepoints": [195], "characters": "\u00C3" },
+  "&Atilde;": { "codepoints": [195], "characters": "\u00C3" },
+  "&Auml": { "codepoints": [196], "characters": "\u00C4" },
+  "&Auml;": { "codepoints": [196], "characters": "\u00C4" },
+  "&Backslash;": { "codepoints": [8726], "characters": "\u2216" },
+  "&Barv;": { "codepoints": [10983], "characters": "\u2AE7" },
+  "&Barwed;": { "codepoints": [8966], "characters": "\u2306" },
+  "&Bcy;": { "codepoints": [1041], "characters": "\u0411" },
+  "&Because;": { "codepoints": [8757], "characters": "\u2235" },
+  "&Bernoullis;": { "codepoints": [8492], "characters": "\u212C" },
+  "&Beta;": { "codepoints": [914], "characters": "\u0392" },
+  "&Bfr;": { "codepoints": [120069], "characters": "\uD835\uDD05" },
+  "&Bopf;": { "codepoints": [120121], "characters": "\uD835\uDD39" },
+  "&Breve;": { "codepoints": [728], "characters": "\u02D8" },
+  "&Bscr;": { "codepoints": [8492], "characters": "\u212C" },
+  "&Bumpeq;": { "codepoints": [8782], "characters": "\u224E" },
+  "&CHcy;": { "codepoints": [1063], "characters": "\u0427" },
+  "&COPY": { "codepoints": [169], "characters": "\u00A9" },
+  "&COPY;": { "codepoints": [169], "characters": "\u00A9" },
+  "&Cacute;": { "codepoints": [262], "characters": "\u0106" },
+  "&Cap;": { "codepoints": [8914], "characters": "\u22D2" },
+  "&CapitalDifferentialD;": { "codepoints": [8517], "characters": "\u2145" },
+  "&Cayleys;": { "codepoints": [8493], "characters": "\u212D" },
+  "&Ccaron;": { "codepoints": [268], "characters": "\u010C" },
+  "&Ccedil": { "codepoints": [199], "characters": "\u00C7" },
+  "&Ccedil;": { "codepoints": [199], "characters": "\u00C7" },
+  "&Ccirc;": { "codepoints": [264], "characters": "\u0108" },
+  "&Cconint;": { "codepoints": [8752], "characters": "\u2230" },
+  "&Cdot;": { "codepoints": [266], "characters": "\u010A" },
+  "&Cedilla;": { "codepoints": [184], "characters": "\u00B8" },
+  "&CenterDot;": { "codepoints": [183], "characters": "\u00B7" },
+  "&Cfr;": { "codepoints": [8493], "characters": "\u212D" },
+  "&Chi;": { "codepoints": [935], "characters": "\u03A7" },
+  "&CircleDot;": { "codepoints": [8857], "characters": "\u2299" },
+  "&CircleMinus;": { "codepoints": [8854], "characters": "\u2296" },
+  "&CirclePlus;": { "codepoints": [8853], "characters": "\u2295" },
+  "&CircleTimes;": { "codepoints": [8855], "characters": "\u2297" },
+  "&ClockwiseContourIntegral;": { "codepoints": [8754], "characters": "\u2232" },
+  "&CloseCurlyDoubleQuote;": { "codepoints": [8221], "characters": "\u201D" },
+  "&CloseCurlyQuote;": { "codepoints": [8217], "characters": "\u2019" },
+  "&Colon;": { "codepoints": [8759], "characters": "\u2237" },
+  "&Colone;": { "codepoints": [10868], "characters": "\u2A74" },
+  "&Congruent;": { "codepoints": [8801], "characters": "\u2261" },
+  "&Conint;": { "codepoints": [8751], "characters": "\u222F" },
+  "&ContourIntegral;": { "codepoints": [8750], "characters": "\u222E" },
+  "&Copf;": { "codepoints": [8450], "characters": "\u2102" },
+  "&Coproduct;": { "codepoints": [8720], "characters": "\u2210" },
+  "&CounterClockwiseContourIntegral;": { "codepoints": [8755], "characters": "\u2233" },
+  "&Cross;": { "codepoints": [10799], "characters": "\u2A2F" },
+  "&Cscr;": { "codepoints": [119966], "characters": "\uD835\uDC9E" },
+  "&Cup;": { "codepoints": [8915], "characters": "\u22D3" },
+  "&CupCap;": { "codepoints": [8781], "characters": "\u224D" },
+  "&DD;": { "codepoints": [8517], "characters": "\u2145" },
+  "&DDotrahd;": { "codepoints": [10513], "characters": "\u2911" },
+  "&DJcy;": { "codepoints": [1026], "characters": "\u0402" },
+  "&DScy;": { "codepoints": [1029], "characters": "\u0405" },
+  "&DZcy;": { "codepoints": [1039], "characters": "\u040F" },
+  "&Dagger;": { "codepoints": [8225], "characters": "\u2021" },
+  "&Darr;": { "codepoints": [8609], "characters": "\u21A1" },
+  "&Dashv;": { "codepoints": [10980], "characters": "\u2AE4" },
+  "&Dcaron;": { "codepoints": [270], "characters": "\u010E" },
+  "&Dcy;": { "codepoints": [1044], "characters": "\u0414" },
+  "&Del;": { "codepoints": [8711], "characters": "\u2207" },
+  "&Delta;": { "codepoints": [916], "characters": "\u0394" },
+  "&Dfr;": { "codepoints": [120071], "characters": "\uD835\uDD07" },
+  "&DiacriticalAcute;": { "codepoints": [180], "characters": "\u00B4" },
+  "&DiacriticalDot;": { "codepoints": [729], "characters": "\u02D9" },
+  "&DiacriticalDoubleAcute;": { "codepoints": [733], "characters": "\u02DD" },
+  "&DiacriticalGrave;": { "codepoints": [96], "characters": "\u0060" },
+  "&DiacriticalTilde;": { "codepoints": [732], "characters": "\u02DC" },
+  "&Diamond;": { "codepoints": [8900], "characters": "\u22C4" },
+  "&DifferentialD;": { "codepoints": [8518], "characters": "\u2146" },
+  "&Dopf;": { "codepoints": [120123], "characters": "\uD835\uDD3B" },
+  "&Dot;": { "codepoints": [168], "characters": "\u00A8" },
+  "&DotDot;": { "codepoints": [8412], "characters": "\u20DC" },
+  "&DotEqual;": { "codepoints": [8784], "characters": "\u2250" },
+  "&DoubleContourIntegral;": { "codepoints": [8751], "characters": "\u222F" },
+  "&DoubleDot;": { "codepoints": [168], "characters": "\u00A8" },
+  "&DoubleDownArrow;": { "codepoints": [8659], "characters": "\u21D3" },
+  "&DoubleLeftArrow;": { "codepoints": [8656], "characters": "\u21D0" },
+  "&DoubleLeftRightArrow;": { "codepoints": [8660], "characters": "\u21D4" },
+  "&DoubleLeftTee;": { "codepoints": [10980], "characters": "\u2AE4" },
+  "&DoubleLongLeftArrow;": { "codepoints": [10232], "characters": "\u27F8" },
+  "&DoubleLongLeftRightArrow;": { "codepoints": [10234], "characters": "\u27FA" },
+  "&DoubleLongRightArrow;": { "codepoints": [10233], "characters": "\u27F9" },
+  "&DoubleRightArrow;": { "codepoints": [8658], "characters": "\u21D2" },
+  "&DoubleRightTee;": { "codepoints": [8872], "characters": "\u22A8" },
+  "&DoubleUpArrow;": { "codepoints": [8657], "characters": "\u21D1" },
+  "&DoubleUpDownArrow;": { "codepoints": [8661], "characters": "\u21D5" },
+  "&DoubleVerticalBar;": { "codepoints": [8741], "characters": "\u2225" },
+  "&DownArrow;": { "codepoints": [8595], "characters": "\u2193" },
+  "&DownArrowBar;": { "codepoints": [10515], "characters": "\u2913" },
+  "&DownArrowUpArrow;": { "codepoints": [8693], "characters": "\u21F5" },
+  "&DownBreve;": { "codepoints": [785], "characters": "\u0311" },
+  "&DownLeftRightVector;": { "codepoints": [10576], "characters": "\u2950" },
+  "&DownLeftTeeVector;": { "codepoints": [10590], "characters": "\u295E" },
+  "&DownLeftVector;": { "codepoints": [8637], "characters": "\u21BD" },
+  "&DownLeftVectorBar;": { "codepoints": [10582], "characters": "\u2956" },
+  "&DownRightTeeVector;": { "codepoints": [10591], "characters": "\u295F" },
+  "&DownRightVector;": { "codepoints": [8641], "characters": "\u21C1" },
+  "&DownRightVectorBar;": { "codepoints": [10583], "characters": "\u2957" },
+  "&DownTee;": { "codepoints": [8868], "characters": "\u22A4" },
+  "&DownTeeArrow;": { "codepoints": [8615], "characters": "\u21A7" },
+  "&Downarrow;": { "codepoints": [8659], "characters": "\u21D3" },
+  "&Dscr;": { "codepoints": [119967], "characters": "\uD835\uDC9F" },
+  "&Dstrok;": { "codepoints": [272], "characters": "\u0110" },
+  "&ENG;": { "codepoints": [330], "characters": "\u014A" },
+  "&ETH": { "codepoints": [208], "characters": "\u00D0" },
+  "&ETH;": { "codepoints": [208], "characters": "\u00D0" },
+  "&Eacute": { "codepoints": [201], "characters": "\u00C9" },
+  "&Eacute;": { "codepoints": [201], "characters": "\u00C9" },
+  "&Ecaron;": { "codepoints": [282], "characters": "\u011A" },
+  "&Ecirc": { "codepoints": [202], "characters": "\u00CA" },
+  "&Ecirc;": { "codepoints": [202], "characters": "\u00CA" },
+  "&Ecy;": { "codepoints": [1069], "characters": "\u042D" },
+  "&Edot;": { "codepoints": [278], "characters": "\u0116" },
+  "&Efr;": { "codepoints": [120072], "characters": "\uD835\uDD08" },
+  "&Egrave": { "codepoints": [200], "characters": "\u00C8" },
+  "&Egrave;": { "codepoints": [200], "characters": "\u00C8" },
+  "&Element;": { "codepoints": [8712], "characters": "\u2208" },
+  "&Emacr;": { "codepoints": [274], "characters": "\u0112" },
+  "&EmptySmallSquare;": { "codepoints": [9723], "characters": "\u25FB" },
+  "&EmptyVerySmallSquare;": { "codepoints": [9643], "characters": "\u25AB" },
+  "&Eogon;": { "codepoints": [280], "characters": "\u0118" },
+  "&Eopf;": { "codepoints": [120124], "characters": "\uD835\uDD3C" },
+  "&Epsilon;": { "codepoints": [917], "characters": "\u0395" },
+  "&Equal;": { "codepoints": [10869], "characters": "\u2A75" },
+  "&EqualTilde;": { "codepoints": [8770], "characters": "\u2242" },
+  "&Equilibrium;": { "codepoints": [8652], "characters": "\u21CC" },
+  "&Escr;": { "codepoints": [8496], "characters": "\u2130" },
+  "&Esim;": { "codepoints": [10867], "characters": "\u2A73" },
+  "&Eta;": { "codepoints": [919], "characters": "\u0397" },
+  "&Euml": { "codepoints": [203], "characters": "\u00CB" },
+  "&Euml;": { "codepoints": [203], "characters": "\u00CB" },
+  "&Exists;": { "codepoints": [8707], "characters": "\u2203" },
+  "&ExponentialE;": { "codepoints": [8519], "characters": "\u2147" },
+  "&Fcy;": { "codepoints": [1060], "characters": "\u0424" },
+  "&Ffr;": { "codepoints": [120073], "characters": "\uD835\uDD09" },
+  "&FilledSmallSquare;": { "codepoints": [9724], "characters": "\u25FC" },
+  "&FilledVerySmallSquare;": { "codepoints": [9642], "characters": "\u25AA" },
+  "&Fopf;": { "codepoints": [120125], "characters": "\uD835\uDD3D" },
+  "&ForAll;": { "codepoints": [8704], "characters": "\u2200" },
+  "&Fouriertrf;": { "codepoints": [8497], "characters": "\u2131" },
+  "&Fscr;": { "codepoints": [8497], "characters": "\u2131" },
+  "&GJcy;": { "codepoints": [1027], "characters": "\u0403" },
+  "&GT": { "codepoints": [62], "characters": "\u003E" },
+  "&GT;": { "codepoints": [62], "characters": "\u003E" },
+  "&Gamma;": { "codepoints": [915], "characters": "\u0393" },
+  "&Gammad;": { "codepoints": [988], "characters": "\u03DC" },
+  "&Gbreve;": { "codepoints": [286], "characters": "\u011E" },
+  "&Gcedil;": { "codepoints": [290], "characters": "\u0122" },
+  "&Gcirc;": { "codepoints": [284], "characters": "\u011C" },
+  "&Gcy;": { "codepoints": [1043], "characters": "\u0413" },
+  "&Gdot;": { "codepoints": [288], "characters": "\u0120" },
+  "&Gfr;": { "codepoints": [120074], "characters": "\uD835\uDD0A" },
+  "&Gg;": { "codepoints": [8921], "characters": "\u22D9" },
+  "&Gopf;": { "codepoints": [120126], "characters": "\uD835\uDD3E" },
+  "&GreaterEqual;": { "codepoints": [8805], "characters": "\u2265" },
+  "&GreaterEqualLess;": { "codepoints": [8923], "characters": "\u22DB" },
+  "&GreaterFullEqual;": { "codepoints": [8807], "characters": "\u2267" },
+  "&GreaterGreater;": { "codepoints": [10914], "characters": "\u2AA2" },
+  "&GreaterLess;": { "codepoints": [8823], "characters": "\u2277" },
+  "&GreaterSlantEqual;": { "codepoints": [10878], "characters": "\u2A7E" },
+  "&GreaterTilde;": { "codepoints": [8819], "characters": "\u2273" },
+  "&Gscr;": { "codepoints": [119970], "characters": "\uD835\uDCA2" },
+  "&Gt;": { "codepoints": [8811], "characters": "\u226B" },
+  "&HARDcy;": { "codepoints": [1066], "characters": "\u042A" },
+  "&Hacek;": { "codepoints": [711], "characters": "\u02C7" },
+  "&Hat;": { "codepoints": [94], "characters": "\u005E" },
+  "&Hcirc;": { "codepoints": [292], "characters": "\u0124" },
+  "&Hfr;": { "codepoints": [8460], "characters": "\u210C" },
+  "&HilbertSpace;": { "codepoints": [8459], "characters": "\u210B" },
+  "&Hopf;": { "codepoints": [8461], "characters": "\u210D" },
+  "&HorizontalLine;": { "codepoints": [9472], "characters": "\u2500" },
+  "&Hscr;": { "codepoints": [8459], "characters": "\u210B" },
+  "&Hstrok;": { "codepoints": [294], "characters": "\u0126" },
+  "&HumpDownHump;": { "codepoints": [8782], "characters": "\u224E" },
+  "&HumpEqual;": { "codepoints": [8783], "characters": "\u224F" },
+  "&IEcy;": { "codepoints": [1045], "characters": "\u0415" },
+  "&IJlig;": { "codepoints": [306], "characters": "\u0132" },
+  "&IOcy;": { "codepoints": [1025], "characters": "\u0401" },
+  "&Iacute": { "codepoints": [205], "characters": "\u00CD" },
+  "&Iacute;": { "codepoints": [205], "characters": "\u00CD" },
+  "&Icirc": { "codepoints": [206], "characters": "\u00CE" },
+  "&Icirc;": { "codepoints": [206], "characters": "\u00CE" },
+  "&Icy;": { "codepoints": [1048], "characters": "\u0418" },
+  "&Idot;": { "codepoints": [304], "characters": "\u0130" },
+  "&Ifr;": { "codepoints": [8465], "characters": "\u2111" },
+  "&Igrave": { "codepoints": [204], "characters": "\u00CC" },
+  "&Igrave;": { "codepoints": [204], "characters": "\u00CC" },
+  "&Im;": { "codepoints": [8465], "characters": "\u2111" },
+  "&Imacr;": { "codepoints": [298], "characters": "\u012A" },
+  "&ImaginaryI;": { "codepoints": [8520], "characters": "\u2148" },
+  "&Implies;": { "codepoints": [8658], "characters": "\u21D2" },
+  "&Int;": { "codepoints": [8748], "characters": "\u222C" },
+  "&Integral;": { "codepoints": [8747], "characters": "\u222B" },
+  "&Intersection;": { "codepoints": [8898], "characters": "\u22C2" },
+  "&InvisibleComma;": { "codepoints": [8291], "characters": "\u2063" },
+  "&InvisibleTimes;": { "codepoints": [8290], "characters": "\u2062" },
+  "&Iogon;": { "codepoints": [302], "characters": "\u012E" },
+  "&Iopf;": { "codepoints": [120128], "characters": "\uD835\uDD40" },
+  "&Iota;": { "codepoints": [921], "characters": "\u0399" },
+  "&Iscr;": { "codepoints": [8464], "characters": "\u2110" },
+  "&Itilde;": { "codepoints": [296], "characters": "\u0128" },
+  "&Iukcy;": { "codepoints": [1030], "characters": "\u0406" },
+  "&Iuml": { "codepoints": [207], "characters": "\u00CF" },
+  "&Iuml;": { "codepoints": [207], "characters": "\u00CF" },
+  "&Jcirc;": { "codepoints": [308], "characters": "\u0134" },
+  "&Jcy;": { "codepoints": [1049], "characters": "\u0419" },
+  "&Jfr;": { "codepoints": [120077], "characters": "\uD835\uDD0D" },
+  "&Jopf;": { "codepoints": [120129], "characters": "\uD835\uDD41" },
+  "&Jscr;": { "codepoints": [119973], "characters": "\uD835\uDCA5" },
+  "&Jsercy;": { "codepoints": [1032], "characters": "\u0408" },
+  "&Jukcy;": { "codepoints": [1028], "characters": "\u0404" },
+  "&KHcy;": { "codepoints": [1061], "characters": "\u0425" },
+  "&KJcy;": { "codepoints": [1036], "characters": "\u040C" },
+  "&Kappa;": { "codepoints": [922], "characters": "\u039A" },
+  "&Kcedil;": { "codepoints": [310], "characters": "\u0136" },
+  "&Kcy;": { "codepoints": [1050], "characters": "\u041A" },
+  "&Kfr;": { "codepoints": [120078], "characters": "\uD835\uDD0E" },
+  "&Kopf;": { "codepoints": [120130], "characters": "\uD835\uDD42" },
+  "&Kscr;": { "codepoints": [119974], "characters": "\uD835\uDCA6" },
+  "&LJcy;": { "codepoints": [1033], "characters": "\u0409" },
+  "&LT": { "codepoints": [60], "characters": "\u003C" },
+  "&LT;": { "codepoints": [60], "characters": "\u003C" },
+  "&Lacute;": { "codepoints": [313], "characters": "\u0139" },
+  "&Lambda;": { "codepoints": [923], "characters": "\u039B" },
+  "&Lang;": { "codepoints": [10218], "characters": "\u27EA" },
+  "&Laplacetrf;": { "codepoints": [8466], "characters": "\u2112" },
+  "&Larr;": { "codepoints": [8606], "characters": "\u219E" },
+  "&Lcaron;": { "codepoints": [317], "characters": "\u013D" },
+  "&Lcedil;": { "codepoints": [315], "characters": "\u013B" },
+  "&Lcy;": { "codepoints": [1051], "characters": "\u041B" },
+  "&LeftAngleBracket;": { "codepoints": [10216], "characters": "\u27E8" },
+  "&LeftArrow;": { "codepoints": [8592], "characters": "\u2190" },
+  "&LeftArrowBar;": { "codepoints": [8676], "characters": "\u21E4" },
+  "&LeftArrowRightArrow;": { "codepoints": [8646], "characters": "\u21C6" },
+  "&LeftCeiling;": { "codepoints": [8968], "characters": "\u2308" },
+  "&LeftDoubleBracket;": { "codepoints": [10214], "characters": "\u27E6" },
+  "&LeftDownTeeVector;": { "codepoints": [10593], "characters": "\u2961" },
+  "&LeftDownVector;": { "codepoints": [8643], "characters": "\u21C3" },
+  "&LeftDownVectorBar;": { "codepoints": [10585], "characters": "\u2959" },
+  "&LeftFloor;": { "codepoints": [8970], "characters": "\u230A" },
+  "&LeftRightArrow;": { "codepoints": [8596], "characters": "\u2194" },
+  "&LeftRightVector;": { "codepoints": [10574], "characters": "\u294E" },
+  "&LeftTee;": { "codepoints": [8867], "characters": "\u22A3" },
+  "&LeftTeeArrow;": { "codepoints": [8612], "characters": "\u21A4" },
+  "&LeftTeeVector;": { "codepoints": [10586], "characters": "\u295A" },
+  "&LeftTriangle;": { "codepoints": [8882], "characters": "\u22B2" },
+  "&LeftTriangleBar;": { "codepoints": [10703], "characters": "\u29CF" },
+  "&LeftTriangleEqual;": { "codepoints": [8884], "characters": "\u22B4" },
+  "&LeftUpDownVector;": { "codepoints": [10577], "characters": "\u2951" },
+  "&LeftUpTeeVector;": { "codepoints": [10592], "characters": "\u2960" },
+  "&LeftUpVector;": { "codepoints": [8639], "characters": "\u21BF" },
+  "&LeftUpVectorBar;": { "codepoints": [10584], "characters": "\u2958" },
+  "&LeftVector;": { "codepoints": [8636], "characters": "\u21BC" },
+  "&LeftVectorBar;": { "codepoints": [10578], "characters": "\u2952" },
+  "&Leftarrow;": { "codepoints": [8656], "characters": "\u21D0" },
+  "&Leftrightarrow;": { "codepoints": [8660], "characters": "\u21D4" },
+  "&LessEqualGreater;": { "codepoints": [8922], "characters": "\u22DA" },
+  "&LessFullEqual;": { "codepoints": [8806], "characters": "\u2266" },
+  "&LessGreater;": { "codepoints": [8822], "characters": "\u2276" },
+  "&LessLess;": { "codepoints": [10913], "characters": "\u2AA1" },
+  "&LessSlantEqual;": { "codepoints": [10877], "characters": "\u2A7D" },
+  "&LessTilde;": { "codepoints": [8818], "characters": "\u2272" },
+  "&Lfr;": { "codepoints": [120079], "characters": "\uD835\uDD0F" },
+  "&Ll;": { "codepoints": [8920], "characters": "\u22D8" },
+  "&Lleftarrow;": { "codepoints": [8666], "characters": "\u21DA" },
+  "&Lmidot;": { "codepoints": [319], "characters": "\u013F" },
+  "&LongLeftArrow;": { "codepoints": [10229], "characters": "\u27F5" },
+  "&LongLeftRightArrow;": { "codepoints": [10231], "characters": "\u27F7" },
+  "&LongRightArrow;": { "codepoints": [10230], "characters": "\u27F6" },
+  "&Longleftarrow;": { "codepoints": [10232], "characters": "\u27F8" },
+  "&Longleftrightarrow;": { "codepoints": [10234], "characters": "\u27FA" },
+  "&Longrightarrow;": { "codepoints": [10233], "characters": "\u27F9" },
+  "&Lopf;": { "codepoints": [120131], "characters": "\uD835\uDD43" },
+  "&LowerLeftArrow;": { "codepoints": [8601], "characters": "\u2199" },
+  "&LowerRightArrow;": { "codepoints": [8600], "characters": "\u2198" },
+  "&Lscr;": { "codepoints": [8466], "characters": "\u2112" },
+  "&Lsh;": { "codepoints": [8624], "characters": "\u21B0" },
+  "&Lstrok;": { "codepoints": [321], "characters": "\u0141" },
+  "&Lt;": { "codepoints": [8810], "characters": "\u226A" },
+  "&Map;": { "codepoints": [10501], "characters": "\u2905" },
+  "&Mcy;": { "codepoints": [1052], "characters": "\u041C" },
+  "&MediumSpace;": { "codepoints": [8287], "characters": "\u205F" },
+  "&Mellintrf;": { "codepoints": [8499], "characters": "\u2133" },
+  "&Mfr;": { "codepoints": [120080], "characters": "\uD835\uDD10" },
+  "&MinusPlus;": { "codepoints": [8723], "characters": "\u2213" },
+  "&Mopf;": { "codepoints": [120132], "characters": "\uD835\uDD44" },
+  "&Mscr;": { "codepoints": [8499], "characters": "\u2133" },
+  "&Mu;": { "codepoints": [924], "characters": "\u039C" },
+  "&NJcy;": { "codepoints": [1034], "characters": "\u040A" },
+  "&Nacute;": { "codepoints": [323], "characters": "\u0143" },
+  "&Ncaron;": { "codepoints": [327], "characters": "\u0147" },
+  "&Ncedil;": { "codepoints": [325], "characters": "\u0145" },
+  "&Ncy;": { "codepoints": [1053], "characters": "\u041D" },
+  "&NegativeMediumSpace;": { "codepoints": [8203], "characters": "\u200B" },
+  "&NegativeThickSpace;": { "codepoints": [8203], "characters": "\u200B" },
+  "&NegativeThinSpace;": { "codepoints": [8203], "characters": "\u200B" },
+  "&NegativeVeryThinSpace;": { "codepoints": [8203], "characters": "\u200B" },
+  "&NestedGreaterGreater;": { "codepoints": [8811], "characters": "\u226B" },
+  "&NestedLessLess;": { "codepoints": [8810], "characters": "\u226A" },
+  "&NewLine;": { "codepoints": [10], "characters": "\u000A" },
+  "&Nfr;": { "codepoints": [120081], "characters": "\uD835\uDD11" },
+  "&NoBreak;": { "codepoints": [8288], "characters": "\u2060" },
+  "&NonBreakingSpace;": { "codepoints": [160], "characters": "\u00A0" },
+  "&Nopf;": { "codepoints": [8469], "characters": "\u2115" },
+  "&Not;": { "codepoints": [10988], "characters": "\u2AEC" },
+  "&NotCongruent;": { "codepoints": [8802], "characters": "\u2262" },
+  "&NotCupCap;": { "codepoints": [8813], "characters": "\u226D" },
+  "&NotDoubleVerticalBar;": { "codepoints": [8742], "characters": "\u2226" },
+  "&NotElement;": { "codepoints": [8713], "characters": "\u2209" },
+  "&NotEqual;": { "codepoints": [8800], "characters": "\u2260" },
+  "&NotEqualTilde;": { "codepoints": [8770, 824], "characters": "\u2242\u0338" },
+  "&NotExists;": { "codepoints": [8708], "characters": "\u2204" },
+  "&NotGreater;": { "codepoints": [8815], "characters": "\u226F" },
+  "&NotGreaterEqual;": { "codepoints": [8817], "characters": "\u2271" },
+  "&NotGreaterFullEqual;": { "codepoints": [8807, 824], "characters": "\u2267\u0338" },
+  "&NotGreaterGreater;": { "codepoints": [8811, 824], "characters": "\u226B\u0338" },
+  "&NotGreaterLess;": { "codepoints": [8825], "characters": "\u2279" },
+  "&NotGreaterSlantEqual;": { "codepoints": [10878, 824], "characters": "\u2A7E\u0338" },
+  "&NotGreaterTilde;": { "codepoints": [8821], "characters": "\u2275" },
+  "&NotHumpDownHump;": { "codepoints": [8782, 824], "characters": "\u224E\u0338" },
+  "&NotHumpEqual;": { "codepoints": [8783, 824], "characters": "\u224F\u0338" },
+  "&NotLeftTriangle;": { "codepoints": [8938], "characters": "\u22EA" },
+  "&NotLeftTriangleBar;": { "codepoints": [10703, 824], "characters": "\u29CF\u0338" },
+  "&NotLeftTriangleEqual;": { "codepoints": [8940], "characters": "\u22EC" },
+  "&NotLess;": { "codepoints": [8814], "characters": "\u226E" },
+  "&NotLessEqual;": { "codepoints": [8816], "characters": "\u2270" },
+  "&NotLessGreater;": { "codepoints": [8824], "characters": "\u2278" },
+  "&NotLessLess;": { "codepoints": [8810, 824], "characters": "\u226A\u0338" },
+  "&NotLessSlantEqual;": { "codepoints": [10877, 824], "characters": "\u2A7D\u0338" },
+  "&NotLessTilde;": { "codepoints": [8820], "characters": "\u2274" },
+  "&NotNestedGreaterGreater;": { "codepoints": [10914, 824], "characters": "\u2AA2\u0338" },
+  "&NotNestedLessLess;": { "codepoints": [10913, 824], "characters": "\u2AA1\u0338" },
+  "&NotPrecedes;": { "codepoints": [8832], "characters": "\u2280" },
+  "&NotPrecedesEqual;": { "codepoints": [10927, 824], "characters": "\u2AAF\u0338" },
+  "&NotPrecedesSlantEqual;": { "codepoints": [8928], "characters": "\u22E0" },
+  "&NotReverseElement;": { "codepoints": [8716], "characters": "\u220C" },
+  "&NotRightTriangle;": { "codepoints": [8939], "characters": "\u22EB" },
+  "&NotRightTriangleBar;": { "codepoints": [10704, 824], "characters": "\u29D0\u0338" },
+  "&NotRightTriangleEqual;": { "codepoints": [8941], "characters": "\u22ED" },
+  "&NotSquareSubset;": { "codepoints": [8847, 824], "characters": "\u228F\u0338" },
+  "&NotSquareSubsetEqual;": { "codepoints": [8930], "characters": "\u22E2" },
+  "&NotSquareSuperset;": { "codepoints": [8848, 824], "characters": "\u2290\u0338" },
+  "&NotSquareSupersetEqual;": { "codepoints": [8931], "characters": "\u22E3" },
+  "&NotSubset;": { "codepoints": [8834, 8402], "characters": "\u2282\u20D2" },
+  "&NotSubsetEqual;": { "codepoints": [8840], "characters": "\u2288" },
+  "&NotSucceeds;": { "codepoints": [8833], "characters": "\u2281" },
+  "&NotSucceedsEqual;": { "codepoints": [10928, 824], "characters": "\u2AB0\u0338" },
+  "&NotSucceedsSlantEqual;": { "codepoints": [8929], "characters": "\u22E1" },
+  "&NotSucceedsTilde;": { "codepoints": [8831, 824], "characters": "\u227F\u0338" },
+  "&NotSuperset;": { "codepoints": [8835, 8402], "characters": "\u2283\u20D2" },
+  "&NotSupersetEqual;": { "codepoints": [8841], "characters": "\u2289" },
+  "&NotTilde;": { "codepoints": [8769], "characters": "\u2241" },
+  "&NotTildeEqual;": { "codepoints": [8772], "characters": "\u2244" },
+  "&NotTildeFullEqual;": { "codepoints": [8775], "characters": "\u2247" },
+  "&NotTildeTilde;": { "codepoints": [8777], "characters": "\u2249" },
+  "&NotVerticalBar;": { "codepoints": [8740], "characters": "\u2224" },
+  "&Nscr;": { "codepoints": [119977], "characters": "\uD835\uDCA9" },
+  "&Ntilde": { "codepoints": [209], "characters": "\u00D1" },
+  "&Ntilde;": { "codepoints": [209], "characters": "\u00D1" },
+  "&Nu;": { "codepoints": [925], "characters": "\u039D" },
+  "&OElig;": { "codepoints": [338], "characters": "\u0152" },
+  "&Oacute": { "codepoints": [211], "characters": "\u00D3" },
+  "&Oacute;": { "codepoints": [211], "characters": "\u00D3" },
+  "&Ocirc": { "codepoints": [212], "characters": "\u00D4" },
+  "&Ocirc;": { "codepoints": [212], "characters": "\u00D4" },
+  "&Ocy;": { "codepoints": [1054], "characters": "\u041E" },
+  "&Odblac;": { "codepoints": [336], "characters": "\u0150" },
+  "&Ofr;": { "codepoints": [120082], "characters": "\uD835\uDD12" },
+  "&Ograve": { "codepoints": [210], "characters": "\u00D2" },
+  "&Ograve;": { "codepoints": [210], "characters": "\u00D2" },
+  "&Omacr;": { "codepoints": [332], "characters": "\u014C" },
+  "&Omega;": { "codepoints": [937], "characters": "\u03A9" },
+  "&Omicron;": { "codepoints": [927], "characters": "\u039F" },
+  "&Oopf;": { "codepoints": [120134], "characters": "\uD835\uDD46" },
+  "&OpenCurlyDoubleQuote;": { "codepoints": [8220], "characters": "\u201C" },
+  "&OpenCurlyQuote;": { "codepoints": [8216], "characters": "\u2018" },
+  "&Or;": { "codepoints": [10836], "characters": "\u2A54" },
+  "&Oscr;": { "codepoints": [119978], "characters": "\uD835\uDCAA" },
+  "&Oslash": { "codepoints": [216], "characters": "\u00D8" },
+  "&Oslash;": { "codepoints": [216], "characters": "\u00D8" },
+  "&Otilde": { "codepoints": [213], "characters": "\u00D5" },
+  "&Otilde;": { "codepoints": [213], "characters": "\u00D5" },
+  "&Otimes;": { "codepoints": [10807], "characters": "\u2A37" },
+  "&Ouml": { "codepoints": [214], "characters": "\u00D6" },
+  "&Ouml;": { "codepoints": [214], "characters": "\u00D6" },
+  "&OverBar;": { "codepoints": [8254], "characters": "\u203E" },
+  "&OverBrace;": { "codepoints": [9182], "characters": "\u23DE" },
+  "&OverBracket;": { "codepoints": [9140], "characters": "\u23B4" },
+  "&OverParenthesis;": { "codepoints": [9180], "characters": "\u23DC" },
+  "&PartialD;": { "codepoints": [8706], "characters": "\u2202" },
+  "&Pcy;": { "codepoints": [1055], "characters": "\u041F" },
+  "&Pfr;": { "codepoints": [120083], "characters": "\uD835\uDD13" },
+  "&Phi;": { "codepoints": [934], "characters": "\u03A6" },
+  "&Pi;": { "codepoints": [928], "characters": "\u03A0" },
+  "&PlusMinus;": { "codepoints": [177], "characters": "\u00B1" },
+  "&Poincareplane;": { "codepoints": [8460], "characters": "\u210C" },
+  "&Popf;": { "codepoints": [8473], "characters": "\u2119" },
+  "&Pr;": { "codepoints": [10939], "characters": "\u2ABB" },
+  "&Precedes;": { "codepoints": [8826], "characters": "\u227A" },
+  "&PrecedesEqual;": { "codepoints": [10927], "characters": "\u2AAF" },
+  "&PrecedesSlantEqual;": { "codepoints": [8828], "characters": "\u227C" },
+  "&PrecedesTilde;": { "codepoints": [8830], "characters": "\u227E" },
+  "&Prime;": { "codepoints": [8243], "characters": "\u2033" },
+  "&Product;": { "codepoints": [8719], "characters": "\u220F" },
+  "&Proportion;": { "codepoints": [8759], "characters": "\u2237" },
+  "&Proportional;": { "codepoints": [8733], "characters": "\u221D" },
+  "&Pscr;": { "codepoints": [119979], "characters": "\uD835\uDCAB" },
+  "&Psi;": { "codepoints": [936], "characters": "\u03A8" },
+  "&QUOT": { "codepoints": [34], "characters": "\u0022" },
+  "&QUOT;": { "codepoints": [34], "characters": "\u0022" },
+  "&Qfr;": { "codepoints": [120084], "characters": "\uD835\uDD14" },
+  "&Qopf;": { "codepoints": [8474], "characters": "\u211A" },
+  "&Qscr;": { "codepoints": [119980], "characters": "\uD835\uDCAC" },
+  "&RBarr;": { "codepoints": [10512], "characters": "\u2910" },
+  "&REG": { "codepoints": [174], "characters": "\u00AE" },
+  "&REG;": { "codepoints": [174], "characters": "\u00AE" },
+  "&Racute;": { "codepoints": [340], "characters": "\u0154" },
+  "&Rang;": { "codepoints": [10219], "characters": "\u27EB" },
+  "&Rarr;": { "codepoints": [8608], "characters": "\u21A0" },
+  "&Rarrtl;": { "codepoints": [10518], "characters": "\u2916" },
+  "&Rcaron;": { "codepoints": [344], "characters": "\u0158" },
+  "&Rcedil;": { "codepoints": [342], "characters": "\u0156" },
+  "&Rcy;": { "codepoints": [1056], "characters": "\u0420" },
+  "&Re;": { "codepoints": [8476], "characters": "\u211C" },
+  "&ReverseElement;": { "codepoints": [8715], "characters": "\u220B" },
+  "&ReverseEquilibrium;": { "codepoints": [8651], "characters": "\u21CB" },
+  "&ReverseUpEquilibrium;": { "codepoints": [10607], "characters": "\u296F" },
+  "&Rfr;": { "codepoints": [8476], "characters": "\u211C" },
+  "&Rho;": { "codepoints": [929], "characters": "\u03A1" },
+  "&RightAngleBracket;": { "codepoints": [10217], "characters": "\u27E9" },
+  "&RightArrow;": { "codepoints": [8594], "characters": "\u2192" },
+  "&RightArrowBar;": { "codepoints": [8677], "characters": "\u21E5" },
+  "&RightArrowLeftArrow;": { "codepoints": [8644], "characters": "\u21C4" },
+  "&RightCeiling;": { "codepoints": [8969], "characters": "\u2309" },
+  "&RightDoubleBracket;": { "codepoints": [10215], "characters": "\u27E7" },
+  "&RightDownTeeVector;": { "codepoints": [10589], "characters": "\u295D" },
+  "&RightDownVector;": { "codepoints": [8642], "characters": "\u21C2" },
+  "&RightDownVectorBar;": { "codepoints": [10581], "characters": "\u2955" },
+  "&RightFloor;": { "codepoints": [8971], "characters": "\u230B" },
+  "&RightTee;": { "codepoints": [8866], "characters": "\u22A2" },
+  "&RightTeeArrow;": { "codepoints": [8614], "characters": "\u21A6" },
+  "&RightTeeVector;": { "codepoints": [10587], "characters": "\u295B" },
+  "&RightTriangle;": { "codepoints": [8883], "characters": "\u22B3" },
+  "&RightTriangleBar;": { "codepoints": [10704], "characters": "\u29D0" },
+  "&RightTriangleEqual;": { "codepoints": [8885], "characters": "\u22B5" },
+  "&RightUpDownVector;": { "codepoints": [10575], "characters": "\u294F" },
+  "&RightUpTeeVector;": { "codepoints": [10588], "characters": "\u295C" },
+  "&RightUpVector;": { "codepoints": [8638], "characters": "\u21BE" },
+  "&RightUpVectorBar;": { "codepoints": [10580], "characters": "\u2954" },
+  "&RightVector;": { "codepoints": [8640], "characters": "\u21C0" },
+  "&RightVectorBar;": { "codepoints": [10579], "characters": "\u2953" },
+  "&Rightarrow;": { "codepoints": [8658], "characters": "\u21D2" },
+  "&Ropf;": { "codepoints": [8477], "characters": "\u211D" },
+  "&RoundImplies;": { "codepoints": [10608], "characters": "\u2970" },
+  "&Rrightarrow;": { "codepoints": [8667], "characters": "\u21DB" },
+  "&Rscr;": { "codepoints": [8475], "characters": "\u211B" },
+  "&Rsh;": { "codepoints": [8625], "characters": "\u21B1" },
+  "&RuleDelayed;": { "codepoints": [10740], "characters": "\u29F4" },
+  "&SHCHcy;": { "codepoints": [1065], "characters": "\u0429" },
+  "&SHcy;": { "codepoints": [1064], "characters": "\u0428" },
+  "&SOFTcy;": { "codepoints": [1068], "characters": "\u042C" },
+  "&Sacute;": { "codepoints": [346], "characters": "\u015A" },
+  "&Sc;": { "codepoints": [10940], "characters": "\u2ABC" },
+  "&Scaron;": { "codepoints": [352], "characters": "\u0160" },
+  "&Scedil;": { "codepoints": [350], "characters": "\u015E" },
+  "&Scirc;": { "codepoints": [348], "characters": "\u015C" },
+  "&Scy;": { "codepoints": [1057], "characters": "\u0421" },
+  "&Sfr;": { "codepoints": [120086], "characters": "\uD835\uDD16" },
+  "&ShortDownArrow;": { "codepoints": [8595], "characters": "\u2193" },
+  "&ShortLeftArrow;": { "codepoints": [8592], "characters": "\u2190" },
+  "&ShortRightArrow;": { "codepoints": [8594], "characters": "\u2192" },
+  "&ShortUpArrow;": { "codepoints": [8593], "characters": "\u2191" },
+  "&Sigma;": { "codepoints": [931], "characters": "\u03A3" },
+  "&SmallCircle;": { "codepoints": [8728], "characters": "\u2218" },
+  "&Sopf;": { "codepoints": [120138], "characters": "\uD835\uDD4A" },
+  "&Sqrt;": { "codepoints": [8730], "characters": "\u221A" },
+  "&Square;": { "codepoints": [9633], "characters": "\u25A1" },
+  "&SquareIntersection;": { "codepoints": [8851], "characters": "\u2293" },
+  "&SquareSubset;": { "codepoints": [8847], "characters": "\u228F" },
+  "&SquareSubsetEqual;": { "codepoints": [8849], "characters": "\u2291" },
+  "&SquareSuperset;": { "codepoints": [8848], "characters": "\u2290" },
+  "&SquareSupersetEqual;": { "codepoints": [8850], "characters": "\u2292" },
+  "&SquareUnion;": { "codepoints": [8852], "characters": "\u2294" },
+  "&Sscr;": { "codepoints": [119982], "characters": "\uD835\uDCAE" },
+  "&Star;": { "codepoints": [8902], "characters": "\u22C6" },
+  "&Sub;": { "codepoints": [8912], "characters": "\u22D0" },
+  "&Subset;": { "codepoints": [8912], "characters": "\u22D0" },
+  "&SubsetEqual;": { "codepoints": [8838], "characters": "\u2286" },
+  "&Succeeds;": { "codepoints": [8827], "characters": "\u227B" },
+  "&SucceedsEqual;": { "codepoints": [10928], "characters": "\u2AB0" },
+  "&SucceedsSlantEqual;": { "codepoints": [8829], "characters": "\u227D" },
+  "&SucceedsTilde;": { "codepoints": [8831], "characters": "\u227F" },
+  "&SuchThat;": { "codepoints": [8715], "characters": "\u220B" },
+  "&Sum;": { "codepoints": [8721], "characters": "\u2211" },
+  "&Sup;": { "codepoints": [8913], "characters": "\u22D1" },
+  "&Superset;": { "codepoints": [8835], "characters": "\u2283" },
+  "&SupersetEqual;": { "codepoints": [8839], "characters": "\u2287" },
+  "&Supset;": { "codepoints": [8913], "characters": "\u22D1" },
+  "&THORN": { "codepoints": [222], "characters": "\u00DE" },
+  "&THORN;": { "codepoints": [222], "characters": "\u00DE" },
+  "&TRADE;": { "codepoints": [8482], "characters": "\u2122" },
+  "&TSHcy;": { "codepoints": [1035], "characters": "\u040B" },
+  "&TScy;": { "codepoints": [1062], "characters": "\u0426" },
+  "&Tab;": { "codepoints": [9], "characters": "\u0009" },
+  "&Tau;": { "codepoints": [932], "characters": "\u03A4" },
+  "&Tcaron;": { "codepoints": [356], "characters": "\u0164" },
+  "&Tcedil;": { "codepoints": [354], "characters": "\u0162" },
+  "&Tcy;": { "codepoints": [1058], "characters": "\u0422" },
+  "&Tfr;": { "codepoints": [120087], "characters": "\uD835\uDD17" },
+  "&Therefore;": { "codepoints": [8756], "characters": "\u2234" },
+  "&Theta;": { "codepoints": [920], "characters": "\u0398" },
+  "&ThickSpace;": { "codepoints": [8287, 8202], "characters": "\u205F\u200A" },
+  "&ThinSpace;": { "codepoints": [8201], "characters": "\u2009" },
+  "&Tilde;": { "codepoints": [8764], "characters": "\u223C" },
+  "&TildeEqual;": { "codepoints": [8771], "characters": "\u2243" },
+  "&TildeFullEqual;": { "codepoints": [8773], "characters": "\u2245" },
+  "&TildeTilde;": { "codepoints": [8776], "characters": "\u2248" },
+  "&Topf;": { "codepoints": [120139], "characters": "\uD835\uDD4B" },
+  "&TripleDot;": { "codepoints": [8411], "characters": "\u20DB" },
+  "&Tscr;": { "codepoints": [119983], "characters": "\uD835\uDCAF" },
+  "&Tstrok;": { "codepoints": [358], "characters": "\u0166" },
+  "&Uacute": { "codepoints": [218], "characters": "\u00DA" },
+  "&Uacute;": { "codepoints": [218], "characters": "\u00DA" },
+  "&Uarr;": { "codepoints": [8607], "characters": "\u219F" },
+  "&Uarrocir;": { "codepoints": [10569], "characters": "\u2949" },
+  "&Ubrcy;": { "codepoints": [1038], "characters": "\u040E" },
+  "&Ubreve;": { "codepoints": [364], "characters": "\u016C" },
+  "&Ucirc": { "codepoints": [219], "characters": "\u00DB" },
+  "&Ucirc;": { "codepoints": [219], "characters": "\u00DB" },
+  "&Ucy;": { "codepoints": [1059], "characters": "\u0423" },
+  "&Udblac;": { "codepoints": [368], "characters": "\u0170" },
+  "&Ufr;": { "codepoints": [120088], "characters": "\uD835\uDD18" },
+  "&Ugrave": { "codepoints": [217], "characters": "\u00D9" },
+  "&Ugrave;": { "codepoints": [217], "characters": "\u00D9" },
+  "&Umacr;": { "codepoints": [362], "characters": "\u016A" },
+  "&UnderBar;": { "codepoints": [95], "characters": "\u005F" },
+  "&UnderBrace;": { "codepoints": [9183], "characters": "\u23DF" },
+  "&UnderBracket;": { "codepoints": [9141], "characters": "\u23B5" },
+  "&UnderParenthesis;": { "codepoints": [9181], "characters": "\u23DD" },
+  "&Union;": { "codepoints": [8899], "characters": "\u22C3" },
+  "&UnionPlus;": { "codepoints": [8846], "characters": "\u228E" },
+  "&Uogon;": { "codepoints": [370], "characters": "\u0172" },
+  "&Uopf;": { "codepoints": [120140], "characters": "\uD835\uDD4C" },
+  "&UpArrow;": { "codepoints": [8593], "characters": "\u2191" },
+  "&UpArrowBar;": { "codepoints": [10514], "characters": "\u2912" },
+  "&UpArrowDownArrow;": { "codepoints": [8645], "characters": "\u21C5" },
+  "&UpDownArrow;": { "codepoints": [8597], "characters": "\u2195" },
+  "&UpEquilibrium;": { "codepoints": [10606], "characters": "\u296E" },
+  "&UpTee;": { "codepoints": [8869], "characters": "\u22A5" },
+  "&UpTeeArrow;": { "codepoints": [8613], "characters": "\u21A5" },
+  "&Uparrow;": { "codepoints": [8657], "characters": "\u21D1" },
+  "&Updownarrow;": { "codepoints": [8661], "characters": "\u21D5" },
+  "&UpperLeftArrow;": { "codepoints": [8598], "characters": "\u2196" },
+  "&UpperRightArrow;": { "codepoints": [8599], "characters": "\u2197" },
+  "&Upsi;": { "codepoints": [978], "characters": "\u03D2" },
+  "&Upsilon;": { "codepoints": [933], "characters": "\u03A5" },
+  "&Uring;": { "codepoints": [366], "characters": "\u016E" },
+  "&Uscr;": { "codepoints": [119984], "characters": "\uD835\uDCB0" },
+  "&Utilde;": { "codepoints": [360], "characters": "\u0168" },
+  "&Uuml": { "codepoints": [220], "characters": "\u00DC" },
+  "&Uuml;": { "codepoints": [220], "characters": "\u00DC" },
+  "&VDash;": { "codepoints": [8875], "characters": "\u22AB" },
+  "&Vbar;": { "codepoints": [10987], "characters": "\u2AEB" },
+  "&Vcy;": { "codepoints": [1042], "characters": "\u0412" },
+  "&Vdash;": { "codepoints": [8873], "characters": "\u22A9" },
+  "&Vdashl;": { "codepoints": [10982], "characters": "\u2AE6" },
+  "&Vee;": { "codepoints": [8897], "characters": "\u22C1" },
+  "&Verbar;": { "codepoints": [8214], "characters": "\u2016" },
+  "&Vert;": { "codepoints": [8214], "characters": "\u2016" },
+  "&VerticalBar;": { "codepoints": [8739], "characters": "\u2223" },
+  "&VerticalLine;": { "codepoints": [124], "characters": "\u007C" },
+  "&VerticalSeparator;": { "codepoints": [10072], "characters": "\u2758" },
+  "&VerticalTilde;": { "codepoints": [8768], "characters": "\u2240" },
+  "&VeryThinSpace;": { "codepoints": [8202], "characters": "\u200A" },
+  "&Vfr;": { "codepoints": [120089], "characters": "\uD835\uDD19" },
+  "&Vopf;": { "codepoints": [120141], "characters": "\uD835\uDD4D" },
+  "&Vscr;": { "codepoints": [119985], "characters": "\uD835\uDCB1" },
+  "&Vvdash;": { "codepoints": [8874], "characters": "\u22AA" },
+  "&Wcirc;": { "codepoints": [372], "characters": "\u0174" },
+  "&Wedge;": { "codepoints": [8896], "characters": "\u22C0" },
+  "&Wfr;": { "codepoints": [120090], "characters": "\uD835\uDD1A" },
+  "&Wopf;": { "codepoints": [120142], "characters": "\uD835\uDD4E" },
+  "&Wscr;": { "codepoints": [119986], "characters": "\uD835\uDCB2" },
+  "&Xfr;": { "codepoints": [120091], "characters": "\uD835\uDD1B" },
+  "&Xi;": { "codepoints": [926], "characters": "\u039E" },
+  "&Xopf;": { "codepoints": [120143], "characters": "\uD835\uDD4F" },
+  "&Xscr;": { "codepoints": [119987], "characters": "\uD835\uDCB3" },
+  "&YAcy;": { "codepoints": [1071], "characters": "\u042F" },
+  "&YIcy;": { "codepoints": [1031], "characters": "\u0407" },
+  "&YUcy;": { "codepoints": [1070], "characters": "\u042E" },
+  "&Yacute": { "codepoints": [221], "characters": "\u00DD" },
+  "&Yacute;": { "codepoints": [221], "characters": "\u00DD" },
+  "&Ycirc;": { "codepoints": [374], "characters": "\u0176" },
+  "&Ycy;": { "codepoints": [1067], "characters": "\u042B" },
+  "&Yfr;": { "codepoints": [120092], "characters": "\uD835\uDD1C" },
+  "&Yopf;": { "codepoints": [120144], "characters": "\uD835\uDD50" },
+  "&Yscr;": { "codepoints": [119988], "characters": "\uD835\uDCB4" },
+  "&Yuml;": { "codepoints": [376], "characters": "\u0178" },
+  "&ZHcy;": { "codepoints": [1046], "characters": "\u0416" },
+  "&Zacute;": { "codepoints": [377], "characters": "\u0179" },
+  "&Zcaron;": { "codepoints": [381], "characters": "\u017D" },
+  "&Zcy;": { "codepoints": [1047], "characters": "\u0417" },
+  "&Zdot;": { "codepoints": [379], "characters": "\u017B" },
+  "&ZeroWidthSpace;": { "codepoints": [8203], "characters": "\u200B" },
+  "&Zeta;": { "codepoints": [918], "characters": "\u0396" },
+  "&Zfr;": { "codepoints": [8488], "characters": "\u2128" },
+  "&Zopf;": { "codepoints": [8484], "characters": "\u2124" },
+  "&Zscr;": { "codepoints": [119989], "characters": "\uD835\uDCB5" },
+  "&aacute": { "codepoints": [225], "characters": "\u00E1" },
+  "&aacute;": { "codepoints": [225], "characters": "\u00E1" },
+  "&abreve;": { "codepoints": [259], "characters": "\u0103" },
+  "&ac;": { "codepoints": [8766], "characters": "\u223E" },
+  "&acE;": { "codepoints": [8766, 819], "characters": "\u223E\u0333" },
+  "&acd;": { "codepoints": [8767], "characters": "\u223F" },
+  "&acirc": { "codepoints": [226], "characters": "\u00E2" },
+  "&acirc;": { "codepoints": [226], "characters": "\u00E2" },
+  "&acute": { "codepoints": [180], "characters": "\u00B4" },
+  "&acute;": { "codepoints": [180], "characters": "\u00B4" },
+  "&acy;": { "codepoints": [1072], "characters": "\u0430" },
+  "&aelig": { "codepoints": [230], "characters": "\u00E6" },
+  "&aelig;": { "codepoints": [230], "characters": "\u00E6" },
+  "&af;": { "codepoints": [8289], "characters": "\u2061" },
+  "&afr;": { "codepoints": [120094], "characters": "\uD835\uDD1E" },
+  "&agrave": { "codepoints": [224], "characters": "\u00E0" },
+  "&agrave;": { "codepoints": [224], "characters": "\u00E0" },
+  "&alefsym;": { "codepoints": [8501], "characters": "\u2135" },
+  "&aleph;": { "codepoints": [8501], "characters": "\u2135" },
+  "&alpha;": { "codepoints": [945], "characters": "\u03B1" },
+  "&amacr;": { "codepoints": [257], "characters": "\u0101" },
+  "&amalg;": { "codepoints": [10815], "characters": "\u2A3F" },
+  "&amp": { "codepoints": [38], "characters": "\u0026" },
+  "&amp;": { "codepoints": [38], "characters": "\u0026" },
+  "&and;": { "codepoints": [8743], "characters": "\u2227" },
+  "&andand;": { "codepoints": [10837], "characters": "\u2A55" },
+  "&andd;": { "codepoints": [10844], "characters": "\u2A5C" },
+  "&andslope;": { "codepoints": [10840], "characters": "\u2A58" },
+  "&andv;": { "codepoints": [10842], "characters": "\u2A5A" },
+  "&ang;": { "codepoints": [8736], "characters": "\u2220" },
+  "&ange;": { "codepoints": [10660], "characters": "\u29A4" },
+  "&angle;": { "codepoints": [8736], "characters": "\u2220" },
+  "&angmsd;": { "codepoints": [8737], "characters": "\u2221" },
+  "&angmsdaa;": { "codepoints": [10664], "characters": "\u29A8" },
+  "&angmsdab;": { "codepoints": [10665], "characters": "\u29A9" },
+  "&angmsdac;": { "codepoints": [10666], "characters": "\u29AA" },
+  "&angmsdad;": { "codepoints": [10667], "characters": "\u29AB" },
+  "&angmsdae;": { "codepoints": [10668], "characters": "\u29AC" },
+  "&angmsdaf;": { "codepoints": [10669], "characters": "\u29AD" },
+  "&angmsdag;": { "codepoints": [10670], "characters": "\u29AE" },
+  "&angmsdah;": { "codepoints": [10671], "characters": "\u29AF" },
+  "&angrt;": { "codepoints": [8735], "characters": "\u221F" },
+  "&angrtvb;": { "codepoints": [8894], "characters": "\u22BE" },
+  "&angrtvbd;": { "codepoints": [10653], "characters": "\u299D" },
+  "&angsph;": { "codepoints": [8738], "characters": "\u2222" },
+  "&angst;": { "codepoints": [197], "characters": "\u00C5" },
+  "&angzarr;": { "codepoints": [9084], "characters": "\u237C" },
+  "&aogon;": { "codepoints": [261], "characters": "\u0105" },
+  "&aopf;": { "codepoints": [120146], "characters": "\uD835\uDD52" },
+  "&ap;": { "codepoints": [8776], "characters": "\u2248" },
+  "&apE;": { "codepoints": [10864], "characters": "\u2A70" },
+  "&apacir;": { "codepoints": [10863], "characters": "\u2A6F" },
+  "&ape;": { "codepoints": [8778], "characters": "\u224A" },
+  "&apid;": { "codepoints": [8779], "characters": "\u224B" },
+  "&apos;": { "codepoints": [39], "characters": "\u0027" },
+  "&approx;": { "codepoints": [8776], "characters": "\u2248" },
+  "&approxeq;": { "codepoints": [8778], "characters": "\u224A" },
+  "&aring": { "codepoints": [229], "characters": "\u00E5" },
+  "&aring;": { "codepoints": [229], "characters": "\u00E5" },
+  "&ascr;": { "codepoints": [119990], "characters": "\uD835\uDCB6" },
+  "&ast;": { "codepoints": [42], "characters": "\u002A" },
+  "&asymp;": { "codepoints": [8776], "characters": "\u2248" },
+  "&asympeq;": { "codepoints": [8781], "characters": "\u224D" },
+  "&atilde": { "codepoints": [227], "characters": "\u00E3" },
+  "&atilde;": { "codepoints": [227], "characters": "\u00E3" },
+  "&auml": { "codepoints": [228], "characters": "\u00E4" },
+  "&auml;": { "codepoints": [228], "characters": "\u00E4" },
+  "&awconint;": { "codepoints": [8755], "characters": "\u2233" },
+  "&awint;": { "codepoints": [10769], "characters": "\u2A11" },
+  "&bNot;": { "codepoints": [10989], "characters": "\u2AED" },
+  "&backcong;": { "codepoints": [8780], "characters": "\u224C" },
+  "&backepsilon;": { "codepoints": [1014], "characters": "\u03F6" },
+  "&backprime;": { "codepoints": [8245], "characters": "\u2035" },
+  "&backsim;": { "codepoints": [8765], "characters": "\u223D" },
+  "&backsimeq;": { "codepoints": [8909], "characters": "\u22CD" },
+  "&barvee;": { "codepoints": [8893], "characters": "\u22BD" },
+  "&barwed;": { "codepoints": [8965], "characters": "\u2305" },
+  "&barwedge;": { "codepoints": [8965], "characters": "\u2305" },
+  "&bbrk;": { "codepoints": [9141], "characters": "\u23B5" },
+  "&bbrktbrk;": { "codepoints": [9142], "characters": "\u23B6" },
+  "&bcong;": { "codepoints": [8780], "characters": "\u224C" },
+  "&bcy;": { "codepoints": [1073], "characters": "\u0431" },
+  "&bdquo;": { "codepoints": [8222], "characters": "\u201E" },
+  "&becaus;": { "codepoints": [8757], "characters": "\u2235" },
+  "&because;": { "codepoints": [8757], "characters": "\u2235" },
+  "&bemptyv;": { "codepoints": [10672], "characters": "\u29B0" },
+  "&bepsi;": { "codepoints": [1014], "characters": "\u03F6" },
+  "&bernou;": { "codepoints": [8492], "characters": "\u212C" },
+  "&beta;": { "codepoints": [946], "characters": "\u03B2" },
+  "&beth;": { "codepoints": [8502], "characters": "\u2136" },
+  "&between;": { "codepoints": [8812], "characters": "\u226C" },
+  "&bfr;": { "codepoints": [120095], "characters": "\uD835\uDD1F" },
+  "&bigcap;": { "codepoints": [8898], "characters": "\u22C2" },
+  "&bigcirc;": { "codepoints": [9711], "characters": "\u25EF" },
+  "&bigcup;": { "codepoints": [8899], "characters": "\u22C3" },
+  "&bigodot;": { "codepoints": [10752], "characters": "\u2A00" },
+  "&bigoplus;": { "codepoints": [10753], "characters": "\u2A01" },
+  "&bigotimes;": { "codepoints": [10754], "characters": "\u2A02" },
+  "&bigsqcup;": { "codepoints": [10758], "characters": "\u2A06" },
+  "&bigstar;": { "codepoints": [9733], "characters": "\u2605" },
+  "&bigtriangledown;": { "codepoints": [9661], "characters": "\u25BD" },
+  "&bigtriangleup;": { "codepoints": [9651], "characters": "\u25B3" },
+  "&biguplus;": { "codepoints": [10756], "characters": "\u2A04" },
+  "&bigvee;": { "codepoints": [8897], "characters": "\u22C1" },
+  "&bigwedge;": { "codepoints": [8896], "characters": "\u22C0" },
+  "&bkarow;": { "codepoints": [10509], "characters": "\u290D" },
+  "&blacklozenge;": { "codepoints": [10731], "characters": "\u29EB" },
+  "&blacksquare;": { "codepoints": [9642], "characters": "\u25AA" },
+  "&blacktriangle;": { "codepoints": [9652], "characters": "\u25B4" },
+  "&blacktriangledown;": { "codepoints": [9662], "characters": "\u25BE" },
+  "&blacktriangleleft;": { "codepoints": [9666], "characters": "\u25C2" },
+  "&blacktriangleright;": { "codepoints": [9656], "characters": "\u25B8" },
+  "&blank;": { "codepoints": [9251], "characters": "\u2423" },
+  "&blk12;": { "codepoints": [9618], "characters": "\u2592" },
+  "&blk14;": { "codepoints": [9617], "characters": "\u2591" },
+  "&blk34;": { "codepoints": [9619], "characters": "\u2593" },
+  "&block;": { "codepoints": [9608], "characters": "\u2588" },
+  "&bne;": { "codepoints": [61, 8421], "characters": "\u003D\u20E5" },
+  "&bnequiv;": { "codepoints": [8801, 8421], "characters": "\u2261\u20E5" },
+  "&bnot;": { "codepoints": [8976], "characters": "\u2310" },
+  "&bopf;": { "codepoints": [120147], "characters": "\uD835\uDD53" },
+  "&bot;": { "codepoints": [8869], "characters": "\u22A5" },
+  "&bottom;": { "codepoints": [8869], "characters": "\u22A5" },
+  "&bowtie;": { "codepoints": [8904], "characters": "\u22C8" },
+  "&boxDL;": { "codepoints": [9559], "characters": "\u2557" },
+  "&boxDR;": { "codepoints": [9556], "characters": "\u2554" },
+  "&boxDl;": { "codepoints": [9558], "characters": "\u2556" },
+  "&boxDr;": { "codepoints": [9555], "characters": "\u2553" },
+  "&boxH;": { "codepoints": [9552], "characters": "\u2550" },
+  "&boxHD;": { "codepoints": [9574], "characters": "\u2566" },
+  "&boxHU;": { "codepoints": [9577], "characters": "\u2569" },
+  "&boxHd;": { "codepoints": [9572], "characters": "\u2564" },
+  "&boxHu;": { "codepoints": [9575], "characters": "\u2567" },
+  "&boxUL;": { "codepoints": [9565], "characters": "\u255D" },
+  "&boxUR;": { "codepoints": [9562], "characters": "\u255A" },
+  "&boxUl;": { "codepoints": [9564], "characters": "\u255C" },
+  "&boxUr;": { "codepoints": [9561], "characters": "\u2559" },
+  "&boxV;": { "codepoints": [9553], "characters": "\u2551" },
+  "&boxVH;": { "codepoints": [9580], "characters": "\u256C" },
+  "&boxVL;": { "codepoints": [9571], "characters": "\u2563" },
+  "&boxVR;": { "codepoints": [9568], "characters": "\u2560" },
+  "&boxVh;": { "codepoints": [9579], "characters": "\u256B" },
+  "&boxVl;": { "codepoints": [9570], "characters": "\u2562" },
+  "&boxVr;": { "codepoints": [9567], "characters": "\u255F" },
+  "&boxbox;": { "codepoints": [10697], "characters": "\u29C9" },
+  "&boxdL;": { "codepoints": [9557], "characters": "\u2555" },
+  "&boxdR;": { "codepoints": [9554], "characters": "\u2552" },
+  "&boxdl;": { "codepoints": [9488], "characters": "\u2510" },
+  "&boxdr;": { "codepoints": [9484], "characters": "\u250C" },
+  "&boxh;": { "codepoints": [9472], "characters": "\u2500" },
+  "&boxhD;": { "codepoints": [9573], "characters": "\u2565" },
+  "&boxhU;": { "codepoints": [9576], "characters": "\u2568" },
+  "&boxhd;": { "codepoints": [9516], "characters": "\u252C" },
+  "&boxhu;": { "codepoints": [9524], "characters": "\u2534" },
+  "&boxminus;": { "codepoints": [8863], "characters": "\u229F" },
+  "&boxplus;": { "codepoints": [8862], "characters": "\u229E" },
+  "&boxtimes;": { "codepoints": [8864], "characters": "\u22A0" },
+  "&boxuL;": { "codepoints": [9563], "characters": "\u255B" },
+  "&boxuR;": { "codepoints": [9560], "characters": "\u2558" },
+  "&boxul;": { "codepoints": [9496], "characters": "\u2518" },
+  "&boxur;": { "codepoints": [9492], "characters": "\u2514" },
+  "&boxv;": { "codepoints": [9474], "characters": "\u2502" },
+  "&boxvH;": { "codepoints": [9578], "characters": "\u256A" },
+  "&boxvL;": { "codepoints": [9569], "characters": "\u2561" },
+  "&boxvR;": { "codepoints": [9566], "characters": "\u255E" },
+  "&boxvh;": { "codepoints": [9532], "characters": "\u253C" },
+  "&boxvl;": { "codepoints": [9508], "characters": "\u2524" },
+  "&boxvr;": { "codepoints": [9500], "characters": "\u251C" },
+  "&bprime;": { "codepoints": [8245], "characters": "\u2035" },
+  "&breve;": { "codepoints": [728], "characters": "\u02D8" },
+  "&brvbar": { "codepoints": [166], "characters": "\u00A6" },
+  "&brvbar;": { "codepoints": [166], "characters": "\u00A6" },
+  "&bscr;": { "codepoints": [119991], "characters": "\uD835\uDCB7" },
+  "&bsemi;": { "codepoints": [8271], "characters": "\u204F" },
+  "&bsim;": { "codepoints": [8765], "characters": "\u223D" },
+  "&bsime;": { "codepoints": [8909], "characters": "\u22CD" },
+  "&bsol;": { "codepoints": [92], "characters": "\u005C" },
+  "&bsolb;": { "codepoints": [10693], "characters": "\u29C5" },
+  "&bsolhsub;": { "codepoints": [10184], "characters": "\u27C8" },
+  "&bull;": { "codepoints": [8226], "characters": "\u2022" },
+  "&bullet;": { "codepoints": [8226], "characters": "\u2022" },
+  "&bump;": { "codepoints": [8782], "characters": "\u224E" },
+  "&bumpE;": { "codepoints": [10926], "characters": "\u2AAE" },
+  "&bumpe;": { "codepoints": [8783], "characters": "\u224F" },
+  "&bumpeq;": { "codepoints": [8783], "characters": "\u224F" },
+  "&cacute;": { "codepoints": [263], "characters": "\u0107" },
+  "&cap;": { "codepoints": [8745], "characters": "\u2229" },
+  "&capand;": { "codepoints": [10820], "characters": "\u2A44" },
+  "&capbrcup;": { "codepoints": [10825], "characters": "\u2A49" },
+  "&capcap;": { "codepoints": [10827], "characters": "\u2A4B" },
+  "&capcup;": { "codepoints": [10823], "characters": "\u2A47" },
+  "&capdot;": { "codepoints": [10816], "characters": "\u2A40" },
+  "&caps;": { "codepoints": [8745, 65024], "characters": "\u2229\uFE00" },
+  "&caret;": { "codepoints": [8257], "characters": "\u2041" },
+  "&caron;": { "codepoints": [711], "characters": "\u02C7" },
+  "&ccaps;": { "codepoints": [10829], "characters": "\u2A4D" },
+  "&ccaron;": { "codepoints": [269], "characters": "\u010D" },
+  "&ccedil": { "codepoints": [231], "characters": "\u00E7" },
+  "&ccedil;": { "codepoints": [231], "characters": "\u00E7" },
+  "&ccirc;": { "codepoints": [265], "characters": "\u0109" },
+  "&ccups;": { "codepoints": [10828], "characters": "\u2A4C" },
+  "&ccupssm;": { "codepoints": [10832], "characters": "\u2A50" },
+  "&cdot;": { "codepoints": [267], "characters": "\u010B" },
+  "&cedil": { "codepoints": [184], "characters": "\u00B8" },
+  "&cedil;": { "codepoints": [184], "characters": "\u00B8" },
+  "&cemptyv;": { "codepoints": [10674], "characters": "\u29B2" },
+  "&cent": { "codepoints": [162], "characters": "\u00A2" },
+  "&cent;": { "codepoints": [162], "characters": "\u00A2" },
+  "&centerdot;": { "codepoints": [183], "characters": "\u00B7" },
+  "&cfr;": { "codepoints": [120096], "characters": "\uD835\uDD20" },
+  "&chcy;": { "codepoints": [1095], "characters": "\u0447" },
+  "&check;": { "codepoints": [10003], "characters": "\u2713" },
+  "&checkmark;": { "codepoints": [10003], "characters": "\u2713" },
+  "&chi;": { "codepoints": [967], "characters": "\u03C7" },
+  "&cir;": { "codepoints": [9675], "characters": "\u25CB" },
+  "&cirE;": { "codepoints": [10691], "characters": "\u29C3" },
+  "&circ;": { "codepoints": [710], "characters": "\u02C6" },
+  "&circeq;": { "codepoints": [8791], "characters": "\u2257" },
+  "&circlearrowleft;": { "codepoints": [8634], "characters": "\u21BA" },
+  "&circlearrowright;": { "codepoints": [8635], "characters": "\u21BB" },
+  "&circledR;": { "codepoints": [174], "characters": "\u00AE" },
+  "&circledS;": { "codepoints": [9416], "characters": "\u24C8" },
+  "&circledast;": { "codepoints": [8859], "characters": "\u229B" },
+  "&circledcirc;": { "codepoints": [8858], "characters": "\u229A" },
+  "&circleddash;": { "codepoints": [8861], "characters": "\u229D" },
+  "&cire;": { "codepoints": [8791], "characters": "\u2257" },
+  "&cirfnint;": { "codepoints": [10768], "characters": "\u2A10" },
+  "&cirmid;": { "codepoints": [10991], "characters": "\u2AEF" },
+  "&cirscir;": { "codepoints": [10690], "characters": "\u29C2" },
+  "&clubs;": { "codepoints": [9827], "characters": "\u2663" },
+  "&clubsuit;": { "codepoints": [9827], "characters": "\u2663" },
+  "&colon;": { "codepoints": [58], "characters": "\u003A" },
+  "&colone;": { "codepoints": [8788], "characters": "\u2254" },
+  "&coloneq;": { "codepoints": [8788], "characters": "\u2254" },
+  "&comma;": { "codepoints": [44], "characters": "\u002C" },
+  "&commat;": { "codepoints": [64], "characters": "\u0040" },
+  "&comp;": { "codepoints": [8705], "characters": "\u2201" },
+  "&compfn;": { "codepoints": [8728], "characters": "\u2218" },
+  "&complement;": { "codepoints": [8705], "characters": "\u2201" },
+  "&complexes;": { "codepoints": [8450], "characters": "\u2102" },
+  "&cong;": { "codepoints": [8773], "characters": "\u2245" },
+  "&congdot;": { "codepoints": [10861], "characters": "\u2A6D" },
+  "&conint;": { "codepoints": [8750], "characters": "\u222E" },
+  "&copf;": { "codepoints": [120148], "characters": "\uD835\uDD54" },
+  "&coprod;": { "codepoints": [8720], "characters": "\u2210" },
+  "&copy": { "codepoints": [169], "characters": "\u00A9" },
+  "&copy;": { "codepoints": [169], "characters": "\u00A9" },
+  "&copysr;": { "codepoints": [8471], "characters": "\u2117" },
+  "&crarr;": { "codepoints": [8629], "characters": "\u21B5" },
+  "&cross;": { "codepoints": [10007], "characters": "\u2717" },
+  "&cscr;": { "codepoints": [119992], "characters": "\uD835\uDCB8" },
+  "&csub;": { "codepoints": [10959], "characters": "\u2ACF" },
+  "&csube;": { "codepoints": [10961], "characters": "\u2AD1" },
+  "&csup;": { "codepoints": [10960], "characters": "\u2AD0" },
+  "&csupe;": { "codepoints": [10962], "characters": "\u2AD2" },
+  "&ctdot;": { "codepoints": [8943], "characters": "\u22EF" },
+  "&cudarrl;": { "codepoints": [10552], "characters": "\u2938" },
+  "&cudarrr;": { "codepoints": [10549], "characters": "\u2935" },
+  "&cuepr;": { "codepoints": [8926], "characters": "\u22DE" },
+  "&cuesc;": { "codepoints": [8927], "characters": "\u22DF" },
+  "&cularr;": { "codepoints": [8630], "characters": "\u21B6" },
+  "&cularrp;": { "codepoints": [10557], "characters": "\u293D" },
+  "&cup;": { "codepoints": [8746], "characters": "\u222A" },
+  "&cupbrcap;": { "codepoints": [10824], "characters": "\u2A48" },
+  "&cupcap;": { "codepoints": [10822], "characters": "\u2A46" },
+  "&cupcup;": { "codepoints": [10826], "characters": "\u2A4A" },
+  "&cupdot;": { "codepoints": [8845], "characters": "\u228D" },
+  "&cupor;": { "codepoints": [10821], "characters": "\u2A45" },
+  "&cups;": { "codepoints": [8746, 65024], "characters": "\u222A\uFE00" },
+  "&curarr;": { "codepoints": [8631], "characters": "\u21B7" },
+  "&curarrm;": { "codepoints": [10556], "characters": "\u293C" },
+  "&curlyeqprec;": { "codepoints": [8926], "characters": "\u22DE" },
+  "&curlyeqsucc;": { "codepoints": [8927], "characters": "\u22DF" },
+  "&curlyvee;": { "codepoints": [8910], "characters": "\u22CE" },
+  "&curlywedge;": { "codepoints": [8911], "characters": "\u22CF" },
+  "&curren": { "codepoints": [164], "characters": "\u00A4" },
+  "&curren;": { "codepoints": [164], "characters": "\u00A4" },
+  "&curvearrowleft;": { "codepoints": [8630], "characters": "\u21B6" },
+  "&curvearrowright;": { "codepoints": [8631], "characters": "\u21B7" },
+  "&cuvee;": { "codepoints": [8910], "characters": "\u22CE" },
+  "&cuwed;": { "codepoints": [8911], "characters": "\u22CF" },
+  "&cwconint;": { "codepoints": [8754], "characters": "\u2232" },
+  "&cwint;": { "codepoints": [8753], "characters": "\u2231" },
+  "&cylcty;": { "codepoints": [9005], "characters": "\u232D" },
+  "&dArr;": { "codepoints": [8659], "characters": "\u21D3" },
+  "&dHar;": { "codepoints": [10597], "characters": "\u2965" },
+  "&dagger;": { "codepoints": [8224], "characters": "\u2020" },
+  "&daleth;": { "codepoints": [8504], "characters": "\u2138" },
+  "&darr;": { "codepoints": [8595], "characters": "\u2193" },
+  "&dash;": { "codepoints": [8208], "characters": "\u2010" },
+  "&dashv;": { "codepoints": [8867], "characters": "\u22A3" },
+  "&dbkarow;": { "codepoints": [10511], "characters": "\u290F" },
+  "&dblac;": { "codepoints": [733], "characters": "\u02DD" },
+  "&dcaron;": { "codepoints": [271], "characters": "\u010F" },
+  "&dcy;": { "codepoints": [1076], "characters": "\u0434" },
+  "&dd;": { "codepoints": [8518], "characters": "\u2146" },
+  "&ddagger;": { "codepoints": [8225], "characters": "\u2021" },
+  "&ddarr;": { "codepoints": [8650], "characters": "\u21CA" },
+  "&ddotseq;": { "codepoints": [10871], "characters": "\u2A77" },
+  "&deg": { "codepoints": [176], "characters": "\u00B0" },
+  "&deg;": { "codepoints": [176], "characters": "\u00B0" },
+  "&delta;": { "codepoints": [948], "characters": "\u03B4" },
+  "&demptyv;": { "codepoints": [10673], "characters": "\u29B1" },
+  "&dfisht;": { "codepoints": [10623], "characters": "\u297F" },
+  "&dfr;": { "codepoints": [120097], "characters": "\uD835\uDD21" },
+  "&dharl;": { "codepoints": [8643], "characters": "\u21C3" },
+  "&dharr;": { "codepoints": [8642], "characters": "\u21C2" },
+  "&diam;": { "codepoints": [8900], "characters": "\u22C4" },
+  "&diamond;": { "codepoints": [8900], "characters": "\u22C4" },
+  "&diamondsuit;": { "codepoints": [9830], "characters": "\u2666" },
+  "&diams;": { "codepoints": [9830], "characters": "\u2666" },
+  "&die;": { "codepoints": [168], "characters": "\u00A8" },
+  "&digamma;": { "codepoints": [989], "characters": "\u03DD" },
+  "&disin;": { "codepoints": [8946], "characters": "\u22F2" },
+  "&div;": { "codepoints": [247], "characters": "\u00F7" },
+  "&divide": { "codepoints": [247], "characters": "\u00F7" },
+  "&divide;": { "codepoints": [247], "characters": "\u00F7" },
+  "&divideontimes;": { "codepoints": [8903], "characters": "\u22C7" },
+  "&divonx;": { "codepoints": [8903], "characters": "\u22C7" },
+  "&djcy;": { "codepoints": [1106], "characters": "\u0452" },
+  "&dlcorn;": { "codepoints": [8990], "characters": "\u231E" },
+  "&dlcrop;": { "codepoints": [8973], "characters": "\u230D" },
+  "&dollar;": { "codepoints": [36], "characters": "\u0024" },
+  "&dopf;": { "codepoints": [120149], "characters": "\uD835\uDD55" },
+  "&dot;": { "codepoints": [729], "characters": "\u02D9" },
+  "&doteq;": { "codepoints": [8784], "characters": "\u2250" },
+  "&doteqdot;": { "codepoints": [8785], "characters": "\u2251" },
+  "&dotminus;": { "codepoints": [8760], "characters": "\u2238" },
+  "&dotplus;": { "codepoints": [8724], "characters": "\u2214" },
+  "&dotsquare;": { "codepoints": [8865], "characters": "\u22A1" },
+  "&doublebarwedge;": { "codepoints": [8966], "characters": "\u2306" },
+  "&downarrow;": { "codepoints": [8595], "characters": "\u2193" },
+  "&downdownarrows;": { "codepoints": [8650], "characters": "\u21CA" },
+  "&downharpoonleft;": { "codepoints": [8643], "characters": "\u21C3" },
+  "&downharpoonright;": { "codepoints": [8642], "characters": "\u21C2" },
+  "&drbkarow;": { "codepoints": [10512], "characters": "\u2910" },
+  "&drcorn;": { "codepoints": [8991], "characters": "\u231F" },
+  "&drcrop;": { "codepoints": [8972], "characters": "\u230C" },
+  "&dscr;": { "codepoints": [119993], "characters": "\uD835\uDCB9" },
+  "&dscy;": { "codepoints": [1109], "characters": "\u0455" },
+  "&dsol;": { "codepoints": [10742], "characters": "\u29F6" },
+  "&dstrok;": { "codepoints": [273], "characters": "\u0111" },
+  "&dtdot;": { "codepoints": [8945], "characters": "\u22F1" },
+  "&dtri;": { "codepoints": [9663], "characters": "\u25BF" },
+  "&dtrif;": { "codepoints": [9662], "characters": "\u25BE" },
+  "&duarr;": { "codepoints": [8693], "characters": "\u21F5" },
+  "&duhar;": { "codepoints": [10607], "characters": "\u296F" },
+  "&dwangle;": { "codepoints": [10662], "characters": "\u29A6" },
+  "&dzcy;": { "codepoints": [1119], "characters": "\u045F" },
+  "&dzigrarr;": { "codepoints": [10239], "characters": "\u27FF" },
+  "&eDDot;": { "codepoints": [10871], "characters": "\u2A77" },
+  "&eDot;": { "codepoints": [8785], "characters": "\u2251" },
+  "&eacute": { "codepoints": [233], "characters": "\u00E9" },
+  "&eacute;": { "codepoints": [233], "characters": "\u00E9" },
+  "&easter;": { "codepoints": [10862], "characters": "\u2A6E" },
+  "&ecaron;": { "codepoints": [283], "characters": "\u011B" },
+  "&ecir;": { "codepoints": [8790], "characters": "\u2256" },
+  "&ecirc": { "codepoints": [234], "characters": "\u00EA" },
+  "&ecirc;": { "codepoints": [234], "characters": "\u00EA" },
+  "&ecolon;": { "codepoints": [8789], "characters": "\u2255" },
+  "&ecy;": { "codepoints": [1101], "characters": "\u044D" },
+  "&edot;": { "codepoints": [279], "characters": "\u0117" },
+  "&ee;": { "codepoints": [8519], "characters": "\u2147" },
+  "&efDot;": { "codepoints": [8786], "characters": "\u2252" },
+  "&efr;": { "codepoints": [120098], "characters": "\uD835\uDD22" },
+  "&eg;": { "codepoints": [10906], "characters": "\u2A9A" },
+  "&egrave": { "codepoints": [232], "characters": "\u00E8" },
+  "&egrave;": { "codepoints": [232], "characters": "\u00E8" },
+  "&egs;": { "codepoints": [10902], "characters": "\u2A96" },
+  "&egsdot;": { "codepoints": [10904], "characters": "\u2A98" },
+  "&el;": { "codepoints": [10905], "characters": "\u2A99" },
+  "&elinters;": { "codepoints": [9191], "characters": "\u23E7" },
+  "&ell;": { "codepoints": [8467], "characters": "\u2113" },
+  "&els;": { "codepoints": [10901], "characters": "\u2A95" },
+  "&elsdot;": { "codepoints": [10903], "characters": "\u2A97" },
+  "&emacr;": { "codepoints": [275], "characters": "\u0113" },
+  "&empty;": { "codepoints": [8709], "characters": "\u2205" },
+  "&emptyset;": { "codepoints": [8709], "characters": "\u2205" },
+  "&emptyv;": { "codepoints": [8709], "characters": "\u2205" },
+  "&emsp13;": { "codepoints": [8196], "characters": "\u2004" },
+  "&emsp14;": { "codepoints": [8197], "characters": "\u2005" },
+  "&emsp;": { "codepoints": [8195], "characters": "\u2003" },
+  "&eng;": { "codepoints": [331], "characters": "\u014B" },
+  "&ensp;": { "codepoints": [8194], "characters": "\u2002" },
+  "&eogon;": { "codepoints": [281], "characters": "\u0119" },
+  "&eopf;": { "codepoints": [120150], "characters": "\uD835\uDD56" },
+  "&epar;": { "codepoints": [8917], "characters": "\u22D5" },
+  "&eparsl;": { "codepoints": [10723], "characters": "\u29E3" },
+  "&eplus;": { "codepoints": [10865], "characters": "\u2A71" },
+  "&epsi;": { "codepoints": [949], "characters": "\u03B5" },
+  "&epsilon;": { "codepoints": [949], "characters": "\u03B5" },
+  "&epsiv;": { "codepoints": [1013], "characters": "\u03F5" },
+  "&eqcirc;": { "codepoints": [8790], "characters": "\u2256" },
+  "&eqcolon;": { "codepoints": [8789], "characters": "\u2255" },
+  "&eqsim;": { "codepoints": [8770], "characters": "\u2242" },
+  "&eqslantgtr;": { "codepoints": [10902], "characters": "\u2A96" },
+  "&eqslantless;": { "codepoints": [10901], "characters": "\u2A95" },
+  "&equals;": { "codepoints": [61], "characters": "\u003D" },
+  "&equest;": { "codepoints": [8799], "characters": "\u225F" },
+  "&equiv;": { "codepoints": [8801], "characters": "\u2261" },
+  "&equivDD;": { "codepoints": [10872], "characters": "\u2A78" },
+  "&eqvparsl;": { "codepoints": [10725], "characters": "\u29E5" },
+  "&erDot;": { "codepoints": [8787], "characters": "\u2253" },
+  "&erarr;": { "codepoints": [10609], "characters": "\u2971" },
+  "&escr;": { "codepoints": [8495], "characters": "\u212F" },
+  "&esdot;": { "codepoints": [8784], "characters": "\u2250" },
+  "&esim;": { "codepoints": [8770], "characters": "\u2242" },
+  "&eta;": { "codepoints": [951], "characters": "\u03B7" },
+  "&eth": { "codepoints": [240], "characters": "\u00F0" },
+  "&eth;": { "codepoints": [240], "characters": "\u00F0" },
+  "&euml": { "codepoints": [235], "characters": "\u00EB" },
+  "&euml;": { "codepoints": [235], "characters": "\u00EB" },
+  "&euro;": { "codepoints": [8364], "characters": "\u20AC" },
+  "&excl;": { "codepoints": [33], "characters": "\u0021" },
+  "&exist;": { "codepoints": [8707], "characters": "\u2203" },
+  "&expectation;": { "codepoints": [8496], "characters": "\u2130" },
+  "&exponentiale;": { "codepoints": [8519], "characters": "\u2147" },
+  "&fallingdotseq;": { "codepoints": [8786], "characters": "\u2252" },
+  "&fcy;": { "codepoints": [1092], "characters": "\u0444" },
+  "&female;": { "codepoints": [9792], "characters": "\u2640" },
+  "&ffilig;": { "codepoints": [64259], "characters": "\uFB03" },
+  "&fflig;": { "codepoints": [64256], "characters": "\uFB00" },
+  "&ffllig;": { "codepoints": [64260], "characters": "\uFB04" },
+  "&ffr;": { "codepoints": [120099], "characters": "\uD835\uDD23" },
+  "&filig;": { "codepoints": [64257], "characters": "\uFB01" },
+  "&fjlig;": { "codepoints": [102, 106], "characters": "\u0066\u006A" },
+  "&flat;": { "codepoints": [9837], "characters": "\u266D" },
+  "&fllig;": { "codepoints": [64258], "characters": "\uFB02" },
+  "&fltns;": { "codepoints": [9649], "characters": "\u25B1" },
+  "&fnof;": { "codepoints": [402], "characters": "\u0192" },
+  "&fopf;": { "codepoints": [120151], "characters": "\uD835\uDD57" },
+  "&forall;": { "codepoints": [8704], "characters": "\u2200" },
+  "&fork;": { "codepoints": [8916], "characters": "\u22D4" },
+  "&forkv;": { "codepoints": [10969], "characters": "\u2AD9" },
+  "&fpartint;": { "codepoints": [10765], "characters": "\u2A0D" },
+  "&frac12": { "codepoints": [189], "characters": "\u00BD" },
+  "&frac12;": { "codepoints": [189], "characters": "\u00BD" },
+  "&frac13;": { "codepoints": [8531], "characters": "\u2153" },
+  "&frac14": { "codepoints": [188], "characters": "\u00BC" },
+  "&frac14;": { "codepoints": [188], "characters": "\u00BC" },
+  "&frac15;": { "codepoints": [8533], "characters": "\u2155" },
+  "&frac16;": { "codepoints": [8537], "characters": "\u2159" },
+  "&frac18;": { "codepoints": [8539], "characters": "\u215B" },
+  "&frac23;": { "codepoints": [8532], "characters": "\u2154" },
+  "&frac25;": { "codepoints": [8534], "characters": "\u2156" },
+  "&frac34": { "codepoints": [190], "characters": "\u00BE" },
+  "&frac34;": { "codepoints": [190], "characters": "\u00BE" },
+  "&frac35;": { "codepoints": [8535], "characters": "\u2157" },
+  "&frac38;": { "codepoints": [8540], "characters": "\u215C" },
+  "&frac45;": { "codepoints": [8536], "characters": "\u2158" },
+  "&frac56;": { "codepoints": [8538], "characters": "\u215A" },
+  "&frac58;": { "codepoints": [8541], "characters": "\u215D" },
+  "&frac78;": { "codepoints": [8542], "characters": "\u215E" },
+  "&frasl;": { "codepoints": [8260], "characters": "\u2044" },
+  "&frown;": { "codepoints": [8994], "characters": "\u2322" },
+  "&fscr;": { "codepoints": [119995], "characters": "\uD835\uDCBB" },
+  "&gE;": { "codepoints": [8807], "characters": "\u2267" },
+  "&gEl;": { "codepoints": [10892], "characters": "\u2A8C" },
+  "&gacute;": { "codepoints": [501], "characters": "\u01F5" },
+  "&gamma;": { "codepoints": [947], "characters": "\u03B3" },
+  "&gammad;": { "codepoints": [989], "characters": "\u03DD" },
+  "&gap;": { "codepoints": [10886], "characters": "\u2A86" },
+  "&gbreve;": { "codepoints": [287], "characters": "\u011F" },
+  "&gcirc;": { "codepoints": [285], "characters": "\u011D" },
+  "&gcy;": { "codepoints": [1075], "characters": "\u0433" },
+  "&gdot;": { "codepoints": [289], "characters": "\u0121" },
+  "&ge;": { "codepoints": [8805], "characters": "\u2265" },
+  "&gel;": { "codepoints": [8923], "characters": "\u22DB" },
+  "&geq;": { "codepoints": [8805], "characters": "\u2265" },
+  "&geqq;": { "codepoints": [8807], "characters": "\u2267" },
+  "&geqslant;": { "codepoints": [10878], "characters": "\u2A7E" },
+  "&ges;": { "codepoints": [10878], "characters": "\u2A7E" },
+  "&gescc;": { "codepoints": [10921], "characters": "\u2AA9" },
+  "&gesdot;": { "codepoints": [10880], "characters": "\u2A80" },
+  "&gesdoto;": { "codepoints": [10882], "characters": "\u2A82" },
+  "&gesdotol;": { "codepoints": [10884], "characters": "\u2A84" },
+  "&gesl;": { "codepoints": [8923, 65024], "characters": "\u22DB\uFE00" },
+  "&gesles;": { "codepoints": [10900], "characters": "\u2A94" },
+  "&gfr;": { "codepoints": [120100], "characters": "\uD835\uDD24" },
+  "&gg;": { "codepoints": [8811], "characters": "\u226B" },
+  "&ggg;": { "codepoints": [8921], "characters": "\u22D9" },
+  "&gimel;": { "codepoints": [8503], "characters": "\u2137" },
+  "&gjcy;": { "codepoints": [1107], "characters": "\u0453" },
+  "&gl;": { "codepoints": [8823], "characters": "\u2277" },
+  "&glE;": { "codepoints": [10898], "characters": "\u2A92" },
+  "&gla;": { "codepoints": [10917], "characters": "\u2AA5" },
+  "&glj;": { "codepoints": [10916], "characters": "\u2AA4" },
+  "&gnE;": { "codepoints": [8809], "characters": "\u2269" },
+  "&gnap;": { "codepoints": [10890], "characters": "\u2A8A" },
+  "&gnapprox;": { "codepoints": [10890], "characters": "\u2A8A" },
+  "&gne;": { "codepoints": [10888], "characters": "\u2A88" },
+  "&gneq;": { "codepoints": [10888], "characters": "\u2A88" },
+  "&gneqq;": { "codepoints": [8809], "characters": "\u2269" },
+  "&gnsim;": { "codepoints": [8935], "characters": "\u22E7" },
+  "&gopf;": { "codepoints": [120152], "characters": "\uD835\uDD58" },
+  "&grave;": { "codepoints": [96], "characters": "\u0060" },
+  "&gscr;": { "codepoints": [8458], "characters": "\u210A" },
+  "&gsim;": { "codepoints": [8819], "characters": "\u2273" },
+  "&gsime;": { "codepoints": [10894], "characters": "\u2A8E" },
+  "&gsiml;": { "codepoints": [10896], "characters": "\u2A90" },
+  "&gt": { "codepoints": [62], "characters": "\u003E" },
+  "&gt;": { "codepoints": [62], "characters": "\u003E" },
+  "&gtcc;": { "codepoints": [10919], "characters": "\u2AA7" },
+  "&gtcir;": { "codepoints": [10874], "characters": "\u2A7A" },
+  "&gtdot;": { "codepoints": [8919], "characters": "\u22D7" },
+  "&gtlPar;": { "codepoints": [10645], "characters": "\u2995" },
+  "&gtquest;": { "codepoints": [10876], "characters": "\u2A7C" },
+  "&gtrapprox;": { "codepoints": [10886], "characters": "\u2A86" },
+  "&gtrarr;": { "codepoints": [10616], "characters": "\u2978" },
+  "&gtrdot;": { "codepoints": [8919], "characters": "\u22D7" },
+  "&gtreqless;": { "codepoints": [8923], "characters": "\u22DB" },
+  "&gtreqqless;": { "codepoints": [10892], "characters": "\u2A8C" },
+  "&gtrless;": { "codepoints": [8823], "characters": "\u2277" },
+  "&gtrsim;": { "codepoints": [8819], "characters": "\u2273" },
+  "&gvertneqq;": { "codepoints": [8809, 65024], "characters": "\u2269\uFE00" },
+  "&gvnE;": { "codepoints": [8809, 65024], "characters": "\u2269\uFE00" },
+  "&hArr;": { "codepoints": [8660], "characters": "\u21D4" },
+  "&hairsp;": { "codepoints": [8202], "characters": "\u200A" },
+  "&half;": { "codepoints": [189], "characters": "\u00BD" },
+  "&hamilt;": { "codepoints": [8459], "characters": "\u210B" },
+  "&hardcy;": { "codepoints": [1098], "characters": "\u044A" },
+  "&harr;": { "codepoints": [8596], "characters": "\u2194" },
+  "&harrcir;": { "codepoints": [10568], "characters": "\u2948" },
+  "&harrw;": { "codepoints": [8621], "characters": "\u21AD" },
+  "&hbar;": { "codepoints": [8463], "characters": "\u210F" },
+  "&hcirc;": { "codepoints": [293], "characters": "\u0125" },
+  "&hearts;": { "codepoints": [9829], "characters": "\u2665" },
+  "&heartsuit;": { "codepoints": [9829], "characters": "\u2665" },
+  "&hellip;": { "codepoints": [8230], "characters": "\u2026" },
+  "&hercon;": { "codepoints": [8889], "characters": "\u22B9" },
+  "&hfr;": { "codepoints": [120101], "characters": "\uD835\uDD25" },
+  "&hksearow;": { "codepoints": [10533], "characters": "\u2925" },
+  "&hkswarow;": { "codepoints": [10534], "characters": "\u2926" },
+  "&hoarr;": { "codepoints": [8703], "characters": "\u21FF" },
+  "&homtht;": { "codepoints": [8763], "characters": "\u223B" },
+  "&hookleftarrow;": { "codepoints": [8617], "characters": "\u21A9" },
+  "&hookrightarrow;": { "codepoints": [8618], "characters": "\u21AA" },
+  "&hopf;": { "codepoints": [120153], "characters": "\uD835\uDD59" },
+  "&horbar;": { "codepoints": [8213], "characters": "\u2015" },
+  "&hscr;": { "codepoints": [119997], "characters": "\uD835\uDCBD" },
+  "&hslash;": { "codepoints": [8463], "characters": "\u210F" },
+  "&hstrok;": { "codepoints": [295], "characters": "\u0127" },
+  "&hybull;": { "codepoints": [8259], "characters": "\u2043" },
+  "&hyphen;": { "codepoints": [8208], "characters": "\u2010" },
+  "&iacute": { "codepoints": [237], "characters": "\u00ED" },
+  "&iacute;": { "codepoints": [237], "characters": "\u00ED" },
+  "&ic;": { "codepoints": [8291], "characters": "\u2063" },
+  "&icirc": { "codepoints": [238], "characters": "\u00EE" },
+  "&icirc;": { "codepoints": [238], "characters": "\u00EE" },
+  "&icy;": { "codepoints": [1080], "characters": "\u0438" },
+  "&iecy;": { "codepoints": [1077], "characters": "\u0435" },
+  "&iexcl": { "codepoints": [161], "characters": "\u00A1" },
+  "&iexcl;": { "codepoints": [161], "characters": "\u00A1" },
+  "&iff;": { "codepoints": [8660], "characters": "\u21D4" },
+  "&ifr;": { "codepoints": [120102], "characters": "\uD835\uDD26" },
+  "&igrave": { "codepoints": [236], "characters": "\u00EC" },
+  "&igrave;": { "codepoints": [236], "characters": "\u00EC" },
+  "&ii;": { "codepoints": [8520], "characters": "\u2148" },
+  "&iiiint;": { "codepoints": [10764], "characters": "\u2A0C" },
+  "&iiint;": { "codepoints": [8749], "characters": "\u222D" },
+  "&iinfin;": { "codepoints": [10716], "characters": "\u29DC" },
+  "&iiota;": { "codepoints": [8489], "characters": "\u2129" },
+  "&ijlig;": { "codepoints": [307], "characters": "\u0133" },
+  "&imacr;": { "codepoints": [299], "characters": "\u012B" },
+  "&image;": { "codepoints": [8465], "characters": "\u2111" },
+  "&imagline;": { "codepoints": [8464], "characters": "\u2110" },
+  "&imagpart;": { "codepoints": [8465], "characters": "\u2111" },
+  "&imath;": { "codepoints": [305], "characters": "\u0131" },
+  "&imof;": { "codepoints": [8887], "characters": "\u22B7" },
+  "&imped;": { "codepoints": [437], "characters": "\u01B5" },
+  "&in;": { "codepoints": [8712], "characters": "\u2208" },
+  "&incare;": { "codepoints": [8453], "characters": "\u2105" },
+  "&infin;": { "codepoints": [8734], "characters": "\u221E" },
+  "&infintie;": { "codepoints": [10717], "characters": "\u29DD" },
+  "&inodot;": { "codepoints": [305], "characters": "\u0131" },
+  "&int;": { "codepoints": [8747], "characters": "\u222B" },
+  "&intcal;": { "codepoints": [8890], "characters": "\u22BA" },
+  "&integers;": { "codepoints": [8484], "characters": "\u2124" },
+  "&intercal;": { "codepoints": [8890], "characters": "\u22BA" },
+  "&intlarhk;": { "codepoints": [10775], "characters": "\u2A17" },
+  "&intprod;": { "codepoints": [10812], "characters": "\u2A3C" },
+  "&iocy;": { "codepoints": [1105], "characters": "\u0451" },
+  "&iogon;": { "codepoints": [303], "characters": "\u012F" },
+  "&iopf;": { "codepoints": [120154], "characters": "\uD835\uDD5A" },
+  "&iota;": { "codepoints": [953], "characters": "\u03B9" },
+  "&iprod;": { "codepoints": [10812], "characters": "\u2A3C" },
+  "&iquest": { "codepoints": [191], "characters": "\u00BF" },
+  "&iquest;": { "codepoints": [191], "characters": "\u00BF" },
+  "&iscr;": { "codepoints": [119998], "characters": "\uD835\uDCBE" },
+  "&isin;": { "codepoints": [8712], "characters": "\u2208" },
+  "&isinE;": { "codepoints": [8953], "characters": "\u22F9" },
+  "&isindot;": { "codepoints": [8949], "characters": "\u22F5" },
+  "&isins;": { "codepoints": [8948], "characters": "\u22F4" },
+  "&isinsv;": { "codepoints": [8947], "characters": "\u22F3" },
+  "&isinv;": { "codepoints": [8712], "characters": "\u2208" },
+  "&it;": { "codepoints": [8290], "characters": "\u2062" },
+  "&itilde;": { "codepoints": [297], "characters": "\u0129" },
+  "&iukcy;": { "codepoints": [1110], "characters": "\u0456" },
+  "&iuml": { "codepoints": [239], "characters": "\u00EF" },
+  "&iuml;": { "codepoints": [239], "characters": "\u00EF" },
+  "&jcirc;": { "codepoints": [309], "characters": "\u0135" },
+  "&jcy;": { "codepoints": [1081], "characters": "\u0439" },
+  "&jfr;": { "codepoints": [120103], "characters": "\uD835\uDD27" },
+  "&jmath;": { "codepoints": [567], "characters": "\u0237" },
+  "&jopf;": { "codepoints": [120155], "characters": "\uD835\uDD5B" },
+  "&jscr;": { "codepoints": [119999], "characters": "\uD835\uDCBF" },
+  "&jsercy;": { "codepoints": [1112], "characters": "\u0458" },
+  "&jukcy;": { "codepoints": [1108], "characters": "\u0454" },
+  "&kappa;": { "codepoints": [954], "characters": "\u03BA" },
+  "&kappav;": { "codepoints": [1008], "characters": "\u03F0" },
+  "&kcedil;": { "codepoints": [311], "characters": "\u0137" },
+  "&kcy;": { "codepoints": [1082], "characters": "\u043A" },
+  "&kfr;": { "codepoints": [120104], "characters": "\uD835\uDD28" },
+  "&kgreen;": { "codepoints": [312], "characters": "\u0138" },
+  "&khcy;": { "codepoints": [1093], "characters": "\u0445" },
+  "&kjcy;": { "codepoints": [1116], "characters": "\u045C" },
+  "&kopf;": { "codepoints": [120156], "characters": "\uD835\uDD5C" },
+  "&kscr;": { "codepoints": [120000], "characters": "\uD835\uDCC0" },
+  "&lAarr;": { "codepoints": [8666], "characters": "\u21DA" },
+  "&lArr;": { "codepoints": [8656], "characters": "\u21D0" },
+  "&lAtail;": { "codepoints": [10523], "characters": "\u291B" },
+  "&lBarr;": { "codepoints": [10510], "characters": "\u290E" },
+  "&lE;": { "codepoints": [8806], "characters": "\u2266" },
+  "&lEg;": { "codepoints": [10891], "characters": "\u2A8B" },
+  "&lHar;": { "codepoints": [10594], "characters": "\u2962" },
+  "&lacute;": { "codepoints": [314], "characters": "\u013A" },
+  "&laemptyv;": { "codepoints": [10676], "characters": "\u29B4" },
+  "&lagran;": { "codepoints": [8466], "characters": "\u2112" },
+  "&lambda;": { "codepoints": [955], "characters": "\u03BB" },
+  "&lang;": { "codepoints": [10216], "characters": "\u27E8" },
+  "&langd;": { "codepoints": [10641], "characters": "\u2991" },
+  "&langle;": { "codepoints": [10216], "characters": "\u27E8" },
+  "&lap;": { "codepoints": [10885], "characters": "\u2A85" },
+  "&laquo": { "codepoints": [171], "characters": "\u00AB" },
+  "&laquo;": { "codepoints": [171], "characters": "\u00AB" },
+  "&larr;": { "codepoints": [8592], "characters": "\u2190" },
+  "&larrb;": { "codepoints": [8676], "characters": "\u21E4" },
+  "&larrbfs;": { "codepoints": [10527], "characters": "\u291F" },
+  "&larrfs;": { "codepoints": [10525], "characters": "\u291D" },
+  "&larrhk;": { "codepoints": [8617], "characters": "\u21A9" },
+  "&larrlp;": { "codepoints": [8619], "characters": "\u21AB" },
+  "&larrpl;": { "codepoints": [10553], "characters": "\u2939" },
+  "&larrsim;": { "codepoints": [10611], "characters": "\u2973" },
+  "&larrtl;": { "codepoints": [8610], "characters": "\u21A2" },
+  "&lat;": { "codepoints": [10923], "characters": "\u2AAB" },
+  "&latail;": { "codepoints": [10521], "characters": "\u2919" },
+  "&late;": { "codepoints": [10925], "characters": "\u2AAD" },
+  "&lates;": { "codepoints": [10925, 65024], "characters": "\u2AAD\uFE00" },
+  "&lbarr;": { "codepoints": [10508], "characters": "\u290C" },
+  "&lbbrk;": { "codepoints": [10098], "characters": "\u2772" },
+  "&lbrace;": { "codepoints": [123], "characters": "\u007B" },
+  "&lbrack;": { "codepoints": [91], "characters": "\u005B" },
+  "&lbrke;": { "codepoints": [10635], "characters": "\u298B" },
+  "&lbrksld;": { "codepoints": [10639], "characters": "\u298F" },
+  "&lbrkslu;": { "codepoints": [10637], "characters": "\u298D" },
+  "&lcaron;": { "codepoints": [318], "characters": "\u013E" },
+  "&lcedil;": { "codepoints": [316], "characters": "\u013C" },
+  "&lceil;": { "codepoints": [8968], "characters": "\u2308" },
+  "&lcub;": { "codepoints": [123], "characters": "\u007B" },
+  "&lcy;": { "codepoints": [1083], "characters": "\u043B" },
+  "&ldca;": { "codepoints": [10550], "characters": "\u2936" },
+  "&ldquo;": { "codepoints": [8220], "characters": "\u201C" },
+  "&ldquor;": { "codepoints": [8222], "characters": "\u201E" },
+  "&ldrdhar;": { "codepoints": [10599], "characters": "\u2967" },
+  "&ldrushar;": { "codepoints": [10571], "characters": "\u294B" },
+  "&ldsh;": { "codepoints": [8626], "characters": "\u21B2" },
+  "&le;": { "codepoints": [8804], "characters": "\u2264" },
+  "&leftarrow;": { "codepoints": [8592], "characters": "\u2190" },
+  "&leftarrowtail;": { "codepoints": [8610], "characters": "\u21A2" },
+  "&leftharpoondown;": { "codepoints": [8637], "characters": "\u21BD" },
+  "&leftharpoonup;": { "codepoints": [8636], "characters": "\u21BC" },
+  "&leftleftarrows;": { "codepoints": [8647], "characters": "\u21C7" },
+  "&leftrightarrow;": { "codepoints": [8596], "characters": "\u2194" },
+  "&leftrightarrows;": { "codepoints": [8646], "characters": "\u21C6" },
+  "&leftrightharpoons;": { "codepoints": [8651], "characters": "\u21CB" },
+  "&leftrightsquigarrow;": { "codepoints": [8621], "characters": "\u21AD" },
+  "&leftthreetimes;": { "codepoints": [8907], "characters": "\u22CB" },
+  "&leg;": { "codepoints": [8922], "characters": "\u22DA" },
+  "&leq;": { "codepoints": [8804], "characters": "\u2264" },
+  "&leqq;": { "codepoints": [8806], "characters": "\u2266" },
+  "&leqslant;": { "codepoints": [10877], "characters": "\u2A7D" },
+  "&les;": { "codepoints": [10877], "characters": "\u2A7D" },
+  "&lescc;": { "codepoints": [10920], "characters": "\u2AA8" },
+  "&lesdot;": { "codepoints": [10879], "characters": "\u2A7F" },
+  "&lesdoto;": { "codepoints": [10881], "characters": "\u2A81" },
+  "&lesdotor;": { "codepoints": [10883], "characters": "\u2A83" },
+  "&lesg;": { "codepoints": [8922, 65024], "characters": "\u22DA\uFE00" },
+  "&lesges;": { "codepoints": [10899], "characters": "\u2A93" },
+  "&lessapprox;": { "codepoints": [10885], "characters": "\u2A85" },
+  "&lessdot;": { "codepoints": [8918], "characters": "\u22D6" },
+  "&lesseqgtr;": { "codepoints": [8922], "characters": "\u22DA" },
+  "&lesseqqgtr;": { "codepoints": [10891], "characters": "\u2A8B" },
+  "&lessgtr;": { "codepoints": [8822], "characters": "\u2276" },
+  "&lesssim;": { "codepoints": [8818], "characters": "\u2272" },
+  "&lfisht;": { "codepoints": [10620], "characters": "\u297C" },
+  "&lfloor;": { "codepoints": [8970], "characters": "\u230A" },
+  "&lfr;": { "codepoints": [120105], "characters": "\uD835\uDD29" },
+  "&lg;": { "codepoints": [8822], "characters": "\u2276" },
+  "&lgE;": { "codepoints": [10897], "characters": "\u2A91" },
+  "&lhard;": { "codepoints": [8637], "characters": "\u21BD" },
+  "&lharu;": { "codepoints": [8636], "characters": "\u21BC" },
+  "&lharul;": { "codepoints": [10602], "characters": "\u296A" },
+  "&lhblk;": { "codepoints": [9604], "characters": "\u2584" },
+  "&ljcy;": { "codepoints": [1113], "characters": "\u0459" },
+  "&ll;": { "codepoints": [8810], "characters": "\u226A" },
+  "&llarr;": { "codepoints": [8647], "characters": "\u21C7" },
+  "&llcorner;": { "codepoints": [8990], "characters": "\u231E" },
+  "&llhard;": { "codepoints": [10603], "characters": "\u296B" },
+  "&lltri;": { "codepoints": [9722], "characters": "\u25FA" },
+  "&lmidot;": { "codepoints": [320], "characters": "\u0140" },
+  "&lmoust;": { "codepoints": [9136], "characters": "\u23B0" },
+  "&lmoustache;": { "codepoints": [9136], "characters": "\u23B0" },
+  "&lnE;": { "codepoints": [8808], "characters": "\u2268" },
+  "&lnap;": { "codepoints": [10889], "characters": "\u2A89" },
+  "&lnapprox;": { "codepoints": [10889], "characters": "\u2A89" },
+  "&lne;": { "codepoints": [10887], "characters": "\u2A87" },
+  "&lneq;": { "codepoints": [10887], "characters": "\u2A87" },
+  "&lneqq;": { "codepoints": [8808], "characters": "\u2268" },
+  "&lnsim;": { "codepoints": [8934], "characters": "\u22E6" },
+  "&loang;": { "codepoints": [10220], "characters": "\u27EC" },
+  "&loarr;": { "codepoints": [8701], "characters": "\u21FD" },
+  "&lobrk;": { "codepoints": [10214], "characters": "\u27E6" },
+  "&longleftarrow;": { "codepoints": [10229], "characters": "\u27F5" },
+  "&longleftrightarrow;": { "codepoints": [10231], "characters": "\u27F7" },
+  "&longmapsto;": { "codepoints": [10236], "characters": "\u27FC" },
+  "&longrightarrow;": { "codepoints": [10230], "characters": "\u27F6" },
+  "&looparrowleft;": { "codepoints": [8619], "characters": "\u21AB" },
+  "&looparrowright;": { "codepoints": [8620], "characters": "\u21AC" },
+  "&lopar;": { "codepoints": [10629], "characters": "\u2985" },
+  "&lopf;": { "codepoints": [120157], "characters": "\uD835\uDD5D" },
+  "&loplus;": { "codepoints": [10797], "characters": "\u2A2D" },
+  "&lotimes;": { "codepoints": [10804], "characters": "\u2A34" },
+  "&lowast;": { "codepoints": [8727], "characters": "\u2217" },
+  "&lowbar;": { "codepoints": [95], "characters": "\u005F" },
+  "&loz;": { "codepoints": [9674], "characters": "\u25CA" },
+  "&lozenge;": { "codepoints": [9674], "characters": "\u25CA" },
+  "&lozf;": { "codepoints": [10731], "characters": "\u29EB" },
+  "&lpar;": { "codepoints": [40], "characters": "\u0028" },
+  "&lparlt;": { "codepoints": [10643], "characters": "\u2993" },
+  "&lrarr;": { "codepoints": [8646], "characters": "\u21C6" },
+  "&lrcorner;": { "codepoints": [8991], "characters": "\u231F" },
+  "&lrhar;": { "codepoints": [8651], "characters": "\u21CB" },
+  "&lrhard;": { "codepoints": [10605], "characters": "\u296D" },
+  "&lrm;": { "codepoints": [8206], "characters": "\u200E" },
+  "&lrtri;": { "codepoints": [8895], "characters": "\u22BF" },
+  "&lsaquo;": { "codepoints": [8249], "characters": "\u2039" },
+  "&lscr;": { "codepoints": [120001], "characters": "\uD835\uDCC1" },
+  "&lsh;": { "codepoints": [8624], "characters": "\u21B0" },
+  "&lsim;": { "codepoints": [8818], "characters": "\u2272" },
+  "&lsime;": { "codepoints": [10893], "characters": "\u2A8D" },
+  "&lsimg;": { "codepoints": [10895], "characters": "\u2A8F" },
+  "&lsqb;": { "codepoints": [91], "characters": "\u005B" },
+  "&lsquo;": { "codepoints": [8216], "characters": "\u2018" },
+  "&lsquor;": { "codepoints": [8218], "characters": "\u201A" },
+  "&lstrok;": { "codepoints": [322], "characters": "\u0142" },
+  "&lt": { "codepoints": [60], "characters": "\u003C" },
+  "&lt;": { "codepoints": [60], "characters": "\u003C" },
+  "&ltcc;": { "codepoints": [10918], "characters": "\u2AA6" },
+  "&ltcir;": { "codepoints": [10873], "characters": "\u2A79" },
+  "&ltdot;": { "codepoints": [8918], "characters": "\u22D6" },
+  "&lthree;": { "codepoints": [8907], "characters": "\u22CB" },
+  "&ltimes;": { "codepoints": [8905], "characters": "\u22C9" },
+  "&ltlarr;": { "codepoints": [10614], "characters": "\u2976" },
+  "&ltquest;": { "codepoints": [10875], "characters": "\u2A7B" },
+  "&ltrPar;": { "codepoints": [10646], "characters": "\u2996" },
+  "&ltri;": { "codepoints": [9667], "characters": "\u25C3" },
+  "&ltrie;": { "codepoints": [8884], "characters": "\u22B4" },
+  "&ltrif;": { "codepoints": [9666], "characters": "\u25C2" },
+  "&lurdshar;": { "codepoints": [10570], "characters": "\u294A" },
+  "&luruhar;": { "codepoints": [10598], "characters": "\u2966" },
+  "&lvertneqq;": { "codepoints": [8808, 65024], "characters": "\u2268\uFE00" },
+  "&lvnE;": { "codepoints": [8808, 65024], "characters": "\u2268\uFE00" },
+  "&mDDot;": { "codepoints": [8762], "characters": "\u223A" },
+  "&macr": { "codepoints": [175], "characters": "\u00AF" },
+  "&macr;": { "codepoints": [175], "characters": "\u00AF" },
+  "&male;": { "codepoints": [9794], "characters": "\u2642" },
+  "&malt;": { "codepoints": [10016], "characters": "\u2720" },
+  "&maltese;": { "codepoints": [10016], "characters": "\u2720" },
+  "&map;": { "codepoints": [8614], "characters": "\u21A6" },
+  "&mapsto;": { "codepoints": [8614], "characters": "\u21A6" },
+  "&mapstodown;": { "codepoints": [8615], "characters": "\u21A7" },
+  "&mapstoleft;": { "codepoints": [8612], "characters": "\u21A4" },
+  "&mapstoup;": { "codepoints": [8613], "characters": "\u21A5" },
+  "&marker;": { "codepoints": [9646], "characters": "\u25AE" },
+  "&mcomma;": { "codepoints": [10793], "characters": "\u2A29" },
+  "&mcy;": { "codepoints": [1084], "characters": "\u043C" },
+  "&mdash;": { "codepoints": [8212], "characters": "\u2014" },
+  "&measuredangle;": { "codepoints": [8737], "characters": "\u2221" },
+  "&mfr;": { "codepoints": [120106], "characters": "\uD835\uDD2A" },
+  "&mho;": { "codepoints": [8487], "characters": "\u2127" },
+  "&micro": { "codepoints": [181], "characters": "\u00B5" },
+  "&micro;": { "codepoints": [181], "characters": "\u00B5" },
+  "&mid;": { "codepoints": [8739], "characters": "\u2223" },
+  "&midast;": { "codepoints": [42], "characters": "\u002A" },
+  "&midcir;": { "codepoints": [10992], "characters": "\u2AF0" },
+  "&middot": { "codepoints": [183], "characters": "\u00B7" },
+  "&middot;": { "codepoints": [183], "characters": "\u00B7" },
+  "&minus;": { "codepoints": [8722], "characters": "\u2212" },
+  "&minusb;": { "codepoints": [8863], "characters": "\u229F" },
+  "&minusd;": { "codepoints": [8760], "characters": "\u2238" },
+  "&minusdu;": { "codepoints": [10794], "characters": "\u2A2A" },
+  "&mlcp;": { "codepoints": [10971], "characters": "\u2ADB" },
+  "&mldr;": { "codepoints": [8230], "characters": "\u2026" },
+  "&mnplus;": { "codepoints": [8723], "characters": "\u2213" },
+  "&models;": { "codepoints": [8871], "characters": "\u22A7" },
+  "&mopf;": { "codepoints": [120158], "characters": "\uD835\uDD5E" },
+  "&mp;": { "codepoints": [8723], "characters": "\u2213" },
+  "&mscr;": { "codepoints": [120002], "characters": "\uD835\uDCC2" },
+  "&mstpos;": { "codepoints": [8766], "characters": "\u223E" },
+  "&mu;": { "codepoints": [956], "characters": "\u03BC" },
+  "&multimap;": { "codepoints": [8888], "characters": "\u22B8" },
+  "&mumap;": { "codepoints": [8888], "characters": "\u22B8" },
+  "&nGg;": { "codepoints": [8921, 824], "characters": "\u22D9\u0338" },
+  "&nGt;": { "codepoints": [8811, 8402], "characters": "\u226B\u20D2" },
+  "&nGtv;": { "codepoints": [8811, 824], "characters": "\u226B\u0338" },
+  "&nLeftarrow;": { "codepoints": [8653], "characters": "\u21CD" },
+  "&nLeftrightarrow;": { "codepoints": [8654], "characters": "\u21CE" },
+  "&nLl;": { "codepoints": [8920, 824], "characters": "\u22D8\u0338" },
+  "&nLt;": { "codepoints": [8810, 8402], "characters": "\u226A\u20D2" },
+  "&nLtv;": { "codepoints": [8810, 824], "characters": "\u226A\u0338" },
+  "&nRightarrow;": { "codepoints": [8655], "characters": "\u21CF" },
+  "&nVDash;": { "codepoints": [8879], "characters": "\u22AF" },
+  "&nVdash;": { "codepoints": [8878], "characters": "\u22AE" },
+  "&nabla;": { "codepoints": [8711], "characters": "\u2207" },
+  "&nacute;": { "codepoints": [324], "characters": "\u0144" },
+  "&nang;": { "codepoints": [8736, 8402], "characters": "\u2220\u20D2" },
+  "&nap;": { "codepoints": [8777], "characters": "\u2249" },
+  "&napE;": { "codepoints": [10864, 824], "characters": "\u2A70\u0338" },
+  "&napid;": { "codepoints": [8779, 824], "characters": "\u224B\u0338" },
+  "&napos;": { "codepoints": [329], "characters": "\u0149" },
+  "&napprox;": { "codepoints": [8777], "characters": "\u2249" },
+  "&natur;": { "codepoints": [9838], "characters": "\u266E" },
+  "&natural;": { "codepoints": [9838], "characters": "\u266E" },
+  "&naturals;": { "codepoints": [8469], "characters": "\u2115" },
+  "&nbsp": { "codepoints": [160], "characters": "\u00A0" },
+  "&nbsp;": { "codepoints": [160], "characters": "\u00A0" },
+  "&nbump;": { "codepoints": [8782, 824], "characters": "\u224E\u0338" },
+  "&nbumpe;": { "codepoints": [8783, 824], "characters": "\u224F\u0338" },
+  "&ncap;": { "codepoints": [10819], "characters": "\u2A43" },
+  "&ncaron;": { "codepoints": [328], "characters": "\u0148" },
+  "&ncedil;": { "codepoints": [326], "characters": "\u0146" },
+  "&ncong;": { "codepoints": [8775], "characters": "\u2247" },
+  "&ncongdot;": { "codepoints": [10861, 824], "characters": "\u2A6D\u0338" },
+  "&ncup;": { "codepoints": [10818], "characters": "\u2A42" },
+  "&ncy;": { "codepoints": [1085], "characters": "\u043D" },
+  "&ndash;": { "codepoints": [8211], "characters": "\u2013" },
+  "&ne;": { "codepoints": [8800], "characters": "\u2260" },
+  "&neArr;": { "codepoints": [8663], "characters": "\u21D7" },
+  "&nearhk;": { "codepoints": [10532], "characters": "\u2924" },
+  "&nearr;": { "codepoints": [8599], "characters": "\u2197" },
+  "&nearrow;": { "codepoints": [8599], "characters": "\u2197" },
+  "&nedot;": { "codepoints": [8784, 824], "characters": "\u2250\u0338" },
+  "&nequiv;": { "codepoints": [8802], "characters": "\u2262" },
+  "&nesear;": { "codepoints": [10536], "characters": "\u2928" },
+  "&nesim;": { "codepoints": [8770, 824], "characters": "\u2242\u0338" },
+  "&nexist;": { "codepoints": [8708], "characters": "\u2204" },
+  "&nexists;": { "codepoints": [8708], "characters": "\u2204" },
+  "&nfr;": { "codepoints": [120107], "characters": "\uD835\uDD2B" },
+  "&ngE;": { "codepoints": [8807, 824], "characters": "\u2267\u0338" },
+  "&nge;": { "codepoints": [8817], "characters": "\u2271" },
+  "&ngeq;": { "codepoints": [8817], "characters": "\u2271" },
+  "&ngeqq;": { "codepoints": [8807, 824], "characters": "\u2267\u0338" },
+  "&ngeqslant;": { "codepoints": [10878, 824], "characters": "\u2A7E\u0338" },
+  "&nges;": { "codepoints": [10878, 824], "characters": "\u2A7E\u0338" },
+  "&ngsim;": { "codepoints": [8821], "characters": "\u2275" },
+  "&ngt;": { "codepoints": [8815], "characters": "\u226F" },
+  "&ngtr;": { "codepoints": [8815], "characters": "\u226F" },
+  "&nhArr;": { "codepoints": [8654], "characters": "\u21CE" },
+  "&nharr;": { "codepoints": [8622], "characters": "\u21AE" },
+  "&nhpar;": { "codepoints": [10994], "characters": "\u2AF2" },
+  "&ni;": { "codepoints": [8715], "characters": "\u220B" },
+  "&nis;": { "codepoints": [8956], "characters": "\u22FC" },
+  "&nisd;": { "codepoints": [8954], "characters": "\u22FA" },
+  "&niv;": { "codepoints": [8715], "characters": "\u220B" },
+  "&njcy;": { "codepoints": [1114], "characters": "\u045A" },
+  "&nlArr;": { "codepoints": [8653], "characters": "\u21CD" },
+  "&nlE;": { "codepoints": [8806, 824], "characters": "\u2266\u0338" },
+  "&nlarr;": { "codepoints": [8602], "characters": "\u219A" },
+  "&nldr;": { "codepoints": [8229], "characters": "\u2025" },
+  "&nle;": { "codepoints": [8816], "characters": "\u2270" },
+  "&nleftarrow;": { "codepoints": [8602], "characters": "\u219A" },
+  "&nleftrightarrow;": { "codepoints": [8622], "characters": "\u21AE" },
+  "&nleq;": { "codepoints": [8816], "characters": "\u2270" },
+  "&nleqq;": { "codepoints": [8806, 824], "characters": "\u2266\u0338" },
+  "&nleqslant;": { "codepoints": [10877, 824], "characters": "\u2A7D\u0338" },
+  "&nles;": { "codepoints": [10877, 824], "characters": "\u2A7D\u0338" },
+  "&nless;": { "codepoints": [8814], "characters": "\u226E" },
+  "&nlsim;": { "codepoints": [8820], "characters": "\u2274" },
+  "&nlt;": { "codepoints": [8814], "characters": "\u226E" },
+  "&nltri;": { "codepoints": [8938], "characters": "\u22EA" },
+  "&nltrie;": { "codepoints": [8940], "characters": "\u22EC" },
+  "&nmid;": { "codepoints": [8740], "characters": "\u2224" },
+  "&nopf;": { "codepoints": [120159], "characters": "\uD835\uDD5F" },
+  "&not": { "codepoints": [172], "characters": "\u00AC" },
+  "&not;": { "codepoints": [172], "characters": "\u00AC" },
+  "&notin;": { "codepoints": [8713], "characters": "\u2209" },
+  "&notinE;": { "codepoints": [8953, 824], "characters": "\u22F9\u0338" },
+  "&notindot;": { "codepoints": [8949, 824], "characters": "\u22F5\u0338" },
+  "&notinva;": { "codepoints": [8713], "characters": "\u2209" },
+  "&notinvb;": { "codepoints": [8951], "characters": "\u22F7" },
+  "&notinvc;": { "codepoints": [8950], "characters": "\u22F6" },
+  "&notni;": { "codepoints": [8716], "characters": "\u220C" },
+  "&notniva;": { "codepoints": [8716], "characters": "\u220C" },
+  "&notnivb;": { "codepoints": [8958], "characters": "\u22FE" },
+  "&notnivc;": { "codepoints": [8957], "characters": "\u22FD" },
+  "&npar;": { "codepoints": [8742], "characters": "\u2226" },
+  "&nparallel;": { "codepoints": [8742], "characters": "\u2226" },
+  "&nparsl;": { "codepoints": [11005, 8421], "characters": "\u2AFD\u20E5" },
+  "&npart;": { "codepoints": [8706, 824], "characters": "\u2202\u0338" },
+  "&npolint;": { "codepoints": [10772], "characters": "\u2A14" },
+  "&npr;": { "codepoints": [8832], "characters": "\u2280" },
+  "&nprcue;": { "codepoints": [8928], "characters": "\u22E0" },
+  "&npre;": { "codepoints": [10927, 824], "characters": "\u2AAF\u0338" },
+  "&nprec;": { "codepoints": [8832], "characters": "\u2280" },
+  "&npreceq;": { "codepoints": [10927, 824], "characters": "\u2AAF\u0338" },
+  "&nrArr;": { "codepoints": [8655], "characters": "\u21CF" },
+  "&nrarr;": { "codepoints": [8603], "characters": "\u219B" },
+  "&nrarrc;": { "codepoints": [10547, 824], "characters": "\u2933\u0338" },
+  "&nrarrw;": { "codepoints": [8605, 824], "characters": "\u219D\u0338" },
+  "&nrightarrow;": { "codepoints": [8603], "characters": "\u219B" },
+  "&nrtri;": { "codepoints": [8939], "characters": "\u22EB" },
+  "&nrtrie;": { "codepoints": [8941], "characters": "\u22ED" },
+  "&nsc;": { "codepoints": [8833], "characters": "\u2281" },
+  "&nsccue;": { "codepoints": [8929], "characters": "\u22E1" },
+  "&nsce;": { "codepoints": [10928, 824], "characters": "\u2AB0\u0338" },
+  "&nscr;": { "codepoints": [120003], "characters": "\uD835\uDCC3" },
+  "&nshortmid;": { "codepoints": [8740], "characters": "\u2224" },
+  "&nshortparallel;": { "codepoints": [8742], "characters": "\u2226" },
+  "&nsim;": { "codepoints": [8769], "characters": "\u2241" },
+  "&nsime;": { "codepoints": [8772], "characters": "\u2244" },
+  "&nsimeq;": { "codepoints": [8772], "characters": "\u2244" },
+  "&nsmid;": { "codepoints": [8740], "characters": "\u2224" },
+  "&nspar;": { "codepoints": [8742], "characters": "\u2226" },
+  "&nsqsube;": { "codepoints": [8930], "characters": "\u22E2" },
+  "&nsqsupe;": { "codepoints": [8931], "characters": "\u22E3" },
+  "&nsub;": { "codepoints": [8836], "characters": "\u2284" },
+  "&nsubE;": { "codepoints": [10949, 824], "characters": "\u2AC5\u0338" },
+  "&nsube;": { "codepoints": [8840], "characters": "\u2288" },
+  "&nsubset;": { "codepoints": [8834, 8402], "characters": "\u2282\u20D2" },
+  "&nsubseteq;": { "codepoints": [8840], "characters": "\u2288" },
+  "&nsubseteqq;": { "codepoints": [10949, 824], "characters": "\u2AC5\u0338" },
+  "&nsucc;": { "codepoints": [8833], "characters": "\u2281" },
+  "&nsucceq;": { "codepoints": [10928, 824], "characters": "\u2AB0\u0338" },
+  "&nsup;": { "codepoints": [8837], "characters": "\u2285" },
+  "&nsupE;": { "codepoints": [10950, 824], "characters": "\u2AC6\u0338" },
+  "&nsupe;": { "codepoints": [8841], "characters": "\u2289" },
+  "&nsupset;": { "codepoints": [8835, 8402], "characters": "\u2283\u20D2" },
+  "&nsupseteq;": { "codepoints": [8841], "characters": "\u2289" },
+  "&nsupseteqq;": { "codepoints": [10950, 824], "characters": "\u2AC6\u0338" },
+  "&ntgl;": { "codepoints": [8825], "characters": "\u2279" },
+  "&ntilde": { "codepoints": [241], "characters": "\u00F1" },
+  "&ntilde;": { "codepoints": [241], "characters": "\u00F1" },
+  "&ntlg;": { "codepoints": [8824], "characters": "\u2278" },
+  "&ntriangleleft;": { "codepoints": [8938], "characters": "\u22EA" },
+  "&ntrianglelefteq;": { "codepoints": [8940], "characters": "\u22EC" },
+  "&ntriangleright;": { "codepoints": [8939], "characters": "\u22EB" },
+  "&ntrianglerighteq;": { "codepoints": [8941], "characters": "\u22ED" },
+  "&nu;": { "codepoints": [957], "characters": "\u03BD" },
+  "&num;": { "codepoints": [35], "characters": "\u0023" },
+  "&numero;": { "codepoints": [8470], "characters": "\u2116" },
+  "&numsp;": { "codepoints": [8199], "characters": "\u2007" },
+  "&nvDash;": { "codepoints": [8877], "characters": "\u22AD" },
+  "&nvHarr;": { "codepoints": [10500], "characters": "\u2904" },
+  "&nvap;": { "codepoints": [8781, 8402], "characters": "\u224D\u20D2" },
+  "&nvdash;": { "codepoints": [8876], "characters": "\u22AC" },
+  "&nvge;": { "codepoints": [8805, 8402], "characters": "\u2265\u20D2" },
+  "&nvgt;": { "codepoints": [62, 8402], "characters": "\u003E\u20D2" },
+  "&nvinfin;": { "codepoints": [10718], "characters": "\u29DE" },
+  "&nvlArr;": { "codepoints": [10498], "characters": "\u2902" },
+  "&nvle;": { "codepoints": [8804, 8402], "characters": "\u2264\u20D2" },
+  "&nvlt;": { "codepoints": [60, 8402], "characters": "\u003C\u20D2" },
+  "&nvltrie;": { "codepoints": [8884, 8402], "characters": "\u22B4\u20D2" },
+  "&nvrArr;": { "codepoints": [10499], "characters": "\u2903" },
+  "&nvrtrie;": { "codepoints": [8885, 8402], "characters": "\u22B5\u20D2" },
+  "&nvsim;": { "codepoints": [8764, 8402], "characters": "\u223C\u20D2" },
+  "&nwArr;": { "codepoints": [8662], "characters": "\u21D6" },
+  "&nwarhk;": { "codepoints": [10531], "characters": "\u2923" },
+  "&nwarr;": { "codepoints": [8598], "characters": "\u2196" },
+  "&nwarrow;": { "codepoints": [8598], "characters": "\u2196" },
+  "&nwnear;": { "codepoints": [10535], "characters": "\u2927" },
+  "&oS;": { "codepoints": [9416], "characters": "\u24C8" },
+  "&oacute": { "codepoints": [243], "characters": "\u00F3" },
+  "&oacute;": { "codepoints": [243], "characters": "\u00F3" },
+  "&oast;": { "codepoints": [8859], "characters": "\u229B" },
+  "&ocir;": { "codepoints": [8858], "characters": "\u229A" },
+  "&ocirc": { "codepoints": [244], "characters": "\u00F4" },
+  "&ocirc;": { "codepoints": [244], "characters": "\u00F4" },
+  "&ocy;": { "codepoints": [1086], "characters": "\u043E" },
+  "&odash;": { "codepoints": [8861], "characters": "\u229D" },
+  "&odblac;": { "codepoints": [337], "characters": "\u0151" },
+  "&odiv;": { "codepoints": [10808], "characters": "\u2A38" },
+  "&odot;": { "codepoints": [8857], "characters": "\u2299" },
+  "&odsold;": { "codepoints": [10684], "characters": "\u29BC" },
+  "&oelig;": { "codepoints": [339], "characters": "\u0153" },
+  "&ofcir;": { "codepoints": [10687], "characters": "\u29BF" },
+  "&ofr;": { "codepoints": [120108], "characters": "\uD835\uDD2C" },
+  "&ogon;": { "codepoints": [731], "characters": "\u02DB" },
+  "&ograve": { "codepoints": [242], "characters": "\u00F2" },
+  "&ograve;": { "codepoints": [242], "characters": "\u00F2" },
+  "&ogt;": { "codepoints": [10689], "characters": "\u29C1" },
+  "&ohbar;": { "codepoints": [10677], "characters": "\u29B5" },
+  "&ohm;": { "codepoints": [937], "characters": "\u03A9" },
+  "&oint;": { "codepoints": [8750], "characters": "\u222E" },
+  "&olarr;": { "codepoints": [8634], "characters": "\u21BA" },
+  "&olcir;": { "codepoints": [10686], "characters": "\u29BE" },
+  "&olcross;": { "codepoints": [10683], "characters": "\u29BB" },
+  "&oline;": { "codepoints": [8254], "characters": "\u203E" },
+  "&olt;": { "codepoints": [10688], "characters": "\u29C0" },
+  "&omacr;": { "codepoints": [333], "characters": "\u014D" },
+  "&omega;": { "codepoints": [969], "characters": "\u03C9" },
+  "&omicron;": { "codepoints": [959], "characters": "\u03BF" },
+  "&omid;": { "codepoints": [10678], "characters": "\u29B6" },
+  "&ominus;": { "codepoints": [8854], "characters": "\u2296" },
+  "&oopf;": { "codepoints": [120160], "characters": "\uD835\uDD60" },
+  "&opar;": { "codepoints": [10679], "characters": "\u29B7" },
+  "&operp;": { "codepoints": [10681], "characters": "\u29B9" },
+  "&oplus;": { "codepoints": [8853], "characters": "\u2295" },
+  "&or;": { "codepoints": [8744], "characters": "\u2228" },
+  "&orarr;": { "codepoints": [8635], "characters": "\u21BB" },
+  "&ord;": { "codepoints": [10845], "characters": "\u2A5D" },
+  "&order;": { "codepoints": [8500], "characters": "\u2134" },
+  "&orderof;": { "codepoints": [8500], "characters": "\u2134" },
+  "&ordf": { "codepoints": [170], "characters": "\u00AA" },
+  "&ordf;": { "codepoints": [170], "characters": "\u00AA" },
+  "&ordm": { "codepoints": [186], "characters": "\u00BA" },
+  "&ordm;": { "codepoints": [186], "characters": "\u00BA" },
+  "&origof;": { "codepoints": [8886], "characters": "\u22B6" },
+  "&oror;": { "codepoints": [10838], "characters": "\u2A56" },
+  "&orslope;": { "codepoints": [10839], "characters": "\u2A57" },
+  "&orv;": { "codepoints": [10843], "characters": "\u2A5B" },
+  "&oscr;": { "codepoints": [8500], "characters": "\u2134" },
+  "&oslash": { "codepoints": [248], "characters": "\u00F8" },
+  "&oslash;": { "codepoints": [248], "characters": "\u00F8" },
+  "&osol;": { "codepoints": [8856], "characters": "\u2298" },
+  "&otilde": { "codepoints": [245], "characters": "\u00F5" },
+  "&otilde;": { "codepoints": [245], "characters": "\u00F5" },
+  "&otimes;": { "codepoints": [8855], "characters": "\u2297" },
+  "&otimesas;": { "codepoints": [10806], "characters": "\u2A36" },
+  "&ouml": { "codepoints": [246], "characters": "\u00F6" },
+  "&ouml;": { "codepoints": [246], "characters": "\u00F6" },
+  "&ovbar;": { "codepoints": [9021], "characters": "\u233D" },
+  "&par;": { "codepoints": [8741], "characters": "\u2225" },
+  "&para": { "codepoints": [182], "characters": "\u00B6" },
+  "&para;": { "codepoints": [182], "characters": "\u00B6" },
+  "&parallel;": { "codepoints": [8741], "characters": "\u2225" },
+  "&parsim;": { "codepoints": [10995], "characters": "\u2AF3" },
+  "&parsl;": { "codepoints": [11005], "characters": "\u2AFD" },
+  "&part;": { "codepoints": [8706], "characters": "\u2202" },
+  "&pcy;": { "codepoints": [1087], "characters": "\u043F" },
+  "&percnt;": { "codepoints": [37], "characters": "\u0025" },
+  "&period;": { "codepoints": [46], "characters": "\u002E" },
+  "&permil;": { "codepoints": [8240], "characters": "\u2030" },
+  "&perp;": { "codepoints": [8869], "characters": "\u22A5" },
+  "&pertenk;": { "codepoints": [8241], "characters": "\u2031" },
+  "&pfr;": { "codepoints": [120109], "characters": "\uD835\uDD2D" },
+  "&phi;": { "codepoints": [966], "characters": "\u03C6" },
+  "&phiv;": { "codepoints": [981], "characters": "\u03D5" },
+  "&phmmat;": { "codepoints": [8499], "characters": "\u2133" },
+  "&phone;": { "codepoints": [9742], "characters": "\u260E" },
+  "&pi;": { "codepoints": [960], "characters": "\u03C0" },
+  "&pitchfork;": { "codepoints": [8916], "characters": "\u22D4" },
+  "&piv;": { "codepoints": [982], "characters": "\u03D6" },
+  "&planck;": { "codepoints": [8463], "characters": "\u210F" },
+  "&planckh;": { "codepoints": [8462], "characters": "\u210E" },
+  "&plankv;": { "codepoints": [8463], "characters": "\u210F" },
+  "&plus;": { "codepoints": [43], "characters": "\u002B" },
+  "&plusacir;": { "codepoints": [10787], "characters": "\u2A23" },
+  "&plusb;": { "codepoints": [8862], "characters": "\u229E" },
+  "&pluscir;": { "codepoints": [10786], "characters": "\u2A22" },
+  "&plusdo;": { "codepoints": [8724], "characters": "\u2214" },
+  "&plusdu;": { "codepoints": [10789], "characters": "\u2A25" },
+  "&pluse;": { "codepoints": [10866], "characters": "\u2A72" },
+  "&plusmn": { "codepoints": [177], "characters": "\u00B1" },
+  "&plusmn;": { "codepoints": [177], "characters": "\u00B1" },
+  "&plussim;": { "codepoints": [10790], "characters": "\u2A26" },
+  "&plustwo;": { "codepoints": [10791], "characters": "\u2A27" },
+  "&pm;": { "codepoints": [177], "characters": "\u00B1" },
+  "&pointint;": { "codepoints": [10773], "characters": "\u2A15" },
+  "&popf;": { "codepoints": [120161], "characters": "\uD835\uDD61" },
+  "&pound": { "codepoints": [163], "characters": "\u00A3" },
+  "&pound;": { "codepoints": [163], "characters": "\u00A3" },
+  "&pr;": { "codepoints": [8826], "characters": "\u227A" },
+  "&prE;": { "codepoints": [10931], "characters": "\u2AB3" },
+  "&prap;": { "codepoints": [10935], "characters": "\u2AB7" },
+  "&prcue;": { "codepoints": [8828], "characters": "\u227C" },
+  "&pre;": { "codepoints": [10927], "characters": "\u2AAF" },
+  "&prec;": { "codepoints": [8826], "characters": "\u227A" },
+  "&precapprox;": { "codepoints": [10935], "characters": "\u2AB7" },
+  "&preccurlyeq;": { "codepoints": [8828], "characters": "\u227C" },
+  "&preceq;": { "codepoints": [10927], "characters": "\u2AAF" },
+  "&precnapprox;": { "codepoints": [10937], "characters": "\u2AB9" },
+  "&precneqq;": { "codepoints": [10933], "characters": "\u2AB5" },
+  "&precnsim;": { "codepoints": [8936], "characters": "\u22E8" },
+  "&precsim;": { "codepoints": [8830], "characters": "\u227E" },
+  "&prime;": { "codepoints": [8242], "characters": "\u2032" },
+  "&primes;": { "codepoints": [8473], "characters": "\u2119" },
+  "&prnE;": { "codepoints": [10933], "characters": "\u2AB5" },
+  "&prnap;": { "codepoints": [10937], "characters": "\u2AB9" },
+  "&prnsim;": { "codepoints": [8936], "characters": "\u22E8" },
+  "&prod;": { "codepoints": [8719], "characters": "\u220F" },
+  "&profalar;": { "codepoints": [9006], "characters": "\u232E" },
+  "&profline;": { "codepoints": [8978], "characters": "\u2312" },
+  "&profsurf;": { "codepoints": [8979], "characters": "\u2313" },
+  "&prop;": { "codepoints": [8733], "characters": "\u221D" },
+  "&propto;": { "codepoints": [8733], "characters": "\u221D" },
+  "&prsim;": { "codepoints": [8830], "characters": "\u227E" },
+  "&prurel;": { "codepoints": [8880], "characters": "\u22B0" },
+  "&pscr;": { "codepoints": [120005], "characters": "\uD835\uDCC5" },
+  "&psi;": { "codepoints": [968], "characters": "\u03C8" },
+  "&puncsp;": { "codepoints": [8200], "characters": "\u2008" },
+  "&qfr;": { "codepoints": [120110], "characters": "\uD835\uDD2E" },
+  "&qint;": { "codepoints": [10764], "characters": "\u2A0C" },
+  "&qopf;": { "codepoints": [120162], "characters": "\uD835\uDD62" },
+  "&qprime;": { "codepoints": [8279], "characters": "\u2057" },
+  "&qscr;": { "codepoints": [120006], "characters": "\uD835\uDCC6" },
+  "&quaternions;": { "codepoints": [8461], "characters": "\u210D" },
+  "&quatint;": { "codepoints": [10774], "characters": "\u2A16" },
+  "&quest;": { "codepoints": [63], "characters": "\u003F" },
+  "&questeq;": { "codepoints": [8799], "characters": "\u225F" },
+  "&quot": { "codepoints": [34], "characters": "\u0022" },
+  "&quot;": { "codepoints": [34], "characters": "\u0022" },
+  "&rAarr;": { "codepoints": [8667], "characters": "\u21DB" },
+  "&rArr;": { "codepoints": [8658], "characters": "\u21D2" },
+  "&rAtail;": { "codepoints": [10524], "characters": "\u291C" },
+  "&rBarr;": { "codepoints": [10511], "characters": "\u290F" },
+  "&rHar;": { "codepoints": [10596], "characters": "\u2964" },
+  "&race;": { "codepoints": [8765, 817], "characters": "\u223D\u0331" },
+  "&racute;": { "codepoints": [341], "characters": "\u0155" },
+  "&radic;": { "codepoints": [8730], "characters": "\u221A" },
+  "&raemptyv;": { "codepoints": [10675], "characters": "\u29B3" },
+  "&rang;": { "codepoints": [10217], "characters": "\u27E9" },
+  "&rangd;": { "codepoints": [10642], "characters": "\u2992" },
+  "&range;": { "codepoints": [10661], "characters": "\u29A5" },
+  "&rangle;": { "codepoints": [10217], "characters": "\u27E9" },
+  "&raquo": { "codepoints": [187], "characters": "\u00BB" },
+  "&raquo;": { "codepoints": [187], "characters": "\u00BB" },
+  "&rarr;": { "codepoints": [8594], "characters": "\u2192" },
+  "&rarrap;": { "codepoints": [10613], "characters": "\u2975" },
+  "&rarrb;": { "codepoints": [8677], "characters": "\u21E5" },
+  "&rarrbfs;": { "codepoints": [10528], "characters": "\u2920" },
+  "&rarrc;": { "codepoints": [10547], "characters": "\u2933" },
+  "&rarrfs;": { "codepoints": [10526], "characters": "\u291E" },
+  "&rarrhk;": { "codepoints": [8618], "characters": "\u21AA" },
+  "&rarrlp;": { "codepoints": [8620], "characters": "\u21AC" },
+  "&rarrpl;": { "codepoints": [10565], "characters": "\u2945" },
+  "&rarrsim;": { "codepoints": [10612], "characters": "\u2974" },
+  "&rarrtl;": { "codepoints": [8611], "characters": "\u21A3" },
+  "&rarrw;": { "codepoints": [8605], "characters": "\u219D" },
+  "&ratail;": { "codepoints": [10522], "characters": "\u291A" },
+  "&ratio;": { "codepoints": [8758], "characters": "\u2236" },
+  "&rationals;": { "codepoints": [8474], "characters": "\u211A" },
+  "&rbarr;": { "codepoints": [10509], "characters": "\u290D" },
+  "&rbbrk;": { "codepoints": [10099], "characters": "\u2773" },
+  "&rbrace;": { "codepoints": [125], "characters": "\u007D" },
+  "&rbrack;": { "codepoints": [93], "characters": "\u005D" },
+  "&rbrke;": { "codepoints": [10636], "characters": "\u298C" },
+  "&rbrksld;": { "codepoints": [10638], "characters": "\u298E" },
+  "&rbrkslu;": { "codepoints": [10640], "characters": "\u2990" },
+  "&rcaron;": { "codepoints": [345], "characters": "\u0159" },
+  "&rcedil;": { "codepoints": [343], "characters": "\u0157" },
+  "&rceil;": { "codepoints": [8969], "characters": "\u2309" },
+  "&rcub;": { "codepoints": [125], "characters": "\u007D" },
+  "&rcy;": { "codepoints": [1088], "characters": "\u0440" },
+  "&rdca;": { "codepoints": [10551], "characters": "\u2937" },
+  "&rdldhar;": { "codepoints": [10601], "characters": "\u2969" },
+  "&rdquo;": { "codepoints": [8221], "characters": "\u201D" },
+  "&rdquor;": { "codepoints": [8221], "characters": "\u201D" },
+  "&rdsh;": { "codepoints": [8627], "characters": "\u21B3" },
+  "&real;": { "codepoints": [8476], "characters": "\u211C" },
+  "&realine;": { "codepoints": [8475], "characters": "\u211B" },
+  "&realpart;": { "codepoints": [8476], "characters": "\u211C" },
+  "&reals;": { "codepoints": [8477], "characters": "\u211D" },
+  "&rect;": { "codepoints": [9645], "characters": "\u25AD" },
+  "&reg": { "codepoints": [174], "characters": "\u00AE" },
+  "&reg;": { "codepoints": [174], "characters": "\u00AE" },
+  "&rfisht;": { "codepoints": [10621], "characters": "\u297D" },
+  "&rfloor;": { "codepoints": [8971], "characters": "\u230B" },
+  "&rfr;": { "codepoints": [120111], "characters": "\uD835\uDD2F" },
+  "&rhard;": { "codepoints": [8641], "characters": "\u21C1" },
+  "&rharu;": { "codepoints": [8640], "characters": "\u21C0" },
+  "&rharul;": { "codepoints": [10604], "characters": "\u296C" },
+  "&rho;": { "codepoints": [961], "characters": "\u03C1" },
+  "&rhov;": { "codepoints": [1009], "characters": "\u03F1" },
+  "&rightarrow;": { "codepoints": [8594], "characters": "\u2192" },
+  "&rightarrowtail;": { "codepoints": [8611], "characters": "\u21A3" },
+  "&rightharpoondown;": { "codepoints": [8641], "characters": "\u21C1" },
+  "&rightharpoonup;": { "codepoints": [8640], "characters": "\u21C0" },
+  "&rightleftarrows;": { "codepoints": [8644], "characters": "\u21C4" },
+  "&rightleftharpoons;": { "codepoints": [8652], "characters": "\u21CC" },
+  "&rightrightarrows;": { "codepoints": [8649], "characters": "\u21C9" },
+  "&rightsquigarrow;": { "codepoints": [8605], "characters": "\u219D" },
+  "&rightthreetimes;": { "codepoints": [8908], "characters": "\u22CC" },
+  "&ring;": { "codepoints": [730], "characters": "\u02DA" },
+  "&risingdotseq;": { "codepoints": [8787], "characters": "\u2253" },
+  "&rlarr;": { "codepoints": [8644], "characters": "\u21C4" },
+  "&rlhar;": { "codepoints": [8652], "characters": "\u21CC" },
+  "&rlm;": { "codepoints": [8207], "characters": "\u200F" },
+  "&rmoust;": { "codepoints": [9137], "characters": "\u23B1" },
+  "&rmoustache;": { "codepoints": [9137], "characters": "\u23B1" },
+  "&rnmid;": { "codepoints": [10990], "characters": "\u2AEE" },
+  "&roang;": { "codepoints": [10221], "characters": "\u27ED" },
+  "&roarr;": { "codepoints": [8702], "characters": "\u21FE" },
+  "&robrk;": { "codepoints": [10215], "characters": "\u27E7" },
+  "&ropar;": { "codepoints": [10630], "characters": "\u2986" },
+  "&ropf;": { "codepoints": [120163], "characters": "\uD835\uDD63" },
+  "&roplus;": { "codepoints": [10798], "characters": "\u2A2E" },
+  "&rotimes;": { "codepoints": [10805], "characters": "\u2A35" },
+  "&rpar;": { "codepoints": [41], "characters": "\u0029" },
+  "&rpargt;": { "codepoints": [10644], "characters": "\u2994" },
+  "&rppolint;": { "codepoints": [10770], "characters": "\u2A12" },
+  "&rrarr;": { "codepoints": [8649], "characters": "\u21C9" },
+  "&rsaquo;": { "codepoints": [8250], "characters": "\u203A" },
+  "&rscr;": { "codepoints": [120007], "characters": "\uD835\uDCC7" },
+  "&rsh;": { "codepoints": [8625], "characters": "\u21B1" },
+  "&rsqb;": { "codepoints": [93], "characters": "\u005D" },
+  "&rsquo;": { "codepoints": [8217], "characters": "\u2019" },
+  "&rsquor;": { "codepoints": [8217], "characters": "\u2019" },
+  "&rthree;": { "codepoints": [8908], "characters": "\u22CC" },
+  "&rtimes;": { "codepoints": [8906], "characters": "\u22CA" },
+  "&rtri;": { "codepoints": [9657], "characters": "\u25B9" },
+  "&rtrie;": { "codepoints": [8885], "characters": "\u22B5" },
+  "&rtrif;": { "codepoints": [9656], "characters": "\u25B8" },
+  "&rtriltri;": { "codepoints": [10702], "characters": "\u29CE" },
+  "&ruluhar;": { "codepoints": [10600], "characters": "\u2968" },
+  "&rx;": { "codepoints": [8478], "characters": "\u211E" },
+  "&sacute;": { "codepoints": [347], "characters": "\u015B" },
+  "&sbquo;": { "codepoints": [8218], "characters": "\u201A" },
+  "&sc;": { "codepoints": [8827], "characters": "\u227B" },
+  "&scE;": { "codepoints": [10932], "characters": "\u2AB4" },
+  "&scap;": { "codepoints": [10936], "characters": "\u2AB8" },
+  "&scaron;": { "codepoints": [353], "characters": "\u0161" },
+  "&sccue;": { "codepoints": [8829], "characters": "\u227D" },
+  "&sce;": { "codepoints": [10928], "characters": "\u2AB0" },
+  "&scedil;": { "codepoints": [351], "characters": "\u015F" },
+  "&scirc;": { "codepoints": [349], "characters": "\u015D" },
+  "&scnE;": { "codepoints": [10934], "characters": "\u2AB6" },
+  "&scnap;": { "codepoints": [10938], "characters": "\u2ABA" },
+  "&scnsim;": { "codepoints": [8937], "characters": "\u22E9" },
+  "&scpolint;": { "codepoints": [10771], "characters": "\u2A13" },
+  "&scsim;": { "codepoints": [8831], "characters": "\u227F" },
+  "&scy;": { "codepoints": [1089], "characters": "\u0441" },
+  "&sdot;": { "codepoints": [8901], "characters": "\u22C5" },
+  "&sdotb;": { "codepoints": [8865], "characters": "\u22A1" },
+  "&sdote;": { "codepoints": [10854], "characters": "\u2A66" },
+  "&seArr;": { "codepoints": [8664], "characters": "\u21D8" },
+  "&searhk;": { "codepoints": [10533], "characters": "\u2925" },
+  "&searr;": { "codepoints": [8600], "characters": "\u2198" },
+  "&searrow;": { "codepoints": [8600], "characters": "\u2198" },
+  "&sect": { "codepoints": [167], "characters": "\u00A7" },
+  "&sect;": { "codepoints": [167], "characters": "\u00A7" },
+  "&semi;": { "codepoints": [59], "characters": "\u003B" },
+  "&seswar;": { "codepoints": [10537], "characters": "\u2929" },
+  "&setminus;": { "codepoints": [8726], "characters": "\u2216" },
+  "&setmn;": { "codepoints": [8726], "characters": "\u2216" },
+  "&sext;": { "codepoints": [10038], "characters": "\u2736" },
+  "&sfr;": { "codepoints": [120112], "characters": "\uD835\uDD30" },
+  "&sfrown;": { "codepoints": [8994], "characters": "\u2322" },
+  "&sharp;": { "codepoints": [9839], "characters": "\u266F" },
+  "&shchcy;": { "codepoints": [1097], "characters": "\u0449" },
+  "&shcy;": { "codepoints": [1096], "characters": "\u0448" },
+  "&shortmid;": { "codepoints": [8739], "characters": "\u2223" },
+  "&shortparallel;": { "codepoints": [8741], "characters": "\u2225" },
+  "&shy": { "codepoints": [173], "characters": "\u00AD" },
+  "&shy;": { "codepoints": [173], "characters": "\u00AD" },
+  "&sigma;": { "codepoints": [963], "characters": "\u03C3" },
+  "&sigmaf;": { "codepoints": [962], "characters": "\u03C2" },
+  "&sigmav;": { "codepoints": [962], "characters": "\u03C2" },
+  "&sim;": { "codepoints": [8764], "characters": "\u223C" },
+  "&simdot;": { "codepoints": [10858], "characters": "\u2A6A" },
+  "&sime;": { "codepoints": [8771], "characters": "\u2243" },
+  "&simeq;": { "codepoints": [8771], "characters": "\u2243" },
+  "&simg;": { "codepoints": [10910], "characters": "\u2A9E" },
+  "&simgE;": { "codepoints": [10912], "characters": "\u2AA0" },
+  "&siml;": { "codepoints": [10909], "characters": "\u2A9D" },
+  "&simlE;": { "codepoints": [10911], "characters": "\u2A9F" },
+  "&simne;": { "codepoints": [8774], "characters": "\u2246" },
+  "&simplus;": { "codepoints": [10788], "characters": "\u2A24" },
+  "&simrarr;": { "codepoints": [10610], "characters": "\u2972" },
+  "&slarr;": { "codepoints": [8592], "characters": "\u2190" },
+  "&smallsetminus;": { "codepoints": [8726], "characters": "\u2216" },
+  "&smashp;": { "codepoints": [10803], "characters": "\u2A33" },
+  "&smeparsl;": { "codepoints": [10724], "characters": "\u29E4" },
+  "&smid;": { "codepoints": [8739], "characters": "\u2223" },
+  "&smile;": { "codepoints": [8995], "characters": "\u2323" },
+  "&smt;": { "codepoints": [10922], "characters": "\u2AAA" },
+  "&smte;": { "codepoints": [10924], "characters": "\u2AAC" },
+  "&smtes;": { "codepoints": [10924, 65024], "characters": "\u2AAC\uFE00" },
+  "&softcy;": { "codepoints": [1100], "characters": "\u044C" },
+  "&sol;": { "codepoints": [47], "characters": "\u002F" },
+  "&solb;": { "codepoints": [10692], "characters": "\u29C4" },
+  "&solbar;": { "codepoints": [9023], "characters": "\u233F" },
+  "&sopf;": { "codepoints": [120164], "characters": "\uD835\uDD64" },
+  "&spades;": { "codepoints": [9824], "characters": "\u2660" },
+  "&spadesuit;": { "codepoints": [9824], "characters": "\u2660" },
+  "&spar;": { "codepoints": [8741], "characters": "\u2225" },
+  "&sqcap;": { "codepoints": [8851], "characters": "\u2293" },
+  "&sqcaps;": { "codepoints": [8851, 65024], "characters": "\u2293\uFE00" },
+  "&sqcup;": { "codepoints": [8852], "characters": "\u2294" },
+  "&sqcups;": { "codepoints": [8852, 65024], "characters": "\u2294\uFE00" },
+  "&sqsub;": { "codepoints": [8847], "characters": "\u228F" },
+  "&sqsube;": { "codepoints": [8849], "characters": "\u2291" },
+  "&sqsubset;": { "codepoints": [8847], "characters": "\u228F" },
+  "&sqsubseteq;": { "codepoints": [8849], "characters": "\u2291" },
+  "&sqsup;": { "codepoints": [8848], "characters": "\u2290" },
+  "&sqsupe;": { "codepoints": [8850], "characters": "\u2292" },
+  "&sqsupset;": { "codepoints": [8848], "characters": "\u2290" },
+  "&sqsupseteq;": { "codepoints": [8850], "characters": "\u2292" },
+  "&squ;": { "codepoints": [9633], "characters": "\u25A1" },
+  "&square;": { "codepoints": [9633], "characters": "\u25A1" },
+  "&squarf;": { "codepoints": [9642], "characters": "\u25AA" },
+  "&squf;": { "codepoints": [9642], "characters": "\u25AA" },
+  "&srarr;": { "codepoints": [8594], "characters": "\u2192" },
+  "&sscr;": { "codepoints": [120008], "characters": "\uD835\uDCC8" },
+  "&ssetmn;": { "codepoints": [8726], "characters": "\u2216" },
+  "&ssmile;": { "codepoints": [8995], "characters": "\u2323" },
+  "&sstarf;": { "codepoints": [8902], "characters": "\u22C6" },
+  "&star;": { "codepoints": [9734], "characters": "\u2606" },
+  "&starf;": { "codepoints": [9733], "characters": "\u2605" },
+  "&straightepsilon;": { "codepoints": [1013], "characters": "\u03F5" },
+  "&straightphi;": { "codepoints": [981], "characters": "\u03D5" },
+  "&strns;": { "codepoints": [175], "characters": "\u00AF" },
+  "&sub;": { "codepoints": [8834], "characters": "\u2282" },
+  "&subE;": { "codepoints": [10949], "characters": "\u2AC5" },
+  "&subdot;": { "codepoints": [10941], "characters": "\u2ABD" },
+  "&sube;": { "codepoints": [8838], "characters": "\u2286" },
+  "&subedot;": { "codepoints": [10947], "characters": "\u2AC3" },
+  "&submult;": { "codepoints": [10945], "characters": "\u2AC1" },
+  "&subnE;": { "codepoints": [10955], "characters": "\u2ACB" },
+  "&subne;": { "codepoints": [8842], "characters": "\u228A" },
+  "&subplus;": { "codepoints": [10943], "characters": "\u2ABF" },
+  "&subrarr;": { "codepoints": [10617], "characters": "\u2979" },
+  "&subset;": { "codepoints": [8834], "characters": "\u2282" },
+  "&subseteq;": { "codepoints": [8838], "characters": "\u2286" },
+  "&subseteqq;": { "codepoints": [10949], "characters": "\u2AC5" },
+  "&subsetneq;": { "codepoints": [8842], "characters": "\u228A" },
+  "&subsetneqq;": { "codepoints": [10955], "characters": "\u2ACB" },
+  "&subsim;": { "codepoints": [10951], "characters": "\u2AC7" },
+  "&subsub;": { "codepoints": [10965], "characters": "\u2AD5" },
+  "&subsup;": { "codepoints": [10963], "characters": "\u2AD3" },
+  "&succ;": { "codepoints": [8827], "characters": "\u227B" },
+  "&succapprox;": { "codepoints": [10936], "characters": "\u2AB8" },
+  "&succcurlyeq;": { "codepoints": [8829], "characters": "\u227D" },
+  "&succeq;": { "codepoints": [10928], "characters": "\u2AB0" },
+  "&succnapprox;": { "codepoints": [10938], "characters": "\u2ABA" },
+  "&succneqq;": { "codepoints": [10934], "characters": "\u2AB6" },
+  "&succnsim;": { "codepoints": [8937], "characters": "\u22E9" },
+  "&succsim;": { "codepoints": [8831], "characters": "\u227F" },
+  "&sum;": { "codepoints": [8721], "characters": "\u2211" },
+  "&sung;": { "codepoints": [9834], "characters": "\u266A" },
+  "&sup1": { "codepoints": [185], "characters": "\u00B9" },
+  "&sup1;": { "codepoints": [185], "characters": "\u00B9" },
+  "&sup2": { "codepoints": [178], "characters": "\u00B2" },
+  "&sup2;": { "codepoints": [178], "characters": "\u00B2" },
+  "&sup3": { "codepoints": [179], "characters": "\u00B3" },
+  "&sup3;": { "codepoints": [179], "characters": "\u00B3" },
+  "&sup;": { "codepoints": [8835], "characters": "\u2283" },
+  "&supE;": { "codepoints": [10950], "characters": "\u2AC6" },
+  "&supdot;": { "codepoints": [10942], "characters": "\u2ABE" },
+  "&supdsub;": { "codepoints": [10968], "characters": "\u2AD8" },
+  "&supe;": { "codepoints": [8839], "characters": "\u2287" },
+  "&supedot;": { "codepoints": [10948], "characters": "\u2AC4" },
+  "&suphsol;": { "codepoints": [10185], "characters": "\u27C9" },
+  "&suphsub;": { "codepoints": [10967], "characters": "\u2AD7" },
+  "&suplarr;": { "codepoints": [10619], "characters": "\u297B" },
+  "&supmult;": { "codepoints": [10946], "characters": "\u2AC2" },
+  "&supnE;": { "codepoints": [10956], "characters": "\u2ACC" },
+  "&supne;": { "codepoints": [8843], "characters": "\u228B" },
+  "&supplus;": { "codepoints": [10944], "characters": "\u2AC0" },
+  "&supset;": { "codepoints": [8835], "characters": "\u2283" },
+  "&supseteq;": { "codepoints": [8839], "characters": "\u2287" },
+  "&supseteqq;": { "codepoints": [10950], "characters": "\u2AC6" },
+  "&supsetneq;": { "codepoints": [8843], "characters": "\u228B" },
+  "&supsetneqq;": { "codepoints": [10956], "characters": "\u2ACC" },
+  "&supsim;": { "codepoints": [10952], "characters": "\u2AC8" },
+  "&supsub;": { "codepoints": [10964], "characters": "\u2AD4" },
+  "&supsup;": { "codepoints": [10966], "characters": "\u2AD6" },
+  "&swArr;": { "codepoints": [8665], "characters": "\u21D9" },
+  "&swarhk;": { "codepoints": [10534], "characters": "\u2926" },
+  "&swarr;": { "codepoints": [8601], "characters": "\u2199" },
+  "&swarrow;": { "codepoints": [8601], "characters": "\u2199" },
+  "&swnwar;": { "codepoints": [10538], "characters": "\u292A" },
+  "&szlig": { "codepoints": [223], "characters": "\u00DF" },
+  "&szlig;": { "codepoints": [223], "characters": "\u00DF" },
+  "&target;": { "codepoints": [8982], "characters": "\u2316" },
+  "&tau;": { "codepoints": [964], "characters": "\u03C4" },
+  "&tbrk;": { "codepoints": [9140], "characters": "\u23B4" },
+  "&tcaron;": { "codepoints": [357], "characters": "\u0165" },
+  "&tcedil;": { "codepoints": [355], "characters": "\u0163" },
+  "&tcy;": { "codepoints": [1090], "characters": "\u0442" },
+  "&tdot;": { "codepoints": [8411], "characters": "\u20DB" },
+  "&telrec;": { "codepoints": [8981], "characters": "\u2315" },
+  "&tfr;": { "codepoints": [120113], "characters": "\uD835\uDD31" },
+  "&there4;": { "codepoints": [8756], "characters": "\u2234" },
+  "&therefore;": { "codepoints": [8756], "characters": "\u2234" },
+  "&theta;": { "codepoints": [952], "characters": "\u03B8" },
+  "&thetasym;": { "codepoints": [977], "characters": "\u03D1" },
+  "&thetav;": { "codepoints": [977], "characters": "\u03D1" },
+  "&thickapprox;": { "codepoints": [8776], "characters": "\u2248" },
+  "&thicksim;": { "codepoints": [8764], "characters": "\u223C" },
+  "&thinsp;": { "codepoints": [8201], "characters": "\u2009" },
+  "&thkap;": { "codepoints": [8776], "characters": "\u2248" },
+  "&thksim;": { "codepoints": [8764], "characters": "\u223C" },
+  "&thorn": { "codepoints": [254], "characters": "\u00FE" },
+  "&thorn;": { "codepoints": [254], "characters": "\u00FE" },
+  "&tilde;": { "codepoints": [732], "characters": "\u02DC" },
+  "&times": { "codepoints": [215], "characters": "\u00D7" },
+  "&times;": { "codepoints": [215], "characters": "\u00D7" },
+  "&timesb;": { "codepoints": [8864], "characters": "\u22A0" },
+  "&timesbar;": { "codepoints": [10801], "characters": "\u2A31" },
+  "&timesd;": { "codepoints": [10800], "characters": "\u2A30" },
+  "&tint;": { "codepoints": [8749], "characters": "\u222D" },
+  "&toea;": { "codepoints": [10536], "characters": "\u2928" },
+  "&top;": { "codepoints": [8868], "characters": "\u22A4" },
+  "&topbot;": { "codepoints": [9014], "characters": "\u2336" },
+  "&topcir;": { "codepoints": [10993], "characters": "\u2AF1" },
+  "&topf;": { "codepoints": [120165], "characters": "\uD835\uDD65" },
+  "&topfork;": { "codepoints": [10970], "characters": "\u2ADA" },
+  "&tosa;": { "codepoints": [10537], "characters": "\u2929" },
+  "&tprime;": { "codepoints": [8244], "characters": "\u2034" },
+  "&trade;": { "codepoints": [8482], "characters": "\u2122" },
+  "&triangle;": { "codepoints": [9653], "characters": "\u25B5" },
+  "&triangledown;": { "codepoints": [9663], "characters": "\u25BF" },
+  "&triangleleft;": { "codepoints": [9667], "characters": "\u25C3" },
+  "&trianglelefteq;": { "codepoints": [8884], "characters": "\u22B4" },
+  "&triangleq;": { "codepoints": [8796], "characters": "\u225C" },
+  "&triangleright;": { "codepoints": [9657], "characters": "\u25B9" },
+  "&trianglerighteq;": { "codepoints": [8885], "characters": "\u22B5" },
+  "&tridot;": { "codepoints": [9708], "characters": "\u25EC" },
+  "&trie;": { "codepoints": [8796], "characters": "\u225C" },
+  "&triminus;": { "codepoints": [10810], "characters": "\u2A3A" },
+  "&triplus;": { "codepoints": [10809], "characters": "\u2A39" },
+  "&trisb;": { "codepoints": [10701], "characters": "\u29CD" },
+  "&tritime;": { "codepoints": [10811], "characters": "\u2A3B" },
+  "&trpezium;": { "codepoints": [9186], "characters": "\u23E2" },
+  "&tscr;": { "codepoints": [120009], "characters": "\uD835\uDCC9" },
+  "&tscy;": { "codepoints": [1094], "characters": "\u0446" },
+  "&tshcy;": { "codepoints": [1115], "characters": "\u045B" },
+  "&tstrok;": { "codepoints": [359], "characters": "\u0167" },
+  "&twixt;": { "codepoints": [8812], "characters": "\u226C" },
+  "&twoheadleftarrow;": { "codepoints": [8606], "characters": "\u219E" },
+  "&twoheadrightarrow;": { "codepoints": [8608], "characters": "\u21A0" },
+  "&uArr;": { "codepoints": [8657], "characters": "\u21D1" },
+  "&uHar;": { "codepoints": [10595], "characters": "\u2963" },
+  "&uacute": { "codepoints": [250], "characters": "\u00FA" },
+  "&uacute;": { "codepoints": [250], "characters": "\u00FA" },
+  "&uarr;": { "codepoints": [8593], "characters": "\u2191" },
+  "&ubrcy;": { "codepoints": [1118], "characters": "\u045E" },
+  "&ubreve;": { "codepoints": [365], "characters": "\u016D" },
+  "&ucirc": { "codepoints": [251], "characters": "\u00FB" },
+  "&ucirc;": { "codepoints": [251], "characters": "\u00FB" },
+  "&ucy;": { "codepoints": [1091], "characters": "\u0443" },
+  "&udarr;": { "codepoints": [8645], "characters": "\u21C5" },
+  "&udblac;": { "codepoints": [369], "characters": "\u0171" },
+  "&udhar;": { "codepoints": [10606], "characters": "\u296E" },
+  "&ufisht;": { "codepoints": [10622], "characters": "\u297E" },
+  "&ufr;": { "codepoints": [120114], "characters": "\uD835\uDD32" },
+  "&ugrave": { "codepoints": [249], "characters": "\u00F9" },
+  "&ugrave;": { "codepoints": [249], "characters": "\u00F9" },
+  "&uharl;": { "codepoints": [8639], "characters": "\u21BF" },
+  "&uharr;": { "codepoints": [8638], "characters": "\u21BE" },
+  "&uhblk;": { "codepoints": [9600], "characters": "\u2580" },
+  "&ulcorn;": { "codepoints": [8988], "characters": "\u231C" },
+  "&ulcorner;": { "codepoints": [8988], "characters": "\u231C" },
+  "&ulcrop;": { "codepoints": [8975], "characters": "\u230F" },
+  "&ultri;": { "codepoints": [9720], "characters": "\u25F8" },
+  "&umacr;": { "codepoints": [363], "characters": "\u016B" },
+  "&uml": { "codepoints": [168], "characters": "\u00A8" },
+  "&uml;": { "codepoints": [168], "characters": "\u00A8" },
+  "&uogon;": { "codepoints": [371], "characters": "\u0173" },
+  "&uopf;": { "codepoints": [120166], "characters": "\uD835\uDD66" },
+  "&uparrow;": { "codepoints": [8593], "characters": "\u2191" },
+  "&updownarrow;": { "codepoints": [8597], "characters": "\u2195" },
+  "&upharpoonleft;": { "codepoints": [8639], "characters": "\u21BF" },
+  "&upharpoonright;": { "codepoints": [8638], "characters": "\u21BE" },
+  "&uplus;": { "codepoints": [8846], "characters": "\u228E" },
+  "&upsi;": { "codepoints": [965], "characters": "\u03C5" },
+  "&upsih;": { "codepoints": [978], "characters": "\u03D2" },
+  "&upsilon;": { "codepoints": [965], "characters": "\u03C5" },
+  "&upuparrows;": { "codepoints": [8648], "characters": "\u21C8" },
+  "&urcorn;": { "codepoints": [8989], "characters": "\u231D" },
+  "&urcorner;": { "codepoints": [8989], "characters": "\u231D" },
+  "&urcrop;": { "codepoints": [8974], "characters": "\u230E" },
+  "&uring;": { "codepoints": [367], "characters": "\u016F" },
+  "&urtri;": { "codepoints": [9721], "characters": "\u25F9" },
+  "&uscr;": { "codepoints": [120010], "characters": "\uD835\uDCCA" },
+  "&utdot;": { "codepoints": [8944], "characters": "\u22F0" },
+  "&utilde;": { "codepoints": [361], "characters": "\u0169" },
+  "&utri;": { "codepoints": [9653], "characters": "\u25B5" },
+  "&utrif;": { "codepoints": [9652], "characters": "\u25B4" },
+  "&uuarr;": { "codepoints": [8648], "characters": "\u21C8" },
+  "&uuml": { "codepoints": [252], "characters": "\u00FC" },
+  "&uuml;": { "codepoints": [252], "characters": "\u00FC" },
+  "&uwangle;": { "codepoints": [10663], "characters": "\u29A7" },
+  "&vArr;": { "codepoints": [8661], "characters": "\u21D5" },
+  "&vBar;": { "codepoints": [10984], "characters": "\u2AE8" },
+  "&vBarv;": { "codepoints": [10985], "characters": "\u2AE9" },
+  "&vDash;": { "codepoints": [8872], "characters": "\u22A8" },
+  "&vangrt;": { "codepoints": [10652], "characters": "\u299C" },
+  "&varepsilon;": { "codepoints": [1013], "characters": "\u03F5" },
+  "&varkappa;": { "codepoints": [1008], "characters": "\u03F0" },
+  "&varnothing;": { "codepoints": [8709], "characters": "\u2205" },
+  "&varphi;": { "codepoints": [981], "characters": "\u03D5" },
+  "&varpi;": { "codepoints": [982], "characters": "\u03D6" },
+  "&varpropto;": { "codepoints": [8733], "characters": "\u221D" },
+  "&varr;": { "codepoints": [8597], "characters": "\u2195" },
+  "&varrho;": { "codepoints": [1009], "characters": "\u03F1" },
+  "&varsigma;": { "codepoints": [962], "characters": "\u03C2" },
+  "&varsubsetneq;": { "codepoints": [8842, 65024], "characters": "\u228A\uFE00" },
+  "&varsubsetneqq;": { "codepoints": [10955, 65024], "characters": "\u2ACB\uFE00" },
+  "&varsupsetneq;": { "codepoints": [8843, 65024], "characters": "\u228B\uFE00" },
+  "&varsupsetneqq;": { "codepoints": [10956, 65024], "characters": "\u2ACC\uFE00" },
+  "&vartheta;": { "codepoints": [977], "characters": "\u03D1" },
+  "&vartriangleleft;": { "codepoints": [8882], "characters": "\u22B2" },
+  "&vartriangleright;": { "codepoints": [8883], "characters": "\u22B3" },
+  "&vcy;": { "codepoints": [1074], "characters": "\u0432" },
+  "&vdash;": { "codepoints": [8866], "characters": "\u22A2" },
+  "&vee;": { "codepoints": [8744], "characters": "\u2228" },
+  "&veebar;": { "codepoints": [8891], "characters": "\u22BB" },
+  "&veeeq;": { "codepoints": [8794], "characters": "\u225A" },
+  "&vellip;": { "codepoints": [8942], "characters": "\u22EE" },
+  "&verbar;": { "codepoints": [124], "characters": "\u007C" },
+  "&vert;": { "codepoints": [124], "characters": "\u007C" },
+  "&vfr;": { "codepoints": [120115], "characters": "\uD835\uDD33" },
+  "&vltri;": { "codepoints": [8882], "characters": "\u22B2" },
+  "&vnsub;": { "codepoints": [8834, 8402], "characters": "\u2282\u20D2" },
+  "&vnsup;": { "codepoints": [8835, 8402], "characters": "\u2283\u20D2" },
+  "&vopf;": { "codepoints": [120167], "characters": "\uD835\uDD67" },
+  "&vprop;": { "codepoints": [8733], "characters": "\u221D" },
+  "&vrtri;": { "codepoints": [8883], "characters": "\u22B3" },
+  "&vscr;": { "codepoints": [120011], "characters": "\uD835\uDCCB" },
+  "&vsubnE;": { "codepoints": [10955, 65024], "characters": "\u2ACB\uFE00" },
+  "&vsubne;": { "codepoints": [8842, 65024], "characters": "\u228A\uFE00" },
+  "&vsupnE;": { "codepoints": [10956, 65024], "characters": "\u2ACC\uFE00" },
+  "&vsupne;": { "codepoints": [8843, 65024], "characters": "\u228B\uFE00" },
+  "&vzigzag;": { "codepoints": [10650], "characters": "\u299A" },
+  "&wcirc;": { "codepoints": [373], "characters": "\u0175" },
+  "&wedbar;": { "codepoints": [10847], "characters": "\u2A5F" },
+  "&wedge;": { "codepoints": [8743], "characters": "\u2227" },
+  "&wedgeq;": { "codepoints": [8793], "characters": "\u2259" },
+  "&weierp;": { "codepoints": [8472], "characters": "\u2118" },
+  "&wfr;": { "codepoints": [120116], "characters": "\uD835\uDD34" },
+  "&wopf;": { "codepoints": [120168], "characters": "\uD835\uDD68" },
+  "&wp;": { "codepoints": [8472], "characters": "\u2118" },
+  "&wr;": { "codepoints": [8768], "characters": "\u2240" },
+  "&wreath;": { "codepoints": [8768], "characters": "\u2240" },
+  "&wscr;": { "codepoints": [120012], "characters": "\uD835\uDCCC" },
+  "&xcap;": { "codepoints": [8898], "characters": "\u22C2" },
+  "&xcirc;": { "codepoints": [9711], "characters": "\u25EF" },
+  "&xcup;": { "codepoints": [8899], "characters": "\u22C3" },
+  "&xdtri;": { "codepoints": [9661], "characters": "\u25BD" },
+  "&xfr;": { "codepoints": [120117], "characters": "\uD835\uDD35" },
+  "&xhArr;": { "codepoints": [10234], "characters": "\u27FA" },
+  "&xharr;": { "codepoints": [10231], "characters": "\u27F7" },
+  "&xi;": { "codepoints": [958], "characters": "\u03BE" },
+  "&xlArr;": { "codepoints": [10232], "characters": "\u27F8" },
+  "&xlarr;": { "codepoints": [10229], "characters": "\u27F5" },
+  "&xmap;": { "codepoints": [10236], "characters": "\u27FC" },
+  "&xnis;": { "codepoints": [8955], "characters": "\u22FB" },
+  "&xodot;": { "codepoints": [10752], "characters": "\u2A00" },
+  "&xopf;": { "codepoints": [120169], "characters": "\uD835\uDD69" },
+  "&xoplus;": { "codepoints": [10753], "characters": "\u2A01" },
+  "&xotime;": { "codepoints": [10754], "characters": "\u2A02" },
+  "&xrArr;": { "codepoints": [10233], "characters": "\u27F9" },
+  "&xrarr;": { "codepoints": [10230], "characters": "\u27F6" },
+  "&xscr;": { "codepoints": [120013], "characters": "\uD835\uDCCD" },
+  "&xsqcup;": { "codepoints": [10758], "characters": "\u2A06" },
+  "&xuplus;": { "codepoints": [10756], "characters": "\u2A04" },
+  "&xutri;": { "codepoints": [9651], "characters": "\u25B3" },
+  "&xvee;": { "codepoints": [8897], "characters": "\u22C1" },
+  "&xwedge;": { "codepoints": [8896], "characters": "\u22C0" },
+  "&yacute": { "codepoints": [253], "characters": "\u00FD" },
+  "&yacute;": { "codepoints": [253], "characters": "\u00FD" },
+  "&yacy;": { "codepoints": [1103], "characters": "\u044F" },
+  "&ycirc;": { "codepoints": [375], "characters": "\u0177" },
+  "&ycy;": { "codepoints": [1099], "characters": "\u044B" },
+  "&yen": { "codepoints": [165], "characters": "\u00A5" },
+  "&yen;": { "codepoints": [165], "characters": "\u00A5" },
+  "&yfr;": { "codepoints": [120118], "characters": "\uD835\uDD36" },
+  "&yicy;": { "codepoints": [1111], "characters": "\u0457" },
+  "&yopf;": { "codepoints": [120170], "characters": "\uD835\uDD6A" },
+  "&yscr;": { "codepoints": [120014], "characters": "\uD835\uDCCE" },
+  "&yucy;": { "codepoints": [1102], "characters": "\u044E" },
+  "&yuml": { "codepoints": [255], "characters": "\u00FF" },
+  "&yuml;": { "codepoints": [255], "characters": "\u00FF" },
+  "&zacute;": { "codepoints": [378], "characters": "\u017A" },
+  "&zcaron;": { "codepoints": [382], "characters": "\u017E" },
+  "&zcy;": { "codepoints": [1079], "characters": "\u0437" },
+  "&zdot;": { "codepoints": [380], "characters": "\u017C" },
+  "&zeetrf;": { "codepoints": [8488], "characters": "\u2128" },
+  "&zeta;": { "codepoints": [950], "characters": "\u03B6" },
+  "&zfr;": { "codepoints": [120119], "characters": "\uD835\uDD37" },
+  "&zhcy;": { "codepoints": [1078], "characters": "\u0436" },
+  "&zigrarr;": { "codepoints": [8669], "characters": "\u21DD" },
+  "&zopf;": { "codepoints": [120171], "characters": "\uD835\uDD6B" },
+  "&zscr;": { "codepoints": [120015], "characters": "\uD835\uDCCF" },
+  "&zwj;": { "codepoints": [8205], "characters": "\u200D" },
+  "&zwnj;": { "codepoints": [8204], "characters": "\u200C" }
+}
diff --git a/enums.nim b/enums.nim
index e95ca6ac..5b765d01 100644
--- a/enums.nim
+++ b/enums.nim
@@ -1,7 +1,19 @@
 type
   NodeType* =
     enum
-    NODE_UNKNOWN, NODE_ELEMENT, NODE_TEXT, NODE_COMMENT, NODE_CDATA, NODE_DOCUMENT
+    UNKNOWN_NODE = 0,
+    ELEMENT_NODE = 1,
+    ATTRIBUTE_NODE = 2,
+    TEXT_NODE = 3,
+    CDATA_SECTION_NODE = 4,
+    ENTITY_REFERENCE_NODE = 5,
+    ENTITY_NODE = 6
+    PROCESSING_INSTRUCTION_NODE = 7,
+    COMMENT_NODE = 8,
+    DOCUMENT_NODE = 9,
+    DOCUMENT_TYPE_NODE = 10,
+    DOCUMENT_FRAGMENT_NODE = 11,
+    NOTATION_NODE = 12
 
   DisplayType* =
     enum
@@ -69,11 +81,13 @@ const DisplayBlockTags* = {
   TAG_FIELDSET, TAG_FORM, TAG_H1, TAG_H2, TAG_H3, TAG_H4, TAG_H5, TAG_H6,
   TAG_HR, TAG_INS, TAG_MENU, TAG_NOFRAMES, TAG_NOSCRIPT, TAG_OL, TAG_P, TAG_PRE,
   TAG_TABLE, TAG_UL, TAG_CENTER, TAG_DIR, TAG_MENU, TAG_NOFRAMES, TAG_BODY,
-  #single
-  TAG_HR
 }
 
-const SingleTagTypes* = {
+const SelfClosingTagTypes* = {
+  TAG_LI, TAG_P
+}
+
+const VoidTagTypes* = {
   TAG_AREA, TAG_BASE, TAG_BR, TAG_COL, TAG_FRAME, TAG_HR, TAG_IMG, TAG_INPUT,
-  TAG_SOURCE, TAG_TRACK, TAG_LINK, TAG_META, TAG_PARAM, TAG_WBR, TAG_LI
+  TAG_SOURCE, TAG_TRACK, TAG_LINK, TAG_META, TAG_PARAM, TAG_WBR, TAG_HR
 }
diff --git a/htmlelement.nim b/htmlelement.nim
deleted file mode 100644
index 2b7c1c64..00000000
--- a/htmlelement.nim
+++ /dev/null
@@ -1,368 +0,0 @@
-import strutils
-import terminal
-import uri
-import unicode
-
-import fusion/htmlparser/xmltree
-
-import twtstr
-import twtio
-import enums
-import macros
-
-type
-  HtmlNode* = ref HtmlNodeObj
-  HtmlNodeObj = object of RootObj
-    nodeType*: NodeType
-    childNodes*: seq[HtmlNode]
-    firstChild*: HtmlNode
-    isConnected*: bool
-    lastChild*: HtmlNode
-    nextSibling*: HtmlNode
-    previousSibling*: HtmlNode
-    parentNode*: HtmlNode
-    parentElement*: HtmlElement
-
-    rawtext*: string
-    fmttext*: seq[string]
-    x*: int
-    y*: int
-    ex*: int
-    ey*: int
-    width*: int
-    height*: int
-    openblock*: bool
-    closeblock*: bool
-    hidden*: bool
-
-  Document* = ref DocumentObj
-  DocumentObj = object of HtmlNodeObj
-    location*: Uri
-
-  HtmlElement* = ref HtmlElementObj
-  HtmlElementObj = object of HtmlNodeObj
-    id*: string
-    class*: string
-    tagType*: TagType
-    centered*: bool
-    display*: DisplayType
-    innerText*: string
-    margintop*: int
-    marginbottom*: int
-    marginleft*: int
-    marginright*: int
-    margin*: int
-    bold*: bool
-    italic*: bool
-    underscore*: bool
-    islink*: bool
-    selected*: bool
-    numChildNodes*: int
-    indent*: int
-
-  HtmlInputElement* = ref HtmlInputElementObj
-  HtmlInputElementObj = object of HtmlElementObj
-    itype*: InputType
-    autofocus*: bool
-    required*: bool
-    value*: string
-    size*: int
-
-  HtmlAnchorElement* = ref HtmlAnchorElementObj
-  HtmlAnchorElementObj = object of HtmlElementObj
-    href*: string
-
-  HtmlSelectElement* = ref HtmlSelectElementObj
-  HtmlSelectElementObj = object of HtmlElementObj
-    name*: string
-    value*: string
-    valueSet*: bool
-
-  HtmlOptionElement* = ref HtmlOptionElementObj
-  HtmlOptionElementObj = object of HtmlElementObj
-    value*: string
-
-#no I won't manually write all this down
-#maybe todo to accept stuff other than tagtype (idk how useful that'd be)
-macro genEnumCase(s: string): untyped =
-  let casestmt = nnkCaseStmt.newTree() 
-  casestmt.add(ident("s"))
-  for i in low(TagType) .. high(TagType):
-    let ret = nnkReturnStmt.newTree()
-    ret.add(newLit(TagType(i)))
-    let branch = nnkOfBranch.newTree()
-    let enumname = $TagType(i)
-    let tagname = enumname.substr("TAG_".len, enumname.len - 1).tolower()
-    branch.add(newLit(tagname))
-    branch.add(ret)
-    casestmt.add(branch)
-  let ret = nnkReturnStmt.newTree()
-  ret.add(newLit(TAG_UNKNOWN))
-  let branch = nnkElse.newTree()
-  branch.add(ret)
-  casestmt.add(branch)
-
-func tagType(s: string): TagType =
-  genEnumCase(s)
-
-func nodeAttr*(node: HtmlNode): HtmlElement =
-  case node.nodeType
-  of NODE_TEXT: return node.parentElement
-  of NODE_ELEMENT: return HtmlElement(node)
-  else: assert(false)
-
-func displayed*(node: HtmlNode): bool =
-  return node.rawtext.len > 0 and node.nodeAttr().display != DISPLAY_NONE
-
-func isTextNode*(node: HtmlNode): bool =
-  return node.nodeType == NODE_TEXT
-
-func isElemNode*(node: HtmlNode): bool =
-  return node.nodeType == NODE_ELEMENT
-
-func isComment*(node: HtmlNode): bool =
-  return node.nodeType == NODE_COMMENT
-
-func isCData*(node: HtmlNode): bool =
-  return node.nodeType == NODE_CDATA
-
-func isDocument*(node: HtmlNode): bool =
-  return node.nodeType == NODE_DOCUMENT
-
-func getFmtLen*(htmlNode: HtmlNode): int =
-  return htmlNode.fmttext.join().runeLen()
-
-func getRawLen*(htmlNode: HtmlNode): int =
-  return htmlNode.rawtext.runeLen()
-
-func toInputType*(str: string): InputType =
-  case str
-  of "button": INPUT_BUTTON
-  of "checkbox": INPUT_CHECKBOX
-  of "color": INPUT_COLOR
-  of "date": INPUT_DATE
-  of "datetime_local": INPUT_DATETIME_LOCAL
-  of "email": INPUT_EMAIL
-  of "file": INPUT_FILE
-  of "hidden": INPUT_HIDDEN
-  of "image": INPUT_IMAGE
-  of "month": INPUT_MONTH
-  of "number": INPUT_NUMBER
-  of "password": INPUT_PASSWORD
-  of "radio": INPUT_RADIO
-  of "range": INPUT_RANGE
-  of "reset": INPUT_RESET
-  of "search": INPUT_SEARCH
-  of "submit": INPUT_SUBMIT
-  of "tel": INPUT_TEL
-  of "text": INPUT_TEXT
-  of "time": INPUT_TIME
-  of "url": INPUT_URL
-  of "week": INPUT_WEEK
-  else: INPUT_UNKNOWN
-
-func toInputSize*(str: string): int =
-  if str.len == 0:
-    return 20
-  for c in str:
-    if not c.isDigit:
-      return 20
-  return str.parseInt()
-
-func getFmtInput(inputElement: HtmlInputElement): seq[string] =
-  case inputElement.itype
-  of INPUT_TEXT, INPUT_SEARCH:
-    let valueFit = fitValueToSize(inputElement.value, inputElement.size)
-    return valueFit.ansiStyle(styleUnderscore).ansiReset().buttonFmt()
-  of INPUT_SUBMIT:
-    return inputElement.value.buttonFmt()
-  else: discard
-
-func getRawInput(inputElement: HtmlInputElement): string =
-  case inputElement.itype
-  of INPUT_TEXT, INPUT_SEARCH:
-    return inputElement.value.fitValueToSize(inputElement.size).buttonRaw()
-  of INPUT_SUBMIT:
-    return inputElement.value.buttonRaw()
-  else: discard
-
-func ancestor*(htmlNode: HtmlNode, tagType: TagType): HtmlElement =
-  result = htmlNode.parentElement
-  while result != nil and result.tagType != tagType:
-    result = result.parentElement
-
-func displayWhitespace*(htmlElem: HtmlElement): bool =
-  return htmlElem.display == DISPLAY_INLINE or htmlElem.display == DISPLAY_INLINE_BLOCK
-
-proc getRawText*(htmlNode: HtmlNode): string =
-  if htmlNode.isElemNode():
-    case HtmlElement(htmlNode).tagType
-    of TAG_INPUT: return HtmlInputElement(htmlNode).getRawInput()
-    else: return ""
-  elif htmlNode.isTextNode():
-    if htmlNode.parentElement != nil and htmlNode.parentElement.tagType != TAG_PRE:
-      result = htmlNode.rawtext.remove("\n")
-      if unicode.strip(result).runeLen() > 0:
-        if htmlNode.nodeAttr().display != DISPLAY_INLINE:
-          if htmlNode.previousSibling == nil or htmlNode.previousSibling.nodeAttr().displayWhitespace():
-            result = unicode.strip(result, true, false)
-          if htmlNode.nextSibling == nil or htmlNode.nextSibling.nodeAttr().displayWhitespace():
-            result = unicode.strip(result, false, true)
-      else:
-        result = ""
-    else:
-      result = unicode.strip(htmlNode.rawtext)
-    if htmlNode.parentElement != nil and htmlNode.parentElement.tagType == TAG_OPTION:
-      result = result.buttonRaw()
-  else:
-    assert(false)
-
-func getFmtText*(htmlNode: HtmlNode): seq[string] =
-  if htmlNode.isElemNode():
-    case HtmlElement(htmlNode).tagType
-    of TAG_INPUT: return HtmlInputElement(htmlNode).getFmtInput()
-    else: return @[]
-  elif htmlNode.isTextNode():
-    result &= htmlNode.rawtext
-    if htmlNode.parentElement != nil:
-      if htmlNode.parentElement.islink:
-        result = result.ansiFgColor(fgBlue).ansiReset()
-        let anchor = htmlNode.ancestor(TAG_A)
-        if anchor != nil and anchor.selected:
-          result = result.ansiStyle(styleUnderscore).ansiReset()
-
-      if htmlNode.parentElement.tagType == TAG_OPTION:
-        result = result.ansiFgColor(fgRed).ansiReset()
-
-      if htmlNode.parentElement.bold:
-        result = result.ansiStyle(styleBright).ansiReset()
-      if htmlNode.parentElement.italic:
-        result = result.ansiStyle(styleItalic).ansiReset()
-      if htmlNode.parentElement.underscore:
-        result = result.ansiStyle(styleUnderscore).ansiReset()
-    else:
-      assert(false, "Uhhhh I'm pretty sure we should have parent elements for text nodes?" & htmlNode.rawtext)
-  else:
-    assert(false)
-
-proc getHtmlElement*(xmlElement: XmlNode, parentNode: HtmlNode): HtmlElement =
-  assert kind(xmlElement) == xnElement
-  let tagType = xmlElement.tag().tagType()
-
-  case tagType
-  of TAG_INPUT: result = new(HtmlInputElement)
-  of TAG_A: result = new(HtmlAnchorElement)
-  else: new(result)
-
-  result.tagType = tagType
-  result.parentNode = parentNode
-  if parentNode.isElemNode():
-    result.parentElement = HtmlElement(parentNode)
-
-  result.id = xmlElement.attr("id")
-
-  if tagType in DisplayInlineTags:
-    result.display = DISPLAY_INLINE
-  elif tagType in DisplayBlockTags:
-    result.display = DISPLAY_BLOCK
-  elif tagType in DisplayInlineBlockTags:
-    result.display = DISPLAY_INLINE_BLOCK
-  elif tagType ==  TAG_LI:
-    result.display = DISPLAY_LIST_ITEM
-  else:
-    result.display = DISPLAY_NONE
-
-  case tagType
-  of TAG_CENTER:
-    result.centered = true
-  of TAG_B:
-    result.bold = true
-  of TAG_I:
-    result.italic = true
-  of TAG_U:
-    result.underscore = true
-  of TAG_HEAD:
-    result.hidden = true
-  of TAG_STYLE:
-    result.hidden = true
-  of TAG_SCRIPT:
-    result.hidden = true
-  of TAG_INPUT:
-    let inputElement = HtmlInputElement(result)
-    inputElement.itype = xmlElement.attr("type").toInputType()
-    if inputElement.itype == INPUT_HIDDEN:
-      inputElement.hidden = true
-    inputElement.size = xmlElement.attr("size").toInputSize()
-    inputElement.value = xmlElement.attr("value")
-    result = inputElement
-  of TAG_A:
-    let anchorElement = HtmlAnchorElement(result)
-    anchorElement.href = xmlElement.attr("href")
-    anchorElement.islink = true
-    result = anchorElement
-  of TAG_SELECT:
-    var selectElement = new(HtmlSelectElement)
-    for item in xmlElement.items:
-      if item.kind == xnElement:
-        if item.tag == "option":
-          selectElement.value = item.attr("value")
-          break
-    selectElement.name = xmlElement.attr("name")
-    result = selectElement
-  of TAG_OPTION:
-    var optionElement = new(HtmlOptionElement)
-    optionElement.value = xmlElement.attr("value")
-    if parentNode.isElemNode() and HtmlSelectElement(parentNode).value != optionElement.value:
-      optionElement.hidden = true
-    result = optionElement
-  of TAG_PRE, TAG_TD, TAG_TH:
-    result.margin = 1
-  of TAG_UL, TAG_OL:
-    result.indent = 1
-  of TAG_H1, TAG_H2, TAG_H3, TAG_H4, TAG_H5, TAG_H6:
-    result.bold = true
-    result.marginbottom = 1
-  else: discard
-
-  if parentNode.isElemNode():
-    let parent = HtmlElement(parentNode)
-    result.centered = result.centered or parent.centered
-    result.bold = result.bold or parent.bold
-    result.italic = result.italic or parent.italic
-    result.underscore = result.underscore or parent.underscore
-    result.hidden = result.hidden or parent.hidden
-    result.islink = result.islink or parent.islink
-
-proc getHtmlNode*(xmlElement: XmlNode, parent: HtmlNode): HtmlNode =
-  case kind(xmlElement)
-  of xnElement:
-    result = getHtmlElement(xmlElement, parent)
-    result.nodeType = NODE_ELEMENT
-  of xnText:
-    new(result)
-    result.nodeType = NODE_TEXT
-    result.rawtext = xmlElement.text
-  of xnComment:
-    new(result)
-    result.nodeType = NODE_COMMENT
-    result.rawtext = xmlElement.text
-  of xnCData:
-    new(result)
-    result.nodeType = NODE_CDATA
-    result.rawtext = xmlElement.text
-  else: assert(false)
-
-  result.parentNode = parent
-  if parent.isElemNode():
-    result.parentElement = HtmlElement(parent)
-  if parent.childNodes.len > 0:
-    result.previousSibling = parent.childNodes[^1]
-    result.previousSibling.nextSibling = result
-  parent.childNodes.add(result)
-
-  result.rawtext = result.getRawText()
-  result.fmttext = result.getFmtText()
-
-func newDocument*(): Document =
-  new(result)
-  result.nodeType = NODE_DOCUMENT
diff --git a/main.nim b/main.nim
index b2ce1ace..677b812f 100644
--- a/main.nim
+++ b/main.nim
@@ -29,7 +29,6 @@ proc getPageUri(uri: Uri): Stream =
   if uri.scheme == "" or uri.scheme == "file":
     return getLocalPage($moduri)
   else:
-    #return nparseHtml(getRemotePage($moduri))
     return getRemotePage($moduri)
 
 var buffers: seq[Buffer]
@@ -44,9 +43,9 @@ proc main*() =
   let buffer = newBuffer(attrs)
   let uri = parseUri(paramStr(1))
   buffers.add(buffer)
-  buffer.document = nparseHtml(getPageUri(uri))
+  buffer.document = parseHtml(getPageUri(uri))
   buffer.setLocation(uri)
-  buffer.nrenderHtml()
+  buffer.renderHtml()
   var lastUri = uri
   while displayPage(attrs, buffer):
     statusMsg("Loading...", buffer.height)
@@ -58,10 +57,9 @@ proc main*() =
       if uri.scheme == "" and uri.path == "" and uri.anchor != "":
         discard
       else:
-        buffer.document = nparseHtml(getPageUri(buffer.document.location))
-      buffer.nrenderHtml()
+        buffer.document = parseHtml(getPageUri(buffer.document.location))
+      buffer.renderHtml()
     lastUri = newUri
 
 #waitFor loadPage("https://lite.duckduckgo.com/lite/?q=hello%20world")
-#eprint mk_wcswidth_cjk("abc•de")
 main()
diff --git a/parser.nim b/parser.nim
index a8951368..11c39c76 100644
--- a/parser.nim
+++ b/parser.nim
@@ -1,131 +1,97 @@
-import parsexml
-import htmlelement
 import streams
-import macros
 import unicode
+import strutils
+import tables
+import json
 
 import twtio
 import enums
-import strutils
+import twtstr
+import dom
+import radixtree
 
 type
   ParseState = object
     stream: Stream
     closed: bool
-    parents: seq[HtmlNode]
-    parsedNode: HtmlNode
+    parents: seq[Node]
+    parsedNode: Node
     a: string
+    b: string
     attrs: seq[string]
+    in_comment: bool
+    in_script: bool
+    in_style: bool
+    in_noscript: bool
+    parentNode: Node
+    textNode: Text
 
-  ParseEvent =
-    enum
-    NO_EVENT, EVENT_COMMENT, EVENT_STARTELEM, EVENT_ENDELEM, EVENT_OPENELEM,
-    EVENT_CLOSEELEM, EVENT_ATTRIBUTE, EVENT_TEXT
-
-#> no I won't manually write all this down
-#yes this is incredibly ugly
-#...but hey, so long as it works
-
-macro genEnumCase(s: string, t: typedesc) =
-  result = quote do:
-    let casestmt = nnkCaseStmt.newTree() 
-    casestmt.add(ident(`s`))
-    var first = true
-    for e in low(`t`) .. high(`t`):
-      if first:
-        first = false
-        continue
-      let ret = nnkReturnStmt.newTree()
-      ret.add(newLit(e))
-      let branch = nnkOfBranch.newTree()
-      let enumname = $e
-      let tagname = enumname.split('_')[1..^1].join("_").tolower()
-      branch.add(newLit(tagname))
-      branch.add(ret)
-      casestmt.add(branch)
-    let ret = nnkReturnStmt.newTree()
-    ret.add(newLit(low(`t`)))
-    let branch = nnkElse.newTree()
-    branch.add(ret)
-    casestmt.add(branch)
-
-macro genTagTypeCase() =
-  genEnumCase("s", TagType)
-
-macro genInputTypeCase() =
-  genEnumCase("s", InputType)
-
-func tagType(s: string): TagType =
-  genTagTypeCase
-
-func inputType(s: string): InputType =
-  genInputTypeCase
-
-func newHtmlElement(tagType: TagType, parentNode: HtmlNode): HtmlElement =
-  case tagType
-  of TAG_INPUT: result = new(HtmlInputElement)
-  of TAG_A: result = new(HtmlAnchorElement)
-  of TAG_SELECT: result = new(HtmlSelectElement)
-  of TAG_OPTION: result = new(HtmlOptionElement)
-  else: result = new(HtmlElement)
-
-  result.nodeType = NODE_ELEMENT
-  result.tagType = tagType
-  result.parentNode = parentNode
-  if parentNode.isElemNode():
-    result.parentElement = HtmlElement(parentNode)
-
-  if tagType in DisplayInlineTags:
-    result.display = DISPLAY_INLINE
-  elif tagType in DisplayBlockTags:
-    result.display = DISPLAY_BLOCK
-  elif tagType in DisplayInlineBlockTags:
-    result.display = DISPLAY_INLINE_BLOCK
-  elif tagType == TAG_LI:
-    result.display = DISPLAY_LIST_ITEM
-  else:
-    result.display = DISPLAY_NONE
-
-  case tagType
-  of TAG_CENTER:
-    result.centered = true
-  of TAG_B:
-    result.bold = true
-  of TAG_I:
-    result.italic = true
-  of TAG_U:
-    result.underscore = true
-  of TAG_HEAD:
-    result.hidden = true
-  of TAG_STYLE:
-    result.hidden = true
-  of TAG_SCRIPT:
-    result.hidden = true
-  of TAG_OPTION:
-    result.hidden = true #TODO
-  of TAG_PRE, TAG_TD, TAG_TH:
-    result.margin = 1
-  of TAG_UL, TAG_OL:
-    result.indent = 1
-  of TAG_H1, TAG_H2, TAG_H3, TAG_H4, TAG_H5, TAG_H6:
-    result.bold = true
-    result.marginbottom = 1
-  of TAG_A:
-    result.islink = true
-  of TAG_INPUT:
-    HtmlInputElement(result).size = 20
-  else: discard
-
-  if parentNode.isElemNode():
-    let parent = HtmlElement(parentNode)
-    result.centered = result.centered or parent.centered
-    result.bold = result.bold or parent.bold
-    result.italic = result.italic or parent.italic
-    result.underscore = result.underscore or parent.underscore
-    result.hidden = result.hidden or parent.hidden
-    result.islink = result.islink or parent.islink
+#func newHtmlElement(tagType: TagType, parentNode: Node): HtmlElement =
+#  case tagType
+#  of TAG_INPUT: result = new(HtmlInputElement)
+#  of TAG_A: result = new(HtmlAnchorElement)
+#  of TAG_SELECT: result = new(HtmlSelectElement)
+#  of TAG_OPTION: result = new(HtmlOptionElement)
+#  else: result = new(HtmlElement)
+#
+#  result.nodeType = ELEMENT_NODE
+#  result.tagType = tagType
+#  result.parentNode = parentNode
+#  if parentNode.isElemNode():
+#    result.parentElement = HtmlElement(parentNode)
+#
+#  if tagType in DisplayInlineTags:
+#    result.display = DISPLAY_INLINE
+#  elif tagType in DisplayBlockTags:
+#    result.display = DISPLAY_BLOCK
+#  elif tagType in DisplayInlineBlockTags:
+#    result.display = DISPLAY_INLINE_BLOCK
+#  elif tagType == TAG_LI:
+#    result.display = DISPLAY_LIST_ITEM
+#  else:
+#    result.display = DISPLAY_NONE
+#
+#  case tagType
+#  of TAG_CENTER:
+#    result.centered = true
+#  of TAG_B:
+#    result.bold = true
+#  of TAG_I:
+#    result.italic = true
+#  of TAG_U:
+#    result.underscore = true
+#  of TAG_HEAD:
+#    result.hidden = true
+#  of TAG_STYLE:
+#    result.hidden = true
+#  of TAG_SCRIPT:
+#    result.hidden = true
+#  of TAG_OPTION:
+#    result.hidden = true #TODO
+#  of TAG_PRE, TAG_TD, TAG_TH:
+#    result.margin = 1
+#  of TAG_UL, TAG_OL:
+#    result.indent = 2
+#    result.margin = 1
+#  of TAG_H1, TAG_H2, TAG_H3, TAG_H4, TAG_H5, TAG_H6:
+#    result.bold = true
+#    result.margin = 1
+#  of TAG_A:
+#    result.islink = true
+#  of TAG_INPUT:
+#    HtmlInputElement(result).size = 20
+#  else: discard
+#
+#  if parentNode.isElemNode():
+#    let parent = HtmlElement(parentNode)
+#    result.centered = result.centered or parent.centered
+#    result.bold = result.bold or parent.bold
+#    result.italic = result.italic or parent.italic
+#    result.underscore = result.underscore or parent.underscore
+#    result.hidden = result.hidden or parent.hidden
+#    result.islink = result.islink or parent.islink
 
-func toInputSize*(str: string): int =
+func inputSize*(str: string): int =
   if str.len == 0:
     return 20
   for c in str:
@@ -133,297 +99,400 @@ func toInputSize*(str: string): int =
       return 20
   return str.parseInt()
 
-proc applyAttribute(htmlElement: HtmlElement, key: string, value: string) =
-  case key
-  of "id": htmlElement.id = value
-  of "class": htmlElement.class = value
-  of "name":
-    case htmlElement.tagType
-    of TAG_SELECT: HtmlSelectElement(htmlElement).name = value
-    else: discard
-  of "value":
-    case htmlElement.tagType
-    of TAG_INPUT: HtmlInputElement(htmlElement).value = value
-    of TAG_SELECT: HtmlSelectElement(htmlElement).value = value
-    of TAG_OPTION: HtmlOptionElement(htmlElement).value = value
-    else: discard
-  of "href":
-    case htmlElement.tagType
-    of TAG_A: HtmlAnchorElement(htmlElement).href = value
-    else: discard
-  of "type":
-    case htmlElement.tagType
-    of TAG_INPUT: HtmlInputElement(htmlElement).itype = value.inputType()
-    else: discard
-  of "size":
-    case htmlElement.tagType
-    of TAG_INPUT: HtmlInputElement(htmlElement).size = value.toInputSize()
-    else: discard
-  else: return
-
-proc closeNode(state: var ParseState) =
-  let node = state.parents[^1]
-  if node.childNodes.len > 0 and node.isElemNode() and HtmlElement(node).display == DISPLAY_BLOCK:
-    node.childNodes[0].openblock = true
-    node.childNodes[^1].closeblock = true
-  state.parents.setLen(state.parents.len - 1)
-  state.closed = true
-
-proc closeSingleNodes(state: var ParseState) =
-  if not state.closed and state.parents[^1].isElemNode() and HtmlElement(state.parents[^1]).tagType in SingleTagTypes:
-    state.closeNode()
-
-proc applyNodeText(htmlNode: HtmlNode) =
-  htmlNode.rawtext = htmlNode.getRawText()
-  htmlNode.fmttext = htmlNode.getFmtText()
-
-proc setParent(state: var ParseState, htmlNode: HtmlNode) =
-  htmlNode.parentNode = state.parents[^1]
-  if state.parents[^1].isElemNode():
-    htmlNode.parentElement = HtmlElement(state.parents[^1])
-  if state.parents[^1].childNodes.len > 0:
-    htmlNode.previousSibling = state.parents[^1].childNodes[^1]
-    htmlNode.previousSibling.nextSibling = htmlNode
-  state.parents[^1].childNodes.add(htmlNode)
-
-proc processHtmlElement(state: var ParseState, htmlElement: HtmlElement) =
-  state.closed = false
-  state.setParent(htmlElement)
-  state.parents.add(htmlElement)
-
-proc parsecomment(state: var ParseState) =
+proc genEntityMap(): RadixTree[string] =
+  let entity = staticRead"entity.json"
+  let entityJson = parseJson(entity)
+  var entityMap = newRadixTree[string]()
+
+  for k, v in entityJson:
+    entityMap[k.substr(1)] = v{"characters"}.getStr()
+
+  return entityMap
+
+const entityMap = genEntityMap()
+
+func genHexCharMap(): seq[int] =
+  for i in 0..255:
+    case chr(i)
+    of '0'..'9': result &= i - ord('0')
+    of 'a'..'f': result &= i - ord('a') + 10
+    of 'A'..'F': result &= i - ord('A') + 10
+    else: result &= -1
+
+func genDecCharMap(): seq[int] =
+  for i in 0..255:
+    case chr(i)
+    of '0'..'9': result &= i - ord('0')
+    else: result &= -1
+
+const hexCharMap = genHexCharMap()
+const decCharMap = genDecCharMap()
+
+#w3m's getescapecmd and parse_tag, transpiled to nim.
+#(C) Copyright 1994-2002 by Akinori Ito
+#(C) Copyright 2002-2011 by Akinori Ito, Hironori Sakamoto, Fumitoshi Ukai
+#
+#Use, modification and redistribution of this software is hereby granted,
+#provided that this entire copyright notice is included on any copies of
+#this software and applications and derivations thereof.
+#
+#This software is provided on an "as is" basis, without warranty of any
+#kind, either expressed or implied, as to any matter including, but not
+#limited to warranty of fitness of purpose, or merchantability, or
+#results obtained from use of this software.
+proc getescapecmd(buf: string, at: var int): string =
+  var i = at
+
+  if buf[i] == '#': #num
+    inc i
+    var num: int
+    if buf[i].tolower() == 'x': #hex
+      inc i
+      if not isdigit(buf[i]):
+        at = i
+        return ""
+
+      num = hexCharMap[int(buf[i])]
+      inc i
+      while i < buf.len and hexCharMap[int(buf[i])] != -1:
+        num *= 0x10
+        num += hexCharMap[int(buf[i])]
+        inc i
+    else: #dec
+      if not isDigit(buf[i]):
+        at = i
+        return ""
+
+      num = decCharMap[int(buf[i])]
+      inc i
+      while i < buf.len and isDigit(buf[i]):
+        num *= 10
+        num += decCharMap[int(buf[i])]
+        inc i
+
+    if buf[i] == ';':
+      inc i
+    at = i
+    return $(Rune(num))
+  elif not isAlphaAscii(buf[i]):
+    return ""
+
+  var n: uint16 = 0
   var s = ""
-  state.a = ""
-  var e = 0
-  while not state.stream.atEnd():
-    let c = cast[char](state.stream.readInt8())
-    if c > char(127):
-      s &= c
-      if s.validateUtf8() == -1:
-        state.a &= s
-        s = ""
+  while true:
+    let c = buf[i]
+    s &= c
+    if not entityMap.hasPrefix(s, n):
+      break
+    let pn = n
+    n = entityMap.getPrefix(s, n)
+    if n != pn:
+      s = ""
+    inc i
+
+  if entityMap.nodes[n].leaf:
+    at = i
+    return entityMap.nodes[n].value
+
+  return ""
+
+type
+  DOMParsedTag = object
+    tagid: TagType
+    attrs: Table[string, string]
+    open: bool
+
+proc parse_tag(buf: string, at: var int): DOMParsedTag =
+  var tag = DOMParsedTag()
+  tag.open = true
+
+  #Parse tag name
+  var tagname = ""
+  inc at
+  if buf[at] == '/':
+    inc at
+    tag.open = false
+    skipBlanks(buf, at)
+
+  while at < buf.len and not buf[at].isWhitespace() and not (tag.open and buf[at] == '/') and buf[at] != '>':
+    tagname &= buf[at].tolower()
+    at += buf.runeLenAt(at)
+
+  tag.tagid = tagType(tagname)
+  skipBlanks(buf, at)
+
+  while at < buf.len and buf[at] != '>':
+    var value = ""
+    var attrname = ""
+    while at < buf.len and buf[at] != '=' and not buf[at].isWhitespace() and buf[at] != '>':
+      attrname &= buf[at].tolower()
+      at += buf.runeLenAt(at)
+
+    skipBlanks(buf, at)
+    if buf[at] == '=':
+      inc at
+      skipBlanks(buf, at)
+      if at < buf.len and (buf[at] == '"' or buf[at] == '\''):
+        let startc = buf[at]
+        inc at
+        while at < buf.len and buf[at] != startc:
+          var r: Rune
+          fastRuneAt(buf, at, r)
+          if r == Rune('&'):
+            value &= getescapecmd(buf, at)
+          else:
+            value &= $r
+        if at < buf.len:
+          inc at
+      elif at < buf.len:
+        while at < buf.len and not buf[at].isWhitespace() and buf[at] != '>':
+          value &= buf[at]
+          at += buf.runeLenAt(at)
+
+    if attrname.len > 0:
+      tag.attrs[attrname] = value
+
+  while at < buf.len and buf[at] != '>':
+    at += buf.runeLenAt(at)
+
+  if at < buf.len and buf[at] == '>':
+    inc at
+  return tag
+
+proc insertNode(parent: Node, node: Node) =
+  parent.childNodes.add(node)
+
+  if parent.firstChild == nil:
+    parent.firstChild = node
+
+  parent.lastChild = node
+
+  if parent.childNodes.len > 1:
+    let prevSibling = parent.childNodes[^1]
+    prevSibling.nextSibling = node
+    node.previousSibling = prevSibling
+
+  node.parentNode = parent
+  if parent.nodeType == ELEMENT_NODE:
+    node.parentElement = Element(parent)
+
+  if parent.ownerDocument != nil:
+    node.ownerDocument = parent.ownerDocument
+  elif parent.nodeType == DOCUMENT_NODE:
+    node.ownerDocument = Document(parent)
+
+proc processDocumentStartNode(state: var ParseState, newNode: Node) =
+  insertNode(state.parentNode, newNode)
+  state.parentNode = newNode
+
+proc processDocumentEndNode(state: var ParseState) =
+  if state.parentNode == nil or state.parentNode.parentNode == nil:
+    return
+  state.parentNode = state.parentNode.parentNode
+
+proc processDocumentText(state: var ParseState) =
+  if state.textNode == nil:
+    state.textNode = newText()
+
+    processDocumentStartNode(state, state.textNode)
+    processDocumentEndNode(state)
+
+proc processDocumentStartElement(state: var ParseState, element: Element, tag: DOMParsedTag) =
+  for k, v in tag.attrs:
+    element.attributes[k] = element.newAttr(k, v)
+  
+  element.id = element.getAttrValue("id")
+  if element.attributes.hasKey("class"):
+    for w in unicode.split(element.attributes["class"].value, Rune(' ')):
+      element.classList.add(w)
+
+  case element.tagType
+  of TAG_SCRIPT:
+    state.in_script = true
+  of TAG_NOSCRIPT:
+    state.in_noscript = true
+  of TAG_STYLE:
+    state.in_style = true
+  of TAG_SELECT:
+    HTMLSelectElement(element).name = element.getAttrValue("name")
+    HTMLSelectElement(element).value = element.getAttrValue("value")
+  of TAG_INPUT:
+    HTMLInputElement(element).value = element.getAttrValue("value")
+    HTMLInputElement(element).itype = element.getAttrValue("type").inputType()
+    HTMLInputElement(element).size = element.getAttrValue("size").inputSize()
+  of TAG_A:
+    HTMLAnchorElement(element).href = element.getAttrValue("href")
+  of TAG_OPTION:
+    HTMLOptionElement(element).value = element.getAttrValue("href")
+  else: discard
+
+  if state.parentNode.nodeType == ELEMENT_NODE:
+    case element.tagType
+    of TAG_LI, TAG_P:
+      if Element(state.parentNode).tagType == element.tagType:
+        processDocumentEndNode(state)
+    of TAG_H1:
+      HTMLHeadingElement(element).rank = 1
+    of TAG_H2:
+      HTMLHeadingElement(element).rank = 2
+    of TAG_H3:
+      HTMLHeadingElement(element).rank = 3
+    of TAG_H4:
+      HTMLHeadingElement(element).rank = 4
+    of TAG_H5:
+      HTMLHeadingElement(element).rank = 5
+    of TAG_H6:
+      HTMLHeadingElement(element).rank = 6
+    else: discard
+
+  processDocumentStartNode(state, element)
+
+  if element.tagType in VoidTagTypes:
+    processDocumentEndNode(state)
+
+proc processDocumentEndElement(state: var ParseState, tag: DOMParsedTag) =
+  if tag.tagid in VoidTagTypes:
+    return
+  if state.parentNode.nodeType == ELEMENT_NODE:
+    if Element(state.parentNode).tagType in {TAG_LI, TAG_P}:
+      processDocumentEndNode(state)
+  
+  processDocumentEndNode(state)
+
+proc processDocumentTag(state: var ParseState, tag: DOMParsedTag) =
+  if state.in_script:
+    if tag.tagid == TAG_SCRIPT:
+      state.in_script = false
     else:
-      case e
-      of 0:
-        if c == '-': inc e
-      of 1:
-        if c == '-': inc e
-        else:
-          e = 0
-          state.a &= '-' & c
-      of 2:
-        if c == '>': return
-        else:
-          e = 0
-          state.a &= "--" & c
-      else: state.a &= c
-
-proc parsecdata(state: var ParseState) =
-  var s = ""
-  var e = 0
-  while not state.stream.atEnd():
-    let c = cast[char](state.stream.readInt8())
-    if c > char(127):
-      s &= c
-      if s.validateUtf8() == -1:
-        state.a &= s
-        s = ""
+      return
+
+  if state.in_style:
+    if tag.tagid == TAG_STYLE:
+      state.in_style = false
     else:
-      case e
-      of 0:
-        if c == ']': inc e
-      of 1:
-        if c == ']': inc e
-        else: e = 0
-      of 2:
-        if c == '>': return
-        else: e = 0
-      else: discard
-      state.a &= c
+      return
 
-proc next(state: var ParseState): ParseEvent =
-  result = NO_EVENT
-  if state.stream.atEnd(): return result
+  if state.in_noscript:
+    if tag.tagid == TAG_NOSCRIPT:
+      state.in_noscript = false
+    else:
+      return
 
-  var c = cast[char](state.stream.readInt8())
-  var cdata = false
-  var s = ""
-  state.a = ""
-  if c < char(128): #ascii
-    case c
-    of '<':
-      if state.stream.atEnd():
-        state.a = $c
-        return EVENT_TEXT
-      let d = char(state.stream.peekInt8())
-      case d
-      of '/': result = EVENT_ENDELEM
-      of '!':
-        state.a = state.stream.readStr(2)
-        case state.a
-        of "[C":
-          state.a &= state.stream.readStr(7)
-          if state.a == "[CDATA[":
-            state.parsecdata()
-            return EVENT_COMMENT
-          result = EVENT_TEXT
-        of "--":
-          state.parsecomment()
-          return EVENT_COMMENT
-        else:
-          while not state.stream.atEnd():
-            c = cast[char](state.stream.readInt8())
-            if s.len == 0 and c == '>':
-              break
-            elif c > char(127):
-              s &= c
-              if s.validateUtf8() == -1:
-                s = ""
-          return NO_EVENT
-      of Letters:
-        result = EVENT_STARTELEM
-      else:
-        result = EVENT_TEXT
-        state.a = c & d
-    of '>':
-      return EVENT_CLOSEELEM
-    else: result = EVENT_TEXT
-  else: result = EVENT_TEXT
-
-  case result
-  of EVENT_STARTELEM:
-    var atspace = false
-    var atattr = false
-    while not state.stream.atEnd():
-      c = cast[char](state.stream.peekInt8())
-      if s.len == 0 and c < char(128):
-        case c
-        of Whitespace: atspace = true
-        of '>':
-          discard state.stream.readInt8()
-          break
-        else:
-          if atspace:
-            return EVENT_OPENELEM
-          else:
-            state.a &= s
+  if tag.open:
+    processDocumentStartElement(state, newHtmlElement(tag.tagid), tag)
+  else:
+    processDocumentEndElement(state, tag)
+  #XXX PROCDOCCASE stuff... good lord I'll never finish this thing
+
+proc processDocumentPart(state: var ParseState, buf: string) =
+  var at = 0
+  var max = 0
+  var was_script = false
+
+  max = buf.len
+
+  while at < max:
+    case buf[at]
+    of '&':
+      inc at
+      let p = getescapecmd(buf, at)
+      if state.in_comment:
+        CharacterData(state.parentNode).data &= p
       else:
-        if atspace:
-          return EVENT_OPENELEM
-        s &= c
-        if s.validateUtf8() == -1:
-          state.a &= s
-          s = ""
-      discard state.stream.readInt8()
-  of EVENT_ENDELEM:
-    while not state.stream.atEnd():
-      c = cast[char](state.stream.readInt8())
-      if s.len == 0 and c < char(128):
-        if c == '>': break
-        elif c in Whitespace: discard
-        else: state.a &= c
+        processDocumentText(state)
+        state.textNode.data &= p
+    of '<':
+      if state.in_comment:
+        CharacterData(state.parentNode).data &= buf[at]
+        inc at
       else:
-        s &= c
-        if s.validateUtf8() == -1:
-          state.a &= s
-          s = ""
-  of EVENT_TEXT:
-    while not state.stream.atEnd():
-      c = cast[char](state.stream.peekInt8())
-      if s.len == 0 and c < char(128):
-        if c in {'<', '>'}: break
-        state.a &= c
+        var p = at
+        inc p
+        if p < max and buf[p] == '!':
+          inc p
+          if p < max and buf[p] == '-':
+            inc p
+            if p < max and buf[p] == '-':
+              inc p
+              at = p
+              state.in_comment = true
+              processDocumentStartNode(state, newComment())
+              if state.textNode != nil:
+                state.textNode.rawtext = state.textNode.getRawText()
+                state.textNode = nil
+
+        if not state.in_comment:
+          if state.textNode != nil:
+            state.textNode.rawtext = state.textNode.getRawText()
+            state.textNode = nil
+          p = at
+          var tag = parse_tag(buf, at)
+          was_script = state.in_script
+
+          processDocumentTag(state, tag)
+#         if (was_script) {
+#             if (state->in_script) {
+#                 ptr = p;
+#                 processDocumentText(&state->parentNode, &state->textNode);
+#                 Strcat_char(((CharacterData *)state->textNode)->data, *ptr++);
+#             } else if (buffer->javascript_enabled) {
+#                 loadJSToBuffer(buffer, childTextContentNode(state->parentNode->lastChild)->ptr, "<inline>", state->document);
+#             }
+#         }
+    elif buf[at] == '-' and state.in_comment:
+      var p = at
+      inc p
+      if p < max and buf[p] == '-':
+        inc p
+        if p < max and buf[p] == '>':
+          inc p
+          at = p
+          state.in_comment = false
+          processDocumentEndNode(state)
+
+      if state.in_comment:
+        CharacterData(state.parentNode).data &= buf[at]
+        inc at
+    else:
+      var r: Rune
+      fastRuneAt(buf, at, r)
+      if state.in_comment:
+        CharacterData(state.parentNode).data &= $r
       else:
-        s &= c
-        if s.validateUtf8() == -1:
-          state.a &= s
-          s = ""
-      discard state.stream.readInt8()
-  else: assert(false)
-
-proc nparseHtml*(inputStream: Stream): Document =
-  var state = ParseState(stream: inputStream)
+        processDocumentText(state)
+        state.textNode.data &= $r
+
+proc parseHtml*(inputStream: Stream): Document =
   let document = newDocument()
-  state.parents.add(document)
-  while state.parents.len > 0 and not inputStream.atEnd():
-    let event = state.next()
-    case event
-    of EVENT_COMMENT: discard #TODO
-    of EVENT_STARTELEM:
-      state.closeSingleNodes()
-      let parsedNode = newHtmlElement(tagType(state.a), state.parents[^1])
-      parsedNode.applyNodeText()
-      state.processHtmlElement(parsedNode)
-    of EVENT_ENDELEM:
-      state.closeNode()
-    of EVENT_OPENELEM:
-      state.closeSingleNodes()
-      let parsedNode = newHtmlElement(tagType(state.a), state.parents[^1])
-      var next = state.next()
-      while next != EVENT_CLOSEELEM and not inputStream.atEnd():
-        #TODO
-        #if next == EVENT_ATTRIBUTE:
-        #  parsedNode.applyAttribute(state.a.tolower(), state.b)
-        #  s &= " " & x.rawdata & "=\"" & x.rawdata2 & "\""
-        #else:
-        #  assert(false, "wtf " & $x.kind & " " & x.rawdata) #TODO
-        next = state.next()
-      parsedNode.applyNodeText()
-      state.processHtmlElement(parsedNode)
-    of EVENT_TEXT:
-      if unicode.strip(state.a).len == 0:
-        continue
-      let textNode = new(HtmlNode)
-      textNode.nodeType = NODE_TEXT
-      state.setParent(textNode)
-      textNode.rawtext = state.a
-      textNode.applyNodeText()
-    else: discard
-  return document
 
-#old nparseHtml because I don't trust myself
-#proc nparseHtml*(inputStream: Stream): Document =
-#  var x: XmlParser
-#  let options = {reportWhitespace, allowUnquotedAttribs, allowEmptyAttribs}
-#  x.open(inputStream, "", options)
-#  var state = ParseState(stream: inputStream)
-#  let document = newDocument()
-#  state.parents.add(document)
-#  while state.parents.len > 0 and x.kind != xmlEof:
-#    #let event = state.next()
-#    x.next()
-#    case x.kind
-#    of xmlComment: discard #TODO
-#    of xmlElementStart:
-#      state.closeSingleNodes()
-#      let parsedNode = newHtmlElement(tagType(x.rawData), state.parents[^1])
-#      parsedNode.applyNodeText()
-#      state.processHtmlElement(parsedNode)
-#    of xmlElementEnd:
-#      state.closeNode()
-#    of xmlElementOpen:
-#      var s = "<" & x.rawdata
-#      state.closeSingleNodes()
-#      let parsedNode = newHtmlElement(tagType(x.rawData), state.parents[^1])
-#      x.next()
-#      while x.kind != xmlElementClose and x.kind != xmlEof:
-#        if x.kind == xmlAttribute:
-#          parsedNode.applyAttribute(x.rawData.tolower(), x.rawData2)
-#          s &= " " & x.rawdata & "=\"" & x.rawdata2 & "\""
-#        else:
-#          assert(false, "wtf " & $x.kind & " " & x.rawdata) #TODO
-#        x.next()
-#      s &= ">"
-#      parsedNode.applyNodeText()
-#      state.processHtmlElement(parsedNode)
-#    of xmlCharData:
-#      let textNode = new(HtmlNode)
-#      textNode.nodeType = NODE_TEXT
-#
-#      state.setParent(textNode)
-#      textNode.rawtext = x.rawData
-#      textNode.applyNodeText()
-#    of xmlEntity: discard #TODO
-#    of xmlEof: break
-#    else: discard
-#  return document
+  var state = ParseState(stream: inputStream)
+  state.parentNode = document
+
+  var till_when = false
+
+  var buf = ""
+  var lineBuf: string
+  while not inputStream.atEnd():
+    lineBuf = inputStream.readLine()
+    if lineBuf.len == 0:
+      break
+    buf &= lineBuf
+
+    var at = 0
+    while at < lineBuf.len:
+      case lineBuf[at]
+      of '<':
+        till_when = true
+      of '>':
+        till_when = false
+      else: discard
+      at += lineBuf.runeLenAt(at)
+
+    if till_when:
+      continue
+
+    processDocumentPart(state, buf)
+    buf = ""
+
+  inputStream.close()
+  return document
diff --git a/radixtree.nim b/radixtree.nim
new file mode 100644
index 00000000..7933f7a3
--- /dev/null
+++ b/radixtree.nim
@@ -0,0 +1,153 @@
+import tables
+import strutils
+import json
+
+type
+  RadixNode[T] = object
+    children*: Table[string, uint16]
+    case leaf*: bool
+    of true: value*: T
+    of false: discard
+
+  RadixTree*[T] = object
+    nodes*: seq[RadixNode[T]]
+
+func newRadixTree*[T](): RadixTree[T] =
+  result.nodes.add(RadixNode[T](leaf: false))
+
+proc `[]=`*[T](tree: var RadixTree[T], key: string, value: T) =
+  var n: uint16 = 0
+  var p: uint16 = 0
+  var i = 0
+  var j = 0
+  var s = ""
+  var t = ""
+  var nodeKey = ""
+  # find last matching node
+  while i < key.len:
+    s &= key[i]
+    if s in tree.nodes[n].children:
+      p = n
+      n = tree.nodes[n].children[s]
+      t &= s
+      j += s.len
+      nodeKey = s
+      s = ""
+    inc i
+
+  for k in tree.nodes[n].children.keys:
+    if s.len > 0 and k.startsWith(s[0]):
+      p = n
+      n = tree.nodes[n].children[k]
+      t &= k
+      nodeKey = k
+      break
+
+  # if first node, just add normally
+  if n == 0:
+    tree.nodes.add(RadixNode[T](leaf: true, value: value))
+    tree.nodes[n].children[key] = uint16(tree.nodes.len - 1)
+  else:
+    i = 0
+    var conflict = false
+    # compare new key with the one we found so far
+    while i < t.len and i < key.len:
+      if key[i] == t[i]:
+        inc i
+      else:
+        conflict = true
+        break
+
+    if conflict:
+      # conflict somewhere, so:
+      # * add new non-leaf to parent
+      # * add old to non-leaf
+      # * add new to non-leaf
+      # * remove old from parent
+      assert(i != 0)
+
+      tree.nodes[p].children[key.substr(j, i - 1)] = uint16(tree.nodes.len)
+      tree.nodes.add(RadixNode[T](leaf: false))
+      tree.nodes[^1].children[t.substr(i)] = n
+      tree.nodes[^1].children[key.substr(i)] = uint16(tree.nodes.len)
+      tree.nodes.add(RadixNode[T](leaf: true, value: value))
+      tree.nodes[p].children.del(nodeKey)
+    else: # new is either substr of old or old is substr of new
+      # new matches a node, so replace
+      if key.len == t.len:
+        let children = tree.nodes[n].children
+        tree.nodes[n] = RadixNode[T](leaf: true, value: value)
+        tree.nodes[n].children = children
+      elif i == j:
+      # new is longer than the old, so add child to old
+        tree.nodes[n].children[key.substr(i)] = uint16(tree.nodes.len)
+        tree.nodes.add(RadixNode[T](leaf: true, value: value))
+      elif i > 0:
+      # new is shorter than old, so:
+      # * add new to parent
+      # * add old to new
+      # * remove old from parent
+        tree.nodes[p].children[key.substr(j, i - 1)] = uint16(tree.nodes.len)
+        tree.nodes.add(RadixNode[T](leaf: true, value: value))
+        tree.nodes[^1].children[t.substr(i)] = n
+        tree.nodes[p].children.del(nodeKey)
+
+func getPrefix*[T](tree: RadixTree[T], prefix: string, at: uint16 = 0): uint16 =
+  var s = ""
+  var t = ""
+  var n = at
+  var i = 0
+  while t.len < prefix.len:
+    s &= prefix[i]
+    t &= prefix[i]
+    if s in tree.nodes[n].children:
+      n = tree.nodes[n].children[s]
+      s = ""
+    inc i
+
+  return n
+
+func hasPrefix*[T](tree: RadixTree[T], prefix: string, at: uint16 = 0): bool =
+  var s = ""
+  var t = ""
+  var n = at
+  var i = 0
+  while i < prefix.len:
+    s &= prefix[i]
+    if s in tree.nodes[n].children:
+      n = tree.nodes[n].children[s]
+      t &= s
+      s = ""
+    inc i
+
+  if t.len == prefix.len:
+    return true
+
+  for k in tree.nodes[n].children.keys:
+    if k.startsWith(prefix.substr(t.len)):
+      return true
+
+  return false
+
+#tests
+#var tree = newRadixTree[string]()
+#tree.insert("hb", "abc")
+#tree.insert("hi", "second")
+#tree.insert("hia", "second")
+#tree.insert("hia", "third")
+#tree.insert("hiahhhooo", "two point fifth")
+#tree.insert("hiahhho", "two point sixth")
+#assert(tree.hasPrefix("h"))
+#assert(tree.hasPrefix("hi"))
+#assert(not tree.hasPrefix("hio"))
+#assert(tree.hasPrefix("hiah"))
+#assert(tree.hasPrefix("hiahhho"))
+#assert(tree.hasPrefix("hiahhhooo"))
+#assert(tree.lookup("hi", "error") != "error")
+#assert(tree.lookup("hb", "error") != "error")
+#assert(tree.lookup("hio", "error") == "error")
+#assert(tree.lookup("hia", "error") != "error")
+#assert(tree.lookup("hiahhhooo", "error") != "error")
+#assert(tree.lookup("hiahhho", "error") != "error")
+#assert(tree.lookup("hiahhhoo", "error") == "error")
+#assert(tree.lookup("h", "error") == "error")
diff --git a/style.nim b/style.nim
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/style.nim
diff --git a/twtio.nim b/twtio.nim
index acb4add7..db811f40 100644
--- a/twtio.nim
+++ b/twtio.nim
@@ -1,21 +1,22 @@
 import terminal
 import tables
-import strutils
 import unicode
 
 import twtstr
 import config
+import radixtree
 
 template print*(s: varargs[string, `$`]) =
   for x in s:
     stdout.write(x)
 
 template printesc*(s: string) =
-  for ruby in s:
-    if ($ruby)[0].isControlChar():
-      stdout.write(($($ruby)[0].getControlLetter()).ansiFgColor(fgBlue).ansiStyle(styleBright).ansiReset())
+  for r in s.runes:
+    if r.isControlChar():
+      stdout.write(('^' & $($r)[0].getControlLetter())
+                   .ansiFgColor(fgBlue).ansiStyle(styleBright).ansiReset())
     else:
-      stdout.write($ruby)
+      stdout.write($r)
 
 template eprint*(s: varargs[string, `$`]) = {.cast(noSideEffect).}:
   var a = false
@@ -40,181 +41,31 @@ proc getLinedAction*(s: string): TwtAction =
     return linedActionRemap[s]
   return NO_ACTION
 
-const breakWord = [
-  Rune('\n'), Rune('/'), Rune('\\'), Rune(' '), Rune('&'), Rune('=')
-]
-
-#proc readLine*(prompt: string, current: var string, termwidth: int): bool =
-#  var new = current
-#  print(prompt)
-#  let maxlen = termwidth - prompt.len
-#  printesc(new)
-#  var s = ""
-#  var feedNext = false
-#  var escNext = false
-#  var cursor = new.runeLen
-#  var shift = 0
-#  while true:
-#    var rl = new.runeLen()
-#
-#    if cursor < shift:
-#      shift = cursor
-#    elif cursor - shift > maxlen:
-#      shift += cursor - maxlen
-#
-#    if not feedNext:
-#      s = ""
-#    else:
-#      feedNext = false
-#    let c = getch()
-#    s &= c
-#    var action = getLinedAction(s)
-#    if escNext:
-#      action = NO_ACTION
-#    case action
-#    of ACTION_LINED_CANCEL:
-#      return false
-#    of ACTION_LINED_SUBMIT:
-#      current = new
-#      return true
-#    of ACTION_LINED_BACKSPACE:
-#      if cursor > 0:
-#        print(' '.repeat(rl - cursor + 1))
-#        print('\b'.repeat(rl - cursor + 1))
-#        print("\b \b")
-#        new = new.runeSubstr(0, cursor - 1) & new.runeSubstr(cursor)
-#        rl = new.runeLen()
-#        cursor -= 1
-#        printesc(new.runeSubstr(cursor))
-#        print('\b'.repeat(rl - cursor))
-#    of ACTION_LINED_ESC:
-#      escNext = true
-#    of ACTION_LINED_CLEAR:
-#      print('\r')
-#      print(' '.repeat(termwidth))
-#      print('\r')
-#      new = new.runeSubstr(cursor)
-#      rl = new.runeLen()
-#      printesc(prompt)
-#      printesc(new.maxString(maxlen + 1))
-#      print('\r')
-#      printesc(prompt)
-#      cursor = 0
-#    of ACTION_LINED_KILL:
-#      print(' '.repeat(rl - cursor + 1))
-#      print('\b'.repeat(rl - cursor + 1))
-#      new = new.runeSubstr(0, cursor)
-#    of ACTION_LINED_BACK:
-#      if cursor > 0:
-#        if cursor < maxlen:
-#          print('\b')
-#        dec cursor
-#    of ACTION_LINED_FORWARD:
-#      if cursor < rl:
-#        if cursor + 1 < maxlen:
-#          var rune: Rune
-#          new.fastRuneAt(cursor, rune, false)
-#          printesc($rune)
-#        elif cursor + 1 == maxlen:
-#          print('$')
-#        inc cursor
-#    of ACTION_LINED_PREV_WORD:
-#      while cursor > 0:
-#        print('\b')
-#        cursor -= 1
-#        var rune: Rune
-#        new.fastRuneAt(cursor, rune, false)
-#        if rune in breakWord:
-#          break
-#    of ACTION_LINED_NEXT_WORD:
-#      while cursor < rl:
-#        var rune: Rune
-#        new.fastRuneAt(cursor, rune, false)
-#        printesc($rune)
-#        inc cursor
-#        if cursor < rl:
-#          new.fastRuneAt(cursor, rune, false)
-#          if rune in breakWord:
-#            break
-#    of ACTION_LINED_KILL_WORD:
-#      var chars = 0
-#      while cursor > chars:
-#        inc chars
-#        var rune: Rune
-#        new.fastRuneAt(cursor - chars, rune, false)
-#        if rune in breakWord:
-#          break
-#      if chars > 0:
-#        print(' '.repeat(rl - cursor + 1))
-#        print('\b'.repeat(rl - cursor + 1))
-#        print("\b \b".repeat(chars))
-#        new = new.runeSubstr(0, cursor - chars) & new.runeSubstr(cursor)
-#        rl = new.runeLen()
-#        cursor -= chars
-#        printesc(new.runeSubstr(cursor))
-#        print('\b'.repeat(rl - cursor))
-#    of ACTION_FEED_NEXT:
-#      feedNext = true
-#    elif validateUtf8(s) == -1:
-#      var cs = ""
-#      for c in s:
-#        if not c.isControlChar():
-#          cs &= c
-#        elif escNext:
-#          cs &= c
-#          escNext = false
-#      escNext = false
-#      if cs.len == 0:
-#        continue
-#      if rl + 1 < maxlen:
-#        print(' '.repeat(rl - cursor + 1))
-#        print('\b'.repeat(rl - cursor + 1))
-#      new = new.runeSubstr(0, cursor) & cs & new.runeSubstr(cursor)
-#      rl = new.runeLen()
-#      if cursor - shift > maxlen:
-#        shift += maxlen - cursor
-#      if shift == 0:
-#        printesc(new.runeSubstr(cursor, min(maxlen - cursor - 1, rl)))
-#        print('\b'.repeat(max(min(maxlen - cursor - 2, rl - cursor - 1), 0)))
-#      else:
-#        print('\r')
-#        print(' '.repeat(termwidth))
-#        print('\r')
-#        print(prompt)
-#        print(new.runeSubstr(shift, min(maxlen - 1, rl - shift)))
-#        if maxlen < rl - shift:
-#          print(new.runeSubstr(shift, maxlen - 1))
-#          print('\b'.repeat(maxlen - cursor + shift))
-#        else:
-#          print(new.runeSubstr(shift, rl - shift))
-#          print('\b'.repeat(rl + shift - cursor))
-#      inc cursor
-#    else:
-#      feedNext = true
-
 proc readLine*(prompt: string, current: var string, termwidth: int): bool =
-  var new = current
-  print(prompt)
+  var news = current.toRunes()
   let maxlen = termwidth - prompt.len
-  printesc(new)
   var s = ""
   var feedNext = false
   var escNext = false
-  var cursor = new.runeLen
+  var comp = false
+  var compi: uint16 = 0
+  var compa = 0
+  var comps = ""
+  var cursor = news.len
   var shift = 0
   while true:
-    var rl = new.runeLen()
-    print('\r')
-    print(' '.repeat(termwidth))
-    print('\r')
-    printesc(prompt & new)
-    print('\r')
-    cursorForward(prompt.len + cursor)
-
+    let rl = news.len
     if cursor < shift:
-      shift = cursor
-    elif cursor - shift > maxlen:
-      shift += cursor - maxlen
+      shift = max(cursor - 1, 0)
+    else:
+      while news.substr(shift, shift + cursor).width() > maxlen - 1:
+        shift += news[^1].width()
+
+    eraseLine()
+    printesc(prompt & $news.substr(shift, shift + maxlen - 1))
+
+    print('\r')
+    cursorForward(prompt.len + news.substr(shift, cursor).width())
 
     if not feedNext:
       s = ""
@@ -229,21 +80,22 @@ proc readLine*(prompt: string, current: var string, termwidth: int): bool =
     of ACTION_LINED_CANCEL:
       return false
     of ACTION_LINED_SUBMIT:
-      current = new
+      current = $news
       return true
     of ACTION_LINED_BACKSPACE:
       if cursor > 0:
-        new = new.runeSubstr(0, cursor - 1) & new.runeSubstr(cursor)
-        rl = new.runeLen()
+        news = news.substr(0, cursor - 1) & news.substr(cursor)
         dec cursor
+    of ACTION_LINED_DELETE:
+      if cursor > 0 and cursor < rl:
+        news = news.substr(0, cursor) & news.substr(cursor + 1)
     of ACTION_LINED_ESC:
       escNext = true
     of ACTION_LINED_CLEAR:
-      new = new.runeSubstr(cursor)
-      rl = new.runeLen()
+      news = news.substr(cursor)
       cursor = 0
     of ACTION_LINED_KILL:
-      new = new.runeSubstr(0, cursor)
+      news = news.substr(0, cursor)
     of ACTION_LINED_BACK:
       if cursor > 0:
         dec cursor
@@ -253,32 +105,64 @@ proc readLine*(prompt: string, current: var string, termwidth: int): bool =
     of ACTION_LINED_PREV_WORD:
       while cursor > 0:
         dec cursor
-        var rune: Rune
-        new.fastRuneAt(cursor, rune, false)
-        if rune in breakWord:
+        if news[cursor].breaksWord():
           break
     of ACTION_LINED_NEXT_WORD:
       while cursor < rl:
-        var rune: Rune
         inc cursor
         if cursor < rl:
-          new.fastRuneAt(cursor, rune, false)
-          if rune in breakWord:
+          if news[cursor].breaksWord():
             break
     of ACTION_LINED_KILL_WORD:
       var chars = 0
       while cursor > chars:
         inc chars
-        var rune: Rune
-        new.fastRuneAt(cursor - chars, rune, false)
-        if rune in breakWord:
+        if news[cursor - chars].breaksWord():
           break
       if chars > 0:
-        new = new.runeSubstr(0, cursor - chars) & new.runeSubstr(cursor)
-        rl = new.runeLen()
+        news = news.substr(0, cursor - chars) & news.substr(cursor)
         cursor -= chars
+    of ACTION_LINED_COMPOSE_ON:
+      comp = true
+      compi = 0
+      compa = 0
+      comps = ""
+    of ACTION_LINED_COMPOSE_OFF:
+      comp = false
+      compi = 0
+      compa = 0
+      comps = ""
+    of ACTION_LINED_COMPOSE_TOGGLE:
+      comp = not comp
+      compi = 0
+      compa = 0
+      comps = ""
     of ACTION_FEED_NEXT:
       feedNext = true
+    elif comp:
+      comps &= c
+      let n = composeRemap.getPrefix(comps, compi)
+      if n != compi:
+        compi = n
+        compa += comps.len
+        comps = ""
+      if composeRemap.hasPrefix(comps, compi) and composeRemap.nodes[n].children.len > 0:
+        feedNext = true
+      else:
+        var cs = ""
+        if composeRemap.nodes[compi].leaf:
+          cs = composeRemap.nodes[compi].value
+        else:
+          cs = s.substr(0, compa - 1)
+        comps = s.substr(compa)
+        if not composeRemap.hasPrefix(comps, 0):
+          cs &= comps
+          comps = ""
+
+        news = news.substr(0, cursor) & cs.toRunes() & news.substr(cursor)
+        cursor += cs.runeLen()
+        compi = 0
+        compa = 0
     elif validateUtf8(s) == -1:
       var cs = ""
       for c in s:
@@ -290,8 +174,7 @@ proc readLine*(prompt: string, current: var string, termwidth: int): bool =
       escNext = false
       if cs.len == 0:
         continue
-      new = new.runeSubstr(0, cursor) & cs & new.runeSubstr(cursor)
-      rl = new.runeLen()
-      inc cursor
+      news = news.substr(0, cursor) & cs.toRunes() & news.substr(cursor)
+      cursor += cs.runeLen()
     else:
       feedNext = true
diff --git a/twtstr.nim b/twtstr.nim
index 63854879..42a64333 100644
--- a/twtstr.nim
+++ b/twtstr.nim
@@ -48,8 +48,18 @@ func remove*(str: string, c: string): string =
     if rem != rune:
       result &= $rune
 
+const ControlChars = {chr(0x00)..chr(0x1F), chr(0x7F)}
+
+const Whitespace = { ' ', '\n', '\r', '\t' }
+
+func isWhitespace*(c: char): bool =
+  return c in Whitespace
+
 func isControlChar*(c: char): bool =
-  return c <= char(0x1F) or c == char(0x7F)
+  return c in ControlChars
+
+func isControlChar*(r: Rune): bool =
+  return int(r) <= int(high(char)) and char(r) in ControlChars
 
 func getControlChar*(c: char): char =
   if c >= 'a':
@@ -84,11 +94,40 @@ func findChar*(str: string, c: Rune, start: int = 0): int =
     i = n
   return -1
 
+func getLowerChars*(): string =
+  result = ""
+  for i in 0..255:
+    if chr(i) >= 'A' and chr(i) <= 'Z':
+      result &= chr(i + 32)
+    else:
+      result &= chr(i)
+
+const lowerChars = getLowerChars()
+
+func tolower*(c: char): char =
+  return lowerChars[int(c)]
+
+const breakWord = [
+  Rune('\n'), Rune('/'), Rune('\\'), Rune(' '), Rune('&'), Rune('='),
+  Rune('?'), Rune('.'), Rune(';')
+]
+
+func breaksWord*(r: Rune): bool =
+  return r in breakWord
+
+func substr*(s: seq[Rune], i: int, j: int): seq[Rune] =
+  if s.len == 0:
+    return @[]
+  return s[min(high(s), i)..min(high(s), j - 1)]
 
-#Measure length of rune. Transpiled from https://www.cl.cam.ac.uk/~mgk25/ucs/wcwidth.c
+func substr*(s: seq[Rune], i: int): seq[Rune] =
+  if i >= high(s) or s.len == 0:
+    return @[]
+  return s[min(high(s), i)..high(s)]
+
+#Measure length of rune. From https://www.cl.cam.ac.uk/~mgk25/ucs/wcwidth.c
 
 #auxiliary function for binary search in interval table
-#TODO: use binary search from stdlib?
 func bisearch(ucs: Rune, table: openarray[(int, int)]): bool =
   var max = table.high
   var min = 0
@@ -107,14 +146,13 @@ func bisearch(ucs: Rune, table: openarray[(int, int)]): bool =
       return true
   return false
 
-
 #The following two functions define the column width of an ISO 10646
 #character as follows:
 #
 #   - The null character (U+0000) has a column width of 0.
 #
 #   - Other C0/C1 control characters and DEL will lead to a return
-#     value of -1.
+#     value of 2.
 #
 #   - Non-spacing and enclosing combining characters (general
 #     category code Mn or Me in the Unicode database) have a
@@ -191,21 +229,18 @@ const combining = [
   ( 0xE0100, 0xE01EF )
 ]
 
-func mk_wcwidth(r: Rune): int =
+func width*(r: Rune): int =
   let ucs = int(r)
   # sorted list of non-overlapping intervals of non-spacing characters
   # generated by "uniset +cat=Me +cat=Mn +cat=Cf -00AD +1160-11FF +200B c"
 
-  # test for 8-bit control characters
-  if ucs == 0:
-    return 0
-  if ucs < 32 or (ucs >= 0x7f and ucs < 0xa0):
-    return -1
-
   # binary search in table of non-spacing characters
   if bisearch(r, combining):
     return 0
 
+  if r.isControlChar():
+    return 2
+
   # if we arrive here, ucs is not a combining or C0/C1 control character
 
   if (ucs >= 0x1100 and
@@ -224,11 +259,13 @@ func mk_wcwidth(r: Rune): int =
     return 2
   return 1
 
+func width*(s: string): int =
+  for r in s.runes():
+    result += width(r)
 
-func mk_wcswidth(s: string): int =
-  for r in s.runes:
-    result += mk_wcwidth(r)
-
+func width*(s: seq[Rune]): int =
+  for r in s:
+    result += width(r)
 
 # 
 # The following functions are the same as mk_wcwidth() and
@@ -294,7 +331,7 @@ const ambiguous = [
   ( 0xFFFD, 0xFFFD ), ( 0xF0000, 0xFFFFD ), ( 0x100000, 0x10FFFD )
 ]
 
-func mk_wcwidth_cjk*(ucs: Rune): int =
+func mk_wcwidth_cjk(ucs: Rune): int =
   # sorted list of non-overlapping intervals of East Asian Ambiguous
   # characters, generated by "uniset +WIDTH-A -cat=Me -cat=Mn -cat=Cf c"
 
@@ -302,24 +339,14 @@ func mk_wcwidth_cjk*(ucs: Rune): int =
   if bisearch(ucs, ambiguous):
     return 2;
 
-  return mk_wcwidth(ucs);
+  return width(ucs);
 
+func mk_wcswidth_cjk(s: string): int =
+  for r in s.runes:
+    result += mk_wcwidth_cjk(r)
+  return result
 
-func mk_wcswidth_cjk*(s: string): int =
-  #result = 0
-  #for r in s.runes:
-  #  result += mk_wcwidth_cjk(r)
-  #return result
-  result = 0
-  var i = 0
-  while i < len(s):
-    var r: Rune
-    fastRuneAt(s, i, r, false)
-    if uint(s[i]) <= 127: inc(i)
-    elif uint(s[i]) shr 5 == 0b110: inc(i, 2)
-    elif uint(s[i]) shr 4 == 0b1110: inc(i, 3)
-    elif uint(s[i]) shr 3 == 0b11110: inc(i, 4)
-    elif uint(s[i]) shr 2 == 0b111110: inc(i, 5)
-    elif uint(s[i]) shr 1 == 0b1111110: inc(i, 6)
-    else: inc i
-    inc(result, mk_wcwidth_cjk(r))
+
+proc skipBlanks*(buf: string, at: var int) =
+  while at < buf.len and buf[at].isWhitespace():
+    inc at