Repository: DmitrySoshnikov/regexp-tree
Pull request #116 (2017-11-19 16:11:43)

Parser: Parse surrogate pairs and es6 unicode code point escapes only when `u` flag is set
I had to mess up with the BNF grammar more than I expected, but it appears to work as intended.

DmitrySoshnikov: CC @mathiasbynens 

Patch:
diff --git a/README.md b/README.md
index 0307b59..fc40304 100644
--- a/README.md
+++ b/README.md
@@ -884,7 +884,6 @@ Unicode char started with `\u`, followed by a hex number:
 
 ```
 \u003B
-\u{003B}
 ```
 
 Node:
@@ -899,6 +898,43 @@ Node:
 }
 ```
 
+When using the `u` flag, unicode chars can also be represented using `\u` followed by a hex number between curly braces:
+
+```
+\u{1F680}
+```
+
+Node:
+
+```js
+{
+  type: 'Char',
+  value: '\\u{1F680}',
+  symbol: '🚀',
+  kind: 'unicode',
+  codePoint: 128640
+}
+```
+
+When using the `u` flag, unicode chars can also be represented using a surrogate pair:
+
+```
+\ud83d\ude80
+```
+
+Node:
+
+```js
+{
+  type: 'Char',
+  value: '\\ud83d\\ude80',
+  symbol: '🚀',
+  kind: 'unicode',
+  codePoint: 128640,
+  isSurrogatePair: true
+}
+```
+
 #### Character class
 
 Character classes define a _set_ of characters. A set may include as simple characters, as well as _character ranges_. A class can be _positive_ (any from the characters in the class match), or _negative_ (any _but_ the characters from the class match).
diff --git a/bin/regexp-tree b/bin/regexp-tree
index 653b4be..fa03633 100755
--- a/bin/regexp-tree
+++ b/bin/regexp-tree
@@ -107,11 +107,7 @@ function main() {
     }
 
     if (shouldPrintDFA) {
-      const dfa = fa.toDFA(expression);
-      dfa.printTransitionTable('\nDFA: Original transition table:\n');
-
-      dfa.minimize();
-      dfa.printTransitionTable('\nDFA: Minimized transition table:\n');
+      fa.toDFA(expression).printTransitionTable();
     }
 
     return;
diff --git a/index.js b/index.js
index 65ebbfd..0ebef0c 100644
--- a/index.js
+++ b/index.js
@@ -120,8 +120,8 @@ const regexpTree = {
    *
    * @return TransformResult object
    */
-  optimize(regexp, whitelist) {
-    return optimizer.optimize(regexp, whitelist);
+  optimize(regexp) {
+    return optimizer.optimize(regexp);
   },
 
   /**
diff --git a/src/interpreter/finite-automaton/dfa/dfa-minimizer.js b/src/interpreter/finite-automaton/dfa/dfa-minimizer.js
deleted file mode 100644
index d601a15..0000000
--- a/src/interpreter/finite-automaton/dfa/dfa-minimizer.js
+++ /dev/null
@@ -1,207 +0,0 @@
-/**
- * The MIT License (MIT)
- * Copyright (c) 2017-present Dmitry Soshnikov <dmitry.soshnikov@gmail.com>
- */
-
-'use strict';
-
-// DFA minization.
-
-/**
- * Map from state to current set it goes.
- */
-let currentTransitionMap = null;
-
-/**
- * Takes a DFA, and returns a minimized version of it
- * compressing some states to groups (using standard, 0-, 1-,
- * 2-, ... N-equivalence algorithm).
- */
-function minimize(dfa) {
-  const table = dfa.getTransitionTable();
-  const allStates = Object.keys(table);
-  const alphabet = dfa.getAlphabet();
-  const accepting = dfa.getAcceptingStateNumbers();
-
-  currentTransitionMap = {};
-
-  const nonAccepting = new Set();
-
-  allStates.forEach(state => {
-    state = Number(state);
-    const isAccepting = accepting.has(state);
-
-    if (isAccepting) {
-      currentTransitionMap[state] = accepting;
-    } else {
-      nonAccepting.add(state);
-      currentTransitionMap[state] = nonAccepting;
-    }
-  });
-
-  // ---------------------------------------------------------------------------
-  // Step 1: build equivalent sets.
-
-  // All [1..N] equivalent sets.
-  const all = [
-    // 0-equivalent sets.
-    [nonAccepting, accepting]
-      .filter(set => set.size > 0),
-  ];
-
-
-  let current;
-  let previous;
-
-  // Top of the stack is the current list of sets to analyze.
-  current = all[all.length - 1];
-
-  // Previous set (to check whether we need to stop).
-  previous = all[all.length - 2];
-
-  // Until we'll not have the same N and N-1 equivalent rows.
-  while (!sameRow(current, previous)) {
-    const newTransitionMap = {};
-
-    for (const set of current) {
-      // Handled states for this set.
-      const handledStates = {};
-
-      const [first, ...rest] = set;
-      handledStates[first] = new Set([first]);
-
-      // Have to compare each from the rest states with
-      // the already handled states, and see if they are equivalent.
-      restSets: for (const state of rest) {
-        for (const handledState of Object.keys(handledStates)) {
-          // This and some previously handled state are equivalent --
-          // just append this state to the same set.
-          if (areEquivalent(state, handledState, alphabet)) {
-            handledStates[handledState].add(state);
-            handledStates[state] = handledStates[handledState];
-            continue restSets;
-          }
-        }
-        // Else, this state is not equivalent to any of the
-        // handled states -- allocate a new set for it.
-        handledStates[state] = new Set([state]);
-      }
-
-      // Add these handled states to all states map.
-      Object.assign(newTransitionMap, handledStates);
-    }
-
-    // Update current transition map for the handled row.
-    currentTransitionMap = newTransitionMap;
-
-    let newSets = new Set(
-      Object.keys(newTransitionMap)
-        .map(state => newTransitionMap[state])
-    );
-
-    all.push([...newSets]);
-
-    // Top of the stack is the current.
-    current = all[all.length - 1];
-
-    // Previous set.
-    previous = all[all.length - 2];
-  }
-
-  // ---------------------------------------------------------------------------
-  // Step 2: build minimized table from the equivalent sets.
-
-  // Remap state numbers from sets to index-based.
-  const remaped = new Map();
-  let idx = 1;
-  current.forEach(set => remaped.set(set, idx++));
-
-  // Build the minimized table from the calculated equivalent sets.
-  const minimizedTable = {};
-
-  const minimizedAcceptingStates = new Set();
-
-  const updateAcceptingStates = (set, idx) => {
-    for (const state of set) {
-      if (accepting.has(state)) {
-        minimizedAcceptingStates.add(idx);
-      }
-    }
-  };
-
-  for (const [set, idx] of remaped.entries()) {
-    minimizedTable[idx] = {};
-    for (const symbol of alphabet) {
-      // All states in a set are equivalent, so take the first one
-      // to see where it goes on symbol.
-      const originalState = set.values().next().value;
-      updateAcceptingStates(set, idx);
-
-      const originalTransition = table[originalState][symbol];
-
-      if (originalTransition) {
-        minimizedTable[idx][symbol] = remaped.get(
-          currentTransitionMap[originalTransition]
-        );
-      }
-    }
-  }
-
-  // Update the table, and accepting states on the original DFA.
-  dfa.setTransitionTable(minimizedTable);
-  dfa.setAcceptingStateNumbers(minimizedAcceptingStates);
-
-  return dfa;
-}
-
-function sameRow(r1, r2) {
-  if (!r2) {
-    return false;
-  }
-
-  if (r1.length !== r2.length) {
-    return false;
-  }
-
-  for (let i = 0; i < r1.length; i++) {
-    const s1 = r1[i];
-    const s2 = r2[i];
-
-    if (s1.size !== s2.size) {
-      return false;
-    }
-
-    if ([...s1].sort().join(',') !== [...s2].sort().join(',')) {
-      return false;
-    }
-  }
-
-  return true;
-}
-
-/**
- * Checks whether two states are N-equivalent, i.e. whether they go
- * to the same set on a symbol.
- */
-function areEquivalent(s1, s2, alphabet) {
-  for (const symbol of alphabet) {
-    if (!goToSameSet(s1, s2, symbol)) {
-      return false;
-    }
-  }
-  return true;
-}
-
-/**
- * Checks whether states go to the same set.
- */
-function goToSameSet(s1, s2, symbol) {
-  if (!currentTransitionMap[s1] || !currentTransitionMap[s2]) {
-    return false;
-  }
-  return currentTransitionMap[s1][symbol] === currentTransitionMap[s2][symbol];
-}
-
-module.exports = {
-  minimize,
-};
\ No newline at end of file
diff --git a/src/interpreter/finite-automaton/dfa/dfa.js b/src/interpreter/finite-automaton/dfa/dfa.js
index da9dfb0..6b0ad4d 100644
--- a/src/interpreter/finite-automaton/dfa/dfa.js
+++ b/src/interpreter/finite-automaton/dfa/dfa.js
@@ -5,7 +5,6 @@
 
 'use strict';
 
-const DFAMinimizer = require('./dfa-minimizer');
 const TablePrinter = require('../table-printer');
 const colors = require('colors');
 
@@ -21,18 +20,6 @@ class DFA {
     this._nfa = nfa;
   }
 
-  /**
-   * Minimizes DFA.
-   */
-  minimize() {
-    this.getTransitionTable();
-
-    this._originalAcceptingStateNumbers = this._acceptingStateNumbers;
-    this._originalTransitionTable = this._transitionTable;
-
-    DFAMinimizer.minimize(this);
-  }
-
   /**
    * Returns alphabet for this DFA.
    */
@@ -64,20 +51,6 @@ class DFA {
     return this._originalAcceptingStateNumbers;
   }
 
-  /**
-   * Sets transition table.
-   */
-  setTransitionTable(table) {
-    this._transitionTable = table;
-  }
-
-  /**
-   * Sets accepting states.
-   */
-  setAcceptingStateNumbers(stateNumbers) {
-    this._acceptingStateNumbers = stateNumbers;
-  }
-
   /**
    * DFA transition table is built from NFA table.
    */
@@ -210,8 +183,8 @@ class DFA {
   /**
    * Prints transition table.
    */
-  printTransitionTable(header = '\nDFA transition table:\n') {
-    console.info(colors.bold(header));
+  printTransitionTable() {
+    console.info(colors.bold(`\nDFA transition table:\n`));
 
     const alphabet = [...this.getAlphabet()];
 
diff --git a/src/optimizer/index.js b/src/optimizer/index.js
index 86a78c3..249e80f 100644
--- a/src/optimizer/index.js
+++ b/src/optimizer/index.js
@@ -25,11 +25,7 @@ module.exports = {
    *
    *   /\w+e+/
    */
-  optimize(regexp, transformsWhitelist = []) {
-    const transformToApply = transformsWhitelist.length > 0
-      ? transformsWhitelist
-      : Object.keys(optimizationTransforms);
-
+  optimize(regexp) {
     let prevResult;
     let result;
     do {
@@ -37,18 +33,7 @@ module.exports = {
         prevResult = result.toRegExp().toString();
         regexp = result.toRegExp();
       }
-      transformToApply.forEach(transformName => {
-
-        if (!optimizationTransforms.hasOwnProperty(transformName)) {
-          throw new Error(
-            `Unknown optimization-transform: ${transformName}. ` +
-            `Available transforms are: ` +
-            Object.keys(optimizationTransforms).join(', ')
-          );
-        }
-
-        const transformer = optimizationTransforms[transformName];
-
+      optimizationTransforms.forEach(transformer => {
         result = transform.transform(regexp, transformer);
         regexp = result.getAST();
       });
diff --git a/src/optimizer/transforms/char-class-to-meta-transform.js b/src/optimizer/transforms/char-class-to-meta-transform.js
index 1e51802..5ce61b3 100644
--- a/src/optimizer/transforms/char-class-to-meta-transform.js
+++ b/src/optimizer/transforms/char-class-to-meta-transform.js
@@ -10,19 +10,13 @@
  * their meta symbols equivalents.
  */
 module.exports = {
-  _hasIFlag: false,
-  _hasUFlag: false,
-  init(ast) {
-    this._hasIFlag = ast.flags.includes('i');
-    this._hasUFlag = ast.flags.includes('u');
-  },
   CharacterClass(path) {
 
     // [0-9] -> \d
     rewriteNumberRanges(path);
 
     // [a-zA-Z_0-9] -> \w
-    rewriteWordRanges(path, this._hasIFlag, this._hasUFlag);
+    rewriteWordRanges(path);
 
     // [ \t\r\n\f] -> \s
     rewriteWhitespaceRanges(path);
@@ -56,15 +50,13 @@ function rewriteNumberRanges(path) {
  * Thus, the ranges may go in any order, and other symbols/ranges
  * are kept untouched, e.g. [a-z_\dA-Z$] -> [\w$]
  */
-function rewriteWordRanges(path, hasIFlag, hasUFlag) {
+function rewriteWordRanges(path) {
   const {node} = path;
 
   let numberPath = null;
   let lowerCasePath = null;
   let upperCasePath = null;
   let underscorePath = null;
-  let u017fPath = null;
-  let u212aPath = null;
 
   node.expressions.forEach((expression, i) => {
 
@@ -87,25 +79,14 @@ function rewriteWordRanges(path, hasIFlag, hasUFlag) {
     else if (isUnderscore(expression)) {
       underscorePath = path.getChild(i);
     }
-
-    else if (hasIFlag && hasUFlag && isU017fPath(expression)) {
-      u017fPath = path.getChild(i);
-    }
-
-    else if (hasIFlag && hasUFlag && isU212aPath(expression)) {
-      u212aPath = path.getChild(i);
-    }
   });
 
   // If we found the whole pattern, replace it.
   if (
     numberPath &&
-    (
-      (lowerCasePath && upperCasePath) ||
-      (hasIFlag && (lowerCasePath || upperCasePath))
-    ) &&
-    underscorePath &&
-    (!hasUFlag || !hasIFlag || (u017fPath && u212aPath))
+    lowerCasePath &&
+    upperCasePath &&
+    underscorePath
   ) {
 
     // Put \w in place of \d.
@@ -121,19 +102,9 @@ function rewriteWordRanges(path, hasIFlag, hasUFlag) {
     });
 
     // Other paths are removed.
-    if (lowerCasePath) {
-      lowerCasePath.remove();
-    }
-    if (upperCasePath) {
-      upperCasePath.remove();
-    }
+    lowerCasePath.remove();
+    upperCasePath.remove();
     underscorePath.remove();
-    if (u017fPath) {
-      u017fPath.remove();
-    }
-    if (u212aPath) {
-      u212aPath.remove();
-    }
   }
 }
 
@@ -245,17 +216,3 @@ function isUnderscore(node) {
   );
 }
 
-function isU017fPath(node) {
-  return (
-    node.type === 'Char' &&
-    node.kind === 'unicode' &&
-    node.codePoint === 0x017f
-  );
-}
-function isU212aPath(node) {
-  return (
-    node.type === 'Char' &&
-    node.kind === 'unicode' &&
-    node.codePoint === 0x212a
-  );
-}
diff --git a/src/optimizer/transforms/char-class-to-single-char-transform.js b/src/optimizer/transforms/char-class-to-single-char-transform.js
index c056f9c..2be33f0 100644
--- a/src/optimizer/transforms/char-class-to-single-char-transform.js
+++ b/src/optimizer/transforms/char-class-to-single-char-transform.js
@@ -58,11 +58,11 @@ function isAppropriateChar(node) {
 }
 
 function isMeta(value) {
-  return /^\\[dwsDWS]$/.test(value);
+  return /^\\[dwsDWS$]/.test(value);
 }
 
 function getInverseMeta(value) {
-  return /[dws]/.test(value)
+  return /[dws$]/.test(value)
     ? value.toUpperCase()
     : value.toLowerCase();
 }
diff --git a/src/optimizer/transforms/index.js b/src/optimizer/transforms/index.js
index 05bf8b9..3b37113 100644
--- a/src/optimizer/transforms/index.js
+++ b/src/optimizer/transforms/index.js
@@ -5,37 +5,37 @@
 
 'use strict';
 
-module.exports = {
+module.exports = [
   // [\d\d] -> [\d]
-  'charClassRemoveDuplicates': require('./char-class-remove-duplicates-transform'),
+  require('./char-class-remove-duplicates-transform'),
 
   // a{1,2}a{2,3} -> a{3,5}
-  'quantifiersMerge': require('./quantifiers-merge-transform'),
+  require('./quantifiers-merge-transform'),
 
   // a{1,} -> a+, a{3,3} -> a{3}, a{1} -> a
-  'quantifierRangeToSymbol': require('./quantifier-range-to-symbol-transform'),
+  require('./quantifier-range-to-symbol-transform'),
 
   // [0-9] -> [\d]
-  'charClassToMeta': require('./char-class-to-meta-transform'),
+  require('./char-class-to-meta-transform'),
 
   // [\d] -> \d, [^\w] -> \W
-  'charClassToSingleChar': require('./char-class-to-single-char-transform'),
+  require('./char-class-to-single-char-transform'),
 
   // \e -> e
-  'charEscapeUnescape': require('./char-escape-unescape-transform'),
+  require('./char-escape-unescape-transform'),
 
   // (ab|ab) -> (ab)
-  'disjunctionRemoveDuplicates': require('./disjunction-remove-duplicates-transform'),
+  require('./disjunction-remove-duplicates-transform'),
 
   // (a|b|c) -> [abc]
-  'groupSingleCharsToCharClass': require('./group-single-chars-to-char-class'),
+  require('./group-single-chars-to-char-class'),
 
   // (?:)a -> a
-  'removeEmptyGroup': require('./remove-empty-group-transform'),
+  require('./remove-empty-group-transform'),
 
   // (?:a) -> a
-  'ungroup': require('./ungroup-transform'),
+  require('./ungroup-transform'),
 
   // abcabcabc -> (?:abc){3}
-  'combineRepeatingPatterns': require('./combine-repeating-patterns-transform')
-};
\ No newline at end of file
+  require('./combine-repeating-patterns-transform')
+];
\ No newline at end of file
diff --git a/src/parser/generated/regexp-tree.js b/src/parser/generated/regexp-tree.js
index 3918614..8ddf1a3 100644
--- a/src/parser/generated/regexp-tree.js
+++ b/src/parser/generated/regexp-tree.js
@@ -159,6 +159,7 @@ const productions = [[-1,1,(_1,_1loc) => { __loc = yyloc(_1loc, _1loc);__ = _1 }
 [9,1,(_1,_1loc) => { __loc = yyloc(_1loc, _1loc);__ = _1 }],
 [10,1,(_1,_1loc) => { __loc = yyloc(_1loc, _1loc); __ = Char(_1, 'simple', __loc)  }],
 [10,1,(_1,_1loc) => { __loc = yyloc(_1loc, _1loc); __ = Char(_1.slice(1), 'simple', __loc); __.escaped = true;  }],
+[10,1,(_1,_1loc) => { __loc = yyloc(_1loc, _1loc); __ = Char(_1, 'unicode', __loc); __.isSurrogatePair = true;  }],
 [10,1,(_1,_1loc) => { __loc = yyloc(_1loc, _1loc); __ = Char(_1, 'unicode', __loc)  }],
 [10,1,(_1,_1loc) => { __loc = yyloc(_1loc, _1loc); __ = Char(_1, 'control', __loc)  }],
 [10,1,(_1,_1loc) => { __loc = yyloc(_1loc, _1loc); __ = Char(_1, 'hex', __loc)  }],
@@ -310,12 +311,12 @@ const productions = [[-1,1,(_1,_1loc) => { __loc = yyloc(_1loc, _1loc);__ = _1 }
 /**
  * Encoded tokens map.
  */
-const tokens = {"SLASH":"23","CHAR":"24","BAR":"25","BOS":"26","EOS":"27","ESC_b":"28","ESC_B":"29","POS_LA_ASSERT":"30","R_PAREN":"31","NEG_LA_ASSERT":"32","POS_LB_ASSERT":"33","NEG_LB_ASSERT":"34","ESC_CHAR":"35","U_CODE":"36","CTRL_CH":"37","HEX_CODE":"38","OCT_CODE":"39","DEC_CODE":"40","META_CHAR":"41","ANY":"42","NAMED_GROUP_REF":"43","Q_MARK":"44","STAR":"45","PLUS":"46","RANGE_EXACT":"47","RANGE_OPEN":"48","RANGE_CLOSED":"49","NAMED_CAPTURE_GROUP":"50","L_PAREN":"51","NON_CAPTURE_GROUP":"52","NEG_CLASS":"53","R_BRACKET":"54","L_BRACKET":"55","DASH":"56","$":"57"};
+const tokens = {"SLASH":"23","CHAR":"24","BAR":"25","BOS":"26","EOS":"27","ESC_b":"28","ESC_B":"29","POS_LA_ASSERT":"30","R_PAREN":"31","NEG_LA_ASSERT":"32","POS_LB_ASSERT":"33","NEG_LB_ASSERT":"34","ESC_CHAR":"35","U_CODE_SURROGATE":"36","U_CODE":"37","CTRL_CH":"38","HEX_CODE":"39","OCT_CODE":"40","DEC_CODE":"41","META_CHAR":"42","ANY":"43","NAMED_GROUP_REF":"44","Q_MARK":"45","STAR":"46","PLUS":"47","RANGE_EXACT":"48","RANGE_OPEN":"49","RANGE_CLOSED":"50","NAMED_CAPTURE_GROUP":"51","L_PAREN":"52","NON_CAPTURE_GROUP":"53","NEG_CLASS":"54","R_BRACKET":"55","L_BRACKET":"56","DASH":"57","$":"58"};
 
 /**
  * Parsing table (generated by Syntax tool).
  */
-const table = [{"0":81,"23":"s1"},{"3":2,"4":3,"5":4,"6":5,"23":"r10","24":"r10","25":"r10","26":"r10","27":"r10","28":"r10","29":"r10","30":"r10","32":"r10","33":"r10","34":"r10","35":"r10","36":"r10","37":"r10","38":"r10","39":"r10","40":"r10","41":"r10","42":"r10","43":"r10","50":"r10","51":"r10","52":"r10","53":"r10","55":"r10"},{"23":"s6"},{"23":"r6","25":"s10"},{"23":"r7","25":"r7","31":"r7"},{"7":12,"8":13,"9":14,"10":23,"14":25,"15":38,"16":39,"17":24,"23":"r9","24":"s26","25":"r9","26":"s15","27":"s16","28":"s17","29":"s18","30":"s19","31":"r9","32":"s20","33":"s21","34":"s22","35":"s27","36":"s28","37":"s29","38":"s30","39":"s31","40":"s32","41":"s33","42":"s34","43":"s35","50":"s40","51":"s41","52":"s42","53":"s36","55":"s37"},{"1":82,"2":7,"24":"s8","57":"r3"},{"24":"s9","57":"r2"},{"24":"r4","57":"r4"},{"24":"r5","57":"r5"},{"5":11,"6":5,"23":"r10","24":"r10","25":"r10","26":"r10","27":"r10","28":"r10","29":"r10","30":"r10","31":"r10","32":"r10","33":"r10","34":"r10","35":"r10","36":"r10","37":"r10","38":"r10","39":"r10","40":"r10","41":"r10","42":"r10","43":"r10","50":"r10","51":"r10","52":"r10","53":"r10","55":"r10"},{"23":"r8","25":"r8","31":"r8"},{"23":"r11","24":"r11","25":"r11","26":"r11","27":"r11","28":"r11","29":"r11","30":"r11","31":"r11","32":"r11","33":"r11","34":"r11","35":"r11","36":"r11","37":"r11","38":"r11","39":"r11","40":"r11","41":"r11","42":"r11","43":"r11","50":"r11","51":"r11","52":"r11","53":"r11","55":"r11"},{"23":"r12","24":"r12","25":"r12","26":"r12","27":"r12","28":"r12","29":"r12","30":"r12","31":"r12","32":"r12","33":"r12","34":"r12","35":"r12","36":"r12","37":"r12","38":"r12","39":"r12","40":"r12","41":"r12","42":"r12","43":"r12","50":"r12","51":"r12","52":"r12","53":"r12","55":"r12"},{"11":43,"12":44,"13":45,"23":"r36","24":"r36","25":"r36","26":"r36","27":"r36","28":"r36","29":"r36","30":"r36","31":"r36","32":"r36","33":"r36","34":"r36","35":"r36","36":"r36","37":"r36","38":"r36","39":"r36","40":"r36","41":"r36","42":"r36","43":"r36","44":"s48","45":"s46","46":"s47","47":"s49","48":"s50","49":"s51","50":"r36","51":"r36","52":"r36","53":"r36","55":"r36"},{"23":"r14","24":"r14","25":"r14","26":"r14","27":"r14","28":"r14","29":"r14","30":"r14","31":"r14","32":"r14","33":"r14","34":"r14","35":"r14","36":"r14","37":"r14","38":"r14","39":"r14","40":"r14","41":"r14","42":"r14","43":"r14","50":"r14","51":"r14","52":"r14","53":"r14","55":"r14"},{"23":"r15","24":"r15","25":"r15","26":"r15","27":"r15","28":"r15","29":"r15","30":"r15","31":"r15","32":"r15","33":"r15","34":"r15","35":"r15","36":"r15","37":"r15","38":"r15","39":"r15","40":"r15","41":"r15","42":"r15","43":"r15","50":"r15","51":"r15","52":"r15","53":"r15","55":"r15"},{"23":"r16","24":"r16","25":"r16","26":"r16","27":"r16","28":"r16","29":"r16","30":"r16","31":"r16","32":"r16","33":"r16","34":"r16","35":"r16","36":"r16","37":"r16","38":"r16","39":"r16","40":"r16","41":"r16","42":"r16","43":"r16","50":"r16","51":"r16","52":"r16","53":"r16","55":"r16"},{"23":"r17","24":"r17","25":"r17","26":"r17","27":"r17","28":"r17","29":"r17","30":"r17","31":"r17","32":"r17","33":"r17","34":"r17","35":"r17","36":"r17","37":"r17","38":"r17","39":"r17","40":"r17","41":"r17","42":"r17","43":"r17","50":"r17","51":"r17","52":"r17","53":"r17","55":"r17"},{"4":53,"5":4,"6":5,"24":"r10","25":"r10","26":"r10","27":"r10","28":"r10","29":"r10","30":"r10","31":"r10","32":"r10","33":"r10","34":"r10","35":"r10","36":"r10","37":"r10","38":"r10","39":"r10","40":"r10","41":"r10","42":"r10","43":"r10","50":"r10","51":"r10","52":"r10","53":"r10","55":"r10"},{"4":55,"5":4,"6":5,"24":"r10","25":"r10","26":"r10","27":"r10","28":"r10","29":"r10","30":"r10","31":"r10","32":"r10","33":"r10","34":"r10","35":"r10","36":"r10","37":"r10","38":"r10","39":"r10","40":"r10","41":"r10","42":"r10","43":"r10","50":"r10","51":"r10","52":"r10","53":"r10","55":"r10"},{"4":57,"5":4,"6":5,"24":"r10","25":"r10","26":"r10","27":"r10","28":"r10","29":"r10","30":"r10","31":"r10","32":"r10","33":"r10","34":"r10","35":"r10","36":"r10","37":"r10","38":"r10","39":"r10","40":"r10","41":"r10","42":"r10","43":"r10","50":"r10","51":"r10","52":"r10","53":"r10","55":"r10"},{"4":59,"5":4,"6":5,"24":"r10","25":"r10","26":"r10","27":"r10","28":"r10","29":"r10","30":"r10","31":"r10","32":"r10","33":"r10","34":"r10","35":"r10","36":"r10","37":"r10","38":"r10","39":"r10","40":"r10","41":"r10","42":"r10","43":"r10","50":"r10","51":"r10","52":"r10","53":"r10","55":"r10"},{"23":"r22","24":"r22","25":"r22","26":"r22","27":"r22","28":"r22","29":"r22","30":"r22","31":"r22","32":"r22","33":"r22","34":"r22","35":"r22","36":"r22","37":"r22","38":"r22","39":"r22","40":"r22","41":"r22","42":"r22","43":"r22","44":"r22","45":"r22","46":"r22","47":"r22","48":"r22","49":"r22","50":"r22","51":"r22","52":"r22","53":"r22","55":"r22"},{"23":"r23","24":"r23","25":"r23","26":"r23","27":"r23","28":"r23","29":"r23","30":"r23","31":"r23","32":"r23","33":"r23","34":"r23","35":"r23","36":"r23","37":"r23","38":"r23","39":"r23","40":"r23","41":"r23","42":"r23","43":"r23","44":"r23","45":"r23","46":"r23","47":"r23","48":"r23","49":"r23","50":"r23","51":"r23","52":"r23","53":"r23","55":"r23"},{"23":"r24","24":"r24","25":"r24","26":"r24","27":"r24","28":"r24","29":"r24","30":"r24","31":"r24","32":"r24","33":"r24","34":"r24","35":"r24","36":"r24","37":"r24","38":"r24","39":"r24","40":"r24","41":"r24","42":"r24","43":"r24","44":"r24","45":"r24","46":"r24","47":"r24","48":"r24","49":"r24","50":"r24","51":"r24","52":"r24","53":"r24","55":"r24"},{"23":"r25","24":"r25","25":"r25","26":"r25","27":"r25","28":"r25","29":"r25","30":"r25","31":"r25","32":"r25","33":"r25","34":"r25","35":"r25","36":"r25","37":"r25","38":"r25","39":"r25","40":"r25","41":"r25","42":"r25","43":"r25","44":"r25","45":"r25","46":"r25","47":"r25","48":"r25","49":"r25","50":"r25","51":"r25","52":"r25","53":"r25","54":"r25","55":"r25","56":"r25"},{"23":"r26","24":"r26","25":"r26","26":"r26","27":"r26","28":"r26","29":"r26","30":"r26","31":"r26","32":"r26","33":"r26","34":"r26","35":"r26","36":"r26","37":"r26","38":"r26","39":"r26","40":"r26","41":"r26","42":"r26","43":"r26","44":"r26","45":"r26","46":"r26","47":"r26","48":"r26","49":"r26","50":"r26","51":"r26","52":"r26","53":"r26","54":"r26","55":"r26","56":"r26"},{"23":"r27","24":"r27","25":"r27","26":"r27","27":"r27","28":"r27","29":"r27","30":"r27","31":"r27","32":"r27","33":"r27","34":"r27","35":"r27","36":"r27","37":"r27","38":"r27","39":"r27","40":"r27","41":"r27","42":"r27","43":"r27","44":"r27","45":"r27","46":"r27","47":"r27","48":"r27","49":"r27","50":"r27","51":"r27","52":"r27","53":"r27","54":"r27","55":"r27","56":"r27"},{"23":"r28","24":"r28","25":"r28","26":"r28","27":"r28","28":"r28","29":"r28","30":"r28","31":"r28","32":"r28","33":"r28","34":"r28","35":"r28","36":"r28","37":"r28","38":"r28","39":"r28","40":"r28","41":"r28","42":"r28","43":"r28","44":"r28","45":"r28","46":"r28","47":"r28","48":"r28","49":"r28","50":"r28","51":"r28","52":"r28","53":"r28","54":"r28","55":"r28","56":"r28"},{"23":"r29","24":"r29","25":"r29","26":"r29","27":"r29","28":"r29","29":"r29","30":"r29","31":"r29","32":"r29","33":"r29","34":"r29","35":"r29","36":"r29","37":"r29","38":"r29","39":"r29","40":"r29","41":"r29","42":"r29","43":"r29","44":"r29","45":"r29","46":"r29","47":"r29","48":"r29","49":"r29","50":"r29","51":"r29","52":"r29","53":"r29","54":"r29","55":"r29","56":"r29"},{"23":"r30","24":"r30","25":"r30","26":"r30","27":"r30","28":"r30","29":"r30","30":"r30","31":"r30","32":"r30","33":"r30","34":"r30","35":"r30","36":"r30","37":"r30","38":"r30","39":"r30","40":"r30","41":"r30","42":"r30","43":"r30","44":"r30","45":"r30","46":"r30","47":"r30","48":"r30","49":"r30","50":"r30","51":"r30","52":"r30","53":"r30","54":"r30","55":"r30","56":"r30"},{"23":"r31","24":"r31","25":"r31","26":"r31","27":"r31","28":"r31","29":"r31","30":"r31","31":"r31","32":"r31","33":"r31","34":"r31","35":"r31","36":"r31","37":"r31","38":"r31","39":"r31","40":"r31","41":"r31","42":"r31","43":"r31","44":"r31","45":"r31","46":"r31","47":"r31","48":"r31","49":"r31","50":"r31","51":"r31","52":"r31","53":"r31","54":"r31","55":"r31","56":"r31"},{"23":"r32","24":"r32","25":"r32","26":"r32","27":"r32","28":"r32","29":"r32","30":"r32","31":"r32","32":"r32","33":"r32","34":"r32","35":"r32","36":"r32","37":"r32","38":"r32","39":"r32","40":"r32","41":"r32","42":"r32","43":"r32","44":"r32","45":"r32","46":"r32","47":"r32","48":"r32","49":"r32","50":"r32","51":"r32","52":"r32","53":"r32","54":"r32","55":"r32","56":"r32"},{"23":"r33","24":"r33","25":"r33","26":"r33","27":"r33","28":"r33","29":"r33","30":"r33","31":"r33","32":"r33","33":"r33","34":"r33","35":"r33","36":"r33","37":"r33","38":"r33","39":"r33","40":"r33","41":"r33","42":"r33","43":"r33","44":"r33","45":"r33","46":"r33","47":"r33","48":"r33","49":"r33","50":"r33","51":"r33","52":"r33","53":"r33","54":"r33","55":"r33","56":"r33"},{"23":"r34","24":"r34","25":"r34","26":"r34","27":"r34","28":"r34","29":"r34","30":"r34","31":"r34","32":"r34","33":"r34","34":"r34","35":"r34","36":"r34","37":"r34","38":"r34","39":"r34","40":"r34","41":"r34","42":"r34","43":"r34","44":"r34","45":"r34","46":"r34","47":"r34","48":"r34","49":"r34","50":"r34","51":"r34","52":"r34","53":"r34","54":"r34","55":"r34","56":"r34"},{"10":65,"18":61,"19":83,"21":62,"22":64,"24":"s26","28":"s66","35":"s27","36":"s28","37":"s29","38":"s30","39":"s31","40":"s32","41":"s33","42":"s34","43":"s35","54":"r52","56":"s63"},{"10":65,"18":73,"19":83,"21":62,"22":64,"24":"s26","28":"s66","35":"s27","36":"s28","37":"s29","38":"s30","39":"s31","40":"s32","41":"s33","42":"s34","43":"s35","54":"r52","56":"s63"},{"23":"r45","24":"r45","25":"r45","26":"r45","27":"r45","28":"r45","29":"r45","30":"r45","31":"r45","32":"r45","33":"r45","34":"r45","35":"r45","36":"r45","37":"r45","38":"r45","39":"r45","40":"r45","41":"r45","42":"r45","43":"r45","44":"r45","45":"r45","46":"r45","47":"r45","48":"r45","49":"r45","50":"r45","51":"r45","52":"r45","53":"r45","55":"r45"},{"23":"r46","24":"r46","25":"r46","26":"r46","27":"r46","28":"r46","29":"r46","30":"r46","31":"r46","32":"r46","33":"r46","34":"r46","35":"r46","36":"r46","37":"r46","38":"r46","39":"r46","40":"r46","41":"r46","42":"r46","43":"r46","44":"r46","45":"r46","46":"r46","47":"r46","48":"r46","49":"r46","50":"r46","51":"r46","52":"r46","53":"r46","55":"r46"},{"4":75,"5":4,"6":5,"24":"r10","25":"r10","26":"r10","27":"r10","28":"r10","29":"r10","30":"r10","31":"r10","32":"r10","33":"r10","34":"r10","35":"r10","36":"r10","37":"r10","38":"r10","39":"r10","40":"r10","41":"r10","42":"r10","43":"r10","50":"r10","51":"r10","52":"r10","53":"r10","55":"r10"},{"4":77,"5":4,"6":5,"24":"r10","25":"r10","26":"r10","27":"r10","28":"r10","29":"r10","30":"r10","31":"r10","32":"r10","33":"r10","34":"r10","35":"r10","36":"r10","37":"r10","38":"r10","39":"r10","40":"r10","41":"r10","42":"r10","43":"r10","50":"r10","51":"r10","52":"r10","53":"r10","55":"r10"},{"4":79,"5":4,"6":5,"24":"r10","25":"r10","26":"r10","27":"r10","28":"r10","29":"r10","30":"r10","31":"r10","32":"r10","33":"r10","34":"r10","35":"r10","36":"r10","37":"r10","38":"r10","39":"r10","40":"r10","41":"r10","42":"r10","43":"r10","50":"r10","51":"r10","52":"r10","53":"r10","55":"r10"},{"23":"r13","24":"r13","25":"r13","26":"r13","27":"r13","28":"r13","29":"r13","30":"r13","31":"r13","32":"r13","33":"r13","34":"r13","35":"r13","36":"r13","37":"r13","38":"r13","39":"r13","40":"r13","41":"r13","42":"r13","43":"r13","50":"r13","51":"r13","52":"r13","53":"r13","55":"r13"},{"23":"r35","24":"r35","25":"r35","26":"r35","27":"r35","28":"r35","29":"r35","30":"r35","31":"r35","32":"r35","33":"r35","34":"r35","35":"r35","36":"r35","37":"r35","38":"r35","39":"r35","40":"r35","41":"r35","42":"r35","43":"r35","50":"r35","51":"r35","52":"r35","53":"r35","55":"r35"},{"23":"r37","24":"r37","25":"r37","26":"r37","27":"r37","28":"r37","29":"r37","30":"r37","31":"r37","32":"r37","33":"r37","34":"r37","35":"r37","36":"r37","37":"r37","38":"r37","39":"r37","40":"r37","41":"r37","42":"r37","43":"r37","44":"s52","50":"r37","51":"r37","52":"r37","53":"r37","55":"r37"},{"23":"r39","24":"r39","25":"r39","26":"r39","27":"r39","28":"r39","29":"r39","30":"r39","31":"r39","32":"r39","33":"r39","34":"r39","35":"r39","36":"r39","37":"r39","38":"r39","39":"r39","40":"r39","41":"r39","42":"r39","43":"r39","44":"r39","50":"r39","51":"r39","52":"r39","53":"r39","55":"r39"},{"23":"r40","24":"r40","25":"r40","26":"r40","27":"r40","28":"r40","29":"r40","30":"r40","31":"r40","32":"r40","33":"r40","34":"r40","35":"r40","36":"r40","37":"r40","38":"r40","39":"r40","40":"r40","41":"r40","42":"r40","43":"r40","44":"r40","50":"r40","51":"r40","52":"r40","53":"r40","55":"r40"},{"23":"r41","24":"r41","25":"r41","26":"r41","27":"r41","28":"r41","29":"r41","30":"r41","31":"r41","32":"r41","33":"r41","34":"r41","35":"r41","36":"r41","37":"r41","38":"r41","39":"r41","40":"r41","41":"r41","42":"r41","43":"r41","44":"r41","50":"r41","51":"r41","52":"r41","53":"r41","55":"r41"},{"23":"r42","24":"r42","25":"r42","26":"r42","27":"r42","28":"r42","29":"r42","30":"r42","31":"r42","32":"r42","33":"r42","34":"r42","35":"r42","36":"r42","37":"r42","38":"r42","39":"r42","40":"r42","41":"r42","42":"r42","43":"r42","44":"r42","50":"r42","51":"r42","52":"r42","53":"r42","55":"r42"},{"23":"r43","24":"r43","25":"r43","26":"r43","27":"r43","28":"r43","29":"r43","30":"r43","31":"r43","32":"r43","33":"r43","34":"r43","35":"r43","36":"r43","37":"r43","38":"r43","39":"r43","40":"r43","41":"r43","42":"r43","43":"r43","44":"r43","50":"r43","51":"r43","52":"r43","53":"r43","55":"r43"},{"23":"r44","24":"r44","25":"r44","26":"r44","27":"r44","28":"r44","29":"r44","30":"r44","31":"r44","32":"r44","33":"r44","34":"r44","35":"r44","36":"r44","37":"r44","38":"r44","39":"r44","40":"r44","41":"r44","42":"r44","43":"r44","44":"r44","50":"r44","51":"r44","52":"r44","53":"r44","55":"r44"},{"23":"r38","24":"r38","25":"r38","26":"r38","27":"r38","28":"r38","29":"r38","30":"r38","31":"r38","32":"r38","33":"r38","34":"r38","35":"r38","36":"r38","37":"r38","38":"r38","39":"r38","40":"r38","41":"r38","42":"r38","43":"r38","50":"r38","51":"r38","52":"r38","53":"r38","55":"r38"},{"25":"s10","31":"s54"},{"23":"r18","24":"r18","25":"r18","26":"r18","27":"r18","28":"r18","29":"r18","30":"r18","31":"r18","32":"r18","33":"r18","34":"r18","35":"r18","36":"r18","37":"r18","38":"r18","39":"r18","40":"r18","41":"r18","42":"r18","43":"r18","50":"r18","51":"r18","52":"r18","53":"r18","55":"r18"},{"25":"s10","31":"s56"},{"23":"r19","24":"r19","25":"r19","26":"r19","27":"r19","28":"r19","29":"r19","30":"r19","31":"r19","32":"r19","33":"r19","34":"r19","35":"r19","36":"r19","37":"r19","38":"r19","39":"r19","40":"r19","41":"r19","42":"r19","43":"r19","50":"r19","51":"r19","52":"r19","53":"r19","55":"r19"},{"25":"s10","31":"s58"},{"23":"r20","24":"r20","25":"r20","26":"r20","27":"r20","28":"r20","29":"r20","30":"r20","31":"r20","32":"r20","33":"r20","34":"r20","35":"r20","36":"r20","37":"r20","38":"r20","39":"r20","40":"r20","41":"r20","42":"r20","43":"r20","50":"r20","51":"r20","52":"r20","53":"r20","55":"r20"},{"25":"s10","31":"s60"},{"23":"r21","24":"r21","25":"r21","26":"r21","27":"r21","28":"r21","29":"r21","30":"r21","31":"r21","32":"r21","33":"r21","34":"r21","35":"r21","36":"r21","37":"r21","38":"r21","39":"r21","40":"r21","41":"r21","42":"r21","43":"r21","50":"r21","51":"r21","52":"r21","53":"r21","55":"r21"},{"54":"s67"},{"10":65,"20":84,"21":85,"22":69,"24":"s26","28":"s66","35":"s27","36":"s28","37":"s29","38":"s30","39":"s31","40":"s32","41":"s33","42":"s34","43":"s35","54":"r54","56":"s68"},{"24":"r60","28":"r60","35":"r60","36":"r60","37":"r60","38":"r60","39":"r60","40":"r60","41":"r60","42":"r60","43":"r60","54":"r60","56":"r60"},{"24":"r61","28":"r61","35":"r61","36":"r61","37":"r61","38":"r61","39":"r61","40":"r61","41":"r61","42":"r61","43":"r61","54":"r61","56":"r61"},{"24":"r62","28":"r62","35":"r62","36":"r62","37":"r62","38":"r62","39":"r62","40":"r62","41":"r62","42":"r62","43":"r62","54":"r62","56":"r62"},{"24":"r63","28":"r63","35":"r63","36":"r63","37":"r63","38":"r63","39":"r63","40":"r63","41":"r63","42":"r63","43":"r63","54":"r63","56":"r63"},{"23":"r50","24":"r50","25":"r50","26":"r50","27":"r50","28":"r50","29":"r50","30":"r50","31":"r50","32":"r50","33":"r50","34":"r50","35":"r50","36":"r50","37":"r50","38":"r50","39":"r50","40":"r50","41":"r50","42":"r50","43":"r50","44":"r50","45":"r50","46":"r50","47":"r50","48":"r50","49":"r50","50":"r50","51":"r50","52":"r50","53":"r50","55":"r50"},{"10":65,"21":70,"22":64,"24":"s26","28":"s66","35":"s27","36":"s28","37":"s29","38":"s30","39":"s31","40":"s32","41":"s33","42":"s34","43":"s35","54":"r60","56":"s63"},{"10":65,"20":87,"21":85,"22":69,"24":"s26","28":"s66","35":"s27","36":"s28","37":"s29","38":"s30","39":"s31","40":"s32","41":"s33","42":"s34","43":"s35","54":"r61","56":"s71"},{"10":65,"18":86,"19":83,"21":62,"22":64,"24":"s26","28":"s66","35":"s27","36":"s28","37":"s29","38":"s30","39":"s31","40":"s32","41":"s33","42":"s34","43":"s35","54":"r52","56":"s63"},{"10":65,"21":72,"22":64,"24":"s26","28":"s66","35":"s27","36":"s28","37":"s29","38":"s30","39":"s31","40":"s32","41":"s33","42":"s34","43":"s35","54":"r60","56":"s63"},{"10":65,"18":88,"19":83,"21":62,"22":64,"24":"s26","28":"s66","35":"s27","36":"s28","37":"s29","38":"s30","39":"s31","40":"s32","41":"s33","42":"s34","43":"s35","54":"r52","56":"s63"},{"54":"s74"},{"23":"r51","24":"r51","25":"r51","26":"r51","27":"r51","28":"r51","29":"r51","30":"r51","31":"r51","32":"r51","33":"r51","34":"r51","35":"r51","36":"r51","37":"r51","38":"r51","39":"r51","40":"r51","41":"r51","42":"r51","43":"r51","44":"r51","45":"r51","46":"r51","47":"r51","48":"r51","49":"r51","50":"r51","51":"r51","52":"r51","53":"r51","55":"r51"},{"25":"s10","31":"s76"},{"23":"r47","24":"r47","25":"r47","26":"r47","27":"r47","28":"r47","29":"r47","30":"r47","31":"r47","32":"r47","33":"r47","34":"r47","35":"r47","36":"r47","37":"r47","38":"r47","39":"r47","40":"r47","41":"r47","42":"r47","43":"r47","44":"r47","45":"r47","46":"r47","47":"r47","48":"r47","49":"r47","50":"r47","51":"r47","52":"r47","53":"r47","55":"r47"},{"25":"s10","31":"s78"},{"23":"r48","24":"r48","25":"r48","26":"r48","27":"r48","28":"r48","29":"r48","30":"r48","31":"r48","32":"r48","33":"r48","34":"r48","35":"r48","36":"r48","37":"r48","38":"r48","39":"r48","40":"r48","41":"r48","42":"r48","43":"r48","44":"r48","45":"r48","46":"r48","47":"r48","48":"r48","49":"r48","50":"r48","51":"r48","52":"r48","53":"r48","55":"r48"},{"25":"s10","31":"s80"},{"23":"r49","24":"r49","25":"r49","26":"r49","27":"r49","28":"r49","29":"r49","30":"r49","31":"r49","32":"r49","33":"r49","34":"r49","35":"r49","36":"r49","37":"r49","38":"r49","39":"r49","40":"r49","41":"r49","42":"r49","43":"r49","44":"r49","45":"r49","46":"r49","47":"r49","48":"r49","49":"r49","50":"r49","51":"r49","52":"r49","53":"r49","55":"r49"},{"57":"acc"},{"57":"r1"},{"54":"r53"},{"54":"r55"},{"54":"r57"},{"54":"r56"},{"54":"r58"},{"54":"r59"}];
+const table = [{"0":82,"23":"s1"},{"3":2,"4":3,"5":4,"6":5,"23":"r10","24":"r10","25":"r10","26":"r10","27":"r10","28":"r10","29":"r10","30":"r10","32":"r10","33":"r10","34":"r10","35":"r10","36":"r10","37":"r10","38":"r10","39":"r10","40":"r10","41":"r10","42":"r10","43":"r10","44":"r10","51":"r10","52":"r10","53":"r10","54":"r10","56":"r10"},{"23":"s6"},{"23":"r6","25":"s10"},{"23":"r7","25":"r7","31":"r7"},{"7":12,"8":13,"9":14,"10":23,"14":25,"15":39,"16":40,"17":24,"23":"r9","24":"s26","25":"r9","26":"s15","27":"s16","28":"s17","29":"s18","30":"s19","31":"r9","32":"s20","33":"s21","34":"s22","35":"s27","36":"s28","37":"s29","38":"s30","39":"s31","40":"s32","41":"s33","42":"s34","43":"s35","44":"s36","51":"s41","52":"s42","53":"s43","54":"s37","56":"s38"},{"1":83,"2":7,"24":"s8","58":"r3"},{"24":"s9","58":"r2"},{"24":"r4","58":"r4"},{"24":"r5","58":"r5"},{"5":11,"6":5,"23":"r10","24":"r10","25":"r10","26":"r10","27":"r10","28":"r10","29":"r10","30":"r10","31":"r10","32":"r10","33":"r10","34":"r10","35":"r10","36":"r10","37":"r10","38":"r10","39":"r10","40":"r10","41":"r10","42":"r10","43":"r10","44":"r10","51":"r10","52":"r10","53":"r10","54":"r10","56":"r10"},{"23":"r8","25":"r8","31":"r8"},{"23":"r11","24":"r11","25":"r11","26":"r11","27":"r11","28":"r11","29":"r11","30":"r11","31":"r11","32":"r11","33":"r11","34":"r11","35":"r11","36":"r11","37":"r11","38":"r11","39":"r11","40":"r11","41":"r11","42":"r11","43":"r11","44":"r11","51":"r11","52":"r11","53":"r11","54":"r11","56":"r11"},{"23":"r12","24":"r12","25":"r12","26":"r12","27":"r12","28":"r12","29":"r12","30":"r12","31":"r12","32":"r12","33":"r12","34":"r12","35":"r12","36":"r12","37":"r12","38":"r12","39":"r12","40":"r12","41":"r12","42":"r12","43":"r12","44":"r12","51":"r12","52":"r12","53":"r12","54":"r12","56":"r12"},{"11":44,"12":45,"13":46,"23":"r37","24":"r37","25":"r37","26":"r37","27":"r37","28":"r37","29":"r37","30":"r37","31":"r37","32":"r37","33":"r37","34":"r37","35":"r37","36":"r37","37":"r37","38":"r37","39":"r37","40":"r37","41":"r37","42":"r37","43":"r37","44":"r37","45":"s49","46":"s47","47":"s48","48":"s50","49":"s51","50":"s52","51":"r37","52":"r37","53":"r37","54":"r37","56":"r37"},{"23":"r14","24":"r14","25":"r14","26":"r14","27":"r14","28":"r14","29":"r14","30":"r14","31":"r14","32":"r14","33":"r14","34":"r14","35":"r14","36":"r14","37":"r14","38":"r14","39":"r14","40":"r14","41":"r14","42":"r14","43":"r14","44":"r14","51":"r14","52":"r14","53":"r14","54":"r14","56":"r14"},{"23":"r15","24":"r15","25":"r15","26":"r15","27":"r15","28":"r15","29":"r15","30":"r15","31":"r15","32":"r15","33":"r15","34":"r15","35":"r15","36":"r15","37":"r15","38":"r15","39":"r15","40":"r15","41":"r15","42":"r15","43":"r15","44":"r15","51":"r15","52":"r15","53":"r15","54":"r15","56":"r15"},{"23":"r16","24":"r16","25":"r16","26":"r16","27":"r16","28":"r16","29":"r16","30":"r16","31":"r16","32":"r16","33":"r16","34":"r16","35":"r16","36":"r16","37":"r16","38":"r16","39":"r16","40":"r16","41":"r16","42":"r16","43":"r16","44":"r16","51":"r16","52":"r16","53":"r16","54":"r16","56":"r16"},{"23":"r17","24":"r17","25":"r17","26":"r17","27":"r17","28":"r17","29":"r17","30":"r17","31":"r17","32":"r17","33":"r17","34":"r17","35":"r17","36":"r17","37":"r17","38":"r17","39":"r17","40":"r17","41":"r17","42":"r17","43":"r17","44":"r17","51":"r17","52":"r17","53":"r17","54":"r17","56":"r17"},{"4":54,"5":4,"6":5,"24":"r10","25":"r10","26":"r10","27":"r10","28":"r10","29":"r10","30":"r10","31":"r10","32":"r10","33":"r10","34":"r10","35":"r10","36":"r10","37":"r10","38":"r10","39":"r10","40":"r10","41":"r10","42":"r10","43":"r10","44":"r10","51":"r10","52":"r10","53":"r10","54":"r10","56":"r10"},{"4":56,"5":4,"6":5,"24":"r10","25":"r10","26":"r10","27":"r10","28":"r10","29":"r10","30":"r10","31":"r10","32":"r10","33":"r10","34":"r10","35":"r10","36":"r10","37":"r10","38":"r10","39":"r10","40":"r10","41":"r10","42":"r10","43":"r10","44":"r10","51":"r10","52":"r10","53":"r10","54":"r10","56":"r10"},{"4":58,"5":4,"6":5,"24":"r10","25":"r10","26":"r10","27":"r10","28":"r10","29":"r10","30":"r10","31":"r10","32":"r10","33":"r10","34":"r10","35":"r10","36":"r10","37":"r10","38":"r10","39":"r10","40":"r10","41":"r10","42":"r10","43":"r10","44":"r10","51":"r10","52":"r10","53":"r10","54":"r10","56":"r10"},{"4":60,"5":4,"6":5,"24":"r10","25":"r10","26":"r10","27":"r10","28":"r10","29":"r10","30":"r10","31":"r10","32":"r10","33":"r10","34":"r10","35":"r10","36":"r10","37":"r10","38":"r10","39":"r10","40":"r10","41":"r10","42":"r10","43":"r10","44":"r10","51":"r10","52":"r10","53":"r10","54":"r10","56":"r10"},{"23":"r22","24":"r22","25":"r22","26":"r22","27":"r22","28":"r22","29":"r22","30":"r22","31":"r22","32":"r22","33":"r22","34":"r22","35":"r22","36":"r22","37":"r22","38":"r22","39":"r22","40":"r22","41":"r22","42":"r22","43":"r22","44":"r22","45":"r22","46":"r22","47":"r22","48":"r22","49":"r22","50":"r22","51":"r22","52":"r22","53":"r22","54":"r22","56":"r22"},{"23":"r23","24":"r23","25":"r23","26":"r23","27":"r23","28":"r23","29":"r23","30":"r23","31":"r23","32":"r23","33":"r23","34":"r23","35":"r23","36":"r23","37":"r23","38":"r23","39":"r23","40":"r23","41":"r23","42":"r23","43":"r23","44":"r23","45":"r23","46":"r23","47":"r23","48":"r23","49":"r23","50":"r23","51":"r23","52":"r23","53":"r23","54":"r23","56":"r23"},{"23":"r24","24":"r24","25":"r24","26":"r24","27":"r24","28":"r24","29":"r24","30":"r24","31":"r24","32":"r24","33":"r24","34":"r24","35":"r24","36":"r24","37":"r24","38":"r24","39":"r24","40":"r24","41":"r24","42":"r24","43":"r24","44":"r24","45":"r24","46":"r24","47":"r24","48":"r24","49":"r24","50":"r24","51":"r24","52":"r24","53":"r24","54":"r24","56":"r24"},{"23":"r25","24":"r25","25":"r25","26":"r25","27":"r25","28":"r25","29":"r25","30":"r25","31":"r25","32":"r25","33":"r25","34":"r25","35":"r25","36":"r25","37":"r25","38":"r25","39":"r25","40":"r25","41":"r25","42":"r25","43":"r25","44":"r25","45":"r25","46":"r25","47":"r25","48":"r25","49":"r25","50":"r25","51":"r25","52":"r25","53":"r25","54":"r25","55":"r25","56":"r25","57":"r25"},{"23":"r26","24":"r26","25":"r26","26":"r26","27":"r26","28":"r26","29":"r26","30":"r26","31":"r26","32":"r26","33":"r26","34":"r26","35":"r26","36":"r26","37":"r26","38":"r26","39":"r26","40":"r26","41":"r26","42":"r26","43":"r26","44":"r26","45":"r26","46":"r26","47":"r26","48":"r26","49":"r26","50":"r26","51":"r26","52":"r26","53":"r26","54":"r26","55":"r26","56":"r26","57":"r26"},{"23":"r27","24":"r27","25":"r27","26":"r27","27":"r27","28":"r27","29":"r27","30":"r27","31":"r27","32":"r27","33":"r27","34":"r27","35":"r27","36":"r27","37":"r27","38":"r27","39":"r27","40":"r27","41":"r27","42":"r27","43":"r27","44":"r27","45":"r27","46":"r27","47":"r27","48":"r27","49":"r27","50":"r27","51":"r27","52":"r27","53":"r27","54":"r27","55":"r27","56":"r27","57":"r27"},{"23":"r28","24":"r28","25":"r28","26":"r28","27":"r28","28":"r28","29":"r28","30":"r28","31":"r28","32":"r28","33":"r28","34":"r28","35":"r28","36":"r28","37":"r28","38":"r28","39":"r28","40":"r28","41":"r28","42":"r28","43":"r28","44":"r28","45":"r28","46":"r28","47":"r28","48":"r28","49":"r28","50":"r28","51":"r28","52":"r28","53":"r28","54":"r28","55":"r28","56":"r28","57":"r28"},{"23":"r29","24":"r29","25":"r29","26":"r29","27":"r29","28":"r29","29":"r29","30":"r29","31":"r29","32":"r29","33":"r29","34":"r29","35":"r29","36":"r29","37":"r29","38":"r29","39":"r29","40":"r29","41":"r29","42":"r29","43":"r29","44":"r29","45":"r29","46":"r29","47":"r29","48":"r29","49":"r29","50":"r29","51":"r29","52":"r29","53":"r29","54":"r29","55":"r29","56":"r29","57":"r29"},{"23":"r30","24":"r30","25":"r30","26":"r30","27":"r30","28":"r30","29":"r30","30":"r30","31":"r30","32":"r30","33":"r30","34":"r30","35":"r30","36":"r30","37":"r30","38":"r30","39":"r30","40":"r30","41":"r30","42":"r30","43":"r30","44":"r30","45":"r30","46":"r30","47":"r30","48":"r30","49":"r30","50":"r30","51":"r30","52":"r30","53":"r30","54":"r30","55":"r30","56":"r30","57":"r30"},{"23":"r31","24":"r31","25":"r31","26":"r31","27":"r31","28":"r31","29":"r31","30":"r31","31":"r31","32":"r31","33":"r31","34":"r31","35":"r31","36":"r31","37":"r31","38":"r31","39":"r31","40":"r31","41":"r31","42":"r31","43":"r31","44":"r31","45":"r31","46":"r31","47":"r31","48":"r31","49":"r31","50":"r31","51":"r31","52":"r31","53":"r31","54":"r31","55":"r31","56":"r31","57":"r31"},{"23":"r32","24":"r32","25":"r32","26":"r32","27":"r32","28":"r32","29":"r32","30":"r32","31":"r32","32":"r32","33":"r32","34":"r32","35":"r32","36":"r32","37":"r32","38":"r32","39":"r32","40":"r32","41":"r32","42":"r32","43":"r32","44":"r32","45":"r32","46":"r32","47":"r32","48":"r32","49":"r32","50":"r32","51":"r32","52":"r32","53":"r32","54":"r32","55":"r32","56":"r32","57":"r32"},{"23":"r33","24":"r33","25":"r33","26":"r33","27":"r33","28":"r33","29":"r33","30":"r33","31":"r33","32":"r33","33":"r33","34":"r33","35":"r33","36":"r33","37":"r33","38":"r33","39":"r33","40":"r33","41":"r33","42":"r33","43":"r33","44":"r33","45":"r33","46":"r33","47":"r33","48":"r33","49":"r33","50":"r33","51":"r33","52":"r33","53":"r33","54":"r33","55":"r33","56":"r33","57":"r33"},{"23":"r34","24":"r34","25":"r34","26":"r34","27":"r34","28":"r34","29":"r34","30":"r34","31":"r34","32":"r34","33":"r34","34":"r34","35":"r34","36":"r34","37":"r34","38":"r34","39":"r34","40":"r34","41":"r34","42":"r34","43":"r34","44":"r34","45":"r34","46":"r34","47":"r34","48":"r34","49":"r34","50":"r34","51":"r34","52":"r34","53":"r34","54":"r34","55":"r34","56":"r34","57":"r34"},{"23":"r35","24":"r35","25":"r35","26":"r35","27":"r35","28":"r35","29":"r35","30":"r35","31":"r35","32":"r35","33":"r35","34":"r35","35":"r35","36":"r35","37":"r35","38":"r35","39":"r35","40":"r35","41":"r35","42":"r35","43":"r35","44":"r35","45":"r35","46":"r35","47":"r35","48":"r35","49":"r35","50":"r35","51":"r35","52":"r35","53":"r35","54":"r35","55":"r35","56":"r35","57":"r35"},{"10":66,"18":62,"19":84,"21":63,"22":65,"24":"s26","28":"s67","35":"s27","36":"s28","37":"s29","38":"s30","39":"s31","40":"s32","41":"s33","42":"s34","43":"s35","44":"s36","55":"r53","57":"s64"},{"10":66,"18":74,"19":84,"21":63,"22":65,"24":"s26","28":"s67","35":"s27","36":"s28","37":"s29","38":"s30","39":"s31","40":"s32","41":"s33","42":"s34","43":"s35","44":"s36","55":"r53","57":"s64"},{"23":"r46","24":"r46","25":"r46","26":"r46","27":"r46","28":"r46","29":"r46","30":"r46","31":"r46","32":"r46","33":"r46","34":"r46","35":"r46","36":"r46","37":"r46","38":"r46","39":"r46","40":"r46","41":"r46","42":"r46","43":"r46","44":"r46","45":"r46","46":"r46","47":"r46","48":"r46","49":"r46","50":"r46","51":"r46","52":"r46","53":"r46","54":"r46","56":"r46"},{"23":"r47","24":"r47","25":"r47","26":"r47","27":"r47","28":"r47","29":"r47","30":"r47","31":"r47","32":"r47","33":"r47","34":"r47","35":"r47","36":"r47","37":"r47","38":"r47","39":"r47","40":"r47","41":"r47","42":"r47","43":"r47","44":"r47","45":"r47","46":"r47","47":"r47","48":"r47","49":"r47","50":"r47","51":"r47","52":"r47","53":"r47","54":"r47","56":"r47"},{"4":76,"5":4,"6":5,"24":"r10","25":"r10","26":"r10","27":"r10","28":"r10","29":"r10","30":"r10","31":"r10","32":"r10","33":"r10","34":"r10","35":"r10","36":"r10","37":"r10","38":"r10","39":"r10","40":"r10","41":"r10","42":"r10","43":"r10","44":"r10","51":"r10","52":"r10","53":"r10","54":"r10","56":"r10"},{"4":78,"5":4,"6":5,"24":"r10","25":"r10","26":"r10","27":"r10","28":"r10","29":"r10","30":"r10","31":"r10","32":"r10","33":"r10","34":"r10","35":"r10","36":"r10","37":"r10","38":"r10","39":"r10","40":"r10","41":"r10","42":"r10","43":"r10","44":"r10","51":"r10","52":"r10","53":"r10","54":"r10","56":"r10"},{"4":80,"5":4,"6":5,"24":"r10","25":"r10","26":"r10","27":"r10","28":"r10","29":"r10","30":"r10","31":"r10","32":"r10","33":"r10","34":"r10","35":"r10","36":"r10","37":"r10","38":"r10","39":"r10","40":"r10","41":"r10","42":"r10","43":"r10","44":"r10","51":"r10","52":"r10","53":"r10","54":"r10","56":"r10"},{"23":"r13","24":"r13","25":"r13","26":"r13","27":"r13","28":"r13","29":"r13","30":"r13","31":"r13","32":"r13","33":"r13","34":"r13","35":"r13","36":"r13","37":"r13","38":"r13","39":"r13","40":"r13","41":"r13","42":"r13","43":"r13","44":"r13","51":"r13","52":"r13","53":"r13","54":"r13","56":"r13"},{"23":"r36","24":"r36","25":"r36","26":"r36","27":"r36","28":"r36","29":"r36","30":"r36","31":"r36","32":"r36","33":"r36","34":"r36","35":"r36","36":"r36","37":"r36","38":"r36","39":"r36","40":"r36","41":"r36","42":"r36","43":"r36","44":"r36","51":"r36","52":"r36","53":"r36","54":"r36","56":"r36"},{"23":"r38","24":"r38","25":"r38","26":"r38","27":"r38","28":"r38","29":"r38","30":"r38","31":"r38","32":"r38","33":"r38","34":"r38","35":"r38","36":"r38","37":"r38","38":"r38","39":"r38","40":"r38","41":"r38","42":"r38","43":"r38","44":"r38","45":"s53","51":"r38","52":"r38","53":"r38","54":"r38","56":"r38"},{"23":"r40","24":"r40","25":"r40","26":"r40","27":"r40","28":"r40","29":"r40","30":"r40","31":"r40","32":"r40","33":"r40","34":"r40","35":"r40","36":"r40","37":"r40","38":"r40","39":"r40","40":"r40","41":"r40","42":"r40","43":"r40","44":"r40","45":"r40","51":"r40","52":"r40","53":"r40","54":"r40","56":"r40"},{"23":"r41","24":"r41","25":"r41","26":"r41","27":"r41","28":"r41","29":"r41","30":"r41","31":"r41","32":"r41","33":"r41","34":"r41","35":"r41","36":"r41","37":"r41","38":"r41","39":"r41","40":"r41","41":"r41","42":"r41","43":"r41","44":"r41","45":"r41","51":"r41","52":"r41","53":"r41","54":"r41","56":"r41"},{"23":"r42","24":"r42","25":"r42","26":"r42","27":"r42","28":"r42","29":"r42","30":"r42","31":"r42","32":"r42","33":"r42","34":"r42","35":"r42","36":"r42","37":"r42","38":"r42","39":"r42","40":"r42","41":"r42","42":"r42","43":"r42","44":"r42","45":"r42","51":"r42","52":"r42","53":"r42","54":"r42","56":"r42"},{"23":"r43","24":"r43","25":"r43","26":"r43","27":"r43","28":"r43","29":"r43","30":"r43","31":"r43","32":"r43","33":"r43","34":"r43","35":"r43","36":"r43","37":"r43","38":"r43","39":"r43","40":"r43","41":"r43","42":"r43","43":"r43","44":"r43","45":"r43","51":"r43","52":"r43","53":"r43","54":"r43","56":"r43"},{"23":"r44","24":"r44","25":"r44","26":"r44","27":"r44","28":"r44","29":"r44","30":"r44","31":"r44","32":"r44","33":"r44","34":"r44","35":"r44","36":"r44","37":"r44","38":"r44","39":"r44","40":"r44","41":"r44","42":"r44","43":"r44","44":"r44","45":"r44","51":"r44","52":"r44","53":"r44","54":"r44","56":"r44"},{"23":"r45","24":"r45","25":"r45","26":"r45","27":"r45","28":"r45","29":"r45","30":"r45","31":"r45","32":"r45","33":"r45","34":"r45","35":"r45","36":"r45","37":"r45","38":"r45","39":"r45","40":"r45","41":"r45","42":"r45","43":"r45","44":"r45","45":"r45","51":"r45","52":"r45","53":"r45","54":"r45","56":"r45"},{"23":"r39","24":"r39","25":"r39","26":"r39","27":"r39","28":"r39","29":"r39","30":"r39","31":"r39","32":"r39","33":"r39","34":"r39","35":"r39","36":"r39","37":"r39","38":"r39","39":"r39","40":"r39","41":"r39","42":"r39","43":"r39","44":"r39","51":"r39","52":"r39","53":"r39","54":"r39","56":"r39"},{"25":"s10","31":"s55"},{"23":"r18","24":"r18","25":"r18","26":"r18","27":"r18","28":"r18","29":"r18","30":"r18","31":"r18","32":"r18","33":"r18","34":"r18","35":"r18","36":"r18","37":"r18","38":"r18","39":"r18","40":"r18","41":"r18","42":"r18","43":"r18","44":"r18","51":"r18","52":"r18","53":"r18","54":"r18","56":"r18"},{"25":"s10","31":"s57"},{"23":"r19","24":"r19","25":"r19","26":"r19","27":"r19","28":"r19","29":"r19","30":"r19","31":"r19","32":"r19","33":"r19","34":"r19","35":"r19","36":"r19","37":"r19","38":"r19","39":"r19","40":"r19","41":"r19","42":"r19","43":"r19","44":"r19","51":"r19","52":"r19","53":"r19","54":"r19","56":"r19"},{"25":"s10","31":"s59"},{"23":"r20","24":"r20","25":"r20","26":"r20","27":"r20","28":"r20","29":"r20","30":"r20","31":"r20","32":"r20","33":"r20","34":"r20","35":"r20","36":"r20","37":"r20","38":"r20","39":"r20","40":"r20","41":"r20","42":"r20","43":"r20","44":"r20","51":"r20","52":"r20","53":"r20","54":"r20","56":"r20"},{"25":"s10","31":"s61"},{"23":"r21","24":"r21","25":"r21","26":"r21","27":"r21","28":"r21","29":"r21","30":"r21","31":"r21","32":"r21","33":"r21","34":"r21","35":"r21","36":"r21","37":"r21","38":"r21","39":"r21","40":"r21","41":"r21","42":"r21","43":"r21","44":"r21","51":"r21","52":"r21","53":"r21","54":"r21","56":"r21"},{"55":"s68"},{"10":66,"20":85,"21":86,"22":70,"24":"s26","28":"s67","35":"s27","36":"s28","37":"s29","38":"s30","39":"s31","40":"s32","41":"s33","42":"s34","43":"s35","44":"s36","55":"r55","57":"s69"},{"24":"r61","28":"r61","35":"r61","36":"r61","37":"r61","38":"r61","39":"r61","40":"r61","41":"r61","42":"r61","43":"r61","44":"r61","55":"r61","57":"r61"},{"24":"r62","28":"r62","35":"r62","36":"r62","37":"r62","38":"r62","39":"r62","40":"r62","41":"r62","42":"r62","43":"r62","44":"r62","55":"r62","57":"r62"},{"24":"r63","28":"r63","35":"r63","36":"r63","37":"r63","38":"r63","39":"r63","40":"r63","41":"r63","42":"r63","43":"r63","44":"r63","55":"r63","57":"r63"},{"24":"r64","28":"r64","35":"r64","36":"r64","37":"r64","38":"r64","39":"r64","40":"r64","41":"r64","42":"r64","43":"r64","44":"r64","55":"r64","57":"r64"},{"23":"r51","24":"r51","25":"r51","26":"r51","27":"r51","28":"r51","29":"r51","30":"r51","31":"r51","32":"r51","33":"r51","34":"r51","35":"r51","36":"r51","37":"r51","38":"r51","39":"r51","40":"r51","41":"r51","42":"r51","43":"r51","44":"r51","45":"r51","46":"r51","47":"r51","48":"r51","49":"r51","50":"r51","51":"r51","52":"r51","53":"r51","54":"r51","56":"r51"},{"10":66,"21":71,"22":65,"24":"s26","28":"s67","35":"s27","36":"s28","37":"s29","38":"s30","39":"s31","40":"s32","41":"s33","42":"s34","43":"s35","44":"s36","55":"r61","57":"s64"},{"10":66,"20":88,"21":86,"22":70,"24":"s26","28":"s67","35":"s27","36":"s28","37":"s29","38":"s30","39":"s31","40":"s32","41":"s33","42":"s34","43":"s35","44":"s36","55":"r62","57":"s72"},{"10":66,"18":87,"19":84,"21":63,"22":65,"24":"s26","28":"s67","35":"s27","36":"s28","37":"s29","38":"s30","39":"s31","40":"s32","41":"s33","42":"s34","43":"s35","44":"s36","55":"r53","57":"s64"},{"10":66,"21":73,"22":65,"24":"s26","28":"s67","35":"s27","36":"s28","37":"s29","38":"s30","39":"s31","40":"s32","41":"s33","42":"s34","43":"s35","44":"s36","55":"r61","57":"s64"},{"10":66,"18":89,"19":84,"21":63,"22":65,"24":"s26","28":"s67","35":"s27","36":"s28","37":"s29","38":"s30","39":"s31","40":"s32","41":"s33","42":"s34","43":"s35","44":"s36","55":"r53","57":"s64"},{"55":"s75"},{"23":"r52","24":"r52","25":"r52","26":"r52","27":"r52","28":"r52","29":"r52","30":"r52","31":"r52","32":"r52","33":"r52","34":"r52","35":"r52","36":"r52","37":"r52","38":"r52","39":"r52","40":"r52","41":"r52","42":"r52","43":"r52","44":"r52","45":"r52","46":"r52","47":"r52","48":"r52","49":"r52","50":"r52","51":"r52","52":"r52","53":"r52","54":"r52","56":"r52"},{"25":"s10","31":"s77"},{"23":"r48","24":"r48","25":"r48","26":"r48","27":"r48","28":"r48","29":"r48","30":"r48","31":"r48","32":"r48","33":"r48","34":"r48","35":"r48","36":"r48","37":"r48","38":"r48","39":"r48","40":"r48","41":"r48","42":"r48","43":"r48","44":"r48","45":"r48","46":"r48","47":"r48","48":"r48","49":"r48","50":"r48","51":"r48","52":"r48","53":"r48","54":"r48","56":"r48"},{"25":"s10","31":"s79"},{"23":"r49","24":"r49","25":"r49","26":"r49","27":"r49","28":"r49","29":"r49","30":"r49","31":"r49","32":"r49","33":"r49","34":"r49","35":"r49","36":"r49","37":"r49","38":"r49","39":"r49","40":"r49","41":"r49","42":"r49","43":"r49","44":"r49","45":"r49","46":"r49","47":"r49","48":"r49","49":"r49","50":"r49","51":"r49","52":"r49","53":"r49","54":"r49","56":"r49"},{"25":"s10","31":"s81"},{"23":"r50","24":"r50","25":"r50","26":"r50","27":"r50","28":"r50","29":"r50","30":"r50","31":"r50","32":"r50","33":"r50","34":"r50","35":"r50","36":"r50","37":"r50","38":"r50","39":"r50","40":"r50","41":"r50","42":"r50","43":"r50","44":"r50","45":"r50","46":"r50","47":"r50","48":"r50","49":"r50","50":"r50","51":"r50","52":"r50","53":"r50","54":"r50","56":"r50"},{"58":"acc"},{"58":"r1"},{"55":"r54"},{"55":"r56"},{"55":"r58"},{"55":"r57"},{"55":"r59"},{"55":"r60"}];
 
 /**
  * Parsing stack.
@@ -353,8 +354,9 @@ const lexRules = [[/^#[^\n]+/, function() { /* skip comments */ }],
 [/^\\0\d{1,2}/, function() { return 'OCT_CODE' }],
 [/^\\0/, function() { return 'DEC_CODE' }],
 [/^\\\d{1,3}/, function() { return 'DEC_CODE' }],
-[/^\\u[0-9a-fA-F]{4}/, function() { return 'U_CODE' }],
+[/^\\u[dD][89abAB][0-9a-fA-F]{2}\\u[dD][c-fC-F][0-9a-fA-F]{2}/, function() { return 'U_CODE_SURROGATE' }],
 [/^\\u\{[0-9a-fA-F]{1,}\}/, function() { return 'U_CODE' }],
+[/^\\u[0-9a-fA-F]{4}/, function() { return 'U_CODE' }],
 [/^\\x[0-9a-fA-F]{2}/, function() { return 'HEX_CODE' }],
 [/^\\[tnrdDsSwWvf]/, function() { return 'META_CHAR' }],
 [/^\\\//, function() { return 'ESC_CHAR' }],
@@ -382,9 +384,9 @@ const lexRules = [[/^#[^\n]+/, function() { /* skip comments */ }],
 [/^\./, function() { return 'ANY' }],
 [/^\//, function() { return 'SLASH' }],
 [/^[^*?+\[()]/, function() { return 'CHAR' }],
-[/^\[\^/, function() {  this.pushState('class'); return 'NEG_CLASS'  }],
-[/^\[/, function() {  this.pushState('class'); return 'L_BRACKET'  }]];
-const lexRulesByConditions = {"INITIAL":[9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,28,29,30,31,32,33,34,35,39,40,41,42,43,44,45,46,47,48,49],"x":[0,1,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,28,29,30,31,32,33,34,35,39,40,41,42,43,44,45,46,47,48,49],"class":[2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49]};
+[/^\[\^/, function() {  const s = this.getCurrentState(); this.pushState(s === 'u' || s === 'xu' ? 'u_class' : 'class'); return 'NEG_CLASS'  }],
+[/^\[/, function() {  const s = this.getCurrentState(); this.pushState(s === 'u' || s === 'xu' ? 'u_class' : 'class'); return 'L_BRACKET'  }]];
+const lexRulesByConditions = {"INITIAL":[9,10,11,12,13,14,15,16,17,18,21,22,23,24,25,26,29,30,31,32,33,34,35,36,40,41,42,43,44,45,46,47,48,49,50],"u":[9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,29,30,31,32,33,34,35,36,40,41,42,43,44,45,46,47,48,49,50],"xu":[0,1,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,29,30,31,32,33,34,35,36,40,41,42,43,44,45,46,47,48,49,50],"x":[0,1,9,10,11,12,13,14,15,16,17,18,21,22,23,24,25,26,29,30,31,32,33,34,35,36,40,41,42,43,44,45,46,47,48,49,50],"u_class":[2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50],"class":[2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50]};
 
 const EOF_TOKEN = {
   type: EOF,
@@ -828,8 +830,15 @@ yyparse.onParseBegin = (string, lexer) => {
   const lastSlash = string.lastIndexOf('/');
   const flags = string.slice(lastSlash);
 
-  if (flags.includes('x')) {
-    lexer.pushState('x');
+  if (flags.includes('x') && flags.includes('u')) {
+    lexer.pushState('xu');
+  } else {
+    if (flags.includes('x')) {
+      lexer.pushState('x');
+    }
+    if (flags.includes('u')) {
+      lexer.pushState('u');
+    }
   }
 };
 
@@ -875,13 +884,22 @@ function Char(value, kind, loc) {
     }
     case 'hex':
     case 'unicode': {
-      const hex = value.slice(2).replace('{', '');
-      codePoint = parseInt(hex, 16);
-      if (codePoint > 0x10ffff) {
-        throw new SyntaxError(`Bad character escape sequence: ${value}`);
-      }
+      if (value.lastIndexOf('\\u') > 0) {
+        let [lead, trail] = value.split('\\u').slice(1);
+        lead = parseInt(lead, 16);
+        trail = parseInt(trail, 16);
+        codePoint = (lead - 0xd800) * 0x400 + (trail - 0xdc00) + 0x10000;
 
-      symbol = String.fromCodePoint(codePoint);
+        symbol = String.fromCodePoint(codePoint);
+      } else {
+        const hex = value.slice(2).replace('{', '');
+        codePoint = parseInt(hex, 16);
+        if (codePoint > 0x10ffff) {
+          throw new SyntaxError(`Bad character escape sequence: ${value}`);
+        }
+
+        symbol = String.fromCodePoint(codePoint);
+      }
       break;
     }
     case 'meta': {
diff --git a/src/parser/regexp.bnf b/src/parser/regexp.bnf
index 2381b61..89b8b2c 100644
--- a/src/parser/regexp.bnf
+++ b/src/parser/regexp.bnf
@@ -19,6 +19,8 @@
 CHAR                    [^*?+\[()]
 ESC                     \\
 U                       [0-9a-fA-F]
+U_LEAD_SURROGATE        [dD][89abAB][0-9a-fA-F]{2}
+U_TRAIL_SURROGATE       [dD][c-fC-F][0-9a-fA-F]{2}
 
 GROUP_NAME              ([\w$]|\\'u'[0-9a-fA-F]{4}|\\'u{'[0-9a-fA-F]{1,}'}')+
 
@@ -35,96 +37,113 @@ GROUP_NAME              ([\w$]|\\'u'[0-9a-fA-F]{4}|\\'u{'[0-9a-fA-F]{1,}'}')+
  *   <class>'('   return 'CHAR'
  *
  * This allows making BNF grammar simpler with less productions, and states.
+ *
+ * <u_class> is the same but with unicode flag.
  */
 %s class
+%s u_class
 
 /**
  * State for "extended" regexp, enabled by `x` flag.
  *
  * In this state whitespace are ignored, and expressions can
  * use #-comments.
+ *
+ * <xu> is the same but with unicode flag.
  */
 %s x
+%s xu
+
+/**
+ * State for "unicode" regexp, enabled by `u` flag.
+ *
+ * In this state surrogate pairs are treated as codepoints and
+ * \u{} syntax is understood.
+ */
+%s u
 
 %%
 
-<x>'#'[^\n]+             /* skip comments */
-<x>\s+                   /* skip whitespace */
+<x,xu>'#'[^\n]+                      /* skip comments */
+<x,xu>\s+                            /* skip whitespace */
+
+<class,u_class>{ESC}'-'              return 'ESC_CHAR'
+<class,u_class>'-'                   return 'DASH'
+<class,u_class>'/'                   return 'CHAR'
+<class,u_class>'#'                   return 'CHAR'
+<class,u_class>'|'                   return 'CHAR'
+<class,u_class>'.'                   return 'CHAR'
 
-<class>{ESC}'-'          return 'ESC_CHAR'
-<class>'-'               return 'DASH'
-<class>'/'               return 'CHAR'
-<class>'#'               return 'CHAR'
-<class>'|'               return 'CHAR'
-<class>'.'               return 'CHAR'
+<class,u_class>'{'                   return 'CHAR'
 
-<class>'{'               return 'CHAR'
+'{'\d+'}'                            return 'RANGE_EXACT'
+'{'\d+',}'                           return 'RANGE_OPEN'
+'{'\d+','\d+'}'                      return 'RANGE_CLOSED'
 
-'{'\d+'}'                return 'RANGE_EXACT'
-'{'\d+',}'               return 'RANGE_OPEN'
-'{'\d+','\d+'}'          return 'RANGE_CLOSED'
+{ESC}'k<'{GROUP_NAME}'>'             return 'NAMED_GROUP_REF'
 
-{ESC}'k<'{GROUP_NAME}'>' return 'NAMED_GROUP_REF'
+{ESC}'b'                             return 'ESC_b'
+{ESC}'B'                             return 'ESC_B'
 
-{ESC}'b'                 return 'ESC_b'
-{ESC}'B'                 return 'ESC_B'
+{ESC}'c'[a-zA-Z]                     return 'CTRL_CH'
 
-{ESC}'c'[a-zA-Z]         return 'CTRL_CH'
+{ESC}'0'\d{1,2}                      return 'OCT_CODE'
+{ESC}'0'                             return 'DEC_CODE'
+{ESC}\d{1,3}                         return 'DEC_CODE'
 
-{ESC}'0'\d{1,2}          return 'OCT_CODE'
-{ESC}'0'                 return 'DEC_CODE'
-{ESC}\d{1,3}             return 'DEC_CODE'
+<u,xu,u_class>{ESC}'u'{U_LEAD_SURROGATE}{ESC}'u'{U_TRAIL_SURROGATE} return 'U_CODE_SURROGATE'
 
-{ESC}'u'{U}{4}           return 'U_CODE'
-{ESC}'u{'{U}{1,}'}'      return 'U_CODE'
+<u,xu,u_class>{ESC}'u{'{U}{1,}'}'   return 'U_CODE'
 
-{ESC}'x'{U}{2}           return 'HEX_CODE'
+{ESC}'u'{U}{4}                      return 'U_CODE'
 
-{ESC}[tnrdDsSwWvf]       return 'META_CHAR'
+{ESC}'x'{U}{2}                      return 'HEX_CODE'
 
-{ESC}'/'                 return 'ESC_CHAR'
+{ESC}[tnrdDsSwWvf]                  return 'META_CHAR'
 
-{ESC}{CHAR}              return 'ESC_CHAR'
-{ESC}[*?+\[()]           return 'ESC_CHAR'
+{ESC}'/'                            return 'ESC_CHAR'
 
-<class>'('               return 'CHAR'
-<class>')'               return 'CHAR'
+{ESC}{CHAR}                         return 'ESC_CHAR'
+{ESC}[*?+\[()]                      return 'ESC_CHAR'
 
-'(?='                    return 'POS_LA_ASSERT'
-'(?!'                    return 'NEG_LA_ASSERT'
+<class,u_class>'('                  return 'CHAR'
+<class,u_class>')'                  return 'CHAR'
 
-'(?<='                   return 'POS_LB_ASSERT'
-'(?<!'                   return 'NEG_LB_ASSERT'
+'(?='                               return 'POS_LA_ASSERT'
+'(?!'                               return 'NEG_LA_ASSERT'
 
-'(?:'                    return 'NON_CAPTURE_GROUP'
+'(?<='                              return 'POS_LB_ASSERT'
+'(?<!'                              return 'NEG_LB_ASSERT'
 
-'(?<'{GROUP_NAME}'>'     { yytext = yytext.slice(3, -1); return 'NAMED_CAPTURE_GROUP' }
+'(?:'                               return 'NON_CAPTURE_GROUP'
 
-'('                      return 'L_PAREN'
-')'                      return 'R_PAREN'
+'(?<'{GROUP_NAME}'>'                { yytext = yytext.slice(3, -1); return 'NAMED_CAPTURE_GROUP' }
 
-<class>[*?+[^$]          return 'CHAR'
-<class>{ESC}']'          return 'ESC_CHAR'
+'('                                 return 'L_PAREN'
+')'                                 return 'R_PAREN'
 
-<class>']'               { this.popState(); return 'R_BRACKET' }
+<class,u_class>[*?+[^$]             return 'CHAR'
+<class,u_class>{ESC}']'             return 'ESC_CHAR'
 
-'^'                      return 'BOS'
-'$'                      return 'EOS'
+<class,u_class>']'                  { this.popState(); return 'R_BRACKET' }
 
-'*'                      return 'STAR'
-'?'                      return 'Q_MARK'
-'+'                      return 'PLUS'
+'^'                                 return 'BOS'
+'$'                                 return 'EOS'
 
-'|'                      return 'BAR'
+'*'                                 return 'STAR'
+'?'                                 return 'Q_MARK'
+'+'                                 return 'PLUS'
 
-'.'                      return 'ANY'
+'|'                                 return 'BAR'
 
-'/'                      return 'SLASH'
+'.'                                 return 'ANY'
 
-{CHAR}                   return 'CHAR'
+'/'                                 return 'SLASH'
 
-'[^'                     { this.pushState('class'); return 'NEG_CLASS' }
-'['                      { this.pushState('class'); return 'L_BRACKET' }
+{CHAR}                              return 'CHAR'
+
+'[^'                                { const s = this.getCurrentState(); this.pushState(s === 'u' || s === 'xu' ? 'u_class' : 'class'); return 'NEG_CLASS' }
+'['                                 { const s = this.getCurrentState(); this.pushState(s === 'u' || s === 'xu' ? 'u_class' : 'class'); return 'L_BRACKET' }
 
 /lex
 
@@ -156,8 +175,15 @@ yyparse.onParseBegin = (string, lexer) => {
   const lastSlash = string.lastIndexOf('/');
   const flags = string.slice(lastSlash);
 
-  if (flags.includes('x')) {
-    lexer.pushState('x');
+  if (flags.includes('x') && flags.includes('u')) {
+    lexer.pushState('xu');
+  } else {
+    if (flags.includes('x')) {
+      lexer.pushState('x');
+    }
+    if (flags.includes('u')) {
+      lexer.pushState('u');
+    }
   }
 };
 
@@ -203,13 +229,22 @@ function Char(value, kind, loc) {
     }
     case 'hex':
     case 'unicode': {
-      const hex = value.slice(2).replace('{', '');
-      codePoint = parseInt(hex, 16);
-      if (codePoint > 0x10ffff) {
-        throw new SyntaxError(`Bad character escape sequence: ${value}`);
-      }
+      if (value.lastIndexOf('\\u') > 0) {
+        let [lead, trail] = value.split('\\u').slice(1);
+        lead = parseInt(lead, 16);
+        trail = parseInt(trail, 16);
+        codePoint = (lead - 0xd800) * 0x400 + (trail - 0xdc00) + 0x10000;
 
-      symbol = String.fromCodePoint(codePoint);
+        symbol = String.fromCodePoint(codePoint);
+      } else {
+        const hex = value.slice(2).replace('{', '');
+        codePoint = parseInt(hex, 16);
+        if (codePoint > 0x10ffff) {
+          throw new SyntaxError(`Bad character escape sequence: ${value}`);
+        }
+
+        symbol = String.fromCodePoint(codePoint);
+      }
       break;
     }
     case 'meta': {
@@ -595,6 +630,9 @@ SourceCharacter
   | ESC_CHAR
     { $$ = Char($1.slice(1), 'simple', @$); $$.escaped = true; }
 
+  | U_CODE_SURROGATE
+    { $$ = Char($1, 'unicode', @$); $$.isSurrogatePair = true; }
+
   | U_CODE
     { $$ = Char($1, 'unicode', @$) }
 
diff --git a/src/traverse/index.js b/src/traverse/index.js
index 95be86a..ff029ca 100644
--- a/src/traverse/index.js
+++ b/src/traverse/index.js
@@ -150,7 +150,7 @@ module.exports = {
     // Allow handlers to initializer themselves.
     handlers.forEach(handler => {
       if (typeof handler.init === 'function') {
-        handler.init(ast);
+        handler.init();
       }
     });
 


Test patch:
diff --git a/src/interpreter/finite-automaton/dfa/__tests__/dfa-minimizer-test.js b/src/interpreter/finite-automaton/dfa/__tests__/dfa-minimizer-test.js
deleted file mode 100644
index f763e89..0000000
--- a/src/interpreter/finite-automaton/dfa/__tests__/dfa-minimizer-test.js
+++ /dev/null
@@ -1,47 +0,0 @@
-/**
- * The MIT License (MIT)
- * Copyright (c) 2017-present Dmitry Soshnikov <dmitry.soshnikov@gmail.com>
- */
-
-'use strict';
-
-const DFAMinimizer = require('../dfa-minimizer');
-const fa = require('../../index');
-
-describe('dfa-minimizer', () => {
-
-  it('a|b', () => {
-    const dfa = fa.toDFA(/a|b/);
-
-    expect(dfa.getTransitionTable()).toEqual({
-      1: {a: 3, b: 2},
-      2: {},
-      3: {},
-    });
-
-    const minimized = DFAMinimizer.minimize(dfa);
-
-    expect(minimized.getTransitionTable()).toEqual({
-      1: {a: 2, b: 2},
-      2: {},
-    });
-
-    expect(minimized.getAcceptingStateNumbers()).toEqual(new Set([2]));
-  });
-
-  it('a*', () => {
-    const dfa = fa.toDFA(/a*/);
-
-    expect(dfa.getTransitionTable()).toEqual({
-      1: {a: 2},
-      2: {a: 2},
-    });
-
-    const minimized = DFAMinimizer.minimize(dfa);
-
-    expect(minimized.getTransitionTable()).toEqual({
-      1: {a: 1},
-    });
-  });
-
-});
\ No newline at end of file
diff --git a/src/interpreter/finite-automaton/dfa/__tests__/dfa-test.js b/src/interpreter/finite-automaton/dfa/__tests__/dfa-test.js
index 5c057cd..9c0bfc2 100644
--- a/src/interpreter/finite-automaton/dfa/__tests__/dfa-test.js
+++ b/src/interpreter/finite-automaton/dfa/__tests__/dfa-test.js
@@ -100,43 +100,5 @@ describe('dfa', () => {
     expect(dfa.matches('aaa')).toBe(true);
   });
 
-  it('minimizes', () => {
-    const dfa = new DFA(getDefaultNFA());
-
-    expect(dfa.getTransitionTable()).toEqual({
-      1: {'x': 3, 'y': 2},
-      2: {},
-      3: {},
-    });
-
-    dfa.minimize();
-
-    expect(dfa.getTransitionTable()).toEqual({
-      1: {'x': 2, 'y': 2},
-      2: {},
-    });
-  });
-
-  it('matches minimize', () => {
-    const dfa = new DFA(getDefaultNFA());
-    dfa.minimize();
-
-    expect(dfa.matches('x')).toBe(true);
-    expect(dfa.matches('y')).toBe(true);
-
-    expect(dfa.matches('z')).toBe(false);
-    expect(dfa.matches('')).toBe(false);
-  });
-
-  it('matches rep', () => {
-    const dfa = new DFA(rep(char('a')));
-    dfa.minimize();
-
-    expect(dfa.matches('')).toBe(true);
-    expect(dfa.matches('a')).toBe(true);
-    expect(dfa.matches('aa')).toBe(true);
-    expect(dfa.matches('aaa')).toBe(true);
-  });
-
 });
 
diff --git a/src/optimizer/__tests__/optimizer-integration-test.js b/src/optimizer/__tests__/optimizer-integration-test.js
index 7825f4a..3a97e29 100644
--- a/src/optimizer/__tests__/optimizer-integration-test.js
+++ b/src/optimizer/__tests__/optimizer-integration-test.js
@@ -81,12 +81,4 @@ describe('optimizer-integration-test', () => {
       .toBe(optimized.toString());
   });
 
-  it('applies whitelist only', () => {
-    const original = '/(?:[a])/';
-    const optimized = '/[a]/';
-
-    expect(optimizer.optimize(original, ['ungroup']).toString())
-      .toBe(optimized.toString());
-  });
-
 });
\ No newline at end of file
diff --git a/src/optimizer/transforms/__tests__/char-class-to-meta-transform-test.js b/src/optimizer/transforms/__tests__/char-class-to-meta-transform-test.js
index 268eb8c..b4e6529 100644
--- a/src/optimizer/transforms/__tests__/char-class-to-meta-transform-test.js
+++ b/src/optimizer/transforms/__tests__/char-class-to-meta-transform-test.js
@@ -24,20 +24,6 @@ describe('char-class-to-meta', () => {
     expect(re.toString()).toBe('/[\\w$]/');
   });
 
-  it('replaces word ranges when regexp has i flag', () => {
-    const re = transform(/[0-9a-z_$]/i, [
-      charClassToMeta,
-    ]);
-    expect(re.toString()).toBe('/[\\w$]/i');
-  });
-
-  it('replaces word ranges when regexp has i and u flags', () => {
-    const re = transform(/[\da-zA-Z_\u017F\u212A$]/iu, [
-      charClassToMeta,
-    ]);
-    expect(re.toString()).toBe('/[\\w$]/iu');
-  });
-
   it('whitespace ranges', () => {
     const re = transform(/[ \t\r\n\f]/, [
       charClassToMeta,
diff --git a/src/parser/__tests__/parser-basic-test.js b/src/parser/__tests__/parser-basic-test.js
index 7d90642..4a4b4e1 100644
--- a/src/parser/__tests__/parser-basic-test.js
+++ b/src/parser/__tests__/parser-basic-test.js
@@ -933,26 +933,67 @@ describe('basic', () => {
       codePoint: 0x000001d306
     });
 
-    // TODO: without `u` flag \u{1234} should be parsed NOT as
-    // a unicode code point, but as an (escaped) `u` character,
-    // repeated 1234 times.
+    // Not using `u` flag, not parsed as a unicode code point,
+    // but as an (escaped) `u` character repeated 1234 times.
+    expect(re(/\u{1234}/).body).toEqual({
+      type: 'Repetition',
+      expression: {
+        type: 'Char',
+        value: 'u',
+        symbol: 'u',
+        kind: 'simple',
+        escaped: true,
+        codePoint: 'u'.codePointAt(0)
+      },
+      quantifier: {
+        type: 'Quantifier',
+        kind: 'Range',
+        from: 1234,
+        to: 1234,
+        greedy: true
+      }
+    });
 
-    // expect(re(/\u{1234}/).body).toEqual({
-    //   type: 'Repetition',
-    //   expression: {
-    //     type: 'Char',
-    //     value: 'u',
-    //     kind: 'simple',
-    //     escaped: true
-    //   },
-    //   quantifier: {
-    //     type: 'Quantifier',
-    //     kind: 'Range',
-    //     from: 1234,
-    //     to: 1234,
-    //     greedy: true
-    //   }
-    // });
+    // Using `u` flag, surrogate pairs.
+    expect(re(/\ud83d\ude80/u).body).toEqual({
+      type: 'Char',
+      value: '\\ud83d\\ude80',
+      kind: 'unicode',
+      symbol: String.fromCodePoint(0x1F680),
+      codePoint: 0x1F680,
+      isSurrogatePair: true
+    });
+
+    // Using `u` flag, surrogate pairs in character class.
+    expect(re(/[\ud83d\ude80]/u).body).toEqual({
+      type: 'CharacterClass',
+      expressions: [{
+        type: 'Char',
+        value: '\\ud83d\\ude80',
+        kind: 'unicode',
+        symbol: String.fromCodePoint(0x1F680),
+        codePoint: 0x1F680,
+        isSurrogatePair: true
+      }]
+    });
+
+    // Not using `u` flag, surrogate pairs are treated as two characters
+    expect(re(/\ud83d\ude80/).body).toEqual({
+      type: 'Alternative',
+      expressions: [{
+        type: 'Char',
+        value: '\\ud83d',
+        kind: 'unicode',
+        symbol: String.fromCodePoint(0xd83d),
+        codePoint: 0xd83d
+      }, {
+        type: 'Char',
+        value: '\\ude80',
+        kind: 'unicode',
+        symbol: String.fromCodePoint(0xde80),
+        codePoint: 0xde80
+      }]
+    });
   });
 
   it('valid sorted flags', () => {