[
  {
    "path": ".gitignore",
    "content": "\n*.class\n*.exe\n*.pyc\n*.komodoproject\n\n.DS_STORE\nxcuserdata\n\ndart/tests/Speedtest.dart.js.deps\ndart/tests/Speedtest.dart.js.map\n"
  },
  {
    "path": "AUTHORS",
    "content": "# Below is a list of people and organizations that have contributed\n# to the Diff Match Patch project.\n\nGoogle Inc.\n\nDuncan Cross <duncan.cross@gmail.com> (Lua port)\nJan Weiß <jan@geheimwerk.de> (Objective C port)\nMatthaeus G. Chajdas <anteru@developer.shelter13.net> (C# port)\nMike Slemmer <mikeslemmer@gmail.com> (C++ port)\n\n"
  },
  {
    "path": "CONTRIBUTING.md",
    "content": "# How to Contribute\n\nWe'd love to accept your patches and contributions to this project. There are\njust a few small guidelines you need to follow.\n\n## Contributor License Agreement\n\nContributions to this project must be accompanied by a Contributor License\nAgreement. You (or your employer) retain the copyright to your contribution,\nthis simply gives us permission to use and redistribute your contributions as\npart of the project. Head over to <https://cla.developers.google.com/> to see\nyour current agreements on file or to sign a new one.\n\nYou generally only need to submit a CLA once, so if you've already submitted one\n(even if it was for a different project), you probably don't need to do it\nagain.\n\n## Code reviews\n\nAll submissions, including submissions by project members, require review. We\nuse GitHub pull requests for this purpose. Consult\n[GitHub Help](https://help.github.com/articles/about-pull-requests/) for more\ninformation on using pull requests.\n"
  },
  {
    "path": "LICENSE",
    "content": "\n                                 Apache License\n                           Version 2.0, January 2004\n                        http://www.apache.org/licenses/\n\n   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION\n\n   1. Definitions.\n\n      \"License\" shall mean the terms and conditions for use, reproduction,\n      and distribution as defined by Sections 1 through 9 of this document.\n\n      \"Licensor\" shall mean the copyright owner or entity authorized by\n      the copyright owner that is granting the License.\n\n      \"Legal Entity\" shall mean the union of the acting entity and all\n      other entities that control, are controlled by, or are under common\n      control with that entity. For the purposes of this definition,\n      \"control\" means (i) the power, direct or indirect, to cause the\n      direction or management of such entity, whether by contract or\n      otherwise, or (ii) ownership of fifty percent (50%) or more of the\n      outstanding shares, or (iii) beneficial ownership of such entity.\n\n      \"You\" (or \"Your\") shall mean an individual or Legal Entity\n      exercising permissions granted by this License.\n\n      \"Source\" form shall mean the preferred form for making modifications,\n      including but not limited to software source code, documentation\n      source, and configuration files.\n\n      \"Object\" form shall mean any form resulting from mechanical\n      transformation or translation of a Source form, including but\n      not limited to compiled object code, generated documentation,\n      and conversions to other media types.\n\n      \"Work\" shall mean the work of authorship, whether in Source or\n      Object form, made available under the License, as indicated by a\n      copyright notice that is included in or attached to the work\n      (an example is provided in the Appendix below).\n\n      \"Derivative Works\" shall mean any work, whether in Source or Object\n      form, that is based on (or derived from) the Work and for which the\n      editorial revisions, annotations, elaborations, or other modifications\n      represent, as a whole, an original work of authorship. For the purposes\n      of this License, Derivative Works shall not include works that remain\n      separable from, or merely link (or bind by name) to the interfaces of,\n      the Work and Derivative Works thereof.\n\n      \"Contribution\" shall mean any work of authorship, including\n      the original version of the Work and any modifications or additions\n      to that Work or Derivative Works thereof, that is intentionally\n      submitted to Licensor for inclusion in the Work by the copyright owner\n      or by an individual or Legal Entity authorized to submit on behalf of\n      the copyright owner. For the purposes of this definition, \"submitted\"\n      means any form of electronic, verbal, or written communication sent\n      to the Licensor or its representatives, including but not limited to\n      communication on electronic mailing lists, source code control systems,\n      and issue tracking systems that are managed by, or on behalf of, the\n      Licensor for the purpose of discussing and improving the Work, but\n      excluding communication that is conspicuously marked or otherwise\n      designated in writing by the copyright owner as \"Not a Contribution.\"\n\n      \"Contributor\" shall mean Licensor and any individual or Legal Entity\n      on behalf of whom a Contribution has been received by Licensor and\n      subsequently incorporated within the Work.\n\n   2. Grant of Copyright License. Subject to the terms and conditions of\n      this License, each Contributor hereby grants to You a perpetual,\n      worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n      copyright license to reproduce, prepare Derivative Works of,\n      publicly display, publicly perform, sublicense, and distribute the\n      Work and such Derivative Works in Source or Object form.\n\n   3. Grant of Patent License. Subject to the terms and conditions of\n      this License, each Contributor hereby grants to You a perpetual,\n      worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n      (except as stated in this section) patent license to make, have made,\n      use, offer to sell, sell, import, and otherwise transfer the Work,\n      where such license applies only to those patent claims licensable\n      by such Contributor that are necessarily infringed by their\n      Contribution(s) alone or by combination of their Contribution(s)\n      with the Work to which such Contribution(s) was submitted. If You\n      institute patent litigation against any entity (including a\n      cross-claim or counterclaim in a lawsuit) alleging that the Work\n      or a Contribution incorporated within the Work constitutes direct\n      or contributory patent infringement, then any patent licenses\n      granted to You under this License for that Work shall terminate\n      as of the date such litigation is filed.\n\n   4. Redistribution. You may reproduce and distribute copies of the\n      Work or Derivative Works thereof in any medium, with or without\n      modifications, and in Source or Object form, provided that You\n      meet the following conditions:\n\n      (a) You must give any other recipients of the Work or\n          Derivative Works a copy of this License; and\n\n      (b) You must cause any modified files to carry prominent notices\n          stating that You changed the files; and\n\n      (c) You must retain, in the Source form of any Derivative Works\n          that You distribute, all copyright, patent, trademark, and\n          attribution notices from the Source form of the Work,\n          excluding those notices that do not pertain to any part of\n          the Derivative Works; and\n\n      (d) If the Work includes a \"NOTICE\" text file as part of its\n          distribution, then any Derivative Works that You distribute must\n          include a readable copy of the attribution notices contained\n          within such NOTICE file, excluding those notices that do not\n          pertain to any part of the Derivative Works, in at least one\n          of the following places: within a NOTICE text file distributed\n          as part of the Derivative Works; within the Source form or\n          documentation, if provided along with the Derivative Works; or,\n          within a display generated by the Derivative Works, if and\n          wherever such third-party notices normally appear. The contents\n          of the NOTICE file are for informational purposes only and\n          do not modify the License. You may add Your own attribution\n          notices within Derivative Works that You distribute, alongside\n          or as an addendum to the NOTICE text from the Work, provided\n          that such additional attribution notices cannot be construed\n          as modifying the License.\n\n      You may add Your own copyright statement to Your modifications and\n      may provide additional or different license terms and conditions\n      for use, reproduction, or distribution of Your modifications, or\n      for any such Derivative Works as a whole, provided Your use,\n      reproduction, and distribution of the Work otherwise complies with\n      the conditions stated in this License.\n\n   5. Submission of Contributions. Unless You explicitly state otherwise,\n      any Contribution intentionally submitted for inclusion in the Work\n      by You to the Licensor shall be under the terms and conditions of\n      this License, without any additional terms or conditions.\n      Notwithstanding the above, nothing herein shall supersede or modify\n      the terms of any separate license agreement you may have executed\n      with Licensor regarding such Contributions.\n\n   6. Trademarks. This License does not grant permission to use the trade\n      names, trademarks, service marks, or product names of the Licensor,\n      except as required for reasonable and customary use in describing the\n      origin of the Work and reproducing the content of the NOTICE file.\n\n   7. Disclaimer of Warranty. Unless required by applicable law or\n      agreed to in writing, Licensor provides the Work (and each\n      Contributor provides its Contributions) on an \"AS IS\" BASIS,\n      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or\n      implied, including, without limitation, any warranties or conditions\n      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A\n      PARTICULAR PURPOSE. You are solely responsible for determining the\n      appropriateness of using or redistributing the Work and assume any\n      risks associated with Your exercise of permissions under this License.\n\n   8. Limitation of Liability. In no event and under no legal theory,\n      whether in tort (including negligence), contract, or otherwise,\n      unless required by applicable law (such as deliberate and grossly\n      negligent acts) or agreed to in writing, shall any Contributor be\n      liable to You for damages, including any direct, indirect, special,\n      incidental, or consequential damages of any character arising as a\n      result of this License or out of the use or inability to use the\n      Work (including but not limited to damages for loss of goodwill,\n      work stoppage, computer failure or malfunction, or any and all\n      other commercial damages or losses), even if such Contributor\n      has been advised of the possibility of such damages.\n\n   9. Accepting Warranty or Additional Liability. While redistributing\n      the Work or Derivative Works thereof, You may choose to offer,\n      and charge a fee for, acceptance of support, warranty, indemnity,\n      or other liability obligations and/or rights consistent with this\n      License. However, in accepting such obligations, You may act only\n      on Your own behalf and on Your sole responsibility, not on behalf\n      of any other Contributor, and only if You agree to indemnify,\n      defend, and hold each Contributor harmless for any liability\n      incurred by, or claims asserted against, such Contributor by reason\n      of your accepting any such warranty or additional liability.\n\n   END OF TERMS AND CONDITIONS\n\n   APPENDIX: How to apply the Apache License to your work.\n\n      To apply the Apache License to your work, attach the following\n      boilerplate notice, with the fields enclosed by brackets \"[]\"\n      replaced with your own identifying information. (Don't include\n      the brackets!)  The text should be enclosed in the appropriate\n      comment syntax for the file format. We also recommend that a\n      file or class name and description of purpose be included on the\n      same \"printed page\" as the copyright notice for easier\n      identification within third-party archives.\n\n   Copyright [yyyy] [name of copyright owner]\n\n   Licensed under the Apache License, Version 2.0 (the \"License\");\n   you may not use this file except in compliance with the License.\n   You may obtain a copy of the License at\n\n       http://www.apache.org/licenses/LICENSE-2.0\n\n   Unless required by applicable law or agreed to in writing, software\n   distributed under the License is distributed on an \"AS IS\" BASIS,\n   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n   See the License for the specific language governing permissions and\n   limitations under the License.\n"
  },
  {
    "path": "README.md",
    "content": "The Diff Match and Patch libraries offer robust algorithms to perform the\noperations required for synchronizing plain text.\n\n1. Diff:\n   * Compare two blocks of plain text and efficiently return a list of differences.\n   * [Diff Demo](https://neil.fraser.name/software/diff_match_patch/demos/diff.html)\n2. Match:\n   * Given a search string, find its best fuzzy match in a block of plain text. Weighted for both accuracy and location.\n   * [Match Demo](https://neil.fraser.name/software/diff_match_patch/demos/match.html)\n3. Patch:\n   * Apply a list of patches onto plain text. Use best-effort to apply patch even when the underlying text doesn't match.\n   * [Patch Demo](https://neil.fraser.name/software/diff_match_patch/demos/patch.html)\n\nOriginally built in 2006 to power Google Docs, this library is now available in C++, C#, Dart, Java, JavaScript, Lua, Objective C, and Python.\n\n### Reference\n\n* [API](https://github.com/google/diff-match-patch/wiki/API) - Common API across all languages.\n* [Line or Word Diffs](https://github.com/google/diff-match-patch/wiki/Line-or-Word-Diffs) - Less detailed diffs.\n* [Plain Text vs. Structured Content](https://github.com/google/diff-match-patch/wiki/Plain-Text-vs.-Structured-Content) - How to deal with data like XML.\n* [Unidiff](https://github.com/google/diff-match-patch/wiki/Unidiff) - The patch serialization format.\n* [Support](https://groups.google.com/forum/#!forum/diff-match-patch) - Newsgroup for developers.\n\n### Languages\nAlthough each language port of Diff Match Patch uses the same API, there are some language-specific notes.\n\n* [C++](https://github.com/google/diff-match-patch/wiki/Language:-Cpp)\n* [C#](https://github.com/google/diff-match-patch/wiki/Language:-C%23)\n* [Dart](https://github.com/google/diff-match-patch/wiki/Language:-Dart)\n* [Java](https://github.com/google/diff-match-patch/wiki/Language:-Java)\n* [JavaScript](https://github.com/google/diff-match-patch/wiki/Language:-JavaScript)\n* [Lua](https://github.com/google/diff-match-patch/wiki/Language:-Lua)\n* [Objective-C](https://github.com/google/diff-match-patch/wiki/Language:-Objective-C)\n* [Python](https://github.com/google/diff-match-patch/wiki/Language:-Python)\n\nA standardized speed test tracks the [relative performance of diffs](https://docs.google.com/spreadsheets/d/1zpZccuBpjMZTvL1nGDMKJc7rWL_m_drF4XKOJvB27Kc/edit#gid=0) in each language.\n\n### Algorithms\nThis library implements [Myer's diff algorithm](https://neil.fraser.name/writing/diff/myers.pdf) which is generally considered to be the best general-purpose diff. A layer of [pre-diff speedups and post-diff cleanups](https://neil.fraser.name/writing/diff/) surround the diff algorithm, improving both performance and output quality.\n\nThis library also implements a [Bitap matching algorithm](https://neil.fraser.name/writing/patch/bitap.ps) at the heart of a [flexible matching and patching strategy](https://neil.fraser.name/writing/patch/).\n"
  },
  {
    "path": "cpp/diff_match_patch.cpp",
    "content": "/*\n * Diff Match and Patch\n * Copyright 2018 The diff-match-patch Authors.\n * https://github.com/google/diff-match-patch\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *   http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <algorithm>\n#include <limits>\n// Code known to compile and run with Qt 4.3 through Qt 4.7.\n#include <QtCore>\n#include <time.h>\n#include \"diff_match_patch.h\"\n\n\n//////////////////////////\n//\n// Diff Class\n//\n//////////////////////////\n\n\n/**\n * Constructor.  Initializes the diff with the provided values.\n * @param operation One of INSERT, DELETE or EQUAL\n * @param text The text being applied\n */\nDiff::Diff(Operation _operation, const QString &_text) :\n  operation(_operation), text(_text) {\n  // Construct a diff with the specified operation and text.\n}\n\nDiff::Diff() {\n}\n\n\nQString Diff::strOperation(Operation op) {\n  switch (op) {\n    case INSERT:\n      return \"INSERT\";\n    case DELETE:\n      return \"DELETE\";\n    case EQUAL:\n      return \"EQUAL\";\n  }\n  throw \"Invalid operation.\";\n}\n\n/**\n * Display a human-readable version of this Diff.\n * @return text version\n */\nQString Diff::toString() const {\n  QString prettyText = text;\n  // Replace linebreaks with Pilcrow signs.\n  prettyText.replace('\\n', L'\\u00b6');\n  return QString(\"Diff(\") + strOperation(operation) + QString(\",\\\"\")\n      + prettyText + QString(\"\\\")\");\n}\n\n/**\n * Is this Diff equivalent to another Diff?\n * @param d Another Diff to compare against\n * @return true or false\n */\nbool Diff::operator==(const Diff &d) const {\n  return (d.operation == this->operation) && (d.text == this->text);\n}\n\nbool Diff::operator!=(const Diff &d) const {\n  return !(operator == (d));\n}\n\n\n/////////////////////////////////////////////\n//\n// Patch Class\n//\n/////////////////////////////////////////////\n\n\n/**\n * Constructor.  Initializes with an empty list of diffs.\n */\nPatch::Patch() :\n  start1(0), start2(0),\n  length1(0), length2(0) {\n}\n\nbool Patch::isNull() const {\n  if (start1 == 0 && start2 == 0 && length1 == 0 && length2 == 0\n      && diffs.size() == 0) {\n    return true;\n  }\n  return false;\n}\n\n\n/**\n * Emulate GNU diff's format.\n * Header: @@ -382,8 +481,9 @@\n * Indices are printed as 1-based, not 0-based.\n * @return The GNU diff string\n */\nQString Patch::toString() {\n  QString coords1, coords2;\n  if (length1 == 0) {\n    coords1 = QString::number(start1) + QString(\",0\");\n  } else if (length1 == 1) {\n    coords1 = QString::number(start1 + 1);\n  } else {\n    coords1 = QString::number(start1 + 1) + QString(\",\")\n        + QString::number(length1);\n  }\n  if (length2 == 0) {\n    coords2 = QString::number(start2) + QString(\",0\");\n  } else if (length2 == 1) {\n    coords2 = QString::number(start2 + 1);\n  } else {\n    coords2 = QString::number(start2 + 1) + QString(\",\")\n        + QString::number(length2);\n  }\n  QString text;\n  text = QString(\"@@ -\") + coords1 + QString(\" +\") + coords2\n      + QString(\" @@\\n\");\n  // Escape the body of the patch with %xx notation.\n  foreach (Diff aDiff, diffs) {\n    switch (aDiff.operation) {\n      case INSERT:\n        text += QString('+');\n        break;\n      case DELETE:\n        text += QString('-');\n        break;\n      case EQUAL:\n        text += QString(' ');\n        break;\n    }\n    text += QString(QUrl::toPercentEncoding(aDiff.text, \" !~*'();/?:@&=+$,#\"))\n        + QString(\"\\n\");\n  }\n\n  return text;\n}\n\n\n/////////////////////////////////////////////\n//\n// diff_match_patch Class\n//\n/////////////////////////////////////////////\n\ndiff_match_patch::diff_match_patch() :\n  Diff_Timeout(1.0f),\n  Diff_EditCost(4),\n  Match_Threshold(0.5f),\n  Match_Distance(1000),\n  Patch_DeleteThreshold(0.5f),\n  Patch_Margin(4),\n  Match_MaxBits(32) {\n}\n\n\nQList<Diff> diff_match_patch::diff_main(const QString &text1,\n                                        const QString &text2) {\n  return diff_main(text1, text2, true);\n}\n\nQList<Diff> diff_match_patch::diff_main(const QString &text1,\n    const QString &text2, bool checklines) {\n  // Set a deadline by which time the diff must be complete.\n  clock_t deadline;\n  if (Diff_Timeout <= 0) {\n    deadline = std::numeric_limits<clock_t>::max();\n  } else {\n    deadline = clock() + (clock_t)(Diff_Timeout * CLOCKS_PER_SEC);\n  }\n  return diff_main(text1, text2, checklines, deadline);\n}\n\nQList<Diff> diff_match_patch::diff_main(const QString &text1,\n    const QString &text2, bool checklines, clock_t deadline) {\n  // Check for null inputs.\n  if (text1.isNull() || text2.isNull()) {\n    throw \"Null inputs. (diff_main)\";\n  }\n\n  // Check for equality (speedup).\n  QList<Diff> diffs;\n  if (text1 == text2) {\n    if (!text1.isEmpty()) {\n      diffs.append(Diff(EQUAL, text1));\n    }\n    return diffs;\n  }\n\n  // Trim off common prefix (speedup).\n  int commonlength = diff_commonPrefix(text1, text2);\n  const QString &commonprefix = text1.left(commonlength);\n  QString textChopped1 = text1.mid(commonlength);\n  QString textChopped2 = text2.mid(commonlength);\n\n  // Trim off common suffix (speedup).\n  commonlength = diff_commonSuffix(textChopped1, textChopped2);\n  const QString &commonsuffix = textChopped1.right(commonlength);\n  textChopped1 = textChopped1.left(textChopped1.length() - commonlength);\n  textChopped2 = textChopped2.left(textChopped2.length() - commonlength);\n\n  // Compute the diff on the middle block.\n  diffs = diff_compute(textChopped1, textChopped2, checklines, deadline);\n\n  // Restore the prefix and suffix.\n  if (!commonprefix.isEmpty()) {\n    diffs.prepend(Diff(EQUAL, commonprefix));\n  }\n  if (!commonsuffix.isEmpty()) {\n    diffs.append(Diff(EQUAL, commonsuffix));\n  }\n\n  diff_cleanupMerge(diffs);\n\n  return diffs;\n}\n\n\nQList<Diff> diff_match_patch::diff_compute(QString text1, QString text2,\n    bool checklines, clock_t deadline) {\n  QList<Diff> diffs;\n\n  if (text1.isEmpty()) {\n    // Just add some text (speedup).\n    diffs.append(Diff(INSERT, text2));\n    return diffs;\n  }\n\n  if (text2.isEmpty()) {\n    // Just delete some text (speedup).\n    diffs.append(Diff(DELETE, text1));\n    return diffs;\n  }\n\n  {\n    const QString longtext = text1.length() > text2.length() ? text1 : text2;\n    const QString shorttext = text1.length() > text2.length() ? text2 : text1;\n    const int i = longtext.indexOf(shorttext);\n    if (i != -1) {\n      // Shorter text is inside the longer text (speedup).\n      const Operation op = (text1.length() > text2.length()) ? DELETE : INSERT;\n      diffs.append(Diff(op, longtext.left(i)));\n      diffs.append(Diff(EQUAL, shorttext));\n      diffs.append(Diff(op, safeMid(longtext, i + shorttext.length())));\n      return diffs;\n    }\n\n    if (shorttext.length() == 1) {\n      // Single character string.\n      // After the previous speedup, the character can't be an equality.\n      diffs.append(Diff(DELETE, text1));\n      diffs.append(Diff(INSERT, text2));\n      return diffs;\n    }\n    // Garbage collect longtext and shorttext by scoping out.\n  }\n\n  // Check to see if the problem can be split in two.\n  const QStringList hm = diff_halfMatch(text1, text2);\n  if (hm.count() > 0) {\n    // A half-match was found, sort out the return data.\n    const QString text1_a = hm[0];\n    const QString text1_b = hm[1];\n    const QString text2_a = hm[2];\n    const QString text2_b = hm[3];\n    const QString mid_common = hm[4];\n    // Send both pairs off for separate processing.\n    const QList<Diff> diffs_a = diff_main(text1_a, text2_a,\n                                          checklines, deadline);\n    const QList<Diff> diffs_b = diff_main(text1_b, text2_b,\n                                          checklines, deadline);\n    // Merge the results.\n    diffs = diffs_a;\n    diffs.append(Diff(EQUAL, mid_common));\n    diffs += diffs_b;\n    return diffs;\n  }\n\n  // Perform a real diff.\n  if (checklines && text1.length() > 100 && text2.length() > 100) {\n    return diff_lineMode(text1, text2, deadline);\n  }\n\n  return diff_bisect(text1, text2, deadline);\n}\n\n\nQList<Diff> diff_match_patch::diff_lineMode(QString text1, QString text2,\n    clock_t deadline) {\n  // Scan the text on a line-by-line basis first.\n  const QList<QVariant> b = diff_linesToChars(text1, text2);\n  text1 = b[0].toString();\n  text2 = b[1].toString();\n  QStringList linearray = b[2].toStringList();\n\n  QList<Diff> diffs = diff_main(text1, text2, false, deadline);\n\n  // Convert the diff back to original text.\n  diff_charsToLines(diffs, linearray);\n  // Eliminate freak matches (e.g. blank lines)\n  diff_cleanupSemantic(diffs);\n\n  // Rediff any replacement blocks, this time character-by-character.\n  // Add a dummy entry at the end.\n  diffs.append(Diff(EQUAL, \"\"));\n  int count_delete = 0;\n  int count_insert = 0;\n  QString text_delete = \"\";\n  QString text_insert = \"\";\n\n  QMutableListIterator<Diff> pointer(diffs);\n  Diff *thisDiff = pointer.hasNext() ? &pointer.next() : NULL;\n  while (thisDiff != NULL) {\n    switch (thisDiff->operation) {\n      case INSERT:\n        count_insert++;\n        text_insert += thisDiff->text;\n        break;\n      case DELETE:\n        count_delete++;\n        text_delete += thisDiff->text;\n        break;\n      case EQUAL:\n        // Upon reaching an equality, check for prior redundancies.\n        if (count_delete >= 1 && count_insert >= 1) {\n          // Delete the offending records and add the merged ones.\n          pointer.previous();\n          for (int j = 0; j < count_delete + count_insert; j++) {\n            pointer.previous();\n            pointer.remove();\n          }\n          foreach(Diff newDiff,\n              diff_main(text_delete, text_insert, false, deadline)) {\n            pointer.insert(newDiff);\n          }\n        }\n        count_insert = 0;\n        count_delete = 0;\n        text_delete = \"\";\n        text_insert = \"\";\n        break;\n    }\n    thisDiff = pointer.hasNext() ? &pointer.next() : NULL;\n  }\n  diffs.removeLast();  // Remove the dummy entry at the end.\n\n  return diffs;\n}\n\n\nQList<Diff> diff_match_patch::diff_bisect(const QString &text1,\n    const QString &text2, clock_t deadline) {\n  // Cache the text lengths to prevent multiple calls.\n  const int text1_length = text1.length();\n  const int text2_length = text2.length();\n  const int max_d = (text1_length + text2_length + 1) / 2;\n  const int v_offset = max_d;\n  const int v_length = 2 * max_d;\n  int *v1 = new int[v_length];\n  int *v2 = new int[v_length];\n  for (int x = 0; x < v_length; x++) {\n    v1[x] = -1;\n    v2[x] = -1;\n  }\n  v1[v_offset + 1] = 0;\n  v2[v_offset + 1] = 0;\n  const int delta = text1_length - text2_length;\n  // If the total number of characters is odd, then the front path will\n  // collide with the reverse path.\n  const bool front = (delta % 2 != 0);\n  // Offsets for start and end of k loop.\n  // Prevents mapping of space beyond the grid.\n  int k1start = 0;\n  int k1end = 0;\n  int k2start = 0;\n  int k2end = 0;\n  for (int d = 0; d < max_d; d++) {\n    // Bail out if deadline is reached.\n    if (clock() > deadline) {\n      break;\n    }\n\n    // Walk the front path one step.\n    for (int k1 = -d + k1start; k1 <= d - k1end; k1 += 2) {\n      const int k1_offset = v_offset + k1;\n      int x1;\n      if (k1 == -d || (k1 != d && v1[k1_offset - 1] < v1[k1_offset + 1])) {\n        x1 = v1[k1_offset + 1];\n      } else {\n        x1 = v1[k1_offset - 1] + 1;\n      }\n      int y1 = x1 - k1;\n      while (x1 < text1_length && y1 < text2_length\n          && text1[x1] == text2[y1]) {\n        x1++;\n        y1++;\n      }\n      v1[k1_offset] = x1;\n      if (x1 > text1_length) {\n        // Ran off the right of the graph.\n        k1end += 2;\n      } else if (y1 > text2_length) {\n        // Ran off the bottom of the graph.\n        k1start += 2;\n      } else if (front) {\n        int k2_offset = v_offset + delta - k1;\n        if (k2_offset >= 0 && k2_offset < v_length && v2[k2_offset] != -1) {\n          // Mirror x2 onto top-left coordinate system.\n          int x2 = text1_length - v2[k2_offset];\n          if (x1 >= x2) {\n            // Overlap detected.\n            delete [] v1;\n            delete [] v2;\n            return diff_bisectSplit(text1, text2, x1, y1, deadline);\n          }\n        }\n      }\n    }\n\n    // Walk the reverse path one step.\n    for (int k2 = -d + k2start; k2 <= d - k2end; k2 += 2) {\n      const int k2_offset = v_offset + k2;\n      int x2;\n      if (k2 == -d || (k2 != d && v2[k2_offset - 1] < v2[k2_offset + 1])) {\n        x2 = v2[k2_offset + 1];\n      } else {\n        x2 = v2[k2_offset - 1] + 1;\n      }\n      int y2 = x2 - k2;\n      while (x2 < text1_length && y2 < text2_length\n          && text1[text1_length - x2 - 1] == text2[text2_length - y2 - 1]) {\n        x2++;\n        y2++;\n      }\n      v2[k2_offset] = x2;\n      if (x2 > text1_length) {\n        // Ran off the left of the graph.\n        k2end += 2;\n      } else if (y2 > text2_length) {\n        // Ran off the top of the graph.\n        k2start += 2;\n      } else if (!front) {\n        int k1_offset = v_offset + delta - k2;\n        if (k1_offset >= 0 && k1_offset < v_length && v1[k1_offset] != -1) {\n          int x1 = v1[k1_offset];\n          int y1 = v_offset + x1 - k1_offset;\n          // Mirror x2 onto top-left coordinate system.\n          x2 = text1_length - x2;\n          if (x1 >= x2) {\n            // Overlap detected.\n            delete [] v1;\n            delete [] v2;\n            return diff_bisectSplit(text1, text2, x1, y1, deadline);\n          }\n        }\n      }\n    }\n  }\n  delete [] v1;\n  delete [] v2;\n  // Diff took too long and hit the deadline or\n  // number of diffs equals number of characters, no commonality at all.\n  QList<Diff> diffs;\n  diffs.append(Diff(DELETE, text1));\n  diffs.append(Diff(INSERT, text2));\n  return diffs;\n}\n\nQList<Diff> diff_match_patch::diff_bisectSplit(const QString &text1,\n    const QString &text2, int x, int y, clock_t deadline) {\n  QString text1a = text1.left(x);\n  QString text2a = text2.left(y);\n  QString text1b = safeMid(text1, x);\n  QString text2b = safeMid(text2, y);\n\n  // Compute both diffs serially.\n  QList<Diff> diffs = diff_main(text1a, text2a, false, deadline);\n  QList<Diff> diffsb = diff_main(text1b, text2b, false, deadline);\n\n  return diffs + diffsb;\n}\n\nQList<QVariant> diff_match_patch::diff_linesToChars(const QString &text1,\n                                                    const QString &text2) {\n  QStringList lineArray;\n  QMap<QString, int> lineHash;\n  // e.g. linearray[4] == \"Hello\\n\"\n  // e.g. linehash.get(\"Hello\\n\") == 4\n\n  // \"\\x00\" is a valid character, but various debuggers don't like it.\n  // So we'll insert a junk entry to avoid generating a null character.\n  lineArray.append(\"\");\n\n  const QString chars1 = diff_linesToCharsMunge(text1, lineArray, lineHash);\n  const QString chars2 = diff_linesToCharsMunge(text2, lineArray, lineHash);\n\n  QList<QVariant> listRet;\n  listRet.append(QVariant::fromValue(chars1));\n  listRet.append(QVariant::fromValue(chars2));\n  listRet.append(QVariant::fromValue(lineArray));\n  return listRet;\n}\n\n\nQString diff_match_patch::diff_linesToCharsMunge(const QString &text,\n                                                 QStringList &lineArray,\n                                                 QMap<QString, int> &lineHash) {\n  int lineStart = 0;\n  int lineEnd = -1;\n  QString line;\n  QString chars;\n  // Walk the text, pulling out a substring for each line.\n  // text.split('\\n') would would temporarily double our memory footprint.\n  // Modifying text would create many large strings to garbage collect.\n  while (lineEnd < text.length() - 1) {\n    lineEnd = text.indexOf('\\n', lineStart);\n    if (lineEnd == -1) {\n      lineEnd = text.length() - 1;\n    }\n    line = safeMid(text, lineStart, lineEnd + 1 - lineStart);\n    lineStart = lineEnd + 1;\n\n    if (lineHash.contains(line)) {\n      chars += QChar(static_cast<ushort>(lineHash.value(line)));\n    } else {\n      lineArray.append(line);\n      lineHash.insert(line, lineArray.size() - 1);\n      chars += QChar(static_cast<ushort>(lineArray.size() - 1));\n    }\n  }\n  return chars;\n}\n\n\n\nvoid diff_match_patch::diff_charsToLines(QList<Diff> &diffs,\n                                         const QStringList &lineArray) {\n  // Qt has no mutable foreach construct.\n  QMutableListIterator<Diff> i(diffs);\n  while (i.hasNext()) {\n    Diff &diff = i.next();\n    QString text;\n    for (int y = 0; y < diff.text.length(); y++) {\n      text += lineArray.value(static_cast<ushort>(diff.text[y].unicode()));\n    }\n    diff.text = text;\n  }\n}\n\n\nint diff_match_patch::diff_commonPrefix(const QString &text1,\n                                        const QString &text2) {\n  // Performance analysis: http://neil.fraser.name/news/2007/10/09/\n  const int n = std::min(text1.length(), text2.length());\n  for (int i = 0; i < n; i++) {\n    if (text1[i] != text2[i]) {\n      return i;\n    }\n  }\n  return n;\n}\n\n\nint diff_match_patch::diff_commonSuffix(const QString &text1,\n                                        const QString &text2) {\n  // Performance analysis: http://neil.fraser.name/news/2007/10/09/\n  const int text1_length = text1.length();\n  const int text2_length = text2.length();\n  const int n = std::min(text1_length, text2_length);\n  for (int i = 1; i <= n; i++) {\n    if (text1[text1_length - i] != text2[text2_length - i]) {\n      return i - 1;\n    }\n  }\n  return n;\n}\n\nint diff_match_patch::diff_commonOverlap(const QString &text1,\n                                         const QString &text2) {\n  // Cache the text lengths to prevent multiple calls.\n  const int text1_length = text1.length();\n  const int text2_length = text2.length();\n  // Eliminate the null case.\n  if (text1_length == 0 || text2_length == 0) {\n    return 0;\n  }\n  // Truncate the longer string.\n  QString text1_trunc = text1;\n  QString text2_trunc = text2;\n  if (text1_length > text2_length) {\n    text1_trunc = text1.right(text2_length);\n  } else if (text1_length < text2_length) {\n    text2_trunc = text2.left(text1_length);\n  }\n  const int text_length = std::min(text1_length, text2_length);\n  // Quick check for the worst case.\n  if (text1_trunc == text2_trunc) {\n    return text_length;\n  }\n\n  // Start by looking for a single character match\n  // and increase length until no match is found.\n  // Performance analysis: http://neil.fraser.name/news/2010/11/04/\n  int best = 0;\n  int length = 1;\n  while (true) {\n    QString pattern = text1_trunc.right(length);\n    int found = text2_trunc.indexOf(pattern);\n    if (found == -1) {\n      return best;\n    }\n    length += found;\n    if (found == 0 || text1_trunc.right(length) == text2_trunc.left(length)) {\n      best = length;\n      length++;\n    }\n  }\n}\n\nQStringList diff_match_patch::diff_halfMatch(const QString &text1,\n                                             const QString &text2) {\n  if (Diff_Timeout <= 0) {\n    // Don't risk returning a non-optimal diff if we have unlimited time.\n    return QStringList();\n  }\n  const QString longtext = text1.length() > text2.length() ? text1 : text2;\n  const QString shorttext = text1.length() > text2.length() ? text2 : text1;\n  if (longtext.length() < 4 || shorttext.length() * 2 < longtext.length()) {\n    return QStringList();  // Pointless.\n  }\n\n  // First check if the second quarter is the seed for a half-match.\n  const QStringList hm1 = diff_halfMatchI(longtext, shorttext,\n      (longtext.length() + 3) / 4);\n  // Check again based on the third quarter.\n  const QStringList hm2 = diff_halfMatchI(longtext, shorttext,\n      (longtext.length() + 1) / 2);\n  QStringList hm;\n  if (hm1.isEmpty() && hm2.isEmpty()) {\n    return QStringList();\n  } else if (hm2.isEmpty()) {\n    hm = hm1;\n  } else if (hm1.isEmpty()) {\n    hm = hm2;\n  } else {\n    // Both matched.  Select the longest.\n    hm = hm1[4].length() > hm2[4].length() ? hm1 : hm2;\n  }\n\n  // A half-match was found, sort out the return data.\n  if (text1.length() > text2.length()) {\n    return hm;\n  } else {\n    QStringList listRet;\n    listRet << hm[2] << hm[3] << hm[0] << hm[1] << hm[4];\n    return listRet;\n  }\n}\n\n\nQStringList diff_match_patch::diff_halfMatchI(const QString &longtext,\n                                              const QString &shorttext,\n                                              int i) {\n  // Start with a 1/4 length substring at position i as a seed.\n  const QString seed = safeMid(longtext, i, longtext.length() / 4);\n  int j = -1;\n  QString best_common;\n  QString best_longtext_a, best_longtext_b;\n  QString best_shorttext_a, best_shorttext_b;\n  while ((j = shorttext.indexOf(seed, j + 1)) != -1) {\n    const int prefixLength = diff_commonPrefix(safeMid(longtext, i),\n        safeMid(shorttext, j));\n    const int suffixLength = diff_commonSuffix(longtext.left(i),\n        shorttext.left(j));\n    if (best_common.length() < suffixLength + prefixLength) {\n      best_common = safeMid(shorttext, j - suffixLength, suffixLength)\n          + safeMid(shorttext, j, prefixLength);\n      best_longtext_a = longtext.left(i - suffixLength);\n      best_longtext_b = safeMid(longtext, i + prefixLength);\n      best_shorttext_a = shorttext.left(j - suffixLength);\n      best_shorttext_b = safeMid(shorttext, j + prefixLength);\n    }\n  }\n  if (best_common.length() * 2 >= longtext.length()) {\n    QStringList listRet;\n    listRet << best_longtext_a << best_longtext_b << best_shorttext_a\n        << best_shorttext_b << best_common;\n    return listRet;\n  } else {\n    return QStringList();\n  }\n}\n\n\nvoid diff_match_patch::diff_cleanupSemantic(QList<Diff> &diffs) {\n  if (diffs.isEmpty()) {\n    return;\n  }\n  bool changes = false;\n  QStack<Diff> equalities;  // Stack of equalities.\n  QString lastequality;  // Always equal to equalities.lastElement().text\n  QMutableListIterator<Diff> pointer(diffs);\n  // Number of characters that changed prior to the equality.\n  int length_insertions1 = 0;\n  int length_deletions1 = 0;\n  // Number of characters that changed after the equality.\n  int length_insertions2 = 0;\n  int length_deletions2 = 0;\n  Diff *thisDiff = pointer.hasNext() ? &pointer.next() : NULL;\n  while (thisDiff != NULL) {\n    if (thisDiff->operation == EQUAL) {\n      // Equality found.\n      equalities.push(*thisDiff);\n      length_insertions1 = length_insertions2;\n      length_deletions1 = length_deletions2;\n      length_insertions2 = 0;\n      length_deletions2 = 0;\n      lastequality = thisDiff->text;\n    } else {\n      // An insertion or deletion.\n      if (thisDiff->operation == INSERT) {\n        length_insertions2 += thisDiff->text.length();\n      } else {\n        length_deletions2 += thisDiff->text.length();\n      }\n      // Eliminate an equality that is smaller or equal to the edits on both\n      // sides of it.\n      if (!lastequality.isNull()\n          && (lastequality.length()\n              <= std::max(length_insertions1, length_deletions1))\n          && (lastequality.length()\n              <= std::max(length_insertions2, length_deletions2))) {\n        // printf(\"Splitting: '%s'\\n\", qPrintable(lastequality));\n        // Walk back to offending equality.\n        while (*thisDiff != equalities.top()) {\n          thisDiff = &pointer.previous();\n        }\n        pointer.next();\n\n        // Replace equality with a delete.\n        pointer.setValue(Diff(DELETE, lastequality));\n        // Insert a corresponding an insert.\n        pointer.insert(Diff(INSERT, lastequality));\n\n        equalities.pop();  // Throw away the equality we just deleted.\n        if (!equalities.isEmpty()) {\n          // Throw away the previous equality (it needs to be reevaluated).\n          equalities.pop();\n        }\n        if (equalities.isEmpty()) {\n          // There are no previous equalities, walk back to the start.\n          while (pointer.hasPrevious()) {\n            pointer.previous();\n          }\n        } else {\n          // There is a safe equality we can fall back to.\n          thisDiff = &equalities.top();\n          while (*thisDiff != pointer.previous()) {\n            // Intentionally empty loop.\n          }\n        }\n\n        length_insertions1 = 0;  // Reset the counters.\n        length_deletions1 = 0;\n        length_insertions2 = 0;\n        length_deletions2 = 0;\n        lastequality = QString();\n        changes = true;\n      }\n    }\n    thisDiff = pointer.hasNext() ? &pointer.next() : NULL;\n  }\n\n  // Normalize the diff.\n  if (changes) {\n    diff_cleanupMerge(diffs);\n  }\n  diff_cleanupSemanticLossless(diffs);\n\n  // Find any overlaps between deletions and insertions.\n  // e.g: <del>abcxxx</del><ins>xxxdef</ins>\n  //   -> <del>abc</del>xxx<ins>def</ins>\n  // e.g: <del>xxxabc</del><ins>defxxx</ins>\n  //   -> <ins>def</ins>xxx<del>abc</del>\n  // Only extract an overlap if it is as big as the edit ahead or behind it.\n  pointer.toFront();\n  Diff *prevDiff = NULL;\n  thisDiff = NULL;\n  if (pointer.hasNext()) {\n    prevDiff = &pointer.next();\n    if (pointer.hasNext()) {\n      thisDiff = &pointer.next();\n    }\n  }\n  while (thisDiff != NULL) {\n    if (prevDiff->operation == DELETE &&\n        thisDiff->operation == INSERT) {\n      QString deletion = prevDiff->text;\n      QString insertion = thisDiff->text;\n      int overlap_length1 = diff_commonOverlap(deletion, insertion);\n      int overlap_length2 = diff_commonOverlap(insertion, deletion);\n      if (overlap_length1 >= overlap_length2) {\n        if (overlap_length1 >= deletion.length() / 2.0 ||\n            overlap_length1 >= insertion.length() / 2.0) {\n          // Overlap found.  Insert an equality and trim the surrounding edits.\n          pointer.previous();\n          pointer.insert(Diff(EQUAL, insertion.left(overlap_length1)));\n          prevDiff->text =\n              deletion.left(deletion.length() - overlap_length1);\n          thisDiff->text = safeMid(insertion, overlap_length1);\n          // pointer.insert inserts the element before the cursor, so there is\n          // no need to step past the new element.\n        }\n      } else {\n        if (overlap_length2 >= deletion.length() / 2.0 ||\n            overlap_length2 >= insertion.length() / 2.0) {\n          // Reverse overlap found.\n          // Insert an equality and swap and trim the surrounding edits.\n          pointer.previous();\n          pointer.insert(Diff(EQUAL, deletion.left(overlap_length2)));\n          prevDiff->operation = INSERT;\n          prevDiff->text =\n              insertion.left(insertion.length() - overlap_length2);\n          thisDiff->operation = DELETE;\n          thisDiff->text = safeMid(deletion, overlap_length2);\n          // pointer.insert inserts the element before the cursor, so there is\n          // no need to step past the new element.\n        }\n      }\n      thisDiff = pointer.hasNext() ? &pointer.next() : NULL;\n    }\n    prevDiff = thisDiff;\n    thisDiff = pointer.hasNext() ? &pointer.next() : NULL;\n  }\n}\n\n\nvoid diff_match_patch::diff_cleanupSemanticLossless(QList<Diff> &diffs) {\n  QString equality1, edit, equality2;\n  QString commonString;\n  int commonOffset;\n  int score, bestScore;\n  QString bestEquality1, bestEdit, bestEquality2;\n  // Create a new iterator at the start.\n  QMutableListIterator<Diff> pointer(diffs);\n  Diff *prevDiff = pointer.hasNext() ? &pointer.next() : NULL;\n  Diff *thisDiff = pointer.hasNext() ? &pointer.next() : NULL;\n  Diff *nextDiff = pointer.hasNext() ? &pointer.next() : NULL;\n\n  // Intentionally ignore the first and last element (don't need checking).\n  while (nextDiff != NULL) {\n    if (prevDiff->operation == EQUAL &&\n      nextDiff->operation == EQUAL) {\n        // This is a single edit surrounded by equalities.\n        equality1 = prevDiff->text;\n        edit = thisDiff->text;\n        equality2 = nextDiff->text;\n\n        // First, shift the edit as far left as possible.\n        commonOffset = diff_commonSuffix(equality1, edit);\n        if (commonOffset != 0) {\n          commonString = safeMid(edit, edit.length() - commonOffset);\n          equality1 = equality1.left(equality1.length() - commonOffset);\n          edit = commonString + edit.left(edit.length() - commonOffset);\n          equality2 = commonString + equality2;\n        }\n\n        // Second, step character by character right, looking for the best fit.\n        bestEquality1 = equality1;\n        bestEdit = edit;\n        bestEquality2 = equality2;\n        bestScore = diff_cleanupSemanticScore(equality1, edit)\n            + diff_cleanupSemanticScore(edit, equality2);\n        while (!edit.isEmpty() && !equality2.isEmpty()\n            && edit[0] == equality2[0]) {\n          equality1 += edit[0];\n          edit = safeMid(edit, 1) + equality2[0];\n          equality2 = safeMid(equality2, 1);\n          score = diff_cleanupSemanticScore(equality1, edit)\n              + diff_cleanupSemanticScore(edit, equality2);\n          // The >= encourages trailing rather than leading whitespace on edits.\n          if (score >= bestScore) {\n            bestScore = score;\n            bestEquality1 = equality1;\n            bestEdit = edit;\n            bestEquality2 = equality2;\n          }\n        }\n\n        if (prevDiff->text != bestEquality1) {\n          // We have an improvement, save it back to the diff.\n          if (!bestEquality1.isEmpty()) {\n            prevDiff->text = bestEquality1;\n          } else {\n            pointer.previous();  // Walk past nextDiff.\n            pointer.previous();  // Walk past thisDiff.\n            pointer.previous();  // Walk past prevDiff.\n            pointer.remove();  // Delete prevDiff.\n            pointer.next();  // Walk past thisDiff.\n            pointer.next();  // Walk past nextDiff.\n          }\n          thisDiff->text = bestEdit;\n          if (!bestEquality2.isEmpty()) {\n            nextDiff->text = bestEquality2;\n          } else {\n            pointer.remove(); // Delete nextDiff.\n            nextDiff = thisDiff;\n            thisDiff = prevDiff;\n          }\n        }\n    }\n    prevDiff = thisDiff;\n    thisDiff = nextDiff;\n    nextDiff = pointer.hasNext() ? &pointer.next() : NULL;\n  }\n}\n\n\nint diff_match_patch::diff_cleanupSemanticScore(const QString &one,\n                                                const QString &two) {\n  if (one.isEmpty() || two.isEmpty()) {\n    // Edges are the best.\n    return 6;\n  }\n\n  // Each port of this function behaves slightly differently due to\n  // subtle differences in each language's definition of things like\n  // 'whitespace'.  Since this function's purpose is largely cosmetic,\n  // the choice has been made to use each language's native features\n  // rather than force total conformity.\n  QChar char1 = one[one.length() - 1];\n  QChar char2 = two[0];\n  bool nonAlphaNumeric1 = !char1.isLetterOrNumber();\n  bool nonAlphaNumeric2 = !char2.isLetterOrNumber();\n  bool whitespace1 = nonAlphaNumeric1 && char1.isSpace();\n  bool whitespace2 = nonAlphaNumeric2 && char2.isSpace();\n  bool lineBreak1 = whitespace1 && char1.category() == QChar::Other_Control;\n  bool lineBreak2 = whitespace2 && char2.category() == QChar::Other_Control;\n  bool blankLine1 = lineBreak1 && BLANKLINEEND.indexIn(one) != -1;\n  bool blankLine2 = lineBreak2 && BLANKLINESTART.indexIn(two) != -1;\n\n  if (blankLine1 || blankLine2) {\n    // Five points for blank lines.\n    return 5;\n  } else if (lineBreak1 || lineBreak2) {\n    // Four points for line breaks.\n    return 4;\n  } else if (nonAlphaNumeric1 && !whitespace1 && whitespace2) {\n    // Three points for end of sentences.\n    return 3;\n  } else if (whitespace1 || whitespace2) {\n    // Two points for whitespace.\n    return 2;\n  } else if (nonAlphaNumeric1 || nonAlphaNumeric2) {\n    // One point for non-alphanumeric.\n    return 1;\n  }\n  return 0;\n}\n\n\n// Define some regex patterns for matching boundaries.\nQRegExp diff_match_patch::BLANKLINEEND = QRegExp(\"\\\\n\\\\r?\\\\n$\");\nQRegExp diff_match_patch::BLANKLINESTART = QRegExp(\"^\\\\r?\\\\n\\\\r?\\\\n\");\n\n\nvoid diff_match_patch::diff_cleanupEfficiency(QList<Diff> &diffs) {\n  if (diffs.isEmpty()) {\n    return;\n  }\n  bool changes = false;\n  QStack<Diff> equalities;  // Stack of equalities.\n  QString lastequality;  // Always equal to equalities.lastElement().text\n  QMutableListIterator<Diff> pointer(diffs);\n  // Is there an insertion operation before the last equality.\n  bool pre_ins = false;\n  // Is there a deletion operation before the last equality.\n  bool pre_del = false;\n  // Is there an insertion operation after the last equality.\n  bool post_ins = false;\n  // Is there a deletion operation after the last equality.\n  bool post_del = false;\n\n  Diff *thisDiff = pointer.hasNext() ? &pointer.next() : NULL;\n  Diff *safeDiff = thisDiff;\n\n  while (thisDiff != NULL) {\n    if (thisDiff->operation == EQUAL) {\n      // Equality found.\n      if (thisDiff->text.length() < Diff_EditCost && (post_ins || post_del)) {\n        // Candidate found.\n        equalities.push(*thisDiff);\n        pre_ins = post_ins;\n        pre_del = post_del;\n        lastequality = thisDiff->text;\n      } else {\n        // Not a candidate, and can never become one.\n        equalities.clear();\n        lastequality = QString();\n        safeDiff = thisDiff;\n      }\n      post_ins = post_del = false;\n    } else {\n      // An insertion or deletion.\n      if (thisDiff->operation == DELETE) {\n        post_del = true;\n      } else {\n        post_ins = true;\n      }\n      /*\n      * Five types to be split:\n      * <ins>A</ins><del>B</del>XY<ins>C</ins><del>D</del>\n      * <ins>A</ins>X<ins>C</ins><del>D</del>\n      * <ins>A</ins><del>B</del>X<ins>C</ins>\n      * <ins>A</del>X<ins>C</ins><del>D</del>\n      * <ins>A</ins><del>B</del>X<del>C</del>\n      */\n      if (!lastequality.isNull()\n          && ((pre_ins && pre_del && post_ins && post_del)\n          || ((lastequality.length() < Diff_EditCost / 2)\n          && ((pre_ins ? 1 : 0) + (pre_del ? 1 : 0)\n          + (post_ins ? 1 : 0) + (post_del ? 1 : 0)) == 3))) {\n        // printf(\"Splitting: '%s'\\n\", qPrintable(lastequality));\n        // Walk back to offending equality.\n        while (*thisDiff != equalities.top()) {\n          thisDiff = &pointer.previous();\n        }\n        pointer.next();\n\n        // Replace equality with a delete.\n        pointer.setValue(Diff(DELETE, lastequality));\n        // Insert a corresponding an insert.\n        pointer.insert(Diff(INSERT, lastequality));\n        thisDiff = &pointer.previous();\n        pointer.next();\n\n        equalities.pop();  // Throw away the equality we just deleted.\n        lastequality = QString();\n        if (pre_ins && pre_del) {\n          // No changes made which could affect previous entry, keep going.\n          post_ins = post_del = true;\n          equalities.clear();\n          safeDiff = thisDiff;\n        } else {\n          if (!equalities.isEmpty()) {\n            // Throw away the previous equality (it needs to be reevaluated).\n            equalities.pop();\n          }\n          if (equalities.isEmpty()) {\n            // There are no previous questionable equalities,\n            // walk back to the last known safe diff.\n            thisDiff = safeDiff;\n          } else {\n            // There is an equality we can fall back to.\n            thisDiff = &equalities.top();\n          }\n          while (*thisDiff != pointer.previous()) {\n            // Intentionally empty loop.\n          }\n          post_ins = post_del = false;\n        }\n\n        changes = true;\n      }\n    }\n    thisDiff = pointer.hasNext() ? &pointer.next() : NULL;\n  }\n\n  if (changes) {\n    diff_cleanupMerge(diffs);\n  }\n}\n\n\nvoid diff_match_patch::diff_cleanupMerge(QList<Diff> &diffs) {\n  diffs.append(Diff(EQUAL, \"\"));  // Add a dummy entry at the end.\n  QMutableListIterator<Diff> pointer(diffs);\n  int count_delete = 0;\n  int count_insert = 0;\n  QString text_delete = \"\";\n  QString text_insert = \"\";\n  Diff *thisDiff = pointer.hasNext() ? &pointer.next() : NULL;\n  Diff *prevEqual = NULL;\n  int commonlength;\n  while (thisDiff != NULL) {\n    switch (thisDiff->operation) {\n      case INSERT:\n        count_insert++;\n        text_insert += thisDiff->text;\n        prevEqual = NULL;\n        break;\n      case DELETE:\n        count_delete++;\n        text_delete += thisDiff->text;\n        prevEqual = NULL;\n        break;\n      case EQUAL:\n        if (count_delete + count_insert > 1) {\n          bool both_types = count_delete != 0 && count_insert != 0;\n          // Delete the offending records.\n          pointer.previous();  // Reverse direction.\n          while (count_delete-- > 0) {\n            pointer.previous();\n            pointer.remove();\n          }\n          while (count_insert-- > 0) {\n            pointer.previous();\n            pointer.remove();\n          }\n          if (both_types) {\n            // Factor out any common prefixies.\n            commonlength = diff_commonPrefix(text_insert, text_delete);\n            if (commonlength != 0) {\n              if (pointer.hasPrevious()) {\n                thisDiff = &pointer.previous();\n                if (thisDiff->operation != EQUAL) {\n                  throw \"Previous diff should have been an equality.\";\n                }\n                thisDiff->text += text_insert.left(commonlength);\n                pointer.next();\n              } else {\n                pointer.insert(Diff(EQUAL, text_insert.left(commonlength)));\n              }\n              text_insert = safeMid(text_insert, commonlength);\n              text_delete = safeMid(text_delete, commonlength);\n            }\n            // Factor out any common suffixies.\n            commonlength = diff_commonSuffix(text_insert, text_delete);\n            if (commonlength != 0) {\n              thisDiff = &pointer.next();\n              thisDiff->text = safeMid(text_insert, text_insert.length()\n                  - commonlength) + thisDiff->text;\n              text_insert = text_insert.left(text_insert.length()\n                  - commonlength);\n              text_delete = text_delete.left(text_delete.length()\n                  - commonlength);\n              pointer.previous();\n            }\n          }\n          // Insert the merged records.\n          if (!text_delete.isEmpty()) {\n            pointer.insert(Diff(DELETE, text_delete));\n          }\n          if (!text_insert.isEmpty()) {\n            pointer.insert(Diff(INSERT, text_insert));\n          }\n          // Step forward to the equality.\n          thisDiff = pointer.hasNext() ? &pointer.next() : NULL;\n\n        } else if (prevEqual != NULL) {\n          // Merge this equality with the previous one.\n          prevEqual->text += thisDiff->text;\n          pointer.remove();\n          thisDiff = &pointer.previous();\n          pointer.next();  // Forward direction\n        }\n        count_insert = 0;\n        count_delete = 0;\n        text_delete = \"\";\n        text_insert = \"\";\n        prevEqual = thisDiff;\n        break;\n      }\n      thisDiff = pointer.hasNext() ? &pointer.next() : NULL;\n  }\n  if (diffs.back().text.isEmpty()) {\n    diffs.removeLast();  // Remove the dummy entry at the end.\n  }\n\n  /*\n  * Second pass: look for single edits surrounded on both sides by equalities\n  * which can be shifted sideways to eliminate an equality.\n  * e.g: A<ins>BA</ins>C -> <ins>AB</ins>AC\n  */\n  bool changes = false;\n  // Create a new iterator at the start.\n  // (As opposed to walking the current one back.)\n  pointer.toFront();\n  Diff *prevDiff = pointer.hasNext() ? &pointer.next() : NULL;\n  thisDiff = pointer.hasNext() ? &pointer.next() : NULL;\n  Diff *nextDiff = pointer.hasNext() ? &pointer.next() : NULL;\n\n  // Intentionally ignore the first and last element (don't need checking).\n  while (nextDiff != NULL) {\n    if (prevDiff->operation == EQUAL &&\n      nextDiff->operation == EQUAL) {\n        // This is a single edit surrounded by equalities.\n        if (thisDiff->text.endsWith(prevDiff->text)) {\n          // Shift the edit over the previous equality.\n          thisDiff->text = prevDiff->text\n              + thisDiff->text.left(thisDiff->text.length()\n              - prevDiff->text.length());\n          nextDiff->text = prevDiff->text + nextDiff->text;\n          pointer.previous();  // Walk past nextDiff.\n          pointer.previous();  // Walk past thisDiff.\n          pointer.previous();  // Walk past prevDiff.\n          pointer.remove();  // Delete prevDiff.\n          pointer.next();  // Walk past thisDiff.\n          thisDiff = &pointer.next();  // Walk past nextDiff.\n          nextDiff = pointer.hasNext() ? &pointer.next() : NULL;\n          changes = true;\n        } else if (thisDiff->text.startsWith(nextDiff->text)) {\n          // Shift the edit over the next equality.\n          prevDiff->text += nextDiff->text;\n          thisDiff->text = safeMid(thisDiff->text, nextDiff->text.length())\n              + nextDiff->text;\n          pointer.remove(); // Delete nextDiff.\n          nextDiff = pointer.hasNext() ? &pointer.next() : NULL;\n          changes = true;\n        }\n    }\n    prevDiff = thisDiff;\n    thisDiff = nextDiff;\n    nextDiff = pointer.hasNext() ? &pointer.next() : NULL;\n  }\n  // If shifts were made, the diff needs reordering and another shift sweep.\n  if (changes) {\n    diff_cleanupMerge(diffs);\n  }\n}\n\n\nint diff_match_patch::diff_xIndex(const QList<Diff> &diffs, int loc) {\n  int chars1 = 0;\n  int chars2 = 0;\n  int last_chars1 = 0;\n  int last_chars2 = 0;\n  Diff lastDiff;\n  foreach(Diff aDiff, diffs) {\n    if (aDiff.operation != INSERT) {\n      // Equality or deletion.\n      chars1 += aDiff.text.length();\n    }\n    if (aDiff.operation != DELETE) {\n      // Equality or insertion.\n      chars2 += aDiff.text.length();\n    }\n    if (chars1 > loc) {\n      // Overshot the location.\n      lastDiff = aDiff;\n      break;\n    }\n    last_chars1 = chars1;\n    last_chars2 = chars2;\n  }\n  if (lastDiff.operation == DELETE) {\n    // The location was deleted.\n    return last_chars2;\n  }\n  // Add the remaining character length.\n  return last_chars2 + (loc - last_chars1);\n}\n\n\nQString diff_match_patch::diff_prettyHtml(const QList<Diff> &diffs) {\n  QString html;\n  QString text;\n  foreach(Diff aDiff, diffs) {\n    text = aDiff.text;\n    text.replace(\"&\", \"&amp;\").replace(\"<\", \"&lt;\")\n        .replace(\">\", \"&gt;\").replace(\"\\n\", \"&para;<br>\");\n    switch (aDiff.operation) {\n      case INSERT:\n        html += QString(\"<ins style=\\\"background:#e6ffe6;\\\">\") + text\n            + QString(\"</ins>\");\n        break;\n      case DELETE:\n        html += QString(\"<del style=\\\"background:#ffe6e6;\\\">\") + text\n            + QString(\"</del>\");\n        break;\n      case EQUAL:\n        html += QString(\"<span>\") + text + QString(\"</span>\");\n        break;\n    }\n  }\n  return html;\n}\n\n\nQString diff_match_patch::diff_text1(const QList<Diff> &diffs) {\n  QString text;\n  foreach(Diff aDiff, diffs) {\n    if (aDiff.operation != INSERT) {\n      text += aDiff.text;\n    }\n  }\n  return text;\n}\n\n\nQString diff_match_patch::diff_text2(const QList<Diff> &diffs) {\n  QString text;\n  foreach(Diff aDiff, diffs) {\n    if (aDiff.operation != DELETE) {\n      text += aDiff.text;\n    }\n  }\n  return text;\n}\n\n\nint diff_match_patch::diff_levenshtein(const QList<Diff> &diffs) {\n  int levenshtein = 0;\n  int insertions = 0;\n  int deletions = 0;\n  foreach(Diff aDiff, diffs) {\n    switch (aDiff.operation) {\n      case INSERT:\n        insertions += aDiff.text.length();\n        break;\n      case DELETE:\n        deletions += aDiff.text.length();\n        break;\n      case EQUAL:\n        // A deletion and an insertion is one substitution.\n        levenshtein += std::max(insertions, deletions);\n        insertions = 0;\n        deletions = 0;\n        break;\n    }\n  }\n  levenshtein += std::max(insertions, deletions);\n  return levenshtein;\n}\n\n\nQString diff_match_patch::diff_toDelta(const QList<Diff> &diffs) {\n  QString text;\n  foreach(Diff aDiff, diffs) {\n    switch (aDiff.operation) {\n      case INSERT: {\n        QString encoded = QString(QUrl::toPercentEncoding(aDiff.text,\n            \" !~*'();/?:@&=+$,#\"));\n        text += QString(\"+\") + encoded + QString(\"\\t\");\n        break;\n      }\n      case DELETE:\n        text += QString(\"-\") + QString::number(aDiff.text.length())\n            + QString(\"\\t\");\n        break;\n      case EQUAL:\n        text += QString(\"=\") + QString::number(aDiff.text.length())\n            + QString(\"\\t\");\n        break;\n    }\n  }\n  if (!text.isEmpty()) {\n    // Strip off trailing tab character.\n    text = text.left(text.length() - 1);\n  }\n  return text;\n}\n\n\nQList<Diff> diff_match_patch::diff_fromDelta(const QString &text1,\n                                             const QString &delta) {\n  QList<Diff> diffs;\n  int pointer = 0;  // Cursor in text1\n  QStringList tokens = delta.split(\"\\t\");\n  foreach(QString token, tokens) {\n    if (token.isEmpty()) {\n      // Blank tokens are ok (from a trailing \\t).\n      continue;\n    }\n    // Each token begins with a one character parameter which specifies the\n    // operation of this token (delete, insert, equality).\n    QString param = safeMid(token, 1);\n    switch (token[0].toAscii()) {\n      case '+':\n        param = QUrl::fromPercentEncoding(qPrintable(param));\n        diffs.append(Diff(INSERT, param));\n        break;\n      case '-':\n        // Fall through.\n      case '=': {\n        int n;\n        n = param.toInt();\n        if (n < 0) {\n          throw QString(\"Negative number in diff_fromDelta: %1\").arg(param);\n        }\n        QString text;\n        text = safeMid(text1, pointer, n);\n        pointer += n;\n        if (token[0] == QChar('=')) {\n          diffs.append(Diff(EQUAL, text));\n        } else {\n          diffs.append(Diff(DELETE, text));\n        }\n        break;\n      }\n      default:\n        throw QString(\"Invalid diff operation in diff_fromDelta: %1\")\n            .arg(token[0]);\n    }\n  }\n  if (pointer != text1.length()) {\n    throw QString(\"Delta length (%1) smaller than source text length (%2)\")\n        .arg(pointer).arg(text1.length());\n  }\n  return diffs;\n}\n\n\n  //  MATCH FUNCTIONS\n\n\nint diff_match_patch::match_main(const QString &text, const QString &pattern,\n                                 int loc) {\n  // Check for null inputs.\n  if (text.isNull() || pattern.isNull()) {\n    throw \"Null inputs. (match_main)\";\n  }\n\n  loc = std::max(0, std::min(loc, text.length()));\n  if (text == pattern) {\n    // Shortcut (potentially not guaranteed by the algorithm)\n    return 0;\n  } else if (text.isEmpty()) {\n    // Nothing to match.\n    return -1;\n  } else if (loc + pattern.length() <= text.length()\n      && safeMid(text, loc, pattern.length()) == pattern) {\n    // Perfect match at the perfect spot!  (Includes case of null pattern)\n    return loc;\n  } else {\n    // Do a fuzzy compare.\n    return match_bitap(text, pattern, loc);\n  }\n}\n\n\nint diff_match_patch::match_bitap(const QString &text, const QString &pattern,\n                                  int loc) {\n  if (!(Match_MaxBits == 0 || pattern.length() <= Match_MaxBits)) {\n    throw \"Pattern too long for this application.\";\n  }\n\n  // Initialise the alphabet.\n  QMap<QChar, int> s = match_alphabet(pattern);\n\n  // Highest score beyond which we give up.\n  double score_threshold = Match_Threshold;\n  // Is there a nearby exact match? (speedup)\n  int best_loc = text.indexOf(pattern, loc);\n  if (best_loc != -1) {\n    score_threshold = std::min(match_bitapScore(0, best_loc, loc, pattern),\n        score_threshold);\n    // What about in the other direction? (speedup)\n    best_loc = text.lastIndexOf(pattern, loc + pattern.length());\n    if (best_loc != -1) {\n      score_threshold = std::min(match_bitapScore(0, best_loc, loc, pattern),\n          score_threshold);\n    }\n  }\n\n  // Initialise the bit arrays.\n  int matchmask = 1 << (pattern.length() - 1);\n  best_loc = -1;\n\n  int bin_min, bin_mid;\n  int bin_max = pattern.length() + text.length();\n  int *rd;\n  int *last_rd = NULL;\n  for (int d = 0; d < pattern.length(); d++) {\n    // Scan for the best match; each iteration allows for one more error.\n    // Run a binary search to determine how far from 'loc' we can stray at\n    // this error level.\n    bin_min = 0;\n    bin_mid = bin_max;\n    while (bin_min < bin_mid) {\n      if (match_bitapScore(d, loc + bin_mid, loc, pattern)\n          <= score_threshold) {\n        bin_min = bin_mid;\n      } else {\n        bin_max = bin_mid;\n      }\n      bin_mid = (bin_max - bin_min) / 2 + bin_min;\n    }\n    // Use the result from this iteration as the maximum for the next.\n    bin_max = bin_mid;\n    int start = std::max(1, loc - bin_mid + 1);\n    int finish = std::min(loc + bin_mid, text.length()) + pattern.length();\n\n    rd = new int[finish + 2];\n    rd[finish + 1] = (1 << d) - 1;\n    for (int j = finish; j >= start; j--) {\n      int charMatch;\n      if (text.length() <= j - 1) {\n        // Out of range.\n        charMatch = 0;\n      } else {\n        charMatch = s.value(text[j - 1], 0);\n      }\n      if (d == 0) {\n        // First pass: exact match.\n        rd[j] = ((rd[j + 1] << 1) | 1) & charMatch;\n      } else {\n        // Subsequent passes: fuzzy match.\n        rd[j] = ((rd[j + 1] << 1) | 1) & charMatch\n            | (((last_rd[j + 1] | last_rd[j]) << 1) | 1)\n            | last_rd[j + 1];\n      }\n      if ((rd[j] & matchmask) != 0) {\n        double score = match_bitapScore(d, j - 1, loc, pattern);\n        // This match will almost certainly be better than any existing\n        // match.  But check anyway.\n        if (score <= score_threshold) {\n          // Told you so.\n          score_threshold = score;\n          best_loc = j - 1;\n          if (best_loc > loc) {\n            // When passing loc, don't exceed our current distance from loc.\n            start = std::max(1, 2 * loc - best_loc);\n          } else {\n            // Already passed loc, downhill from here on in.\n            break;\n          }\n        }\n      }\n    }\n    if (match_bitapScore(d + 1, loc, loc, pattern) > score_threshold) {\n      // No hope for a (better) match at greater error levels.\n      break;\n    }\n    delete [] last_rd;\n    last_rd = rd;\n  }\n  delete [] last_rd;\n  delete [] rd;\n  return best_loc;\n}\n\n\ndouble diff_match_patch::match_bitapScore(int e, int x, int loc,\n                                          const QString &pattern) {\n  const float accuracy = static_cast<float> (e) / pattern.length();\n  const int proximity = qAbs(loc - x);\n  if (Match_Distance == 0) {\n    // Dodge divide by zero error.\n    return proximity == 0 ? accuracy : 1.0;\n  }\n  return accuracy + (proximity / static_cast<float> (Match_Distance));\n}\n\n\nQMap<QChar, int> diff_match_patch::match_alphabet(const QString &pattern) {\n  QMap<QChar, int> s;\n  int i;\n  for (i = 0; i < pattern.length(); i++) {\n    QChar c = pattern[i];\n    s.insert(c, 0);\n  }\n  for (i = 0; i < pattern.length(); i++) {\n    QChar c = pattern[i];\n    s.insert(c, s.value(c) | (1 << (pattern.length() - i - 1)));\n  }\n  return s;\n}\n\n\n//  PATCH FUNCTIONS\n\n\nvoid diff_match_patch::patch_addContext(Patch &patch, const QString &text) {\n  if (text.isEmpty()) {\n    return;\n  }\n  QString pattern = safeMid(text, patch.start2, patch.length1);\n  int padding = 0;\n\n  // Look for the first and last matches of pattern in text.  If two different\n  // matches are found, increase the pattern length.\n  while (text.indexOf(pattern) != text.lastIndexOf(pattern)\n      && pattern.length() < Match_MaxBits - Patch_Margin - Patch_Margin) {\n    padding += Patch_Margin;\n    pattern = safeMid(text, std::max(0, patch.start2 - padding),\n        std::min(text.length(), patch.start2 + patch.length1 + padding)\n        - std::max(0, patch.start2 - padding));\n  }\n  // Add one chunk for good luck.\n  padding += Patch_Margin;\n\n  // Add the prefix.\n  QString prefix = safeMid(text, std::max(0, patch.start2 - padding),\n      patch.start2 - std::max(0, patch.start2 - padding));\n  if (!prefix.isEmpty()) {\n    patch.diffs.prepend(Diff(EQUAL, prefix));\n  }\n  // Add the suffix.\n  QString suffix = safeMid(text, patch.start2 + patch.length1,\n      std::min(text.length(), patch.start2 + patch.length1 + padding)\n      - (patch.start2 + patch.length1));\n  if (!suffix.isEmpty()) {\n    patch.diffs.append(Diff(EQUAL, suffix));\n  }\n\n  // Roll back the start points.\n  patch.start1 -= prefix.length();\n  patch.start2 -= prefix.length();\n  // Extend the lengths.\n  patch.length1 += prefix.length() + suffix.length();\n  patch.length2 += prefix.length() + suffix.length();\n}\n\n\nQList<Patch> diff_match_patch::patch_make(const QString &text1,\n                                          const QString &text2) {\n  // Check for null inputs.\n  if (text1.isNull() || text2.isNull()) {\n    throw \"Null inputs. (patch_make)\";\n  }\n\n  // No diffs provided, compute our own.\n  QList<Diff> diffs = diff_main(text1, text2, true);\n  if (diffs.size() > 2) {\n    diff_cleanupSemantic(diffs);\n    diff_cleanupEfficiency(diffs);\n  }\n\n  return patch_make(text1, diffs);\n}\n\n\nQList<Patch> diff_match_patch::patch_make(const QList<Diff> &diffs) {\n  // No origin string provided, compute our own.\n  const QString text1 = diff_text1(diffs);\n  return patch_make(text1, diffs);\n}\n\n\nQList<Patch> diff_match_patch::patch_make(const QString &text1,\n                                          const QString &text2,\n                                          const QList<Diff> &diffs) {\n  // text2 is entirely unused.\n  return patch_make(text1, diffs);\n\n  Q_UNUSED(text2)\n}\n\n\nQList<Patch> diff_match_patch::patch_make(const QString &text1,\n                                          const QList<Diff> &diffs) {\n  // Check for null inputs.\n  if (text1.isNull()) {\n    throw \"Null inputs. (patch_make)\";\n  }\n\n  QList<Patch> patches;\n  if (diffs.isEmpty()) {\n    return patches;  // Get rid of the null case.\n  }\n  Patch patch;\n  int char_count1 = 0;  // Number of characters into the text1 string.\n  int char_count2 = 0;  // Number of characters into the text2 string.\n  // Start with text1 (prepatch_text) and apply the diffs until we arrive at\n  // text2 (postpatch_text).  We recreate the patches one by one to determine\n  // context info.\n  QString prepatch_text = text1;\n  QString postpatch_text = text1;\n  foreach(Diff aDiff, diffs) {\n    if (patch.diffs.isEmpty() && aDiff.operation != EQUAL) {\n      // A new patch starts here.\n      patch.start1 = char_count1;\n      patch.start2 = char_count2;\n    }\n\n    switch (aDiff.operation) {\n      case INSERT:\n        patch.diffs.append(aDiff);\n        patch.length2 += aDiff.text.length();\n        postpatch_text = postpatch_text.left(char_count2)\n            + aDiff.text + safeMid(postpatch_text, char_count2);\n        break;\n      case DELETE:\n        patch.length1 += aDiff.text.length();\n        patch.diffs.append(aDiff);\n        postpatch_text = postpatch_text.left(char_count2)\n            + safeMid(postpatch_text, char_count2 + aDiff.text.length());\n        break;\n      case EQUAL:\n        if (aDiff.text.length() <= 2 * Patch_Margin\n            && !patch.diffs.isEmpty() && !(aDiff == diffs.back())) {\n          // Small equality inside a patch.\n          patch.diffs.append(aDiff);\n          patch.length1 += aDiff.text.length();\n          patch.length2 += aDiff.text.length();\n        }\n\n        if (aDiff.text.length() >= 2 * Patch_Margin) {\n          // Time for a new patch.\n          if (!patch.diffs.isEmpty()) {\n            patch_addContext(patch, prepatch_text);\n            patches.append(patch);\n            patch = Patch();\n            // Unlike Unidiff, our patch lists have a rolling context.\n            // http://code.google.com/p/google-diff-match-patch/wiki/Unidiff\n            // Update prepatch text & pos to reflect the application of the\n            // just completed patch.\n            prepatch_text = postpatch_text;\n            char_count1 = char_count2;\n          }\n        }\n        break;\n    }\n\n    // Update the current character count.\n    if (aDiff.operation != INSERT) {\n      char_count1 += aDiff.text.length();\n    }\n    if (aDiff.operation != DELETE) {\n      char_count2 += aDiff.text.length();\n    }\n  }\n  // Pick up the leftover patch if not empty.\n  if (!patch.diffs.isEmpty()) {\n    patch_addContext(patch, prepatch_text);\n    patches.append(patch);\n  }\n\n  return patches;\n}\n\n\nQList<Patch> diff_match_patch::patch_deepCopy(QList<Patch> &patches) {\n  QList<Patch> patchesCopy;\n  foreach(Patch aPatch, patches) {\n    Patch patchCopy = Patch();\n    foreach(Diff aDiff, aPatch.diffs) {\n      Diff diffCopy = Diff(aDiff.operation, aDiff.text);\n      patchCopy.diffs.append(diffCopy);\n    }\n    patchCopy.start1 = aPatch.start1;\n    patchCopy.start2 = aPatch.start2;\n    patchCopy.length1 = aPatch.length1;\n    patchCopy.length2 = aPatch.length2;\n    patchesCopy.append(patchCopy);\n  }\n  return patchesCopy;\n}\n\n\nQPair<QString, QVector<bool> > diff_match_patch::patch_apply(\n    QList<Patch> &patches, const QString &sourceText) {\n  QString text = sourceText;  // Copy to preserve original.\n  if (patches.isEmpty()) {\n    return QPair<QString,QVector<bool> >(text, QVector<bool>(0));\n  }\n\n  // Deep copy the patches so that no changes are made to originals.\n  QList<Patch> patchesCopy = patch_deepCopy(patches);\n\n  QString nullPadding = patch_addPadding(patchesCopy);\n  text = nullPadding + text + nullPadding;\n  patch_splitMax(patchesCopy);\n\n  int x = 0;\n  // delta keeps track of the offset between the expected and actual location\n  // of the previous patch.  If there are patches expected at positions 10 and\n  // 20, but the first patch was found at 12, delta is 2 and the second patch\n  // has an effective expected position of 22.\n  int delta = 0;\n  QVector<bool> results(patchesCopy.size());\n  foreach(Patch aPatch, patchesCopy) {\n    int expected_loc = aPatch.start2 + delta;\n    QString text1 = diff_text1(aPatch.diffs);\n    int start_loc;\n    int end_loc = -1;\n    if (text1.length() > Match_MaxBits) {\n      // patch_splitMax will only provide an oversized pattern in the case of\n      // a monster delete.\n      start_loc = match_main(text, text1.left(Match_MaxBits), expected_loc);\n      if (start_loc != -1) {\n        end_loc = match_main(text, text1.right(Match_MaxBits),\n            expected_loc + text1.length() - Match_MaxBits);\n        if (end_loc == -1 || start_loc >= end_loc) {\n          // Can't find valid trailing context.  Drop this patch.\n          start_loc = -1;\n        }\n      }\n    } else {\n      start_loc = match_main(text, text1, expected_loc);\n    }\n    if (start_loc == -1) {\n      // No match found.  :(\n      results[x] = false;\n      // Subtract the delta for this failed patch from subsequent patches.\n      delta -= aPatch.length2 - aPatch.length1;\n    } else {\n      // Found a match.  :)\n      results[x] = true;\n      delta = start_loc - expected_loc;\n      QString text2;\n      if (end_loc == -1) {\n        text2 = safeMid(text, start_loc, text1.length());\n      } else {\n        text2 = safeMid(text, start_loc, end_loc + Match_MaxBits - start_loc);\n      }\n      if (text1 == text2) {\n        // Perfect match, just shove the replacement text in.\n        text = text.left(start_loc) + diff_text2(aPatch.diffs)\n            + safeMid(text, start_loc + text1.length());\n      } else {\n        // Imperfect match.  Run a diff to get a framework of equivalent\n        // indices.\n        QList<Diff> diffs = diff_main(text1, text2, false);\n        if (text1.length() > Match_MaxBits\n            && diff_levenshtein(diffs) / static_cast<float> (text1.length())\n            > Patch_DeleteThreshold) {\n          // The end points match, but the content is unacceptably bad.\n          results[x] = false;\n        } else {\n          diff_cleanupSemanticLossless(diffs);\n          int index1 = 0;\n          foreach(Diff aDiff, aPatch.diffs) {\n            if (aDiff.operation != EQUAL) {\n              int index2 = diff_xIndex(diffs, index1);\n              if (aDiff.operation == INSERT) {\n                // Insertion\n                text = text.left(start_loc + index2) + aDiff.text\n                    + safeMid(text, start_loc + index2);\n              } else if (aDiff.operation == DELETE) {\n                // Deletion\n                text = text.left(start_loc + index2)\n                    + safeMid(text, start_loc + diff_xIndex(diffs,\n                    index1 + aDiff.text.length()));\n              }\n            }\n            if (aDiff.operation != DELETE) {\n              index1 += aDiff.text.length();\n            }\n          }\n        }\n      }\n    }\n    x++;\n  }\n  // Strip the padding off.\n  text = safeMid(text, nullPadding.length(), text.length()\n      - 2 * nullPadding.length());\n  return QPair<QString, QVector<bool> >(text, results);\n}\n\n\nQString diff_match_patch::patch_addPadding(QList<Patch> &patches) {\n  short paddingLength = Patch_Margin;\n  QString nullPadding = \"\";\n  for (short x = 1; x <= paddingLength; x++) {\n    nullPadding += QChar((ushort)x);\n  }\n\n  // Bump all the patches forward.\n  QMutableListIterator<Patch> pointer(patches);\n  while (pointer.hasNext()) {\n    Patch &aPatch = pointer.next();\n    aPatch.start1 += paddingLength;\n    aPatch.start2 += paddingLength;\n  }\n\n  // Add some padding on start of first diff.\n  Patch &firstPatch = patches.first();\n  QList<Diff> &firstPatchDiffs = firstPatch.diffs;\n  if (firstPatchDiffs.empty() || firstPatchDiffs.first().operation != EQUAL) {\n    // Add nullPadding equality.\n    firstPatchDiffs.prepend(Diff(EQUAL, nullPadding));\n    firstPatch.start1 -= paddingLength;  // Should be 0.\n    firstPatch.start2 -= paddingLength;  // Should be 0.\n    firstPatch.length1 += paddingLength;\n    firstPatch.length2 += paddingLength;\n  } else if (paddingLength > firstPatchDiffs.first().text.length()) {\n    // Grow first equality.\n    Diff &firstDiff = firstPatchDiffs.first();\n    int extraLength = paddingLength - firstDiff.text.length();\n    firstDiff.text = safeMid(nullPadding, firstDiff.text.length(),\n        paddingLength - firstDiff.text.length()) + firstDiff.text;\n    firstPatch.start1 -= extraLength;\n    firstPatch.start2 -= extraLength;\n    firstPatch.length1 += extraLength;\n    firstPatch.length2 += extraLength;\n  }\n\n  // Add some padding on end of last diff.\n  Patch &lastPatch = patches.first();\n  QList<Diff> &lastPatchDiffs = lastPatch.diffs;\n  if (lastPatchDiffs.empty() || lastPatchDiffs.last().operation != EQUAL) {\n    // Add nullPadding equality.\n    lastPatchDiffs.append(Diff(EQUAL, nullPadding));\n    lastPatch.length1 += paddingLength;\n    lastPatch.length2 += paddingLength;\n  } else if (paddingLength > lastPatchDiffs.last().text.length()) {\n    // Grow last equality.\n    Diff &lastDiff = lastPatchDiffs.last();\n    int extraLength = paddingLength - lastDiff.text.length();\n    lastDiff.text += nullPadding.left(extraLength);\n    lastPatch.length1 += extraLength;\n    lastPatch.length2 += extraLength;\n  }\n\n  return nullPadding;\n}\n\n\nvoid diff_match_patch::patch_splitMax(QList<Patch> &patches) {\n  short patch_size = Match_MaxBits;\n  QString precontext, postcontext;\n  Patch patch;\n  int start1, start2;\n  bool empty;\n  Operation diff_type;\n  QString diff_text;\n  QMutableListIterator<Patch> pointer(patches);\n  Patch bigpatch;\n\n  if (pointer.hasNext()) {\n    bigpatch = pointer.next();\n  }\n\n  while (!bigpatch.isNull()) {\n    if (bigpatch.length1 <= patch_size) {\n      bigpatch = pointer.hasNext() ? pointer.next() : Patch();\n      continue;\n    }\n    // Remove the big old patch.\n    pointer.remove();\n    start1 = bigpatch.start1;\n    start2 = bigpatch.start2;\n    precontext = \"\";\n    while (!bigpatch.diffs.isEmpty()) {\n      // Create one of several smaller patches.\n      patch = Patch();\n      empty = true;\n      patch.start1 = start1 - precontext.length();\n      patch.start2 = start2 - precontext.length();\n      if (!precontext.isEmpty()) {\n        patch.length1 = patch.length2 = precontext.length();\n        patch.diffs.append(Diff(EQUAL, precontext));\n      }\n      while (!bigpatch.diffs.isEmpty()\n          && patch.length1 < patch_size - Patch_Margin) {\n        diff_type = bigpatch.diffs.front().operation;\n        diff_text = bigpatch.diffs.front().text;\n        if (diff_type == INSERT) {\n          // Insertions are harmless.\n          patch.length2 += diff_text.length();\n          start2 += diff_text.length();\n          patch.diffs.append(bigpatch.diffs.front());\n          bigpatch.diffs.removeFirst();\n          empty = false;\n        } else if (diff_type == DELETE && patch.diffs.size() == 1\n            && patch.diffs.front().operation == EQUAL\n            && diff_text.length() > 2 * patch_size) {\n          // This is a large deletion.  Let it pass in one chunk.\n          patch.length1 += diff_text.length();\n          start1 += diff_text.length();\n          empty = false;\n          patch.diffs.append(Diff(diff_type, diff_text));\n          bigpatch.diffs.removeFirst();\n        } else {\n          // Deletion or equality.  Only take as much as we can stomach.\n          diff_text = diff_text.left(std::min(diff_text.length(),\n              patch_size - patch.length1 - Patch_Margin));\n          patch.length1 += diff_text.length();\n          start1 += diff_text.length();\n          if (diff_type == EQUAL) {\n            patch.length2 += diff_text.length();\n            start2 += diff_text.length();\n          } else {\n            empty = false;\n          }\n          patch.diffs.append(Diff(diff_type, diff_text));\n          if (diff_text == bigpatch.diffs.front().text) {\n            bigpatch.diffs.removeFirst();\n          } else {\n            bigpatch.diffs.front().text = safeMid(bigpatch.diffs.front().text,\n                diff_text.length());\n          }\n        }\n      }\n      // Compute the head context for the next patch.\n      precontext = diff_text2(patch.diffs);\n      precontext = safeMid(precontext, precontext.length() - Patch_Margin);\n      // Append the end context for this patch.\n      if (diff_text1(bigpatch.diffs).length() > Patch_Margin) {\n        postcontext = diff_text1(bigpatch.diffs).left(Patch_Margin);\n      } else {\n        postcontext = diff_text1(bigpatch.diffs);\n      }\n      if (!postcontext.isEmpty()) {\n        patch.length1 += postcontext.length();\n        patch.length2 += postcontext.length();\n        if (!patch.diffs.isEmpty()\n            && patch.diffs.back().operation == EQUAL) {\n          patch.diffs.back().text += postcontext;\n        } else {\n          patch.diffs.append(Diff(EQUAL, postcontext));\n        }\n      }\n      if (!empty) {\n        pointer.insert(patch);\n      }\n    }\n    bigpatch = pointer.hasNext() ? pointer.next() : Patch();\n  }\n}\n\n\nQString diff_match_patch::patch_toText(const QList<Patch> &patches) {\n  QString text;\n  foreach(Patch aPatch, patches) {\n    text.append(aPatch.toString());\n  }\n  return text;\n}\n\n\nQList<Patch> diff_match_patch::patch_fromText(const QString &textline) {\n  QList<Patch> patches;\n  if (textline.isEmpty()) {\n    return patches;\n  }\n  QStringList text = textline.split(\"\\n\", QString::SkipEmptyParts);\n  Patch patch;\n  QRegExp patchHeader(\"^@@ -(\\\\d+),?(\\\\d*) \\\\+(\\\\d+),?(\\\\d*) @@$\");\n  char sign;\n  QString line;\n  while (!text.isEmpty()) {\n    if (!patchHeader.exactMatch(text.front())) {\n      throw QString(\"Invalid patch string: %1\").arg(text.front());\n    }\n\n    patch = Patch();\n    patch.start1 = patchHeader.cap(1).toInt();\n    if (patchHeader.cap(2).isEmpty()) {\n      patch.start1--;\n      patch.length1 = 1;\n    } else if (patchHeader.cap(2) == \"0\") {\n      patch.length1 = 0;\n    } else {\n      patch.start1--;\n      patch.length1 = patchHeader.cap(2).toInt();\n    }\n\n    patch.start2 = patchHeader.cap(3).toInt();\n    if (patchHeader.cap(4).isEmpty()) {\n      patch.start2--;\n      patch.length2 = 1;\n    } else if (patchHeader.cap(4) == \"0\") {\n      patch.length2 = 0;\n    } else {\n      patch.start2--;\n      patch.length2 = patchHeader.cap(4).toInt();\n    }\n    text.removeFirst();\n\n    while (!text.isEmpty()) {\n      if (text.front().isEmpty()) {\n        text.removeFirst();\n        continue;\n      }\n      sign = text.front()[0].toAscii();\n      line = safeMid(text.front(), 1);\n      line = line.replace(\"+\", \"%2B\");  // decode would change all \"+\" to \" \"\n      line = QUrl::fromPercentEncoding(qPrintable(line));\n      if (sign == '-') {\n        // Deletion.\n        patch.diffs.append(Diff(DELETE, line));\n      } else if (sign == '+') {\n        // Insertion.\n        patch.diffs.append(Diff(INSERT, line));\n      } else if (sign == ' ') {\n        // Minor equality.\n        patch.diffs.append(Diff(EQUAL, line));\n      } else if (sign == '@') {\n        // Start of next patch.\n        break;\n      } else {\n        // WTF?\n        throw QString(\"Invalid patch mode '%1' in: %2\").arg(sign).arg(line);\n        return QList<Patch>();\n      }\n      text.removeFirst();\n    }\n\n    patches.append(patch);\n\n  }\n  return patches;\n}\n"
  },
  {
    "path": "cpp/diff_match_patch.h",
    "content": "/*\n * Diff Match and Patch\n * Copyright 2018 The diff-match-patch Authors.\n * https://github.com/google/diff-match-patch\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *   http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#ifndef DIFF_MATCH_PATCH_H\n#define DIFF_MATCH_PATCH_H\n\n/*\n * Functions for diff, match and patch.\n * Computes the difference between two texts to create a patch.\n * Applies the patch onto another text, allowing for errors.\n *\n * @author fraser@google.com (Neil Fraser)\n *\n * Qt/C++ port by mikeslemmer@gmail.com (Mike Slemmer):\n *\n * Code known to compile and run with Qt 4.3 through Qt 4.7.\n *\n * Here is a trivial sample program which works properly when linked with this\n * library:\n *\n\n #include <QtCore>\n #include <QString>\n #include <QList>\n #include <QMap>\n #include <QVariant>\n #include \"diff_match_patch.h\"\n int main(int argc, char **argv) {\n   diff_match_patch dmp;\n   QString str1 = QString(\"First string in diff\");\n   QString str2 = QString(\"Second string in diff\");\n\n   QString strPatch = dmp.patch_toText(dmp.patch_make(str1, str2));\n   QPair<QString, QVector<bool> > out\n       = dmp.patch_apply(dmp.patch_fromText(strPatch), str1);\n   QString strResult = out.first;\n\n   // here, strResult will equal str2 above.\n   return 0;\n }\n\n */\n\n\n/**-\n* The data structure representing a diff is a Linked list of Diff objects:\n* {Diff(Operation.DELETE, \"Hello\"), Diff(Operation.INSERT, \"Goodbye\"),\n*  Diff(Operation.EQUAL, \" world.\")}\n* which means: delete \"Hello\", add \"Goodbye\" and keep \" world.\"\n*/\nenum Operation {\n  DELETE, INSERT, EQUAL\n};\n\n\n/**\n* Class representing one diff operation.\n*/\nclass Diff {\n public:\n  Operation operation;\n  // One of: INSERT, DELETE or EQUAL.\n  QString text;\n  // The text associated with this diff operation.\n\n  /**\n   * Constructor.  Initializes the diff with the provided values.\n   * @param operation One of INSERT, DELETE or EQUAL.\n   * @param text The text being applied.\n   */\n  Diff(Operation _operation, const QString &_text);\n  Diff();\n  inline bool isNull() const;\n  QString toString() const;\n  bool operator==(const Diff &d) const;\n  bool operator!=(const Diff &d) const;\n\n  static QString strOperation(Operation op);\n};\n\n\n/**\n* Class representing one patch operation.\n*/\nclass Patch {\n public:\n  QList<Diff> diffs;\n  int start1;\n  int start2;\n  int length1;\n  int length2;\n\n  /**\n   * Constructor.  Initializes with an empty list of diffs.\n   */\n  Patch();\n  bool isNull() const;\n  QString toString();\n};\n\n\n/**\n * Class containing the diff, match and patch methods.\n * Also contains the behaviour settings.\n */\nclass diff_match_patch {\n\n  friend class diff_match_patch_test;\n\n public:\n  // Defaults.\n  // Set these on your diff_match_patch instance to override the defaults.\n\n  // Number of seconds to map a diff before giving up (0 for infinity).\n  float Diff_Timeout;\n  // Cost of an empty edit operation in terms of edit characters.\n  short Diff_EditCost;\n  // At what point is no match declared (0.0 = perfection, 1.0 = very loose).\n  float Match_Threshold;\n  // How far to search for a match (0 = exact location, 1000+ = broad match).\n  // A match this many characters away from the expected location will add\n  // 1.0 to the score (0.0 is a perfect match).\n  int Match_Distance;\n  // When deleting a large block of text (over ~64 characters), how close does\n  // the contents have to match the expected contents. (0.0 = perfection,\n  // 1.0 = very loose).  Note that Match_Threshold controls how closely the\n  // end points of a delete need to match.\n  float Patch_DeleteThreshold;\n  // Chunk size for context length.\n  short Patch_Margin;\n\n  // The number of bits in an int.\n  short Match_MaxBits;\n\n private:\n  // Define some regex patterns for matching boundaries.\n  static QRegExp BLANKLINEEND;\n  static QRegExp BLANKLINESTART;\n\n\n public:\n\n  diff_match_patch();\n\n  //  DIFF FUNCTIONS\n\n\n  /**\n   * Find the differences between two texts.\n   * Run a faster slightly less optimal diff.\n   * This method allows the 'checklines' of diff_main() to be optional.\n   * Most of the time checklines is wanted, so default to true.\n   * @param text1 Old string to be diffed.\n   * @param text2 New string to be diffed.\n   * @return Linked List of Diff objects.\n   */\n  QList<Diff> diff_main(const QString &text1, const QString &text2);\n\n  /**\n   * Find the differences between two texts.\n   * @param text1 Old string to be diffed.\n   * @param text2 New string to be diffed.\n   * @param checklines Speedup flag.  If false, then don't run a\n   *     line-level diff first to identify the changed areas.\n   *     If true, then run a faster slightly less optimal diff.\n   * @return Linked List of Diff objects.\n   */\n  QList<Diff> diff_main(const QString &text1, const QString &text2, bool checklines);\n\n  /**\n   * Find the differences between two texts.  Simplifies the problem by\n   * stripping any common prefix or suffix off the texts before diffing.\n   * @param text1 Old string to be diffed.\n   * @param text2 New string to be diffed.\n   * @param checklines Speedup flag.  If false, then don't run a\n   *     line-level diff first to identify the changed areas.\n   *     If true, then run a faster slightly less optimal diff.\n   * @param deadline Time when the diff should be complete by.  Used\n   *     internally for recursive calls.  Users should set DiffTimeout instead.\n   * @return Linked List of Diff objects.\n   */\n private:\n  QList<Diff> diff_main(const QString &text1, const QString &text2, bool checklines, clock_t deadline);\n\n  /**\n   * Find the differences between two texts.  Assumes that the texts do not\n   * have any common prefix or suffix.\n   * @param text1 Old string to be diffed.\n   * @param text2 New string to be diffed.\n   * @param checklines Speedup flag.  If false, then don't run a\n   *     line-level diff first to identify the changed areas.\n   *     If true, then run a faster slightly less optimal diff.\n   * @param deadline Time when the diff should be complete by.\n   * @return Linked List of Diff objects.\n   */\n private:\n  QList<Diff> diff_compute(QString text1, QString text2, bool checklines, clock_t deadline);\n\n  /**\n   * Do a quick line-level diff on both strings, then rediff the parts for\n   * greater accuracy.\n   * This speedup can produce non-minimal diffs.\n   * @param text1 Old string to be diffed.\n   * @param text2 New string to be diffed.\n   * @param deadline Time when the diff should be complete by.\n   * @return Linked List of Diff objects.\n   */\n private:\n  QList<Diff> diff_lineMode(QString text1, QString text2, clock_t deadline);\n\n  /**\n   * Find the 'middle snake' of a diff, split the problem in two\n   * and return the recursively constructed diff.\n   * See Myers 1986 paper: An O(ND) Difference Algorithm and Its Variations.\n   * @param text1 Old string to be diffed.\n   * @param text2 New string to be diffed.\n   * @return Linked List of Diff objects.\n   */\n protected:\n  QList<Diff> diff_bisect(const QString &text1, const QString &text2, clock_t deadline);\n\n  /**\n   * Given the location of the 'middle snake', split the diff in two parts\n   * and recurse.\n   * @param text1 Old string to be diffed.\n   * @param text2 New string to be diffed.\n   * @param x Index of split point in text1.\n   * @param y Index of split point in text2.\n   * @param deadline Time at which to bail if not yet complete.\n   * @return LinkedList of Diff objects.\n   */\n private:\n  QList<Diff> diff_bisectSplit(const QString &text1, const QString &text2, int x, int y, clock_t deadline);\n\n  /**\n   * Split two texts into a list of strings.  Reduce the texts to a string of\n   * hashes where each Unicode character represents one line.\n   * @param text1 First string.\n   * @param text2 Second string.\n   * @return Three element Object array, containing the encoded text1, the\n   *     encoded text2 and the List of unique strings.  The zeroth element\n   *     of the List of unique strings is intentionally blank.\n   */\n protected:\n  QList<QVariant> diff_linesToChars(const QString &text1, const QString &text2); // return elems 0 and 1 are QString, elem 2 is QStringList\n\n  /**\n   * Split a text into a list of strings.  Reduce the texts to a string of\n   * hashes where each Unicode character represents one line.\n   * @param text String to encode.\n   * @param lineArray List of unique strings.\n   * @param lineHash Map of strings to indices.\n   * @return Encoded string.\n   */\n private:\n  QString diff_linesToCharsMunge(const QString &text, QStringList &lineArray,\n                                 QMap<QString, int> &lineHash);\n\n  /**\n   * Rehydrate the text in a diff from a string of line hashes to real lines of\n   * text.\n   * @param diffs LinkedList of Diff objects.\n   * @param lineArray List of unique strings.\n   */\n private:\n  void diff_charsToLines(QList<Diff> &diffs, const QStringList &lineArray);\n\n  /**\n   * Determine the common prefix of two strings.\n   * @param text1 First string.\n   * @param text2 Second string.\n   * @return The number of characters common to the start of each string.\n   */\n public:\n  int diff_commonPrefix(const QString &text1, const QString &text2);\n\n  /**\n   * Determine the common suffix of two strings.\n   * @param text1 First string.\n   * @param text2 Second string.\n   * @return The number of characters common to the end of each string.\n   */\n public:\n  int diff_commonSuffix(const QString &text1, const QString &text2);\n\n  /**\n   * Determine if the suffix of one string is the prefix of another.\n   * @param text1 First string.\n   * @param text2 Second string.\n   * @return The number of characters common to the end of the first\n   *     string and the start of the second string.\n   */\n protected:\n  int diff_commonOverlap(const QString &text1, const QString &text2);\n\n  /**\n   * Do the two texts share a substring which is at least half the length of\n   * the longer text?\n   * This speedup can produce non-minimal diffs.\n   * @param text1 First string.\n   * @param text2 Second string.\n   * @return Five element String array, containing the prefix of text1, the\n   *     suffix of text1, the prefix of text2, the suffix of text2 and the\n   *     common middle.  Or null if there was no match.\n   */\n protected:\n  QStringList diff_halfMatch(const QString &text1, const QString &text2);\n\n  /**\n   * Does a substring of shorttext exist within longtext such that the\n   * substring is at least half the length of longtext?\n   * @param longtext Longer string.\n   * @param shorttext Shorter string.\n   * @param i Start index of quarter length substring within longtext.\n   * @return Five element String array, containing the prefix of longtext, the\n   *     suffix of longtext, the prefix of shorttext, the suffix of shorttext\n   *     and the common middle.  Or null if there was no match.\n   */\n private:\n  QStringList diff_halfMatchI(const QString &longtext, const QString &shorttext, int i);\n\n  /**\n   * Reduce the number of edits by eliminating semantically trivial equalities.\n   * @param diffs LinkedList of Diff objects.\n   */\n public:\n  void diff_cleanupSemantic(QList<Diff> &diffs);\n\n  /**\n   * Look for single edits surrounded on both sides by equalities\n   * which can be shifted sideways to align the edit to a word boundary.\n   * e.g: The c<ins>at c</ins>ame. -> The <ins>cat </ins>came.\n   * @param diffs LinkedList of Diff objects.\n   */\n public:\n  void diff_cleanupSemanticLossless(QList<Diff> &diffs);\n\n  /**\n   * Given two strings, compute a score representing whether the internal\n   * boundary falls on logical boundaries.\n   * Scores range from 6 (best) to 0 (worst).\n   * @param one First string.\n   * @param two Second string.\n   * @return The score.\n   */\n private:\n  int diff_cleanupSemanticScore(const QString &one, const QString &two);\n\n  /**\n   * Reduce the number of edits by eliminating operationally trivial equalities.\n   * @param diffs LinkedList of Diff objects.\n   */\n public:\n  void diff_cleanupEfficiency(QList<Diff> &diffs);\n\n  /**\n   * Reorder and merge like edit sections.  Merge equalities.\n   * Any edit section can move as long as it doesn't cross an equality.\n   * @param diffs LinkedList of Diff objects.\n   */\n public:\n  void diff_cleanupMerge(QList<Diff> &diffs);\n\n  /**\n   * loc is a location in text1, compute and return the equivalent location in\n   * text2.\n   * e.g. \"The cat\" vs \"The big cat\", 1->1, 5->8\n   * @param diffs LinkedList of Diff objects.\n   * @param loc Location within text1.\n   * @return Location within text2.\n   */\n public:\n  int diff_xIndex(const QList<Diff> &diffs, int loc);\n\n  /**\n   * Convert a Diff list into a pretty HTML report.\n   * @param diffs LinkedList of Diff objects.\n   * @return HTML representation.\n   */\n public:\n  QString diff_prettyHtml(const QList<Diff> &diffs);\n\n  /**\n   * Compute and return the source text (all equalities and deletions).\n   * @param diffs LinkedList of Diff objects.\n   * @return Source text.\n   */\n public:\n  QString diff_text1(const QList<Diff> &diffs);\n\n  /**\n   * Compute and return the destination text (all equalities and insertions).\n   * @param diffs LinkedList of Diff objects.\n   * @return Destination text.\n   */\n public:\n  QString diff_text2(const QList<Diff> &diffs);\n\n  /**\n   * Compute the Levenshtein distance; the number of inserted, deleted or\n   * substituted characters.\n   * @param diffs LinkedList of Diff objects.\n   * @return Number of changes.\n   */\n public:\n  int diff_levenshtein(const QList<Diff> &diffs);\n\n  /**\n   * Crush the diff into an encoded string which describes the operations\n   * required to transform text1 into text2.\n   * E.g. =3\\t-2\\t+ing  -> Keep 3 chars, delete 2 chars, insert 'ing'.\n   * Operations are tab-separated.  Inserted text is escaped using %xx notation.\n   * @param diffs Array of diff tuples.\n   * @return Delta text.\n   */\n public:\n  QString diff_toDelta(const QList<Diff> &diffs);\n\n  /**\n   * Given the original text1, and an encoded string which describes the\n   * operations required to transform text1 into text2, compute the full diff.\n   * @param text1 Source string for the diff.\n   * @param delta Delta text.\n   * @return Array of diff tuples or null if invalid.\n   * @throws QString If invalid input.\n   */\n public:\n  QList<Diff> diff_fromDelta(const QString &text1, const QString &delta);\n\n\n  //  MATCH FUNCTIONS\n\n\n  /**\n   * Locate the best instance of 'pattern' in 'text' near 'loc'.\n   * Returns -1 if no match found.\n   * @param text The text to search.\n   * @param pattern The pattern to search for.\n   * @param loc The location to search around.\n   * @return Best match index or -1.\n   */\n public:\n  int match_main(const QString &text, const QString &pattern, int loc);\n\n  /**\n   * Locate the best instance of 'pattern' in 'text' near 'loc' using the\n   * Bitap algorithm.  Returns -1 if no match found.\n   * @param text The text to search.\n   * @param pattern The pattern to search for.\n   * @param loc The location to search around.\n   * @return Best match index or -1.\n   */\n protected:\n  int match_bitap(const QString &text, const QString &pattern, int loc);\n\n  /**\n   * Compute and return the score for a match with e errors and x location.\n   * @param e Number of errors in match.\n   * @param x Location of match.\n   * @param loc Expected location of match.\n   * @param pattern Pattern being sought.\n   * @return Overall score for match (0.0 = good, 1.0 = bad).\n   */\n private:\n  double match_bitapScore(int e, int x, int loc, const QString &pattern);\n\n  /**\n   * Initialise the alphabet for the Bitap algorithm.\n   * @param pattern The text to encode.\n   * @return Hash of character locations.\n   */\n protected:\n  QMap<QChar, int> match_alphabet(const QString &pattern);\n\n\n //  PATCH FUNCTIONS\n\n\n  /**\n   * Increase the context until it is unique,\n   * but don't let the pattern expand beyond Match_MaxBits.\n   * @param patch The patch to grow.\n   * @param text Source text.\n   */\n protected:\n  void patch_addContext(Patch &patch, const QString &text);\n\n  /**\n   * Compute a list of patches to turn text1 into text2.\n   * A set of diffs will be computed.\n   * @param text1 Old text.\n   * @param text2 New text.\n   * @return LinkedList of Patch objects.\n   */\n public:\n  QList<Patch> patch_make(const QString &text1, const QString &text2);\n\n  /**\n   * Compute a list of patches to turn text1 into text2.\n   * text1 will be derived from the provided diffs.\n   * @param diffs Array of diff tuples for text1 to text2.\n   * @return LinkedList of Patch objects.\n   */\n public:\n  QList<Patch> patch_make(const QList<Diff> &diffs);\n\n  /**\n   * Compute a list of patches to turn text1 into text2.\n   * text2 is ignored, diffs are the delta between text1 and text2.\n   * @param text1 Old text.\n   * @param text2 Ignored.\n   * @param diffs Array of diff tuples for text1 to text2.\n   * @return LinkedList of Patch objects.\n   * @deprecated Prefer patch_make(const QString &text1, const QList<Diff> &diffs).\n   */\n public:\n  QList<Patch> patch_make(const QString &text1, const QString &text2, const QList<Diff> &diffs);\n\n  /**\n   * Compute a list of patches to turn text1 into text2.\n   * text2 is not provided, diffs are the delta between text1 and text2.\n   * @param text1 Old text.\n   * @param diffs Array of diff tuples for text1 to text2.\n   * @return LinkedList of Patch objects.\n   */\n public:\n  QList<Patch> patch_make(const QString &text1, const QList<Diff> &diffs);\n\n  /**\n   * Given an array of patches, return another array that is identical.\n   * @param patches Array of patch objects.\n   * @return Array of patch objects.\n   */\n public:\n  QList<Patch> patch_deepCopy(QList<Patch> &patches);\n\n  /**\n   * Merge a set of patches onto the text.  Return a patched text, as well\n   * as an array of true/false values indicating which patches were applied.\n   * @param patches Array of patch objects.\n   * @param text Old text.\n   * @return Two element Object array, containing the new text and an array of\n   *      boolean values.\n   */\n public:\n  QPair<QString,QVector<bool> > patch_apply(QList<Patch> &patches, const QString &text);\n\n  /**\n   * Add some padding on text start and end so that edges can match something.\n   * Intended to be called only from within patch_apply.\n   * @param patches Array of patch objects.\n   * @return The padding string added to each side.\n   */\n public:\n  QString patch_addPadding(QList<Patch> &patches);\n\n  /**\n   * Look through the patches and break up any which are longer than the\n   * maximum limit of the match algorithm.\n   * Intended to be called only from within patch_apply.\n   * @param patches LinkedList of Patch objects.\n   */\n public:\n  void patch_splitMax(QList<Patch> &patches);\n\n  /**\n   * Take a list of patches and return a textual representation.\n   * @param patches List of Patch objects.\n   * @return Text representation of patches.\n   */\n public:\n  QString patch_toText(const QList<Patch> &patches);\n\n  /**\n   * Parse a textual representation of patches and return a List of Patch\n   * objects.\n   * @param textline Text representation of patches.\n   * @return List of Patch objects.\n   * @throws QString If invalid input.\n   */\n public:\n  QList<Patch> patch_fromText(const QString &textline);\n\n  /**\n   * A safer version of QString.mid(pos).  This one returns \"\" instead of\n   * null when the postion equals the string length.\n   * @param str String to take a substring from.\n   * @param pos Position to start the substring from.\n   * @return Substring.\n   */\n private:\n  static inline QString safeMid(const QString &str, int pos) {\n    return (pos == str.length()) ? QString(\"\") : str.mid(pos);\n  }\n\n  /**\n   * A safer version of QString.mid(pos, len).  This one returns \"\" instead of\n   * null when the postion equals the string length.\n   * @param str String to take a substring from.\n   * @param pos Position to start the substring from.\n   * @param len Length of substring.\n   * @return Substring.\n   */\n private:\n  static inline QString safeMid(const QString &str, int pos, int len) {\n    return (pos == str.length()) ? QString(\"\") : str.mid(pos, len);\n  }\n};\n\n#endif // DIFF_MATCH_PATCH_H\n"
  },
  {
    "path": "cpp/diff_match_patch.pro",
    "content": "#QT += sql xml network\nTEMPLATE = app\nCONFIG += qt debug_and_release\n\nmac {\n  CONFIG -= app_bundle\n}\n\n# don't embed the manifest for now (doesn't work :( )\n#CONFIG -= embed_manifest_exe \n\nFORMS =\n\nHEADERS = diff_match_patch.h diff_match_patch_test.h\n\nSOURCES = diff_match_patch.cpp diff_match_patch_test.cpp\n\nRESOURCES = \n\n"
  },
  {
    "path": "cpp/diff_match_patch_test.cpp",
    "content": "/*\n * Diff Match and Patch -- Test Harness\n * Copyright 2018 The diff-match-patch Authors.\n * https://github.com/google/diff-match-patch\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *   http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n// Code known to compile and run with Qt 4.3 through Qt 4.7.\n#include <QtCore>\n#include \"diff_match_patch.h\"\n#include \"diff_match_patch_test.h\"\n\nint main(int argc, char **argv) {\n  diff_match_patch_test dmp_test;\n  qDebug(\"Starting diff_match_patch unit tests.\");\n  dmp_test.run_all_tests();\n  qDebug(\"Done.\");\n  return 0;\n  Q_UNUSED(argc)\n  Q_UNUSED(argv)\n}\n\n\ndiff_match_patch_test::diff_match_patch_test() {\n}\n\nvoid diff_match_patch_test::run_all_tests() {\n  QTime t;\n  t.start();\n  try {\n    testDiffCommonPrefix();\n    testDiffCommonSuffix();\n    testDiffCommonOverlap();\n    testDiffHalfmatch();\n    testDiffLinesToChars();\n    testDiffCharsToLines();\n    testDiffCleanupMerge();\n    testDiffCleanupSemanticLossless();\n    testDiffCleanupSemantic();\n    testDiffCleanupEfficiency();\n    testDiffPrettyHtml();\n    testDiffText();\n    testDiffDelta();\n    testDiffXIndex();\n    testDiffLevenshtein();\n    testDiffBisect();\n    testDiffMain();\n\n    testMatchAlphabet();\n    testMatchBitap();\n    testMatchMain();\n\n    testPatchObj();\n    testPatchFromText();\n    testPatchToText();\n    testPatchAddContext();\n    testPatchMake();\n    testPatchSplitMax();\n    testPatchAddPadding();\n    testPatchApply();\n    qDebug(\"All tests passed.\");\n  } catch (QString strCase) {\n    qDebug(\"Test failed: %s\", qPrintable(strCase));\n  }\n  qDebug(\"Total time: %d ms\", t.elapsed());\n}\n\n//  DIFF TEST FUNCTIONS\n\nvoid diff_match_patch_test::testDiffCommonPrefix() {\n  // Detect any common prefix.\n  assertEquals(\"diff_commonPrefix: Null case.\", 0, dmp.diff_commonPrefix(\"abc\", \"xyz\"));\n\n  assertEquals(\"diff_commonPrefix: Non-null case.\", 4, dmp.diff_commonPrefix(\"1234abcdef\", \"1234xyz\"));\n\n  assertEquals(\"diff_commonPrefix: Whole case.\", 4, dmp.diff_commonPrefix(\"1234\", \"1234xyz\"));\n}\n\nvoid diff_match_patch_test::testDiffCommonSuffix() {\n  // Detect any common suffix.\n  assertEquals(\"diff_commonSuffix: Null case.\", 0, dmp.diff_commonSuffix(\"abc\", \"xyz\"));\n\n  assertEquals(\"diff_commonSuffix: Non-null case.\", 4, dmp.diff_commonSuffix(\"abcdef1234\", \"xyz1234\"));\n\n  assertEquals(\"diff_commonSuffix: Whole case.\", 4, dmp.diff_commonSuffix(\"1234\", \"xyz1234\"));\n}\n\nvoid diff_match_patch_test::testDiffCommonOverlap() {\n  // Detect any suffix/prefix overlap.\n  assertEquals(\"diff_commonOverlap: Null case.\", 0, dmp.diff_commonOverlap(\"\", \"abcd\"));\n\n  assertEquals(\"diff_commonOverlap: Whole case.\", 3, dmp.diff_commonOverlap(\"abc\", \"abcd\"));\n\n  assertEquals(\"diff_commonOverlap: No overlap.\", 0, dmp.diff_commonOverlap(\"123456\", \"abcd\"));\n\n  assertEquals(\"diff_commonOverlap: Overlap.\", 3, dmp.diff_commonOverlap(\"123456xxx\", \"xxxabcd\"));\n\n  // Some overly clever languages (C#) may treat ligatures as equal to their\n  // component letters.  E.g. U+FB01 == 'fi'\n  assertEquals(\"diff_commonOverlap: Unicode.\", 0, dmp.diff_commonOverlap(\"fi\", QString::fromWCharArray((const wchar_t*) L\"\\ufb01i\", 2)));\n}\n\nvoid diff_match_patch_test::testDiffHalfmatch() {\n  // Detect a halfmatch.\n  dmp.Diff_Timeout = 1;\n  assertEmpty(\"diff_halfMatch: No match #1.\", dmp.diff_halfMatch(\"1234567890\", \"abcdef\"));\n\n  assertEmpty(\"diff_halfMatch: No match #2.\", dmp.diff_halfMatch(\"12345\", \"23\"));\n\n  assertEquals(\"diff_halfMatch: Single Match #1.\", QString(\"12,90,a,z,345678\").split(\",\"), dmp.diff_halfMatch(\"1234567890\", \"a345678z\"));\n\n  assertEquals(\"diff_halfMatch: Single Match #2.\", QString(\"a,z,12,90,345678\").split(\",\"), dmp.diff_halfMatch(\"a345678z\", \"1234567890\"));\n\n  assertEquals(\"diff_halfMatch: Single Match #3.\", QString(\"abc,z,1234,0,56789\").split(\",\"), dmp.diff_halfMatch(\"abc56789z\", \"1234567890\"));\n\n  assertEquals(\"diff_halfMatch: Single Match #4.\", QString(\"a,xyz,1,7890,23456\").split(\",\"), dmp.diff_halfMatch(\"a23456xyz\", \"1234567890\"));\n\n  assertEquals(\"diff_halfMatch: Multiple Matches #1.\", QString(\"12123,123121,a,z,1234123451234\").split(\",\"), dmp.diff_halfMatch(\"121231234123451234123121\", \"a1234123451234z\"));\n\n  assertEquals(\"diff_halfMatch: Multiple Matches #2.\", QString(\",-=-=-=-=-=,x,,x-=-=-=-=-=-=-=\").split(\",\"), dmp.diff_halfMatch(\"x-=-=-=-=-=-=-=-=-=-=-=-=\", \"xx-=-=-=-=-=-=-=\"));\n\n  assertEquals(\"diff_halfMatch: Multiple Matches #3.\", QString(\"-=-=-=-=-=,,,y,-=-=-=-=-=-=-=y\").split(\",\"), dmp.diff_halfMatch(\"-=-=-=-=-=-=-=-=-=-=-=-=y\", \"-=-=-=-=-=-=-=yy\"));\n\n  // Optimal diff would be -q+x=H-i+e=lloHe+Hu=llo-Hew+y not -qHillo+x=HelloHe-w+Hulloy\n  assertEquals(\"diff_halfMatch: Non-optimal halfmatch.\", QString(\"qHillo,w,x,Hulloy,HelloHe\").split(\",\"), dmp.diff_halfMatch(\"qHilloHelloHew\", \"xHelloHeHulloy\"));\n\n  dmp.Diff_Timeout = 0;\n  assertEmpty(\"diff_halfMatch: Optimal no halfmatch.\", dmp.diff_halfMatch(\"qHilloHelloHew\", \"xHelloHeHulloy\"));\n}\n\nvoid diff_match_patch_test::testDiffLinesToChars() {\n  // Convert lines down to characters.\n  QStringList tmpVector;\n  QList<QVariant> tmpVarList;\n  tmpVector.append(\"\");\n  tmpVector.append(\"alpha\\n\");\n  tmpVector.append(\"beta\\n\");\n  tmpVarList << QVariant::fromValue(QString() + QChar((ushort)1) + QChar((ushort)2) + QChar((ushort)1));  //((\"\\u0001\\u0002\\u0001\"));\n  tmpVarList << QVariant::fromValue(QString() + QChar((ushort)2) + QChar((ushort)1) + QChar((ushort)2));  // ((\"\\u0002\\u0001\\u0002\"));\n  tmpVarList << QVariant::fromValue(tmpVector);\n  assertEquals(\"diff_linesToChars:\", tmpVarList, dmp.diff_linesToChars(\"alpha\\nbeta\\nalpha\\n\", \"beta\\nalpha\\nbeta\\n\"));\n\n  tmpVector.clear();\n  tmpVarList.clear();\n  tmpVector.append(\"\");\n  tmpVector.append(\"alpha\\r\\n\");\n  tmpVector.append(\"beta\\r\\n\");\n  tmpVector.append(\"\\r\\n\");\n  tmpVarList << QVariant::fromValue(QString(\"\"));\n  tmpVarList << QVariant::fromValue(QString() + QChar((ushort)1) + QChar((ushort)2) + QChar((ushort)3) + QChar((ushort)3));  // ((\"\\u0001\\u0002\\u0003\\u0003\"));\n  tmpVarList << QVariant::fromValue(tmpVector);\n  assertEquals(\"diff_linesToChars:\", tmpVarList, dmp.diff_linesToChars(\"\", \"alpha\\r\\nbeta\\r\\n\\r\\n\\r\\n\"));\n\n  tmpVector.clear();\n  tmpVarList.clear();\n  tmpVector.append(\"\");\n  tmpVector.append(\"a\");\n  tmpVector.append(\"b\");\n  tmpVarList << QVariant::fromValue(QString() + QChar((ushort)1));  // ((\"\\u0001\"));\n  tmpVarList << QVariant::fromValue(QString() + QChar((ushort)2));  // ((\"\\u0002\"));\n  tmpVarList << QVariant::fromValue(tmpVector);\n  assertEquals(\"diff_linesToChars:\", tmpVarList, dmp.diff_linesToChars(\"a\", \"b\"));\n\n  // More than 256 to reveal any 8-bit limitations.\n  int n = 300;\n  tmpVector.clear();\n  tmpVarList.clear();\n  QString lines;\n  QString chars;\n  for (int x = 1; x < n + 1; x++) {\n    tmpVector.append(QString::number(x) + \"\\n\");\n    lines += QString::number(x) + \"\\n\";\n    chars += QChar(static_cast<ushort>(x));\n  }\n  assertEquals(\"diff_linesToChars: More than 256 (setup).\", n, tmpVector.size());\n  assertEquals(\"diff_linesToChars: More than 256 (setup).\", n, chars.length());\n  tmpVector.prepend(\"\");\n  tmpVarList << QVariant::fromValue(chars);\n  tmpVarList << QVariant::fromValue(QString(\"\"));\n  tmpVarList << QVariant::fromValue(tmpVector);\n  assertEquals(\"diff_linesToChars: More than 256.\", tmpVarList, dmp.diff_linesToChars(lines, \"\"));\n}\n\nvoid diff_match_patch_test::testDiffCharsToLines() {\n  // First check that Diff equality works.\n  assertTrue(\"diff_charsToLines:\", Diff(EQUAL, \"a\") == Diff(EQUAL, \"a\"));\n\n  assertEquals(\"diff_charsToLines:\", Diff(EQUAL, \"a\"), Diff(EQUAL, \"a\"));\n\n  // Convert chars up to lines.\n  QList<Diff> diffs;\n  diffs << Diff(EQUAL, QString() + QChar((ushort)1) + QChar((ushort)2) + QChar((ushort)1));  // (\"\\u0001\\u0002\\u0001\");\n  diffs << Diff(INSERT, QString() + QChar((ushort)2) + QChar((ushort)1) + QChar((ushort)2));  // (\"\\u0002\\u0001\\u0002\");\n  QStringList tmpVector;\n  tmpVector.append(\"\");\n  tmpVector.append(\"alpha\\n\");\n  tmpVector.append(\"beta\\n\");\n  dmp.diff_charsToLines(diffs, tmpVector);\n  assertEquals(\"diff_charsToLines:\", diffList(Diff(EQUAL, \"alpha\\nbeta\\nalpha\\n\"), Diff(INSERT, \"beta\\nalpha\\nbeta\\n\")), diffs);\n\n  // More than 256 to reveal any 8-bit limitations.\n  int n = 300;\n  tmpVector.clear();\n  QList<QVariant> tmpVarList;\n  QString lines;\n  QString chars;\n  for (int x = 1; x < n + 1; x++) {\n    tmpVector.append(QString::number(x) + \"\\n\");\n    lines += QString::number(x) + \"\\n\";\n    chars += QChar(static_cast<ushort>(x));\n  }\n  assertEquals(\"diff_linesToChars: More than 256 (setup).\", n, tmpVector.size());\n  assertEquals(\"diff_linesToChars: More than 256 (setup).\", n, chars.length());\n  tmpVector.prepend(\"\");\n  diffs = diffList(Diff(DELETE, chars));\n  dmp.diff_charsToLines(diffs, tmpVector);\n  assertEquals(\"diff_charsToLines: More than 256.\", diffList(Diff(DELETE, lines)), diffs);\n}\n\nvoid diff_match_patch_test::testDiffCleanupMerge() {\n  // Cleanup a messy diff.\n  QList<Diff> diffs;\n  dmp.diff_cleanupMerge(diffs);\n  assertEquals(\"diff_cleanupMerge: Null case.\", diffList(), diffs);\n\n  diffs = diffList(Diff(EQUAL, \"a\"), Diff(DELETE, \"b\"), Diff(INSERT, \"c\"));\n  dmp.diff_cleanupMerge(diffs);\n  assertEquals(\"diff_cleanupMerge: No change case.\", diffList(Diff(EQUAL, \"a\"), Diff(DELETE, \"b\"), Diff(INSERT, \"c\")), diffs);\n\n  diffs = diffList(Diff(EQUAL, \"a\"), Diff(EQUAL, \"b\"), Diff(EQUAL, \"c\"));\n  dmp.diff_cleanupMerge(diffs);\n  assertEquals(\"diff_cleanupMerge: Merge equalities.\", diffList(Diff(EQUAL, \"abc\")), diffs);\n\n  diffs = diffList(Diff(DELETE, \"a\"), Diff(DELETE, \"b\"), Diff(DELETE, \"c\"));\n  dmp.diff_cleanupMerge(diffs);\n  assertEquals(\"diff_cleanupMerge: Merge deletions.\", diffList(Diff(DELETE, \"abc\")), diffs);\n\n  diffs = diffList(Diff(INSERT, \"a\"), Diff(INSERT, \"b\"), Diff(INSERT, \"c\"));\n  dmp.diff_cleanupMerge(diffs);\n  assertEquals(\"diff_cleanupMerge: Merge insertions.\", diffList(Diff(INSERT, \"abc\")), diffs);\n\n  diffs = diffList(Diff(DELETE, \"a\"), Diff(INSERT, \"b\"), Diff(DELETE, \"c\"), Diff(INSERT, \"d\"), Diff(EQUAL, \"e\"), Diff(EQUAL, \"f\"));\n  dmp.diff_cleanupMerge(diffs);\n  assertEquals(\"diff_cleanupMerge: Merge interweave.\", diffList(Diff(DELETE, \"ac\"), Diff(INSERT, \"bd\"), Diff(EQUAL, \"ef\")), diffs);\n\n  diffs = diffList(Diff(DELETE, \"a\"), Diff(INSERT, \"abc\"), Diff(DELETE, \"dc\"));\n  dmp.diff_cleanupMerge(diffs);\n  assertEquals(\"diff_cleanupMerge: Prefix and suffix detection.\", diffList(Diff(EQUAL, \"a\"), Diff(DELETE, \"d\"), Diff(INSERT, \"b\"), Diff(EQUAL, \"c\")), diffs);\n\n  diffs = diffList(Diff(EQUAL, \"x\"), Diff(DELETE, \"a\"), Diff(INSERT, \"abc\"), Diff(DELETE, \"dc\"), Diff(EQUAL, \"y\"));\n  dmp.diff_cleanupMerge(diffs);\n  assertEquals(\"diff_cleanupMerge: Prefix and suffix detection with equalities.\", diffList(Diff(EQUAL, \"xa\"), Diff(DELETE, \"d\"), Diff(INSERT, \"b\"), Diff(EQUAL, \"cy\")), diffs);\n\n  diffs = diffList(Diff(EQUAL, \"a\"), Diff(INSERT, \"ba\"), Diff(EQUAL, \"c\"));\n  dmp.diff_cleanupMerge(diffs);\n  assertEquals(\"diff_cleanupMerge: Slide edit left.\", diffList(Diff(INSERT, \"ab\"), Diff(EQUAL, \"ac\")), diffs);\n\n  diffs = diffList(Diff(EQUAL, \"c\"), Diff(INSERT, \"ab\"), Diff(EQUAL, \"a\"));\n  dmp.diff_cleanupMerge(diffs);\n  assertEquals(\"diff_cleanupMerge: Slide edit right.\", diffList(Diff(EQUAL, \"ca\"), Diff(INSERT, \"ba\")), diffs);\n\n  diffs = diffList(Diff(EQUAL, \"a\"), Diff(DELETE, \"b\"), Diff(EQUAL, \"c\"), Diff(DELETE, \"ac\"), Diff(EQUAL, \"x\"));\n  dmp.diff_cleanupMerge(diffs);\n  assertEquals(\"diff_cleanupMerge: Slide edit left recursive.\", diffList(Diff(DELETE, \"abc\"), Diff(EQUAL, \"acx\")), diffs);\n\n  diffs = diffList(Diff(EQUAL, \"x\"), Diff(DELETE, \"ca\"), Diff(EQUAL, \"c\"), Diff(DELETE, \"b\"), Diff(EQUAL, \"a\"));\n  dmp.diff_cleanupMerge(diffs);\n  assertEquals(\"diff_cleanupMerge: Slide edit right recursive.\", diffList(Diff(EQUAL, \"xca\"), Diff(DELETE, \"cba\")), diffs);\n}\n\nvoid diff_match_patch_test::testDiffCleanupSemanticLossless() {\n  // Slide diffs to match logical boundaries.\n  QList<Diff> diffs = diffList();\n  dmp.diff_cleanupSemanticLossless(diffs);\n  assertEquals(\"diff_cleanupSemantic: Null case.\", diffList(), diffs);\n\n  diffs = diffList(Diff(EQUAL, \"AAA\\r\\n\\r\\nBBB\"), Diff(INSERT, \"\\r\\nDDD\\r\\n\\r\\nBBB\"), Diff(EQUAL, \"\\r\\nEEE\"));\n  dmp.diff_cleanupSemanticLossless(diffs);\n  assertEquals(\"diff_cleanupSemanticLossless: Blank lines.\", diffList(Diff(EQUAL, \"AAA\\r\\n\\r\\n\"), Diff(INSERT, \"BBB\\r\\nDDD\\r\\n\\r\\n\"), Diff(EQUAL, \"BBB\\r\\nEEE\")), diffs);\n\n  diffs = diffList(Diff(EQUAL, \"AAA\\r\\nBBB\"), Diff(INSERT, \" DDD\\r\\nBBB\"), Diff(EQUAL, \" EEE\"));\n  dmp.diff_cleanupSemanticLossless(diffs);\n  assertEquals(\"diff_cleanupSemanticLossless: Line boundaries.\", diffList(Diff(EQUAL, \"AAA\\r\\n\"), Diff(INSERT, \"BBB DDD\\r\\n\"), Diff(EQUAL, \"BBB EEE\")), diffs);\n\n  diffs = diffList(Diff(EQUAL, \"The c\"), Diff(INSERT, \"ow and the c\"), Diff(EQUAL, \"at.\"));\n  dmp.diff_cleanupSemanticLossless(diffs);\n  assertEquals(\"diff_cleanupSemantic: Word boundaries.\", diffList(Diff(EQUAL, \"The \"), Diff(INSERT, \"cow and the \"), Diff(EQUAL, \"cat.\")), diffs);\n\n  diffs = diffList(Diff(EQUAL, \"The-c\"), Diff(INSERT, \"ow-and-the-c\"), Diff(EQUAL, \"at.\"));\n  dmp.diff_cleanupSemanticLossless(diffs);\n  assertEquals(\"diff_cleanupSemantic: Alphanumeric boundaries.\", diffList(Diff(EQUAL, \"The-\"), Diff(INSERT, \"cow-and-the-\"), Diff(EQUAL, \"cat.\")), diffs);\n\n  diffs = diffList(Diff(EQUAL, \"a\"), Diff(DELETE, \"a\"), Diff(EQUAL, \"ax\"));\n  dmp.diff_cleanupSemanticLossless(diffs);\n  assertEquals(\"diff_cleanupSemantic: Hitting the start.\", diffList(Diff(DELETE, \"a\"), Diff(EQUAL, \"aax\")), diffs);\n\n  diffs = diffList(Diff(EQUAL, \"xa\"), Diff(DELETE, \"a\"), Diff(EQUAL, \"a\"));\n  dmp.diff_cleanupSemanticLossless(diffs);\n  assertEquals(\"diff_cleanupSemantic: Hitting the end.\", diffList(Diff(EQUAL, \"xaa\"), Diff(DELETE, \"a\")), diffs);\n\n  diffs = diffList(Diff(EQUAL, \"The xxx. The \"), Diff(INSERT, \"zzz. The \"), Diff(EQUAL, \"yyy.\"));\n  dmp.diff_cleanupSemanticLossless(diffs);\n  assertEquals(\"diff_cleanupSemantic: Sentence boundaries.\", diffList(Diff(EQUAL, \"The xxx.\"), Diff(INSERT, \" The zzz.\"), Diff(EQUAL, \" The yyy.\")), diffs);\n}\n\nvoid diff_match_patch_test::testDiffCleanupSemantic() {\n  // Cleanup semantically trivial equalities.\n  QList<Diff> diffs = diffList();\n  dmp.diff_cleanupSemantic(diffs);\n  assertEquals(\"diff_cleanupSemantic: Null case.\", diffList(), diffs);\n\n  diffs = diffList(Diff(DELETE, \"ab\"), Diff(INSERT, \"cd\"), Diff(EQUAL, \"12\"), Diff(DELETE, \"e\"));\n  dmp.diff_cleanupSemantic(diffs);\n  assertEquals(\"diff_cleanupSemantic: No elimination #1.\", diffList(Diff(DELETE, \"ab\"), Diff(INSERT, \"cd\"), Diff(EQUAL, \"12\"), Diff(DELETE, \"e\")), diffs);\n\n  diffs = diffList(Diff(DELETE, \"abc\"), Diff(INSERT, \"ABC\"), Diff(EQUAL, \"1234\"), Diff(DELETE, \"wxyz\"));\n  dmp.diff_cleanupSemantic(diffs);\n  assertEquals(\"diff_cleanupSemantic: No elimination #2.\", diffList(Diff(DELETE, \"abc\"), Diff(INSERT, \"ABC\"), Diff(EQUAL, \"1234\"), Diff(DELETE, \"wxyz\")), diffs);\n\n  diffs = diffList(Diff(DELETE, \"a\"), Diff(EQUAL, \"b\"), Diff(DELETE, \"c\"));\n  dmp.diff_cleanupSemantic(diffs);\n  assertEquals(\"diff_cleanupSemantic: Simple elimination.\", diffList(Diff(DELETE, \"abc\"), Diff(INSERT, \"b\")), diffs);\n\n  diffs = diffList(Diff(DELETE, \"ab\"), Diff(EQUAL, \"cd\"), Diff(DELETE, \"e\"), Diff(EQUAL, \"f\"), Diff(INSERT, \"g\"));\n  dmp.diff_cleanupSemantic(diffs);\n  assertEquals(\"diff_cleanupSemantic: Backpass elimination.\", diffList(Diff(DELETE, \"abcdef\"), Diff(INSERT, \"cdfg\")), diffs);\n\n  diffs = diffList(Diff(INSERT, \"1\"), Diff(EQUAL, \"A\"), Diff(DELETE, \"B\"), Diff(INSERT, \"2\"), Diff(EQUAL, \"_\"), Diff(INSERT, \"1\"), Diff(EQUAL, \"A\"), Diff(DELETE, \"B\"), Diff(INSERT, \"2\"));\n  dmp.diff_cleanupSemantic(diffs);\n  assertEquals(\"diff_cleanupSemantic: Multiple elimination.\", diffList(Diff(DELETE, \"AB_AB\"), Diff(INSERT, \"1A2_1A2\")), diffs);\n\n  diffs = diffList(Diff(EQUAL, \"The c\"), Diff(DELETE, \"ow and the c\"), Diff(EQUAL, \"at.\"));\n  dmp.diff_cleanupSemantic(diffs);\n  assertEquals(\"diff_cleanupSemantic: Word boundaries.\", diffList(Diff(EQUAL, \"The \"), Diff(DELETE, \"cow and the \"), Diff(EQUAL, \"cat.\")), diffs);\n\n  diffs = diffList(Diff(DELETE, \"abcxx\"), Diff(INSERT, \"xxdef\"));\n  dmp.diff_cleanupSemantic(diffs);\n  assertEquals(\"diff_cleanupSemantic: No overlap elimination.\", diffList(Diff(DELETE, \"abcxx\"), Diff(INSERT, \"xxdef\")), diffs);\n\n  diffs = diffList(Diff(DELETE, \"abcxxx\"), Diff(INSERT, \"xxxdef\"));\n  dmp.diff_cleanupSemantic(diffs);\n  assertEquals(\"diff_cleanupSemantic: Overlap elimination.\", diffList(Diff(DELETE, \"abc\"), Diff(EQUAL, \"xxx\"), Diff(INSERT, \"def\")), diffs);\n\n  diffs = diffList(Diff(DELETE, \"xxxabc\"), Diff(INSERT, \"defxxx\"));\n  dmp.diff_cleanupSemantic(diffs);\n  assertEquals(\"diff_cleanupSemantic: Reverse overlap elimination.\", diffList(Diff(INSERT, \"def\"), Diff(EQUAL, \"xxx\"), Diff(DELETE, \"abc\")), diffs);\n\n  diffs = diffList(Diff(DELETE, \"abcd1212\"), Diff(INSERT, \"1212efghi\"), Diff(EQUAL, \"----\"), Diff(DELETE, \"A3\"), Diff(INSERT, \"3BC\"));\n  dmp.diff_cleanupSemantic(diffs);\n  assertEquals(\"diff_cleanupSemantic: Two overlap eliminations.\", diffList(Diff(DELETE, \"abcd\"), Diff(EQUAL, \"1212\"), Diff(INSERT, \"efghi\"), Diff(EQUAL, \"----\"), Diff(DELETE, \"A\"), Diff(EQUAL, \"3\"), Diff(INSERT, \"BC\")), diffs);\n}\n\nvoid diff_match_patch_test::testDiffCleanupEfficiency() {\n  // Cleanup operationally trivial equalities.\n  dmp.Diff_EditCost = 4;\n  QList<Diff> diffs = diffList();\n  dmp.diff_cleanupEfficiency(diffs);\n  assertEquals(\"diff_cleanupEfficiency: Null case.\", diffList(), diffs);\n\n  diffs = diffList(Diff(DELETE, \"ab\"), Diff(INSERT, \"12\"), Diff(EQUAL, \"wxyz\"), Diff(DELETE, \"cd\"), Diff(INSERT, \"34\"));\n  dmp.diff_cleanupEfficiency(diffs);\n  assertEquals(\"diff_cleanupEfficiency: No elimination.\", diffList(Diff(DELETE, \"ab\"), Diff(INSERT, \"12\"), Diff(EQUAL, \"wxyz\"), Diff(DELETE, \"cd\"), Diff(INSERT, \"34\")), diffs);\n\n  diffs = diffList(Diff(DELETE, \"ab\"), Diff(INSERT, \"12\"), Diff(EQUAL, \"xyz\"), Diff(DELETE, \"cd\"), Diff(INSERT, \"34\"));\n  dmp.diff_cleanupEfficiency(diffs);\n  assertEquals(\"diff_cleanupEfficiency: Four-edit elimination.\", diffList(Diff(DELETE, \"abxyzcd\"), Diff(INSERT, \"12xyz34\")), diffs);\n\n  diffs = diffList(Diff(INSERT, \"12\"), Diff(EQUAL, \"x\"), Diff(DELETE, \"cd\"), Diff(INSERT, \"34\"));\n  dmp.diff_cleanupEfficiency(diffs);\n  assertEquals(\"diff_cleanupEfficiency: Three-edit elimination.\", diffList(Diff(DELETE, \"xcd\"), Diff(INSERT, \"12x34\")), diffs);\n\n  diffs = diffList(Diff(DELETE, \"ab\"), Diff(INSERT, \"12\"), Diff(EQUAL, \"xy\"), Diff(INSERT, \"34\"), Diff(EQUAL, \"z\"), Diff(DELETE, \"cd\"), Diff(INSERT, \"56\"));\n  dmp.diff_cleanupEfficiency(diffs);\n  assertEquals(\"diff_cleanupEfficiency: Backpass elimination.\", diffList(Diff(DELETE, \"abxyzcd\"), Diff(INSERT, \"12xy34z56\")), diffs);\n\n  dmp.Diff_EditCost = 5;\n  diffs = diffList(Diff(DELETE, \"ab\"), Diff(INSERT, \"12\"), Diff(EQUAL, \"wxyz\"), Diff(DELETE, \"cd\"), Diff(INSERT, \"34\"));\n  dmp.diff_cleanupEfficiency(diffs);\n  assertEquals(\"diff_cleanupEfficiency: High cost elimination.\", diffList(Diff(DELETE, \"abwxyzcd\"), Diff(INSERT, \"12wxyz34\")), diffs);\n  dmp.Diff_EditCost = 4;\n}\n\nvoid diff_match_patch_test::testDiffPrettyHtml() {\n  // Pretty print.\n  QList<Diff> diffs = diffList(Diff(EQUAL, \"a\\n\"), Diff(DELETE, \"<B>b</B>\"), Diff(INSERT, \"c&d\"));\n  assertEquals(\"diff_prettyHtml:\", \"<span>a&para;<br></span><del style=\\\"background:#ffe6e6;\\\">&lt;B&gt;b&lt;/B&gt;</del><ins style=\\\"background:#e6ffe6;\\\">c&amp;d</ins>\", dmp.diff_prettyHtml(diffs));\n}\n\nvoid diff_match_patch_test::testDiffText() {\n  // Compute the source and destination texts.\n  QList<Diff> diffs = diffList(Diff(EQUAL, \"jump\"), Diff(DELETE, \"s\"), Diff(INSERT, \"ed\"), Diff(EQUAL, \" over \"), Diff(DELETE, \"the\"), Diff(INSERT, \"a\"), Diff(EQUAL, \" lazy\"));\n  assertEquals(\"diff_text1:\", \"jumps over the lazy\", dmp.diff_text1(diffs));\n  assertEquals(\"diff_text2:\", \"jumped over a lazy\", dmp.diff_text2(diffs));\n}\n\nvoid diff_match_patch_test::testDiffDelta() {\n  // Convert a diff into delta string.\n  QList<Diff> diffs = diffList(Diff(EQUAL, \"jump\"), Diff(DELETE, \"s\"), Diff(INSERT, \"ed\"), Diff(EQUAL, \" over \"), Diff(DELETE, \"the\"), Diff(INSERT, \"a\"), Diff(EQUAL, \" lazy\"), Diff(INSERT, \"old dog\"));\n  QString text1 = dmp.diff_text1(diffs);\n  assertEquals(\"diff_text1: Base text.\", \"jumps over the lazy\", text1);\n\n  QString delta = dmp.diff_toDelta(diffs);\n  assertEquals(\"diff_toDelta:\", \"=4\\t-1\\t+ed\\t=6\\t-3\\t+a\\t=5\\t+old dog\", delta);\n\n  // Convert delta string into a diff.\n  assertEquals(\"diff_fromDelta: Normal.\", diffs, dmp.diff_fromDelta(text1, delta));\n\n  // Generates error (19 < 20).\n  try {\n    dmp.diff_fromDelta(text1 + \"x\", delta);\n    assertFalse(\"diff_fromDelta: Too long.\", true);\n  } catch (QString ex) {\n    // Exception expected.\n  }\n\n  // Generates error (19 > 18).\n  try {\n    dmp.diff_fromDelta(text1.mid(1), delta);\n    assertFalse(\"diff_fromDelta: Too short.\", true);\n  } catch (QString ex) {\n    // Exception expected.\n  }\n\n  // Generates error (%c3%xy invalid Unicode).\n  /* This test does not work because QUrl::fromPercentEncoding(\"%xy\") -> \"?\"\n  try {\n    dmp.diff_fromDelta(\"\", \"+%c3%xy\");\n    assertFalse(\"diff_fromDelta: Invalid character.\", true);\n  } catch (QString ex) {\n    // Exception expected.\n  }\n  */\n\n  // Test deltas with special characters.\n  diffs = diffList(Diff(EQUAL, QString::fromWCharArray((const wchar_t*) L\"\\u0680 \\000 \\t %\", 7)), Diff(DELETE, QString::fromWCharArray((const wchar_t*) L\"\\u0681 \\001 \\n ^\", 7)), Diff(INSERT, QString::fromWCharArray((const wchar_t*) L\"\\u0682 \\002 \\\\ |\", 7)));\n  text1 = dmp.diff_text1(diffs);\n  assertEquals(\"diff_text1: Unicode text.\", QString::fromWCharArray((const wchar_t*) L\"\\u0680 \\000 \\t %\\u0681 \\001 \\n ^\", 14), text1);\n\n  delta = dmp.diff_toDelta(diffs);\n  assertEquals(\"diff_toDelta: Unicode.\", \"=7\\t-7\\t+%DA%82 %02 %5C %7C\", delta);\n\n  assertEquals(\"diff_fromDelta: Unicode.\", diffs, dmp.diff_fromDelta(text1, delta));\n\n  // Verify pool of unchanged characters.\n  diffs = diffList(Diff(INSERT, \"A-Z a-z 0-9 - _ . ! ~ * ' ( ) ; / ? : @ & = + $ , # \"));\n  QString text2 = dmp.diff_text2(diffs);\n  assertEquals(\"diff_text2: Unchanged characters.\", \"A-Z a-z 0-9 - _ . ! ~ * \\' ( ) ; / ? : @ & = + $ , # \", text2);\n\n  delta = dmp.diff_toDelta(diffs);\n  assertEquals(\"diff_toDelta: Unchanged characters.\", \"+A-Z a-z 0-9 - _ . ! ~ * \\' ( ) ; / ? : @ & = + $ , # \", delta);\n\n  // Convert delta string into a diff.\n  assertEquals(\"diff_fromDelta: Unchanged characters.\", diffs, dmp.diff_fromDelta(\"\", delta));\n}\n\nvoid diff_match_patch_test::testDiffXIndex() {\n  // Translate a location in text1 to text2.\n  QList<Diff> diffs = diffList(Diff(DELETE, \"a\"), Diff(INSERT, \"1234\"), Diff(EQUAL, \"xyz\"));\n  assertEquals(\"diff_xIndex: Translation on equality.\", 5, dmp.diff_xIndex(diffs, 2));\n\n  diffs = diffList(Diff(EQUAL, \"a\"), Diff(DELETE, \"1234\"), Diff(EQUAL, \"xyz\"));\n  assertEquals(\"diff_xIndex: Translation on deletion.\", 1, dmp.diff_xIndex(diffs, 3));\n}\n\nvoid diff_match_patch_test::testDiffLevenshtein() {\n  QList<Diff> diffs = diffList(Diff(DELETE, \"abc\"), Diff(INSERT, \"1234\"), Diff(EQUAL, \"xyz\"));\n  assertEquals(\"diff_levenshtein: Trailing equality.\", 4, dmp.diff_levenshtein(diffs));\n\n  diffs = diffList(Diff(EQUAL, \"xyz\"), Diff(DELETE, \"abc\"), Diff(INSERT, \"1234\"));\n  assertEquals(\"diff_levenshtein: Leading equality.\", 4, dmp.diff_levenshtein(diffs));\n\n  diffs = diffList(Diff(DELETE, \"abc\"), Diff(EQUAL, \"xyz\"), Diff(INSERT, \"1234\"));\n  assertEquals(\"diff_levenshtein: Middle equality.\", 7, dmp.diff_levenshtein(diffs));\n}\n\nvoid diff_match_patch_test::testDiffBisect() {\n  // Normal.\n  QString a = \"cat\";\n  QString b = \"map\";\n  // Since the resulting diff hasn't been normalized, it would be ok if\n  // the insertion and deletion pairs are swapped.\n  // If the order changes, tweak this test as required.\n  QList<Diff> diffs = diffList(Diff(DELETE, \"c\"), Diff(INSERT, \"m\"), Diff(EQUAL, \"a\"), Diff(DELETE, \"t\"), Diff(INSERT, \"p\"));\n  assertEquals(\"diff_bisect: Normal.\", diffs, dmp.diff_bisect(a, b, std::numeric_limits<clock_t>::max()));\n\n  // Timeout.\n  diffs = diffList(Diff(DELETE, \"cat\"), Diff(INSERT, \"map\"));\n  assertEquals(\"diff_bisect: Timeout.\", diffs, dmp.diff_bisect(a, b, 0));\n}\n\nvoid diff_match_patch_test::testDiffMain() {\n  // Perform a trivial diff.\n  QList<Diff> diffs = diffList();\n  assertEquals(\"diff_main: Null case.\", diffs, dmp.diff_main(\"\", \"\", false));\n\n  diffs = diffList(Diff(EQUAL, \"abc\"));\n  assertEquals(\"diff_main: Equality.\", diffs, dmp.diff_main(\"abc\", \"abc\", false));\n\n  diffs = diffList(Diff(EQUAL, \"ab\"), Diff(INSERT, \"123\"), Diff(EQUAL, \"c\"));\n  assertEquals(\"diff_main: Simple insertion.\", diffs, dmp.diff_main(\"abc\", \"ab123c\", false));\n\n  diffs = diffList(Diff(EQUAL, \"a\"), Diff(DELETE, \"123\"), Diff(EQUAL, \"bc\"));\n  assertEquals(\"diff_main: Simple deletion.\", diffs, dmp.diff_main(\"a123bc\", \"abc\", false));\n\n  diffs = diffList(Diff(EQUAL, \"a\"), Diff(INSERT, \"123\"), Diff(EQUAL, \"b\"), Diff(INSERT, \"456\"), Diff(EQUAL, \"c\"));\n  assertEquals(\"diff_main: Two insertions.\", diffs, dmp.diff_main(\"abc\", \"a123b456c\", false));\n\n  diffs = diffList(Diff(EQUAL, \"a\"), Diff(DELETE, \"123\"), Diff(EQUAL, \"b\"), Diff(DELETE, \"456\"), Diff(EQUAL, \"c\"));\n  assertEquals(\"diff_main: Two deletions.\", diffs, dmp.diff_main(\"a123b456c\", \"abc\", false));\n\n  // Perform a real diff.\n  // Switch off the timeout.\n  dmp.Diff_Timeout = 0;\n  diffs = diffList(Diff(DELETE, \"a\"), Diff(INSERT, \"b\"));\n  assertEquals(\"diff_main: Simple case #1.\", diffs, dmp.diff_main(\"a\", \"b\", false));\n\n  diffs = diffList(Diff(DELETE, \"Apple\"), Diff(INSERT, \"Banana\"), Diff(EQUAL, \"s are a\"), Diff(INSERT, \"lso\"), Diff(EQUAL, \" fruit.\"));\n  assertEquals(\"diff_main: Simple case #2.\", diffs, dmp.diff_main(\"Apples are a fruit.\", \"Bananas are also fruit.\", false));\n\n  diffs = diffList(Diff(DELETE, \"a\"), Diff(INSERT, QString::fromWCharArray((const wchar_t*) L\"\\u0680\", 1)), Diff(EQUAL, \"x\"), Diff(DELETE, \"\\t\"), Diff(INSERT, QString::fromWCharArray((const wchar_t*) L\"\\000\", 1)));\n  assertEquals(\"diff_main: Simple case #3.\", diffs, dmp.diff_main(\"ax\\t\", QString::fromWCharArray((const wchar_t*) L\"\\u0680x\\000\", 3), false));\n\n  diffs = diffList(Diff(DELETE, \"1\"), Diff(EQUAL, \"a\"), Diff(DELETE, \"y\"), Diff(EQUAL, \"b\"), Diff(DELETE, \"2\"), Diff(INSERT, \"xab\"));\n  assertEquals(\"diff_main: Overlap #1.\", diffs, dmp.diff_main(\"1ayb2\", \"abxab\", false));\n\n  diffs = diffList(Diff(INSERT, \"xaxcx\"), Diff(EQUAL, \"abc\"), Diff(DELETE, \"y\"));\n  assertEquals(\"diff_main: Overlap #2.\", diffs, dmp.diff_main(\"abcy\", \"xaxcxabc\", false));\n\n  diffs = diffList(Diff(DELETE, \"ABCD\"), Diff(EQUAL, \"a\"), Diff(DELETE, \"=\"), Diff(INSERT, \"-\"), Diff(EQUAL, \"bcd\"), Diff(DELETE, \"=\"), Diff(INSERT, \"-\"), Diff(EQUAL, \"efghijklmnopqrs\"), Diff(DELETE, \"EFGHIJKLMNOefg\"));\n  assertEquals(\"diff_main: Overlap #3.\", diffs, dmp.diff_main(\"ABCDa=bcd=efghijklmnopqrsEFGHIJKLMNOefg\", \"a-bcd-efghijklmnopqrs\", false));\n\n  diffs = diffList(Diff(INSERT, \" \"), Diff(EQUAL, \"a\"), Diff(INSERT, \"nd\"), Diff(EQUAL, \" [[Pennsylvania]]\"), Diff(DELETE, \" and [[New\"));\n  assertEquals(\"diff_main: Large equality.\", diffs, dmp.diff_main(\"a [[Pennsylvania]] and [[New\", \" and [[Pennsylvania]]\", false));\n\n  dmp.Diff_Timeout = 0.1f;  // 100ms\n  // This test may 'fail' on extremely fast computers.  If so, just increase the text lengths.\n  QString a = \"`Twas brillig, and the slithy toves\\nDid gyre and gimble in the wabe:\\nAll mimsy were the borogoves,\\nAnd the mome raths outgrabe.\\n\";\n  QString b = \"I am the very model of a modern major general,\\nI've information vegetable, animal, and mineral,\\nI know the kings of England, and I quote the fights historical,\\nFrom Marathon to Waterloo, in order categorical.\\n\";\n  // Increase the text lengths by 1024 times to ensure a timeout.\n  for (int x = 0; x < 10; x++) {\n    a = a + a;\n    b = b + b;\n  }\n  clock_t startTime = clock();\n  dmp.diff_main(a, b);\n  clock_t endTime = clock();\n  // Test that we took at least the timeout period.\n  assertTrue(\"diff_main: Timeout min.\", dmp.Diff_Timeout * CLOCKS_PER_SEC <= endTime - startTime);\n  // Test that we didn't take forever (be forgiving).\n  // Theoretically this test could fail very occasionally if the\n  // OS task swaps or locks up for a second at the wrong moment.\n  // Java seems to overrun by ~80% (compared with 10% for other languages).\n  // Therefore use an upper limit of 0.5s instead of 0.2s.\n  assertTrue(\"diff_main: Timeout max.\", dmp.Diff_Timeout * CLOCKS_PER_SEC * 2 > endTime - startTime);\n  dmp.Diff_Timeout = 0;\n\n  // Test the linemode speedup.\n  // Must be long to pass the 100 char cutoff.\n  a = \"1234567890\\n1234567890\\n1234567890\\n1234567890\\n1234567890\\n1234567890\\n1234567890\\n1234567890\\n1234567890\\n1234567890\\n1234567890\\n1234567890\\n1234567890\\n\";\n  b = \"abcdefghij\\nabcdefghij\\nabcdefghij\\nabcdefghij\\nabcdefghij\\nabcdefghij\\nabcdefghij\\nabcdefghij\\nabcdefghij\\nabcdefghij\\nabcdefghij\\nabcdefghij\\nabcdefghij\\n\";\n  assertEquals(\"diff_main: Simple line-mode.\", dmp.diff_main(a, b, true), dmp.diff_main(a, b, false));\n\n  a = \"1234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890\";\n  b = \"abcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghij\";\n  assertEquals(\"diff_main: Single line-mode.\", dmp.diff_main(a, b, true), dmp.diff_main(a, b, false));\n\n  a = \"1234567890\\n1234567890\\n1234567890\\n1234567890\\n1234567890\\n1234567890\\n1234567890\\n1234567890\\n1234567890\\n1234567890\\n1234567890\\n1234567890\\n1234567890\\n\";\n  b = \"abcdefghij\\n1234567890\\n1234567890\\n1234567890\\nabcdefghij\\n1234567890\\n1234567890\\n1234567890\\nabcdefghij\\n1234567890\\n1234567890\\n1234567890\\nabcdefghij\\n\";\n  QStringList texts_linemode = diff_rebuildtexts(dmp.diff_main(a, b, true));\n  QStringList texts_textmode = diff_rebuildtexts(dmp.diff_main(a, b, false));\n  assertEquals(\"diff_main: Overlap line-mode.\", texts_textmode, texts_linemode);\n\n  // Test null inputs.\n  try {\n    dmp.diff_main(NULL, NULL);\n    assertFalse(\"diff_main: Null inputs.\", true);\n  } catch (const char* ex) {\n    // Exception expected.\n  }\n}\n\n\n//  MATCH TEST FUNCTIONS\n\n\nvoid diff_match_patch_test::testMatchAlphabet() {\n  // Initialise the bitmasks for Bitap.\n  QMap<QChar, int> bitmask;\n  bitmask.insert('a', 4);\n  bitmask.insert('b', 2);\n  bitmask.insert('c', 1);\n  assertEquals(\"match_alphabet: Unique.\", bitmask, dmp.match_alphabet(\"abc\"));\n\n  bitmask = QMap<QChar, int>();\n  bitmask.insert('a', 37);\n  bitmask.insert('b', 18);\n  bitmask.insert('c', 8);\n  assertEquals(\"match_alphabet: Duplicates.\", bitmask, dmp.match_alphabet(\"abcaba\"));\n}\n\nvoid diff_match_patch_test::testMatchBitap() {\n  // Bitap algorithm.\n  dmp.Match_Distance = 100;\n  dmp.Match_Threshold = 0.5f;\n  assertEquals(\"match_bitap: Exact match #1.\", 5, dmp.match_bitap(\"abcdefghijk\", \"fgh\", 5));\n\n  assertEquals(\"match_bitap: Exact match #2.\", 5, dmp.match_bitap(\"abcdefghijk\", \"fgh\", 0));\n\n  assertEquals(\"match_bitap: Fuzzy match #1.\", 4, dmp.match_bitap(\"abcdefghijk\", \"efxhi\", 0));\n\n  assertEquals(\"match_bitap: Fuzzy match #2.\", 2, dmp.match_bitap(\"abcdefghijk\", \"cdefxyhijk\", 5));\n\n  assertEquals(\"match_bitap: Fuzzy match #3.\", -1, dmp.match_bitap(\"abcdefghijk\", \"bxy\", 1));\n\n  assertEquals(\"match_bitap: Overflow.\", 2, dmp.match_bitap(\"123456789xx0\", \"3456789x0\", 2));\n\n  assertEquals(\"match_bitap: Before start match.\", 0, dmp.match_bitap(\"abcdef\", \"xxabc\", 4));\n\n  assertEquals(\"match_bitap: Beyond end match.\", 3, dmp.match_bitap(\"abcdef\", \"defyy\", 4));\n\n  assertEquals(\"match_bitap: Oversized pattern.\", 0, dmp.match_bitap(\"abcdef\", \"xabcdefy\", 0));\n\n  dmp.Match_Threshold = 0.4f;\n  assertEquals(\"match_bitap: Threshold #1.\", 4, dmp.match_bitap(\"abcdefghijk\", \"efxyhi\", 1));\n\n  dmp.Match_Threshold = 0.3f;\n  assertEquals(\"match_bitap: Threshold #2.\", -1, dmp.match_bitap(\"abcdefghijk\", \"efxyhi\", 1));\n\n  dmp.Match_Threshold = 0.0f;\n  assertEquals(\"match_bitap: Threshold #3.\", 1, dmp.match_bitap(\"abcdefghijk\", \"bcdef\", 1));\n\n  dmp.Match_Threshold = 0.5f;\n  assertEquals(\"match_bitap: Multiple select #1.\", 0, dmp.match_bitap(\"abcdexyzabcde\", \"abccde\", 3));\n\n  assertEquals(\"match_bitap: Multiple select #2.\", 8, dmp.match_bitap(\"abcdexyzabcde\", \"abccde\", 5));\n\n  dmp.Match_Distance = 10;  // Strict location.\n  assertEquals(\"match_bitap: Distance test #1.\", -1, dmp.match_bitap(\"abcdefghijklmnopqrstuvwxyz\", \"abcdefg\", 24));\n\n  assertEquals(\"match_bitap: Distance test #2.\", 0, dmp.match_bitap(\"abcdefghijklmnopqrstuvwxyz\", \"abcdxxefg\", 1));\n\n  dmp.Match_Distance = 1000;  // Loose location.\n  assertEquals(\"match_bitap: Distance test #3.\", 0, dmp.match_bitap(\"abcdefghijklmnopqrstuvwxyz\", \"abcdefg\", 24));\n}\n\nvoid diff_match_patch_test::testMatchMain() {\n  // Full match.\n  assertEquals(\"match_main: Equality.\", 0, dmp.match_main(\"abcdef\", \"abcdef\", 1000));\n\n  assertEquals(\"match_main: Null text.\", -1, dmp.match_main(\"\", \"abcdef\", 1));\n\n  assertEquals(\"match_main: Null pattern.\", 3, dmp.match_main(\"abcdef\", \"\", 3));\n\n  assertEquals(\"match_main: Exact match.\", 3, dmp.match_main(\"abcdef\", \"de\", 3));\n\n  dmp.Match_Threshold = 0.7f;\n  assertEquals(\"match_main: Complex match.\", 4, dmp.match_main(\"I am the very model of a modern major general.\", \" that berry \", 5));\n  dmp.Match_Threshold = 0.5f;\n\n  // Test null inputs.\n  try {\n    dmp.match_main(NULL, NULL, 0);\n    assertFalse(\"match_main: Null inputs.\", true);\n  } catch (const char* ex) {\n    // Exception expected.\n  }\n}\n\n\n//  PATCH TEST FUNCTIONS\n\n\nvoid diff_match_patch_test::testPatchObj() {\n  // Patch Object.\n  Patch p;\n  p.start1 = 20;\n  p.start2 = 21;\n  p.length1 = 18;\n  p.length2 = 17;\n  p.diffs = diffList(Diff(EQUAL, \"jump\"), Diff(DELETE, \"s\"), Diff(INSERT, \"ed\"), Diff(EQUAL, \" over \"), Diff(DELETE, \"the\"), Diff(INSERT, \"a\"), Diff(EQUAL, \"\\nlaz\"));\n  QString strp = \"@@ -21,18 +22,17 @@\\n jump\\n-s\\n+ed\\n  over \\n-the\\n+a\\n %0Alaz\\n\";\n  assertEquals(\"Patch: toString.\", strp, p.toString());\n}\n\nvoid diff_match_patch_test::testPatchFromText() {\n  assertTrue(\"patch_fromText: #0.\", dmp.patch_fromText(\"\").isEmpty());\n\n  QString strp = \"@@ -21,18 +22,17 @@\\n jump\\n-s\\n+ed\\n  over \\n-the\\n+a\\n %0Alaz\\n\";\n  assertEquals(\"patch_fromText: #1.\", strp, dmp.patch_fromText(strp).value(0).toString());\n\n  assertEquals(\"patch_fromText: #2.\", \"@@ -1 +1 @@\\n-a\\n+b\\n\", dmp.patch_fromText(\"@@ -1 +1 @@\\n-a\\n+b\\n\").value(0).toString());\n\n  assertEquals(\"patch_fromText: #3.\", \"@@ -1,3 +0,0 @@\\n-abc\\n\", dmp.patch_fromText(\"@@ -1,3 +0,0 @@\\n-abc\\n\").value(0).toString());\n\n  assertEquals(\"patch_fromText: #4.\", \"@@ -0,0 +1,3 @@\\n+abc\\n\", dmp.patch_fromText(\"@@ -0,0 +1,3 @@\\n+abc\\n\").value(0).toString());\n\n  // Generates error.\n  try {\n    dmp.patch_fromText(\"Bad\\nPatch\\n\");\n    assertFalse(\"patch_fromText: #5.\", true);\n  } catch (QString ex) {\n    // Exception expected.\n  }\n}\n\nvoid diff_match_patch_test::testPatchToText() {\n  QString strp = \"@@ -21,18 +22,17 @@\\n jump\\n-s\\n+ed\\n  over \\n-the\\n+a\\n  laz\\n\";\n  QList<Patch> patches;\n  patches = dmp.patch_fromText(strp);\n  assertEquals(\"patch_toText: Single\", strp, dmp.patch_toText(patches));\n\n  strp = \"@@ -1,9 +1,9 @@\\n-f\\n+F\\n oo+fooba\\n@@ -7,9 +7,9 @@\\n obar\\n-,\\n+.\\n  tes\\n\";\n  patches = dmp.patch_fromText(strp);\n  assertEquals(\"patch_toText: Dual\", strp, dmp.patch_toText(patches));\n}\n\nvoid diff_match_patch_test::testPatchAddContext() {\n  dmp.Patch_Margin = 4;\n  Patch p;\n  p = dmp.patch_fromText(\"@@ -21,4 +21,10 @@\\n-jump\\n+somersault\\n\").value(0);\n  dmp.patch_addContext(p, \"The quick brown fox jumps over the lazy dog.\");\n  assertEquals(\"patch_addContext: Simple case.\", \"@@ -17,12 +17,18 @@\\n fox \\n-jump\\n+somersault\\n s ov\\n\", p.toString());\n\n  p = dmp.patch_fromText(\"@@ -21,4 +21,10 @@\\n-jump\\n+somersault\\n\").value(0);\n  dmp.patch_addContext(p, \"The quick brown fox jumps.\");\n  assertEquals(\"patch_addContext: Not enough trailing context.\", \"@@ -17,10 +17,16 @@\\n fox \\n-jump\\n+somersault\\n s.\\n\", p.toString());\n\n  p = dmp.patch_fromText(\"@@ -3 +3,2 @@\\n-e\\n+at\\n\").value(0);\n  dmp.patch_addContext(p, \"The quick brown fox jumps.\");\n  assertEquals(\"patch_addContext: Not enough leading context.\", \"@@ -1,7 +1,8 @@\\n Th\\n-e\\n+at\\n  qui\\n\", p.toString());\n\n  p = dmp.patch_fromText(\"@@ -3 +3,2 @@\\n-e\\n+at\\n\").value(0);\n  dmp.patch_addContext(p, \"The quick brown fox jumps.  The quick brown fox crashes.\");\n  assertEquals(\"patch_addContext: Ambiguity.\", \"@@ -1,27 +1,28 @@\\n Th\\n-e\\n+at\\n  quick brown fox jumps. \\n\", p.toString());\n}\n\nvoid diff_match_patch_test::testPatchMake() {\n  QList<Patch> patches;\n  patches = dmp.patch_make(\"\", \"\");\n  assertEquals(\"patch_make: Null case\", \"\", dmp.patch_toText(patches));\n\n  QString text1 = \"The quick brown fox jumps over the lazy dog.\";\n  QString text2 = \"That quick brown fox jumped over a lazy dog.\";\n  QString expectedPatch = \"@@ -1,8 +1,7 @@\\n Th\\n-at\\n+e\\n  qui\\n@@ -21,17 +21,18 @@\\n jump\\n-ed\\n+s\\n  over \\n-a\\n+the\\n  laz\\n\";\n  // The second patch must be \"-21,17 +21,18\", not \"-22,17 +21,18\" due to rolling context.\n  patches = dmp.patch_make(text2, text1);\n  assertEquals(\"patch_make: Text2+Text1 inputs\", expectedPatch, dmp.patch_toText(patches));\n\n  expectedPatch = \"@@ -1,11 +1,12 @@\\n Th\\n-e\\n+at\\n  quick b\\n@@ -22,18 +22,17 @@\\n jump\\n-s\\n+ed\\n  over \\n-the\\n+a\\n  laz\\n\";\n  patches = dmp.patch_make(text1, text2);\n  assertEquals(\"patch_make: Text1+Text2 inputs\", expectedPatch, dmp.patch_toText(patches));\n\n  QList<Diff> diffs = dmp.diff_main(text1, text2, false);\n  patches = dmp.patch_make(diffs);\n  assertEquals(\"patch_make: Diff input\", expectedPatch, dmp.patch_toText(patches));\n\n  patches = dmp.patch_make(text1, diffs);\n  assertEquals(\"patch_make: Text1+Diff inputs\", expectedPatch, dmp.patch_toText(patches));\n\n  patches = dmp.patch_make(text1, text2, diffs);\n  assertEquals(\"patch_make: Text1+Text2+Diff inputs (deprecated)\", expectedPatch, dmp.patch_toText(patches));\n\n  patches = dmp.patch_make(\"`1234567890-=[]\\\\;',./\", \"~!@#$%^&*()_+{}|:\\\"<>?\");\n  assertEquals(\"patch_toText: Character encoding.\", \"@@ -1,21 +1,21 @@\\n-%601234567890-=%5B%5D%5C;',./\\n+~!@#$%25%5E&*()_+%7B%7D%7C:%22%3C%3E?\\n\", dmp.patch_toText(patches));\n\n  diffs = diffList(Diff(DELETE, \"`1234567890-=[]\\\\;',./\"), Diff(INSERT, \"~!@#$%^&*()_+{}|:\\\"<>?\"));\n  assertEquals(\"patch_fromText: Character decoding.\", diffs, dmp.patch_fromText(\"@@ -1,21 +1,21 @@\\n-%601234567890-=%5B%5D%5C;',./\\n+~!@#$%25%5E&*()_+%7B%7D%7C:%22%3C%3E?\\n\").value(0).diffs);\n\n  text1 = \"\";\n  for (int x = 0; x < 100; x++) {\n    text1 += \"abcdef\";\n  }\n  text2 = text1 + \"123\";\n  expectedPatch = \"@@ -573,28 +573,31 @@\\n cdefabcdefabcdefabcdefabcdef\\n+123\\n\";\n  patches = dmp.patch_make(text1, text2);\n  assertEquals(\"patch_make: Long string with repeats.\", expectedPatch, dmp.patch_toText(patches));\n\n  // Test null inputs.\n  try {\n    dmp.patch_make(NULL, NULL);\n    assertFalse(\"patch_make: Null inputs.\", true);\n  } catch (const char* ex) {\n    // Exception expected.\n  }\n}\n\nvoid diff_match_patch_test::testPatchSplitMax() {\n  // Assumes that Match_MaxBits is 32.\n  QList<Patch> patches;\n  patches = dmp.patch_make(\"abcdefghijklmnopqrstuvwxyz01234567890\", \"XabXcdXefXghXijXklXmnXopXqrXstXuvXwxXyzX01X23X45X67X89X0\");\n  dmp.patch_splitMax(patches);\n  assertEquals(\"patch_splitMax: #1.\", \"@@ -1,32 +1,46 @@\\n+X\\n ab\\n+X\\n cd\\n+X\\n ef\\n+X\\n gh\\n+X\\n ij\\n+X\\n kl\\n+X\\n mn\\n+X\\n op\\n+X\\n qr\\n+X\\n st\\n+X\\n uv\\n+X\\n wx\\n+X\\n yz\\n+X\\n 012345\\n@@ -25,13 +39,18 @@\\n zX01\\n+X\\n 23\\n+X\\n 45\\n+X\\n 67\\n+X\\n 89\\n+X\\n 0\\n\", dmp.patch_toText(patches));\n\n  patches = dmp.patch_make(\"abcdef1234567890123456789012345678901234567890123456789012345678901234567890uvwxyz\", \"abcdefuvwxyz\");\n  QString oldToText = dmp.patch_toText(patches);\n  dmp.patch_splitMax(patches);\n  assertEquals(\"patch_splitMax: #2.\", oldToText, dmp.patch_toText(patches));\n\n  patches = dmp.patch_make(\"1234567890123456789012345678901234567890123456789012345678901234567890\", \"abc\");\n  dmp.patch_splitMax(patches);\n  assertEquals(\"patch_splitMax: #3.\", \"@@ -1,32 +1,4 @@\\n-1234567890123456789012345678\\n 9012\\n@@ -29,32 +1,4 @@\\n-9012345678901234567890123456\\n 7890\\n@@ -57,14 +1,3 @@\\n-78901234567890\\n+abc\\n\", dmp.patch_toText(patches));\n\n  patches = dmp.patch_make(\"abcdefghij , h : 0 , t : 1 abcdefghij , h : 0 , t : 1 abcdefghij , h : 0 , t : 1\", \"abcdefghij , h : 1 , t : 1 abcdefghij , h : 1 , t : 1 abcdefghij , h : 0 , t : 1\");\n  dmp.patch_splitMax(patches);\n  assertEquals(\"patch_splitMax: #4.\", \"@@ -2,32 +2,32 @@\\n bcdefghij , h : \\n-0\\n+1\\n  , t : 1 abcdef\\n@@ -29,32 +29,32 @@\\n bcdefghij , h : \\n-0\\n+1\\n  , t : 1 abcdef\\n\", dmp.patch_toText(patches));\n}\n\nvoid diff_match_patch_test::testPatchAddPadding() {\n  QList<Patch> patches;\n  patches = dmp.patch_make(\"\", \"test\");\n  assertEquals(\"patch_addPadding: Both edges full.\", \"@@ -0,0 +1,4 @@\\n+test\\n\", dmp.patch_toText(patches));\n  dmp.patch_addPadding(patches);\n  assertEquals(\"patch_addPadding: Both edges full.\", \"@@ -1,8 +1,12 @@\\n %01%02%03%04\\n+test\\n %01%02%03%04\\n\", dmp.patch_toText(patches));\n\n  patches = dmp.patch_make(\"XY\", \"XtestY\");\n  assertEquals(\"patch_addPadding: Both edges partial.\", \"@@ -1,2 +1,6 @@\\n X\\n+test\\n Y\\n\", dmp.patch_toText(patches));\n  dmp.patch_addPadding(patches);\n  assertEquals(\"patch_addPadding: Both edges partial.\", \"@@ -2,8 +2,12 @@\\n %02%03%04X\\n+test\\n Y%01%02%03\\n\", dmp.patch_toText(patches));\n\n  patches = dmp.patch_make(\"XXXXYYYY\", \"XXXXtestYYYY\");\n  assertEquals(\"patch_addPadding: Both edges none.\", \"@@ -1,8 +1,12 @@\\n XXXX\\n+test\\n YYYY\\n\", dmp.patch_toText(patches));\n  dmp.patch_addPadding(patches);\n  assertEquals(\"patch_addPadding: Both edges none.\", \"@@ -5,8 +5,12 @@\\n XXXX\\n+test\\n YYYY\\n\", dmp.patch_toText(patches));\n}\n\nvoid diff_match_patch_test::testPatchApply() {\n  dmp.Match_Distance = 1000;\n  dmp.Match_Threshold = 0.5f;\n  dmp.Patch_DeleteThreshold = 0.5f;\n  QList<Patch> patches;\n  patches = dmp.patch_make(\"\", \"\");\n  QPair<QString, QVector<bool> > results = dmp.patch_apply(patches, \"Hello world.\");\n  QVector<bool> boolArray = results.second;\n\n  QString resultStr = QString(\"%1\\t%2\").arg(results.first).arg(boolArray.count());\n  assertEquals(\"patch_apply: Null case.\", \"Hello world.\\t0\", resultStr);\n\n  patches = dmp.patch_make(\"The quick brown fox jumps over the lazy dog.\", \"That quick brown fox jumped over a lazy dog.\");\n  results = dmp.patch_apply(patches, \"The quick brown fox jumps over the lazy dog.\");\n  boolArray = results.second;\n  resultStr = results.first + \"\\t\" + (boolArray[0] ? \"true\" : \"false\") + \"\\t\" + (boolArray[1] ? \"true\" : \"false\");\n  assertEquals(\"patch_apply: Exact match.\", \"That quick brown fox jumped over a lazy dog.\\ttrue\\ttrue\", resultStr);\n\n  results = dmp.patch_apply(patches, \"The quick red rabbit jumps over the tired tiger.\");\n  boolArray = results.second;\n  resultStr = results.first + \"\\t\" + (boolArray[0] ? \"true\" : \"false\") + \"\\t\" + (boolArray[1] ? \"true\" : \"false\");\n  assertEquals(\"patch_apply: Partial match.\", \"That quick red rabbit jumped over a tired tiger.\\ttrue\\ttrue\", resultStr);\n\n  results = dmp.patch_apply(patches, \"I am the very model of a modern major general.\");\n  boolArray = results.second;\n  resultStr = results.first + \"\\t\" + (boolArray[0] ? \"true\" : \"false\") + \"\\t\" + (boolArray[1] ? \"true\" : \"false\");\n  assertEquals(\"patch_apply: Failed match.\", \"I am the very model of a modern major general.\\tfalse\\tfalse\", resultStr);\n\n  patches = dmp.patch_make(\"x1234567890123456789012345678901234567890123456789012345678901234567890y\", \"xabcy\");\n  results = dmp.patch_apply(patches, \"x123456789012345678901234567890-----++++++++++-----123456789012345678901234567890y\");\n  boolArray = results.second;\n  resultStr = results.first + \"\\t\" + (boolArray[0] ? \"true\" : \"false\") + \"\\t\" + (boolArray[1] ? \"true\" : \"false\");\n  assertEquals(\"patch_apply: Big delete, small change.\", \"xabcy\\ttrue\\ttrue\", resultStr);\n\n  patches = dmp.patch_make(\"x1234567890123456789012345678901234567890123456789012345678901234567890y\", \"xabcy\");\n  results = dmp.patch_apply(patches, \"x12345678901234567890---------------++++++++++---------------12345678901234567890y\");\n  boolArray = results.second;\n  resultStr = results.first + \"\\t\" + (boolArray[0] ? \"true\" : \"false\") + \"\\t\" + (boolArray[1] ? \"true\" : \"false\");\n  assertEquals(\"patch_apply: Big delete, large change 1.\", \"xabc12345678901234567890---------------++++++++++---------------12345678901234567890y\\tfalse\\ttrue\", resultStr);\n\n  dmp.Patch_DeleteThreshold = 0.6f;\n  patches = dmp.patch_make(\"x1234567890123456789012345678901234567890123456789012345678901234567890y\", \"xabcy\");\n  results = dmp.patch_apply(patches, \"x12345678901234567890---------------++++++++++---------------12345678901234567890y\");\n  boolArray = results.second;\n  resultStr = results.first + \"\\t\" + (boolArray[0] ? \"true\" : \"false\") + \"\\t\" + (boolArray[1] ? \"true\" : \"false\");\n  assertEquals(\"patch_apply: Big delete, large change 2.\", \"xabcy\\ttrue\\ttrue\", resultStr);\n  dmp.Patch_DeleteThreshold = 0.5f;\n\n  dmp.Match_Threshold = 0.0f;\n  dmp.Match_Distance = 0;\n  patches = dmp.patch_make(\"abcdefghijklmnopqrstuvwxyz--------------------1234567890\", \"abcXXXXXXXXXXdefghijklmnopqrstuvwxyz--------------------1234567YYYYYYYYYY890\");\n  results = dmp.patch_apply(patches, \"ABCDEFGHIJKLMNOPQRSTUVWXYZ--------------------1234567890\");\n  boolArray = results.second;\n  resultStr = results.first + \"\\t\" + (boolArray[0] ? \"true\" : \"false\") + \"\\t\" + (boolArray[1] ? \"true\" : \"false\");\n  assertEquals(\"patch_apply: Compensate for failed patch.\", \"ABCDEFGHIJKLMNOPQRSTUVWXYZ--------------------1234567YYYYYYYYYY890\\tfalse\\ttrue\", resultStr);\n  dmp.Match_Threshold = 0.5f;\n  dmp.Match_Distance = 1000;\n\n  patches = dmp.patch_make(\"\", \"test\");\n  QString patchStr = dmp.patch_toText(patches);\n  dmp.patch_apply(patches, \"\");\n  assertEquals(\"patch_apply: No side effects.\", patchStr, dmp.patch_toText(patches));\n\n  patches = dmp.patch_make(\"The quick brown fox jumps over the lazy dog.\", \"Woof\");\n  patchStr = dmp.patch_toText(patches);\n  dmp.patch_apply(patches, \"The quick brown fox jumps over the lazy dog.\");\n  assertEquals(\"patch_apply: No side effects with major delete.\", patchStr, dmp.patch_toText(patches));\n\n  patches = dmp.patch_make(\"\", \"test\");\n  results = dmp.patch_apply(patches, \"\");\n  boolArray = results.second;\n  resultStr = results.first + \"\\t\" + (boolArray[0] ? \"true\" : \"false\");\n  assertEquals(\"patch_apply: Edge exact match.\", \"test\\ttrue\", resultStr);\n\n  patches = dmp.patch_make(\"XY\", \"XtestY\");\n  results = dmp.patch_apply(patches, \"XY\");\n  boolArray = results.second;\n  resultStr = results.first + \"\\t\" + (boolArray[0] ? \"true\" : \"false\");\n  assertEquals(\"patch_apply: Near edge exact match.\", \"XtestY\\ttrue\", resultStr);\n\n  patches = dmp.patch_make(\"y\", \"y123\");\n  results = dmp.patch_apply(patches, \"x\");\n  boolArray = results.second;\n  resultStr = results.first + \"\\t\" + (boolArray[0] ? \"true\" : \"false\");\n  assertEquals(\"patch_apply: Edge partial match.\", \"x123\\ttrue\", resultStr);\n}\n\n\nvoid diff_match_patch_test::assertEquals(const QString &strCase, int n1, int n2) {\n  if (n1 != n2) {\n    qDebug(\"%s FAIL\\nExpected: %d\\nActual: %d\", qPrintable(strCase), n1, n2);\n    throw strCase;\n  }\n  qDebug(\"%s OK\", qPrintable(strCase));\n}\n\nvoid diff_match_patch_test::assertEquals(const QString &strCase, const QString &s1, const QString &s2) {\n  if (s1 != s2) {\n    qDebug(\"%s FAIL\\nExpected: %s\\nActual: %s\",\n           qPrintable(strCase), qPrintable(s1), qPrintable(s2));\n    throw strCase;\n  }\n  qDebug(\"%s OK\", qPrintable(strCase));\n}\n\nvoid diff_match_patch_test::assertEquals(const QString &strCase, const Diff &d1, const Diff &d2) {\n  if (d1 != d2) {\n    qDebug(\"%s FAIL\\nExpected: %s\\nActual: %s\", qPrintable(strCase),\n        qPrintable(d1.toString()), qPrintable(d2.toString()));\n    throw strCase;\n  }\n  qDebug(\"%s OK\", qPrintable(strCase));\n}\n\nvoid diff_match_patch_test::assertEquals(const QString &strCase, const QList<Diff> &list1, const QList<Diff> &list2) {\n  bool fail = false;\n  if (list1.count() == list2.count()) {\n    int i = 0;\n    foreach(Diff d1, list1) {\n      Diff d2 = list2.value(i);\n      if (d1 != d2) {\n        fail = true;\n        break;\n      }\n      i++;\n    }\n  } else {\n    fail = true;\n  }\n\n  if (fail) {\n    // Build human readable description of both lists.\n    QString listString1 = \"(\";\n    bool first = true;\n    foreach(Diff d1, list1) {\n      if (!first) {\n        listString1 += \", \";\n      }\n      listString1 += d1.toString();\n      first = false;\n    }\n    listString1 += \")\";\n    QString listString2 = \"(\";\n    first = true;\n    foreach(Diff d2, list2) {\n      if (!first) {\n        listString2 += \", \";\n      }\n      listString2 += d2.toString();\n      first = false;\n    }\n    listString2 += \")\";\n    qDebug(\"%s FAIL\\nExpected: %s\\nActual: %s\",\n        qPrintable(strCase), qPrintable(listString1), qPrintable(listString2));\n    throw strCase;\n  }\n  qDebug(\"%s OK\", qPrintable(strCase));\n}\n\nvoid diff_match_patch_test::assertEquals(const QString &strCase, const QList<QVariant> &list1, const QList<QVariant> &list2) {\n  bool fail = false;\n  if (list1.count() == list2.count()) {\n    int i = 0;\n    foreach(QVariant q1, list1) {\n      QVariant q2 = list2.value(i);\n      if (q1 != q2) {\n        fail = true;\n        break;\n      }\n      i++;\n    }\n  } else {\n    fail = true;\n  }\n\n  if (fail) {\n    // Build human readable description of both lists.\n    QString listString1 = \"(\";\n    bool first = true;\n    foreach(QVariant q1, list1) {\n      if (!first) {\n        listString1 += \", \";\n      }\n      listString1 += q1.toString();\n      first = false;\n    }\n    listString1 += \")\";\n    QString listString2 = \"(\";\n    first = true;\n    foreach(QVariant q2, list2) {\n      if (!first) {\n        listString2 += \", \";\n      }\n      listString2 += q2.toString();\n      first = false;\n    }\n    listString2 += \")\";\n    qDebug(\"%s FAIL\\nExpected: %s\\nActual: %s\",\n        qPrintable(strCase), qPrintable(listString1), qPrintable(listString2));\n    throw strCase;\n  }\n  qDebug(\"%s OK\", qPrintable(strCase));\n}\n\nvoid diff_match_patch_test::assertEquals(const QString &strCase, const QVariant &var1, const QVariant &var2) {\n  if (var1 != var2) {\n    qDebug(\"%s FAIL\\nExpected: %s\\nActual: %s\", qPrintable(strCase),\n        qPrintable(var1.toString()), qPrintable(var2.toString()));\n    throw strCase;\n  }\n  qDebug(\"%s OK\", qPrintable(strCase));\n}\n\nvoid diff_match_patch_test::assertEquals(const QString &strCase, const QMap<QChar, int> &m1, const QMap<QChar, int> &m2) {\n  QMapIterator<QChar, int> i1(m1), i2(m2);\n\n  while (i1.hasNext() && i2.hasNext()) {\n    i1.next();\n    i2.next();\n    if (i1.key() != i2.key() || i1.value() != i2.value()) {\n      qDebug(\"%s FAIL\\nExpected: (%c, %d)\\nActual: (%c, %d)\", qPrintable(strCase),\n          i1.key().toAscii(), i1.value(), i2.key().toAscii(), i2.value());\n      throw strCase;\n    }\n  }\n\n  if (i1.hasNext()) {\n    i1.next();\n    qDebug(\"%s FAIL\\nExpected: (%c, %d)\\nActual: none\",\n        qPrintable(strCase), i1.key().toAscii(), i1.value());\n    throw strCase;\n  }\n  if (i2.hasNext()) {\n    i2.next();\n    qDebug(\"%s FAIL\\nExpected: none\\nActual: (%c, %d)\",\n        qPrintable(strCase), i2.key().toAscii(), i2.value());\n    throw strCase;\n  }\n  qDebug(\"%s OK\", qPrintable(strCase));\n}\n\nvoid diff_match_patch_test::assertEquals(const QString &strCase, const QStringList &list1, const QStringList &list2) {\n  if (list1 != list2) {\n    qDebug(\"%s FAIL\\nExpected: %s\\nActual: %s\", qPrintable(strCase),\n        qPrintable(list1.join(\",\")), qPrintable(list2.join(\",\")));\n    throw strCase;\n  }\n  qDebug(\"%s OK\", qPrintable(strCase));\n}\n\nvoid diff_match_patch_test::assertTrue(const QString &strCase, bool value) {\n  if (!value) {\n    qDebug(\"%s FAIL\\nExpected: true\\nActual: false\", qPrintable(strCase));\n    throw strCase;\n  }\n  qDebug(\"%s OK\", qPrintable(strCase));\n}\n\nvoid diff_match_patch_test::assertFalse(const QString &strCase, bool value) {\n  if (value) {\n    qDebug(\"%s FAIL\\nExpected: false\\nActual: true\", qPrintable(strCase));\n    throw strCase;\n  }\n  qDebug(\"%s OK\", qPrintable(strCase));\n}\n\n\n// Construct the two texts which made up the diff originally.\nQStringList diff_match_patch_test::diff_rebuildtexts(QList<Diff> diffs) {\n  QStringList text;\n  text << QString(\"\") << QString(\"\");\n  foreach (Diff myDiff, diffs) {\n    if (myDiff.operation != INSERT) {\n      text[0] += myDiff.text;\n    }\n    if (myDiff.operation != DELETE) {\n      text[1] += myDiff.text;\n    }\n  }\n  return text;\n}\n\nvoid diff_match_patch_test::assertEmpty(const QString &strCase, const QStringList &list) {\n  if (!list.isEmpty()) {\n    throw strCase;\n  }\n}\n\n\n// Private function for quickly building lists of diffs.\nQList<Diff> diff_match_patch_test::diffList(Diff d1, Diff d2, Diff d3, Diff d4, Diff d5,\n  Diff d6, Diff d7, Diff d8, Diff d9, Diff d10) {\n  // Diff(INSERT, NULL) is invalid and thus is used as the default argument.\n  QList<Diff> listRet;\n  if (d1.operation == INSERT && d1.text == NULL) {\n    return listRet;\n  }\n  listRet << d1;\n\n  if (d2.operation == INSERT && d2.text == NULL) {\n    return listRet;\n  }\n  listRet << d2;\n\n  if (d3.operation == INSERT && d3.text == NULL) {\n    return listRet;\n  }\n  listRet << d3;\n\n  if (d4.operation == INSERT && d4.text == NULL) {\n    return listRet;\n  }\n  listRet << d4;\n\n  if (d5.operation == INSERT && d5.text == NULL) {\n    return listRet;\n  }\n  listRet << d5;\n\n  if (d6.operation == INSERT && d6.text == NULL) {\n    return listRet;\n  }\n  listRet << d6;\n\n  if (d7.operation == INSERT && d7.text == NULL) {\n    return listRet;\n  }\n  listRet << d7;\n\n  if (d8.operation == INSERT && d8.text == NULL) {\n    return listRet;\n  }\n  listRet << d8;\n\n  if (d9.operation == INSERT && d9.text == NULL) {\n    return listRet;\n  }\n  listRet << d9;\n\n  if (d10.operation == INSERT && d10.text == NULL) {\n    return listRet;\n  }\n  listRet << d10;\n\n  return listRet;\n}\n\n\n/*\nCompile instructions for MinGW and QT4 on Windows:\nqmake -project\nqmake\nmingw32-make\ng++ -o diff_match_patch_test debug\\diff_match_patch_test.o debug\\diff_match_patch.o \\qt4\\lib\\libQtCore4.a\ndiff_match_patch_test.exe\n\nCompile insructions for OS X:\nqmake -spec macx-g++\nmake\n./diff_match_patch\n*/\n"
  },
  {
    "path": "cpp/diff_match_patch_test.h",
    "content": "/*\n * Diff Match and Patch -- Test Harness\n * Copyright 2018 The diff-match-patch Authors.\n * https://github.com/google/diff-match-patch\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *   http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#ifndef DIFF_MATCH_PATCH_TEST_H\n#define DIFF_MATCH_PATCH_TEST_H\n\nclass diff_match_patch_test {\n public:\n  diff_match_patch_test();\n  void run_all_tests();\n\n  //  DIFF TEST FUNCTIONS\n  void testDiffCommonPrefix();\n  void testDiffCommonSuffix();\n  void testDiffCommonOverlap();\n  void testDiffHalfmatch();\n  void testDiffLinesToChars();\n  void testDiffCharsToLines();\n  void testDiffCleanupMerge();\n  void testDiffCleanupSemanticLossless();\n  void testDiffCleanupSemantic();\n  void testDiffCleanupEfficiency();\n  void testDiffPrettyHtml();\n  void testDiffText();\n  void testDiffDelta();\n  void testDiffXIndex();\n  void testDiffLevenshtein();\n  void testDiffBisect();\n  void testDiffMain();\n\n  //  MATCH TEST FUNCTIONS\n  void testMatchAlphabet();\n  void testMatchBitap();\n  void testMatchMain();\n\n  //  PATCH TEST FUNCTIONS\n  void testPatchObj();\n  void testPatchFromText();\n  void testPatchToText();\n  void testPatchAddContext();\n  void testPatchMake();\n  void testPatchSplitMax();\n  void testPatchAddPadding();\n  void testPatchApply();\n\n private:\n  diff_match_patch dmp;\n\n  // Define equality.\n  void assertEquals(const QString &strCase, int n1, int n2);\n  void assertEquals(const QString &strCase, const QString &s1, const QString &s2);\n  void assertEquals(const QString &strCase, const Diff &d1, const Diff &d2);\n  void assertEquals(const QString &strCase, const QList<Diff> &list1, const QList<Diff> &list2);\n  void assertEquals(const QString &strCase, const QList<QVariant> &list1, const QList<QVariant> &list2);\n  void assertEquals(const QString &strCase, const QVariant &var1, const QVariant &var2);\n  void assertEquals(const QString &strCase, const QMap<QChar, int> &m1, const QMap<QChar, int> &m2);\n  void assertEquals(const QString &strCase, const QStringList &list1, const QStringList &list2);\n  void assertTrue(const QString &strCase, bool value);\n  void assertFalse(const QString &strCase, bool value);\n  void assertEmpty(const QString &strCase, const QStringList &list);\n\n  // Construct the two texts which made up the diff originally.\n  QStringList diff_rebuildtexts(QList<Diff> diffs);\n  // Private function for quickly building lists of diffs.\n  QList<Diff> diffList(\n      // Diff(INSERT, NULL) is invalid and thus is used as the default argument.\n      Diff d1 = Diff(INSERT, NULL), Diff d2 = Diff(INSERT, NULL),\n      Diff d3 = Diff(INSERT, NULL), Diff d4 = Diff(INSERT, NULL),\n      Diff d5 = Diff(INSERT, NULL), Diff d6 = Diff(INSERT, NULL),\n      Diff d7 = Diff(INSERT, NULL), Diff d8 = Diff(INSERT, NULL),\n      Diff d9 = Diff(INSERT, NULL), Diff d10 = Diff(INSERT, NULL));\n};\n\n#endif // DIFF_MATCH_PATCH_TEST_H\n"
  },
  {
    "path": "csharp/DiffMatchPatch.cs",
    "content": "﻿/*\n * Diff Match and Patch\n * Copyright 2018 The diff-match-patch Authors.\n * https://github.com/google/diff-match-patch\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *   http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nusing System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing System.Text.RegularExpressions;\nusing System.Web;\n\nnamespace DiffMatchPatch {\n  internal static class CompatibilityExtensions {\n    // JScript splice function\n    public static List<T> Splice<T>(this List<T> input, int start, int count,\n        params T[] objects) {\n      List<T> deletedRange = input.GetRange(start, count);\n      input.RemoveRange(start, count);\n      input.InsertRange(start, objects);\n\n      return deletedRange;\n    }\n\n    // Java substring function\n    public static string JavaSubstring(this string s, int begin, int end) {\n      return s.Substring(begin, end - begin);\n    }\n  }\n\n  /**-\n   * The data structure representing a diff is a List of Diff objects:\n   * {Diff(Operation.DELETE, \"Hello\"), Diff(Operation.INSERT, \"Goodbye\"),\n   *  Diff(Operation.EQUAL, \" world.\")}\n   * which means: delete \"Hello\", add \"Goodbye\" and keep \" world.\"\n   */\n  public enum Operation {\n    DELETE, INSERT, EQUAL\n  }\n\n\n  /**\n   * Class representing one diff operation.\n   */\n  public class Diff {\n    public Operation operation;\n    // One of: INSERT, DELETE or EQUAL.\n    public string text;\n    // The text associated with this diff operation.\n\n    /**\n     * Constructor.  Initializes the diff with the provided values.\n     * @param operation One of INSERT, DELETE or EQUAL.\n     * @param text The text being applied.\n     */\n    public Diff(Operation operation, string text) {\n      // Construct a diff with the specified operation and text.\n      this.operation = operation;\n      this.text = text;\n    }\n\n    /**\n     * Display a human-readable version of this Diff.\n     * @return text version.\n     */\n    public override string ToString() {\n      string prettyText = this.text.Replace('\\n', '\\u00b6');\n      return \"Diff(\" + this.operation + \",\\\"\" + prettyText + \"\\\")\";\n    }\n\n    /**\n     * Is this Diff equivalent to another Diff?\n     * @param d Another Diff to compare against.\n     * @return true or false.\n     */\n    public override bool Equals(Object obj) {\n      // If parameter is null return false.\n      if (obj == null) {\n        return false;\n      }\n\n      // If parameter cannot be cast to Diff return false.\n      Diff p = obj as Diff;\n      if ((System.Object)p == null) {\n        return false;\n      }\n\n      // Return true if the fields match.\n      return p.operation == this.operation && p.text == this.text;\n    }\n\n    public bool Equals(Diff obj) {\n      // If parameter is null return false.\n      if (obj == null) {\n        return false;\n      }\n\n      // Return true if the fields match.\n      return obj.operation == this.operation && obj.text == this.text;\n    }\n\n    public override int GetHashCode() {\n      return text.GetHashCode() ^ operation.GetHashCode();\n    }\n  }\n\n\n  /**\n   * Class representing one patch operation.\n   */\n  public class Patch {\n    public List<Diff> diffs = new List<Diff>();\n    public int start1;\n    public int start2;\n    public int length1;\n    public int length2;\n\n    /**\n     * Emulate GNU diff's format.\n     * Header: @@ -382,8 +481,9 @@\n     * Indices are printed as 1-based, not 0-based.\n     * @return The GNU diff string.\n     */\n    public override string ToString() {\n      string coords1, coords2;\n      if (this.length1 == 0) {\n        coords1 = this.start1 + \",0\";\n      } else if (this.length1 == 1) {\n        coords1 = Convert.ToString(this.start1 + 1);\n      } else {\n        coords1 = (this.start1 + 1) + \",\" + this.length1;\n      }\n      if (this.length2 == 0) {\n        coords2 = this.start2 + \",0\";\n      } else if (this.length2 == 1) {\n        coords2 = Convert.ToString(this.start2 + 1);\n      } else {\n        coords2 = (this.start2 + 1) + \",\" + this.length2;\n      }\n      StringBuilder text = new StringBuilder();\n      text.Append(\"@@ -\").Append(coords1).Append(\" +\").Append(coords2)\n          .Append(\" @@\\n\");\n      // Escape the body of the patch with %xx notation.\n      foreach (Diff aDiff in this.diffs) {\n        switch (aDiff.operation) {\n          case Operation.INSERT:\n            text.Append('+');\n            break;\n          case Operation.DELETE:\n            text.Append('-');\n            break;\n          case Operation.EQUAL:\n            text.Append(' ');\n            break;\n        }\n\n        text.Append(diff_match_patch.encodeURI(aDiff.text)).Append(\"\\n\");\n      }\n      return text.ToString();\n    }\n  }\n\n\n  /**\n   * Class containing the diff, match and patch methods.\n   * Also Contains the behaviour settings.\n   */\n  public class diff_match_patch {\n    // Defaults.\n    // Set these on your diff_match_patch instance to override the defaults.\n\n    // Number of seconds to map a diff before giving up (0 for infinity).\n    public float Diff_Timeout = 1.0f;\n    // Cost of an empty edit operation in terms of edit characters.\n    public short Diff_EditCost = 4;\n    // At what point is no match declared (0.0 = perfection, 1.0 = very loose).\n    public float Match_Threshold = 0.5f;\n    // How far to search for a match (0 = exact location, 1000+ = broad match).\n    // A match this many characters away from the expected location will add\n    // 1.0 to the score (0.0 is a perfect match).\n    public int Match_Distance = 1000;\n    // When deleting a large block of text (over ~64 characters), how close\n    // do the contents have to be to match the expected contents. (0.0 =\n    // perfection, 1.0 = very loose).  Note that Match_Threshold controls\n    // how closely the end points of a delete need to match.\n    public float Patch_DeleteThreshold = 0.5f;\n    // Chunk size for context length.\n    public short Patch_Margin = 4;\n\n    // The number of bits in an int.\n    private short Match_MaxBits = 32;\n\n\n    //  DIFF FUNCTIONS\n\n\n    /**\n     * Find the differences between two texts.\n     * Run a faster, slightly less optimal diff.\n     * This method allows the 'checklines' of diff_main() to be optional.\n     * Most of the time checklines is wanted, so default to true.\n     * @param text1 Old string to be diffed.\n     * @param text2 New string to be diffed.\n     * @return List of Diff objects.\n     */\n    public List<Diff> diff_main(string text1, string text2) {\n      return diff_main(text1, text2, true);\n    }\n\n    /**\n     * Find the differences between two texts.\n     * @param text1 Old string to be diffed.\n     * @param text2 New string to be diffed.\n     * @param checklines Speedup flag.  If false, then don't run a\n     *     line-level diff first to identify the changed areas.\n     *     If true, then run a faster slightly less optimal diff.\n     * @return List of Diff objects.\n     */\n    public List<Diff> diff_main(string text1, string text2, bool checklines) {\n      // Set a deadline by which time the diff must be complete.\n      DateTime deadline;\n      if (this.Diff_Timeout <= 0) {\n        deadline = DateTime.MaxValue;\n      } else {\n        deadline = DateTime.Now +\n            new TimeSpan(((long)(Diff_Timeout * 1000)) * 10000);\n      }\n      return diff_main(text1, text2, checklines, deadline);\n    }\n\n    /**\n     * Find the differences between two texts.  Simplifies the problem by\n     * stripping any common prefix or suffix off the texts before diffing.\n     * @param text1 Old string to be diffed.\n     * @param text2 New string to be diffed.\n     * @param checklines Speedup flag.  If false, then don't run a\n     *     line-level diff first to identify the changed areas.\n     *     If true, then run a faster slightly less optimal diff.\n     * @param deadline Time when the diff should be complete by.  Used\n     *     internally for recursive calls.  Users should set DiffTimeout\n     *     instead.\n     * @return List of Diff objects.\n     */\n    private List<Diff> diff_main(string text1, string text2, bool checklines,\n        DateTime deadline) {\n      // Check for null inputs not needed since null can't be passed in C#.\n\n      // Check for equality (speedup).\n      List<Diff> diffs;\n      if (text1 == text2) {\n        diffs = new List<Diff>();\n        if (text1.Length != 0) {\n          diffs.Add(new Diff(Operation.EQUAL, text1));\n        }\n        return diffs;\n      }\n\n      // Trim off common prefix (speedup).\n      int commonlength = diff_commonPrefix(text1, text2);\n      string commonprefix = text1.Substring(0, commonlength);\n      text1 = text1.Substring(commonlength);\n      text2 = text2.Substring(commonlength);\n\n      // Trim off common suffix (speedup).\n      commonlength = diff_commonSuffix(text1, text2);\n      string commonsuffix = text1.Substring(text1.Length - commonlength);\n      text1 = text1.Substring(0, text1.Length - commonlength);\n      text2 = text2.Substring(0, text2.Length - commonlength);\n\n      // Compute the diff on the middle block.\n      diffs = diff_compute(text1, text2, checklines, deadline);\n\n      // Restore the prefix and suffix.\n      if (commonprefix.Length != 0) {\n        diffs.Insert(0, (new Diff(Operation.EQUAL, commonprefix)));\n      }\n      if (commonsuffix.Length != 0) {\n        diffs.Add(new Diff(Operation.EQUAL, commonsuffix));\n      }\n\n      diff_cleanupMerge(diffs);\n      return diffs;\n    }\n\n    /**\n     * Find the differences between two texts.  Assumes that the texts do not\n     * have any common prefix or suffix.\n     * @param text1 Old string to be diffed.\n     * @param text2 New string to be diffed.\n     * @param checklines Speedup flag.  If false, then don't run a\n     *     line-level diff first to identify the changed areas.\n     *     If true, then run a faster slightly less optimal diff.\n     * @param deadline Time when the diff should be complete by.\n     * @return List of Diff objects.\n     */\n    private List<Diff> diff_compute(string text1, string text2,\n                                    bool checklines, DateTime deadline) {\n      List<Diff> diffs = new List<Diff>();\n\n      if (text1.Length == 0) {\n        // Just add some text (speedup).\n        diffs.Add(new Diff(Operation.INSERT, text2));\n        return diffs;\n      }\n\n      if (text2.Length == 0) {\n        // Just delete some text (speedup).\n        diffs.Add(new Diff(Operation.DELETE, text1));\n        return diffs;\n      }\n\n      string longtext = text1.Length > text2.Length ? text1 : text2;\n      string shorttext = text1.Length > text2.Length ? text2 : text1;\n      int i = longtext.IndexOf(shorttext, StringComparison.Ordinal);\n      if (i != -1) {\n        // Shorter text is inside the longer text (speedup).\n        Operation op = (text1.Length > text2.Length) ?\n            Operation.DELETE : Operation.INSERT;\n        diffs.Add(new Diff(op, longtext.Substring(0, i)));\n        diffs.Add(new Diff(Operation.EQUAL, shorttext));\n        diffs.Add(new Diff(op, longtext.Substring(i + shorttext.Length)));\n        return diffs;\n      }\n\n      if (shorttext.Length == 1) {\n        // Single character string.\n        // After the previous speedup, the character can't be an equality.\n        diffs.Add(new Diff(Operation.DELETE, text1));\n        diffs.Add(new Diff(Operation.INSERT, text2));\n        return diffs;\n      }\n\n      // Check to see if the problem can be split in two.\n      string[] hm = diff_halfMatch(text1, text2);\n      if (hm != null) {\n        // A half-match was found, sort out the return data.\n        string text1_a = hm[0];\n        string text1_b = hm[1];\n        string text2_a = hm[2];\n        string text2_b = hm[3];\n        string mid_common = hm[4];\n        // Send both pairs off for separate processing.\n        List<Diff> diffs_a = diff_main(text1_a, text2_a, checklines, deadline);\n        List<Diff> diffs_b = diff_main(text1_b, text2_b, checklines, deadline);\n        // Merge the results.\n        diffs = diffs_a;\n        diffs.Add(new Diff(Operation.EQUAL, mid_common));\n        diffs.AddRange(diffs_b);\n        return diffs;\n      }\n\n      if (checklines && text1.Length > 100 && text2.Length > 100) {\n        return diff_lineMode(text1, text2, deadline);\n      }\n\n      return diff_bisect(text1, text2, deadline);\n    }\n\n    /**\n     * Do a quick line-level diff on both strings, then rediff the parts for\n     * greater accuracy.\n     * This speedup can produce non-minimal diffs.\n     * @param text1 Old string to be diffed.\n     * @param text2 New string to be diffed.\n     * @param deadline Time when the diff should be complete by.\n     * @return List of Diff objects.\n     */\n    private List<Diff> diff_lineMode(string text1, string text2,\n                                     DateTime deadline) {\n      // Scan the text on a line-by-line basis first.\n      Object[] a = diff_linesToChars(text1, text2);\n      text1 = (string)a[0];\n      text2 = (string)a[1];\n      List<string> linearray = (List<string>)a[2];\n\n      List<Diff> diffs = diff_main(text1, text2, false, deadline);\n\n      // Convert the diff back to original text.\n      diff_charsToLines(diffs, linearray);\n      // Eliminate freak matches (e.g. blank lines)\n      diff_cleanupSemantic(diffs);\n\n      // Rediff any replacement blocks, this time character-by-character.\n      // Add a dummy entry at the end.\n      diffs.Add(new Diff(Operation.EQUAL, string.Empty));\n      int pointer = 0;\n      int count_delete = 0;\n      int count_insert = 0;\n      string text_delete = string.Empty;\n      string text_insert = string.Empty;\n      while (pointer < diffs.Count) {\n        switch (diffs[pointer].operation) {\n          case Operation.INSERT:\n            count_insert++;\n            text_insert += diffs[pointer].text;\n            break;\n          case Operation.DELETE:\n            count_delete++;\n            text_delete += diffs[pointer].text;\n            break;\n          case Operation.EQUAL:\n            // Upon reaching an equality, check for prior redundancies.\n            if (count_delete >= 1 && count_insert >= 1) {\n              // Delete the offending records and add the merged ones.\n              diffs.RemoveRange(pointer - count_delete - count_insert,\n                  count_delete + count_insert);\n              pointer = pointer - count_delete - count_insert;\n              List<Diff> subDiff =\n                  this.diff_main(text_delete, text_insert, false, deadline);\n              diffs.InsertRange(pointer, subDiff);\n              pointer = pointer + subDiff.Count;\n            }\n            count_insert = 0;\n            count_delete = 0;\n            text_delete = string.Empty;\n            text_insert = string.Empty;\n            break;\n        }\n        pointer++;\n      }\n      diffs.RemoveAt(diffs.Count - 1);  // Remove the dummy entry at the end.\n\n      return diffs;\n    }\n\n    /**\n     * Find the 'middle snake' of a diff, split the problem in two\n     * and return the recursively constructed diff.\n     * See Myers 1986 paper: An O(ND) Difference Algorithm and Its Variations.\n     * @param text1 Old string to be diffed.\n     * @param text2 New string to be diffed.\n     * @param deadline Time at which to bail if not yet complete.\n     * @return List of Diff objects.\n     */\n    protected List<Diff> diff_bisect(string text1, string text2,\n        DateTime deadline) {\n      // Cache the text lengths to prevent multiple calls.\n      int text1_length = text1.Length;\n      int text2_length = text2.Length;\n      int max_d = (text1_length + text2_length + 1) / 2;\n      int v_offset = max_d;\n      int v_length = 2 * max_d;\n      int[] v1 = new int[v_length];\n      int[] v2 = new int[v_length];\n      for (int x = 0; x < v_length; x++) {\n        v1[x] = -1;\n        v2[x] = -1;\n      }\n      v1[v_offset + 1] = 0;\n      v2[v_offset + 1] = 0;\n      int delta = text1_length - text2_length;\n      // If the total number of characters is odd, then the front path will\n      // collide with the reverse path.\n      bool front = (delta % 2 != 0);\n      // Offsets for start and end of k loop.\n      // Prevents mapping of space beyond the grid.\n      int k1start = 0;\n      int k1end = 0;\n      int k2start = 0;\n      int k2end = 0;\n      for (int d = 0; d < max_d; d++) {\n        // Bail out if deadline is reached.\n        if (DateTime.Now > deadline) {\n          break;\n        }\n\n        // Walk the front path one step.\n        for (int k1 = -d + k1start; k1 <= d - k1end; k1 += 2) {\n          int k1_offset = v_offset + k1;\n          int x1;\n          if (k1 == -d || k1 != d && v1[k1_offset - 1] < v1[k1_offset + 1]) {\n            x1 = v1[k1_offset + 1];\n          } else {\n            x1 = v1[k1_offset - 1] + 1;\n          }\n          int y1 = x1 - k1;\n          while (x1 < text1_length && y1 < text2_length\n                && text1[x1] == text2[y1]) {\n            x1++;\n            y1++;\n          }\n          v1[k1_offset] = x1;\n          if (x1 > text1_length) {\n            // Ran off the right of the graph.\n            k1end += 2;\n          } else if (y1 > text2_length) {\n            // Ran off the bottom of the graph.\n            k1start += 2;\n          } else if (front) {\n            int k2_offset = v_offset + delta - k1;\n            if (k2_offset >= 0 && k2_offset < v_length && v2[k2_offset] != -1) {\n              // Mirror x2 onto top-left coordinate system.\n              int x2 = text1_length - v2[k2_offset];\n              if (x1 >= x2) {\n                // Overlap detected.\n                return diff_bisectSplit(text1, text2, x1, y1, deadline);\n              }\n            }\n          }\n        }\n\n        // Walk the reverse path one step.\n        for (int k2 = -d + k2start; k2 <= d - k2end; k2 += 2) {\n          int k2_offset = v_offset + k2;\n          int x2;\n          if (k2 == -d || k2 != d && v2[k2_offset - 1] < v2[k2_offset + 1]) {\n            x2 = v2[k2_offset + 1];\n          } else {\n            x2 = v2[k2_offset - 1] + 1;\n          }\n          int y2 = x2 - k2;\n          while (x2 < text1_length && y2 < text2_length\n              && text1[text1_length - x2 - 1]\n              == text2[text2_length - y2 - 1]) {\n            x2++;\n            y2++;\n          }\n          v2[k2_offset] = x2;\n          if (x2 > text1_length) {\n            // Ran off the left of the graph.\n            k2end += 2;\n          } else if (y2 > text2_length) {\n            // Ran off the top of the graph.\n            k2start += 2;\n          } else if (!front) {\n            int k1_offset = v_offset + delta - k2;\n            if (k1_offset >= 0 && k1_offset < v_length && v1[k1_offset] != -1) {\n              int x1 = v1[k1_offset];\n              int y1 = v_offset + x1 - k1_offset;\n              // Mirror x2 onto top-left coordinate system.\n              x2 = text1_length - v2[k2_offset];\n              if (x1 >= x2) {\n                // Overlap detected.\n                return diff_bisectSplit(text1, text2, x1, y1, deadline);\n              }\n            }\n          }\n        }\n      }\n      // Diff took too long and hit the deadline or\n      // number of diffs equals number of characters, no commonality at all.\n      List<Diff> diffs = new List<Diff>();\n      diffs.Add(new Diff(Operation.DELETE, text1));\n      diffs.Add(new Diff(Operation.INSERT, text2));\n      return diffs;\n    }\n\n    /**\n     * Given the location of the 'middle snake', split the diff in two parts\n     * and recurse.\n     * @param text1 Old string to be diffed.\n     * @param text2 New string to be diffed.\n     * @param x Index of split point in text1.\n     * @param y Index of split point in text2.\n     * @param deadline Time at which to bail if not yet complete.\n     * @return LinkedList of Diff objects.\n     */\n    private List<Diff> diff_bisectSplit(string text1, string text2,\n        int x, int y, DateTime deadline) {\n      string text1a = text1.Substring(0, x);\n      string text2a = text2.Substring(0, y);\n      string text1b = text1.Substring(x);\n      string text2b = text2.Substring(y);\n\n      // Compute both diffs serially.\n      List<Diff> diffs = diff_main(text1a, text2a, false, deadline);\n      List<Diff> diffsb = diff_main(text1b, text2b, false, deadline);\n\n      diffs.AddRange(diffsb);\n      return diffs;\n    }\n\n    /**\n     * Split two texts into a list of strings.  Reduce the texts to a string of\n     * hashes where each Unicode character represents one line.\n     * @param text1 First string.\n     * @param text2 Second string.\n     * @return Three element Object array, containing the encoded text1, the\n     *     encoded text2 and the List of unique strings.  The zeroth element\n     *     of the List of unique strings is intentionally blank.\n     */\n    protected Object[] diff_linesToChars(string text1, string text2) {\n      List<string> lineArray = new List<string>();\n      Dictionary<string, int> lineHash = new Dictionary<string, int>();\n      // e.g. linearray[4] == \"Hello\\n\"\n      // e.g. linehash.get(\"Hello\\n\") == 4\n\n      // \"\\x00\" is a valid character, but various debuggers don't like it.\n      // So we'll insert a junk entry to avoid generating a null character.\n      lineArray.Add(string.Empty);\n\n      // Allocate 2/3rds of the space for text1, the rest for text2.\n      string chars1 = diff_linesToCharsMunge(text1, lineArray, lineHash, 40000);\n      string chars2 = diff_linesToCharsMunge(text2, lineArray, lineHash, 65535);\n      return new Object[] { chars1, chars2, lineArray };\n    }\n\n    /**\n     * Split a text into a list of strings.  Reduce the texts to a string of\n     * hashes where each Unicode character represents one line.\n     * @param text String to encode.\n     * @param lineArray List of unique strings.\n     * @param lineHash Map of strings to indices.\n     * @param maxLines Maximum length of lineArray.\n     * @return Encoded string.\n     */\n    private string diff_linesToCharsMunge(string text, List<string> lineArray,\n        Dictionary<string, int> lineHash, int maxLines) {\n      int lineStart = 0;\n      int lineEnd = -1;\n      string line;\n      StringBuilder chars = new StringBuilder();\n      // Walk the text, pulling out a Substring for each line.\n      // text.split('\\n') would would temporarily double our memory footprint.\n      // Modifying text would create many large strings to garbage collect.\n      while (lineEnd < text.Length - 1) {\n        lineEnd = text.IndexOf('\\n', lineStart);\n        if (lineEnd == -1) {\n          lineEnd = text.Length - 1;\n        }\n        line = text.JavaSubstring(lineStart, lineEnd + 1);\n\n        if (lineHash.ContainsKey(line)) {\n          chars.Append(((char)(int)lineHash[line]));\n        } else {\n          if (lineArray.Count == maxLines) {\n            // Bail out at 65535 because char 65536 == char 0.\n            line = text.Substring(lineStart);\n            lineEnd = text.Length;\n          }\n          lineArray.Add(line);\n          lineHash.Add(line, lineArray.Count - 1);\n          chars.Append(((char)(lineArray.Count - 1)));\n        }\n        lineStart = lineEnd + 1;\n      }\n      return chars.ToString();\n    }\n\n    /**\n     * Rehydrate the text in a diff from a string of line hashes to real lines\n     * of text.\n     * @param diffs List of Diff objects.\n     * @param lineArray List of unique strings.\n     */\n    protected void diff_charsToLines(ICollection<Diff> diffs,\n                    IList<string> lineArray) {\n      StringBuilder text;\n      foreach (Diff diff in diffs) {\n        text = new StringBuilder();\n        for (int j = 0; j < diff.text.Length; j++) {\n          text.Append(lineArray[diff.text[j]]);\n        }\n        diff.text = text.ToString();\n      }\n    }\n\n    /**\n     * Determine the common prefix of two strings.\n     * @param text1 First string.\n     * @param text2 Second string.\n     * @return The number of characters common to the start of each string.\n     */\n    public int diff_commonPrefix(string text1, string text2) {\n      // Performance analysis: https://neil.fraser.name/news/2007/10/09/\n      int n = Math.Min(text1.Length, text2.Length);\n      for (int i = 0; i < n; i++) {\n        if (text1[i] != text2[i]) {\n          return i;\n        }\n      }\n      return n;\n    }\n\n    /**\n     * Determine the common suffix of two strings.\n     * @param text1 First string.\n     * @param text2 Second string.\n     * @return The number of characters common to the end of each string.\n     */\n    public int diff_commonSuffix(string text1, string text2) {\n      // Performance analysis: https://neil.fraser.name/news/2007/10/09/\n      int text1_length = text1.Length;\n      int text2_length = text2.Length;\n      int n = Math.Min(text1.Length, text2.Length);\n      for (int i = 1; i <= n; i++) {\n        if (text1[text1_length - i] != text2[text2_length - i]) {\n          return i - 1;\n        }\n      }\n      return n;\n    }\n\n    /**\n     * Determine if the suffix of one string is the prefix of another.\n     * @param text1 First string.\n     * @param text2 Second string.\n     * @return The number of characters common to the end of the first\n     *     string and the start of the second string.\n     */\n    protected int diff_commonOverlap(string text1, string text2) {\n      // Cache the text lengths to prevent multiple calls.\n      int text1_length = text1.Length;\n      int text2_length = text2.Length;\n      // Eliminate the null case.\n      if (text1_length == 0 || text2_length == 0) {\n        return 0;\n      }\n      // Truncate the longer string.\n      if (text1_length > text2_length) {\n        text1 = text1.Substring(text1_length - text2_length);\n      } else if (text1_length < text2_length) {\n        text2 = text2.Substring(0, text1_length);\n      }\n      int text_length = Math.Min(text1_length, text2_length);\n      // Quick check for the worst case.\n      if (text1 == text2) {\n        return text_length;\n      }\n\n      // Start by looking for a single character match\n      // and increase length until no match is found.\n      // Performance analysis: https://neil.fraser.name/news/2010/11/04/\n      int best = 0;\n      int length = 1;\n      while (true) {\n        string pattern = text1.Substring(text_length - length);\n        int found = text2.IndexOf(pattern, StringComparison.Ordinal);\n        if (found == -1) {\n          return best;\n        }\n        length += found;\n        if (found == 0 || text1.Substring(text_length - length) ==\n            text2.Substring(0, length)) {\n          best = length;\n          length++;\n        }\n      }\n    }\n\n    /**\n     * Do the two texts share a Substring which is at least half the length of\n     * the longer text?\n     * This speedup can produce non-minimal diffs.\n     * @param text1 First string.\n     * @param text2 Second string.\n     * @return Five element String array, containing the prefix of text1, the\n     *     suffix of text1, the prefix of text2, the suffix of text2 and the\n     *     common middle.  Or null if there was no match.\n     */\n\n    protected string[] diff_halfMatch(string text1, string text2) {\n      if (this.Diff_Timeout <= 0) {\n        // Don't risk returning a non-optimal diff if we have unlimited time.\n        return null;\n      }\n      string longtext = text1.Length > text2.Length ? text1 : text2;\n      string shorttext = text1.Length > text2.Length ? text2 : text1;\n      if (longtext.Length < 4 || shorttext.Length * 2 < longtext.Length) {\n        return null;  // Pointless.\n      }\n\n      // First check if the second quarter is the seed for a half-match.\n      string[] hm1 = diff_halfMatchI(longtext, shorttext,\n                                     (longtext.Length + 3) / 4);\n      // Check again based on the third quarter.\n      string[] hm2 = diff_halfMatchI(longtext, shorttext,\n                                     (longtext.Length + 1) / 2);\n      string[] hm;\n      if (hm1 == null && hm2 == null) {\n        return null;\n      } else if (hm2 == null) {\n        hm = hm1;\n      } else if (hm1 == null) {\n        hm = hm2;\n      } else {\n        // Both matched.  Select the longest.\n        hm = hm1[4].Length > hm2[4].Length ? hm1 : hm2;\n      }\n\n      // A half-match was found, sort out the return data.\n      if (text1.Length > text2.Length) {\n        return hm;\n        //return new string[]{hm[0], hm[1], hm[2], hm[3], hm[4]};\n      } else {\n        return new string[] { hm[2], hm[3], hm[0], hm[1], hm[4] };\n      }\n    }\n\n    /**\n     * Does a Substring of shorttext exist within longtext such that the\n     * Substring is at least half the length of longtext?\n     * @param longtext Longer string.\n     * @param shorttext Shorter string.\n     * @param i Start index of quarter length Substring within longtext.\n     * @return Five element string array, containing the prefix of longtext, the\n     *     suffix of longtext, the prefix of shorttext, the suffix of shorttext\n     *     and the common middle.  Or null if there was no match.\n     */\n    private string[] diff_halfMatchI(string longtext, string shorttext, int i) {\n      // Start with a 1/4 length Substring at position i as a seed.\n      string seed = longtext.Substring(i, longtext.Length / 4);\n      int j = -1;\n      string best_common = string.Empty;\n      string best_longtext_a = string.Empty, best_longtext_b = string.Empty;\n      string best_shorttext_a = string.Empty, best_shorttext_b = string.Empty;\n      while (j < shorttext.Length && (j = shorttext.IndexOf(seed, j + 1,\n          StringComparison.Ordinal)) != -1) {\n        int prefixLength = diff_commonPrefix(longtext.Substring(i),\n                                             shorttext.Substring(j));\n        int suffixLength = diff_commonSuffix(longtext.Substring(0, i),\n                                             shorttext.Substring(0, j));\n        if (best_common.Length < suffixLength + prefixLength) {\n          best_common = shorttext.Substring(j - suffixLength, suffixLength)\n              + shorttext.Substring(j, prefixLength);\n          best_longtext_a = longtext.Substring(0, i - suffixLength);\n          best_longtext_b = longtext.Substring(i + prefixLength);\n          best_shorttext_a = shorttext.Substring(0, j - suffixLength);\n          best_shorttext_b = shorttext.Substring(j + prefixLength);\n        }\n      }\n      if (best_common.Length * 2 >= longtext.Length) {\n        return new string[]{best_longtext_a, best_longtext_b,\n            best_shorttext_a, best_shorttext_b, best_common};\n      } else {\n        return null;\n      }\n    }\n\n    /**\n     * Reduce the number of edits by eliminating semantically trivial\n     * equalities.\n     * @param diffs List of Diff objects.\n     */\n    public void diff_cleanupSemantic(List<Diff> diffs) {\n      bool changes = false;\n      // Stack of indices where equalities are found.\n      Stack<int> equalities = new Stack<int>();\n      // Always equal to equalities[equalitiesLength-1][1]\n      string lastEquality = null;\n      int pointer = 0;  // Index of current position.\n      // Number of characters that changed prior to the equality.\n      int length_insertions1 = 0;\n      int length_deletions1 = 0;\n      // Number of characters that changed after the equality.\n      int length_insertions2 = 0;\n      int length_deletions2 = 0;\n      while (pointer < diffs.Count) {\n        if (diffs[pointer].operation == Operation.EQUAL) {  // Equality found.\n          equalities.Push(pointer);\n          length_insertions1 = length_insertions2;\n          length_deletions1 = length_deletions2;\n          length_insertions2 = 0;\n          length_deletions2 = 0;\n          lastEquality = diffs[pointer].text;\n        } else {  // an insertion or deletion\n          if (diffs[pointer].operation == Operation.INSERT) {\n            length_insertions2 += diffs[pointer].text.Length;\n          } else {\n            length_deletions2 += diffs[pointer].text.Length;\n          }\n          // Eliminate an equality that is smaller or equal to the edits on both\n          // sides of it.\n          if (lastEquality != null && (lastEquality.Length\n              <= Math.Max(length_insertions1, length_deletions1))\n              && (lastEquality.Length\n                  <= Math.Max(length_insertions2, length_deletions2))) {\n            // Duplicate record.\n            diffs.Insert(equalities.Peek(),\n                         new Diff(Operation.DELETE, lastEquality));\n            // Change second copy to insert.\n            diffs[equalities.Peek() + 1].operation = Operation.INSERT;\n            // Throw away the equality we just deleted.\n            equalities.Pop();\n            if (equalities.Count > 0) {\n              equalities.Pop();\n            }\n            pointer = equalities.Count > 0 ? equalities.Peek() : -1;\n            length_insertions1 = 0;  // Reset the counters.\n            length_deletions1 = 0;\n            length_insertions2 = 0;\n            length_deletions2 = 0;\n            lastEquality = null;\n            changes = true;\n          }\n        }\n        pointer++;\n      }\n\n      // Normalize the diff.\n      if (changes) {\n        diff_cleanupMerge(diffs);\n      }\n      diff_cleanupSemanticLossless(diffs);\n\n      // Find any overlaps between deletions and insertions.\n      // e.g: <del>abcxxx</del><ins>xxxdef</ins>\n      //   -> <del>abc</del>xxx<ins>def</ins>\n      // e.g: <del>xxxabc</del><ins>defxxx</ins>\n      //   -> <ins>def</ins>xxx<del>abc</del>\n      // Only extract an overlap if it is as big as the edit ahead or behind it.\n      pointer = 1;\n      while (pointer < diffs.Count) {\n        if (diffs[pointer - 1].operation == Operation.DELETE &&\n            diffs[pointer].operation == Operation.INSERT) {\n          string deletion = diffs[pointer - 1].text;\n          string insertion = diffs[pointer].text;\n          int overlap_length1 = diff_commonOverlap(deletion, insertion);\n          int overlap_length2 = diff_commonOverlap(insertion, deletion);\n          if (overlap_length1 >= overlap_length2) {\n            if (overlap_length1 >= deletion.Length / 2.0 ||\n                overlap_length1 >= insertion.Length / 2.0) {\n              // Overlap found.\n              // Insert an equality and trim the surrounding edits.\n              diffs.Insert(pointer, new Diff(Operation.EQUAL,\n                  insertion.Substring(0, overlap_length1)));\n              diffs[pointer - 1].text =\n                  deletion.Substring(0, deletion.Length - overlap_length1);\n              diffs[pointer + 1].text = insertion.Substring(overlap_length1);\n              pointer++;\n            }\n          } else {\n            if (overlap_length2 >= deletion.Length / 2.0 ||\n                overlap_length2 >= insertion.Length / 2.0) {\n              // Reverse overlap found.\n              // Insert an equality and swap and trim the surrounding edits.\n              diffs.Insert(pointer, new Diff(Operation.EQUAL,\n                  deletion.Substring(0, overlap_length2)));\n              diffs[pointer - 1].operation = Operation.INSERT;\n              diffs[pointer - 1].text =\n                  insertion.Substring(0, insertion.Length - overlap_length2);\n              diffs[pointer + 1].operation = Operation.DELETE;\n              diffs[pointer + 1].text = deletion.Substring(overlap_length2);\n              pointer++;\n            }\n          }\n          pointer++;\n        }\n        pointer++;\n      }\n    }\n\n    /**\n     * Look for single edits surrounded on both sides by equalities\n     * which can be shifted sideways to align the edit to a word boundary.\n     * e.g: The c<ins>at c</ins>ame. -> The <ins>cat </ins>came.\n     * @param diffs List of Diff objects.\n     */\n    public void diff_cleanupSemanticLossless(List<Diff> diffs) {\n      int pointer = 1;\n      // Intentionally ignore the first and last element (don't need checking).\n      while (pointer < diffs.Count - 1) {\n        if (diffs[pointer - 1].operation == Operation.EQUAL &&\n          diffs[pointer + 1].operation == Operation.EQUAL) {\n          // This is a single edit surrounded by equalities.\n          string equality1 = diffs[pointer - 1].text;\n          string edit = diffs[pointer].text;\n          string equality2 = diffs[pointer + 1].text;\n\n          // First, shift the edit as far left as possible.\n          int commonOffset = this.diff_commonSuffix(equality1, edit);\n          if (commonOffset > 0) {\n            string commonString = edit.Substring(edit.Length - commonOffset);\n            equality1 = equality1.Substring(0, equality1.Length - commonOffset);\n            edit = commonString + edit.Substring(0, edit.Length - commonOffset);\n            equality2 = commonString + equality2;\n          }\n\n          // Second, step character by character right,\n          // looking for the best fit.\n          string bestEquality1 = equality1;\n          string bestEdit = edit;\n          string bestEquality2 = equality2;\n          int bestScore = diff_cleanupSemanticScore(equality1, edit) +\n              diff_cleanupSemanticScore(edit, equality2);\n          while (edit.Length != 0 && equality2.Length != 0\n              && edit[0] == equality2[0]) {\n            equality1 += edit[0];\n            edit = edit.Substring(1) + equality2[0];\n            equality2 = equality2.Substring(1);\n            int score = diff_cleanupSemanticScore(equality1, edit) +\n                diff_cleanupSemanticScore(edit, equality2);\n            // The >= encourages trailing rather than leading whitespace on\n            // edits.\n            if (score >= bestScore) {\n              bestScore = score;\n              bestEquality1 = equality1;\n              bestEdit = edit;\n              bestEquality2 = equality2;\n            }\n          }\n\n          if (diffs[pointer - 1].text != bestEquality1) {\n            // We have an improvement, save it back to the diff.\n            if (bestEquality1.Length != 0) {\n              diffs[pointer - 1].text = bestEquality1;\n            } else {\n              diffs.RemoveAt(pointer - 1);\n              pointer--;\n            }\n            diffs[pointer].text = bestEdit;\n            if (bestEquality2.Length != 0) {\n              diffs[pointer + 1].text = bestEquality2;\n            } else {\n              diffs.RemoveAt(pointer + 1);\n              pointer--;\n            }\n          }\n        }\n        pointer++;\n      }\n    }\n\n    /**\n     * Given two strings, compute a score representing whether the internal\n     * boundary falls on logical boundaries.\n     * Scores range from 6 (best) to 0 (worst).\n     * @param one First string.\n     * @param two Second string.\n     * @return The score.\n     */\n    private int diff_cleanupSemanticScore(string one, string two) {\n      if (one.Length == 0 || two.Length == 0) {\n        // Edges are the best.\n        return 6;\n      }\n\n      // Each port of this function behaves slightly differently due to\n      // subtle differences in each language's definition of things like\n      // 'whitespace'.  Since this function's purpose is largely cosmetic,\n      // the choice has been made to use each language's native features\n      // rather than force total conformity.\n      char char1 = one[one.Length - 1];\n      char char2 = two[0];\n      bool nonAlphaNumeric1 = !Char.IsLetterOrDigit(char1);\n      bool nonAlphaNumeric2 = !Char.IsLetterOrDigit(char2);\n      bool whitespace1 = nonAlphaNumeric1 && Char.IsWhiteSpace(char1);\n      bool whitespace2 = nonAlphaNumeric2 && Char.IsWhiteSpace(char2);\n      bool lineBreak1 = whitespace1 && Char.IsControl(char1);\n      bool lineBreak2 = whitespace2 && Char.IsControl(char2);\n      bool blankLine1 = lineBreak1 && BLANKLINEEND.IsMatch(one);\n      bool blankLine2 = lineBreak2 && BLANKLINESTART.IsMatch(two);\n\n      if (blankLine1 || blankLine2) {\n        // Five points for blank lines.\n        return 5;\n      } else if (lineBreak1 || lineBreak2) {\n        // Four points for line breaks.\n        return 4;\n      } else if (nonAlphaNumeric1 && !whitespace1 && whitespace2) {\n        // Three points for end of sentences.\n        return 3;\n      } else if (whitespace1 || whitespace2) {\n        // Two points for whitespace.\n        return 2;\n      } else if (nonAlphaNumeric1 || nonAlphaNumeric2) {\n        // One point for non-alphanumeric.\n        return 1;\n      }\n      return 0;\n    }\n\n    // Define some regex patterns for matching boundaries.\n    private Regex BLANKLINEEND = new Regex(\"\\\\n\\\\r?\\\\n\\\\Z\");\n    private Regex BLANKLINESTART = new Regex(\"\\\\A\\\\r?\\\\n\\\\r?\\\\n\");\n\n    /**\n     * Reduce the number of edits by eliminating operationally trivial\n     * equalities.\n     * @param diffs List of Diff objects.\n     */\n    public void diff_cleanupEfficiency(List<Diff> diffs) {\n      bool changes = false;\n      // Stack of indices where equalities are found.\n      Stack<int> equalities = new Stack<int>();\n      // Always equal to equalities[equalitiesLength-1][1]\n      string lastEquality = string.Empty;\n      int pointer = 0;  // Index of current position.\n      // Is there an insertion operation before the last equality.\n      bool pre_ins = false;\n      // Is there a deletion operation before the last equality.\n      bool pre_del = false;\n      // Is there an insertion operation after the last equality.\n      bool post_ins = false;\n      // Is there a deletion operation after the last equality.\n      bool post_del = false;\n      while (pointer < diffs.Count) {\n        if (diffs[pointer].operation == Operation.EQUAL) {  // Equality found.\n          if (diffs[pointer].text.Length < this.Diff_EditCost\n              && (post_ins || post_del)) {\n            // Candidate found.\n            equalities.Push(pointer);\n            pre_ins = post_ins;\n            pre_del = post_del;\n            lastEquality = diffs[pointer].text;\n          } else {\n            // Not a candidate, and can never become one.\n            equalities.Clear();\n            lastEquality = string.Empty;\n          }\n          post_ins = post_del = false;\n        } else {  // An insertion or deletion.\n          if (diffs[pointer].operation == Operation.DELETE) {\n            post_del = true;\n          } else {\n            post_ins = true;\n          }\n          /*\n           * Five types to be split:\n           * <ins>A</ins><del>B</del>XY<ins>C</ins><del>D</del>\n           * <ins>A</ins>X<ins>C</ins><del>D</del>\n           * <ins>A</ins><del>B</del>X<ins>C</ins>\n           * <ins>A</del>X<ins>C</ins><del>D</del>\n           * <ins>A</ins><del>B</del>X<del>C</del>\n           */\n          if ((lastEquality.Length != 0)\n              && ((pre_ins && pre_del && post_ins && post_del)\n              || ((lastEquality.Length < this.Diff_EditCost / 2)\n              && ((pre_ins ? 1 : 0) + (pre_del ? 1 : 0) + (post_ins ? 1 : 0)\n              + (post_del ? 1 : 0)) == 3))) {\n            // Duplicate record.\n            diffs.Insert(equalities.Peek(),\n                         new Diff(Operation.DELETE, lastEquality));\n            // Change second copy to insert.\n            diffs[equalities.Peek() + 1].operation = Operation.INSERT;\n            equalities.Pop();  // Throw away the equality we just deleted.\n            lastEquality = string.Empty;\n            if (pre_ins && pre_del) {\n              // No changes made which could affect previous entry, keep going.\n              post_ins = post_del = true;\n              equalities.Clear();\n            } else {\n              if (equalities.Count > 0) {\n                equalities.Pop();\n              }\n\n              pointer = equalities.Count > 0 ? equalities.Peek() : -1;\n              post_ins = post_del = false;\n            }\n            changes = true;\n          }\n        }\n        pointer++;\n      }\n\n      if (changes) {\n        diff_cleanupMerge(diffs);\n      }\n    }\n\n    /**\n     * Reorder and merge like edit sections.  Merge equalities.\n     * Any edit section can move as long as it doesn't cross an equality.\n     * @param diffs List of Diff objects.\n     */\n    public void diff_cleanupMerge(List<Diff> diffs) {\n      // Add a dummy entry at the end.\n      diffs.Add(new Diff(Operation.EQUAL, string.Empty));\n      int pointer = 0;\n      int count_delete = 0;\n      int count_insert = 0;\n      string text_delete = string.Empty;\n      string text_insert = string.Empty;\n      int commonlength;\n      while (pointer < diffs.Count) {\n        switch (diffs[pointer].operation) {\n          case Operation.INSERT:\n            count_insert++;\n            text_insert += diffs[pointer].text;\n            pointer++;\n            break;\n          case Operation.DELETE:\n            count_delete++;\n            text_delete += diffs[pointer].text;\n            pointer++;\n            break;\n          case Operation.EQUAL:\n            // Upon reaching an equality, check for prior redundancies.\n            if (count_delete + count_insert > 1) {\n              if (count_delete != 0 && count_insert != 0) {\n                // Factor out any common prefixies.\n                commonlength = this.diff_commonPrefix(text_insert, text_delete);\n                if (commonlength != 0) {\n                  if ((pointer - count_delete - count_insert) > 0 &&\n                    diffs[pointer - count_delete - count_insert - 1].operation\n                        == Operation.EQUAL) {\n                    diffs[pointer - count_delete - count_insert - 1].text\n                        += text_insert.Substring(0, commonlength);\n                  } else {\n                    diffs.Insert(0, new Diff(Operation.EQUAL,\n                        text_insert.Substring(0, commonlength)));\n                    pointer++;\n                  }\n                  text_insert = text_insert.Substring(commonlength);\n                  text_delete = text_delete.Substring(commonlength);\n                }\n                // Factor out any common suffixies.\n                commonlength = this.diff_commonSuffix(text_insert, text_delete);\n                if (commonlength != 0) {\n                  diffs[pointer].text = text_insert.Substring(text_insert.Length\n                      - commonlength) + diffs[pointer].text;\n                  text_insert = text_insert.Substring(0, text_insert.Length\n                      - commonlength);\n                  text_delete = text_delete.Substring(0, text_delete.Length\n                      - commonlength);\n                }\n              }\n              // Delete the offending records and add the merged ones.\n              pointer -= count_delete + count_insert;\n              diffs.Splice(pointer, count_delete + count_insert);\n              if (text_delete.Length != 0) {\n                diffs.Splice(pointer, 0,\n                    new Diff(Operation.DELETE, text_delete));\n                pointer++;\n              }\n              if (text_insert.Length != 0) {\n                diffs.Splice(pointer, 0,\n                    new Diff(Operation.INSERT, text_insert));\n                pointer++;\n              }\n              pointer++;\n            } else if (pointer != 0\n                && diffs[pointer - 1].operation == Operation.EQUAL) {\n              // Merge this equality with the previous one.\n              diffs[pointer - 1].text += diffs[pointer].text;\n              diffs.RemoveAt(pointer);\n            } else {\n              pointer++;\n            }\n            count_insert = 0;\n            count_delete = 0;\n            text_delete = string.Empty;\n            text_insert = string.Empty;\n            break;\n        }\n      }\n      if (diffs[diffs.Count - 1].text.Length == 0) {\n        diffs.RemoveAt(diffs.Count - 1);  // Remove the dummy entry at the end.\n      }\n\n      // Second pass: look for single edits surrounded on both sides by\n      // equalities which can be shifted sideways to eliminate an equality.\n      // e.g: A<ins>BA</ins>C -> <ins>AB</ins>AC\n      bool changes = false;\n      pointer = 1;\n      // Intentionally ignore the first and last element (don't need checking).\n      while (pointer < (diffs.Count - 1)) {\n        if (diffs[pointer - 1].operation == Operation.EQUAL &&\n          diffs[pointer + 1].operation == Operation.EQUAL) {\n          // This is a single edit surrounded by equalities.\n          if (diffs[pointer].text.EndsWith(diffs[pointer - 1].text,\n              StringComparison.Ordinal)) {\n            // Shift the edit over the previous equality.\n            diffs[pointer].text = diffs[pointer - 1].text +\n                diffs[pointer].text.Substring(0, diffs[pointer].text.Length -\n                                              diffs[pointer - 1].text.Length);\n            diffs[pointer + 1].text = diffs[pointer - 1].text\n                + diffs[pointer + 1].text;\n            diffs.Splice(pointer - 1, 1);\n            changes = true;\n          } else if (diffs[pointer].text.StartsWith(diffs[pointer + 1].text,\n              StringComparison.Ordinal)) {\n            // Shift the edit over the next equality.\n            diffs[pointer - 1].text += diffs[pointer + 1].text;\n            diffs[pointer].text =\n                diffs[pointer].text.Substring(diffs[pointer + 1].text.Length)\n                + diffs[pointer + 1].text;\n            diffs.Splice(pointer + 1, 1);\n            changes = true;\n          }\n        }\n        pointer++;\n      }\n      // If shifts were made, the diff needs reordering and another shift sweep.\n      if (changes) {\n        this.diff_cleanupMerge(diffs);\n      }\n    }\n\n    /**\n     * loc is a location in text1, compute and return the equivalent location in\n     * text2.\n     * e.g. \"The cat\" vs \"The big cat\", 1->1, 5->8\n     * @param diffs List of Diff objects.\n     * @param loc Location within text1.\n     * @return Location within text2.\n     */\n    public int diff_xIndex(List<Diff> diffs, int loc) {\n      int chars1 = 0;\n      int chars2 = 0;\n      int last_chars1 = 0;\n      int last_chars2 = 0;\n      Diff lastDiff = null;\n      foreach (Diff aDiff in diffs) {\n        if (aDiff.operation != Operation.INSERT) {\n          // Equality or deletion.\n          chars1 += aDiff.text.Length;\n        }\n        if (aDiff.operation != Operation.DELETE) {\n          // Equality or insertion.\n          chars2 += aDiff.text.Length;\n        }\n        if (chars1 > loc) {\n          // Overshot the location.\n          lastDiff = aDiff;\n          break;\n        }\n        last_chars1 = chars1;\n        last_chars2 = chars2;\n      }\n      if (lastDiff != null && lastDiff.operation == Operation.DELETE) {\n        // The location was deleted.\n        return last_chars2;\n      }\n      // Add the remaining character length.\n      return last_chars2 + (loc - last_chars1);\n    }\n\n    /**\n     * Convert a Diff list into a pretty HTML report.\n     * @param diffs List of Diff objects.\n     * @return HTML representation.\n     */\n    public string diff_prettyHtml(List<Diff> diffs) {\n      StringBuilder html = new StringBuilder();\n      foreach (Diff aDiff in diffs) {\n        string text = aDiff.text.Replace(\"&\", \"&amp;\").Replace(\"<\", \"&lt;\")\n          .Replace(\">\", \"&gt;\").Replace(\"\\n\", \"&para;<br>\");\n        switch (aDiff.operation) {\n          case Operation.INSERT:\n            html.Append(\"<ins style=\\\"background:#e6ffe6;\\\">\").Append(text)\n                .Append(\"</ins>\");\n            break;\n          case Operation.DELETE:\n            html.Append(\"<del style=\\\"background:#ffe6e6;\\\">\").Append(text)\n                .Append(\"</del>\");\n            break;\n          case Operation.EQUAL:\n            html.Append(\"<span>\").Append(text).Append(\"</span>\");\n            break;\n        }\n      }\n      return html.ToString();\n    }\n\n    /**\n     * Compute and return the source text (all equalities and deletions).\n     * @param diffs List of Diff objects.\n     * @return Source text.\n     */\n    public string diff_text1(List<Diff> diffs) {\n      StringBuilder text = new StringBuilder();\n      foreach (Diff aDiff in diffs) {\n        if (aDiff.operation != Operation.INSERT) {\n          text.Append(aDiff.text);\n        }\n      }\n      return text.ToString();\n    }\n\n    /**\n     * Compute and return the destination text (all equalities and insertions).\n     * @param diffs List of Diff objects.\n     * @return Destination text.\n     */\n    public string diff_text2(List<Diff> diffs) {\n      StringBuilder text = new StringBuilder();\n      foreach (Diff aDiff in diffs) {\n        if (aDiff.operation != Operation.DELETE) {\n          text.Append(aDiff.text);\n        }\n      }\n      return text.ToString();\n    }\n\n    /**\n     * Compute the Levenshtein distance; the number of inserted, deleted or\n     * substituted characters.\n     * @param diffs List of Diff objects.\n     * @return Number of changes.\n     */\n    public int diff_levenshtein(List<Diff> diffs) {\n      int levenshtein = 0;\n      int insertions = 0;\n      int deletions = 0;\n      foreach (Diff aDiff in diffs) {\n        switch (aDiff.operation) {\n          case Operation.INSERT:\n            insertions += aDiff.text.Length;\n            break;\n          case Operation.DELETE:\n            deletions += aDiff.text.Length;\n            break;\n          case Operation.EQUAL:\n            // A deletion and an insertion is one substitution.\n            levenshtein += Math.Max(insertions, deletions);\n            insertions = 0;\n            deletions = 0;\n            break;\n        }\n      }\n      levenshtein += Math.Max(insertions, deletions);\n      return levenshtein;\n    }\n\n    /**\n     * Crush the diff into an encoded string which describes the operations\n     * required to transform text1 into text2.\n     * E.g. =3\\t-2\\t+ing  -> Keep 3 chars, delete 2 chars, insert 'ing'.\n     * Operations are tab-separated.  Inserted text is escaped using %xx\n     * notation.\n     * @param diffs Array of Diff objects.\n     * @return Delta text.\n     */\n    public string diff_toDelta(List<Diff> diffs) {\n      StringBuilder text = new StringBuilder();\n      foreach (Diff aDiff in diffs) {\n        switch (aDiff.operation) {\n          case Operation.INSERT:\n            text.Append(\"+\").Append(encodeURI(aDiff.text)).Append(\"\\t\");\n            break;\n          case Operation.DELETE:\n            text.Append(\"-\").Append(aDiff.text.Length).Append(\"\\t\");\n            break;\n          case Operation.EQUAL:\n            text.Append(\"=\").Append(aDiff.text.Length).Append(\"\\t\");\n            break;\n        }\n      }\n      string delta = text.ToString();\n      if (delta.Length != 0) {\n        // Strip off trailing tab character.\n        delta = delta.Substring(0, delta.Length - 1);\n      }\n      return delta;\n    }\n\n    /**\n     * Given the original text1, and an encoded string which describes the\n     * operations required to transform text1 into text2, compute the full diff.\n     * @param text1 Source string for the diff.\n     * @param delta Delta text.\n     * @return Array of Diff objects or null if invalid.\n     * @throws ArgumentException If invalid input.\n     */\n    public List<Diff> diff_fromDelta(string text1, string delta) {\n      List<Diff> diffs = new List<Diff>();\n      int pointer = 0;  // Cursor in text1\n      string[] tokens = delta.Split(new string[] { \"\\t\" },\n          StringSplitOptions.None);\n      foreach (string token in tokens) {\n        if (token.Length == 0) {\n          // Blank tokens are ok (from a trailing \\t).\n          continue;\n        }\n        // Each token begins with a one character parameter which specifies the\n        // operation of this token (delete, insert, equality).\n        string param = token.Substring(1);\n        switch (token[0]) {\n          case '+':\n            // decode would change all \"+\" to \" \"\n            param = param.Replace(\"+\", \"%2b\");\n\n            param = HttpUtility.UrlDecode(param);\n            //} catch (UnsupportedEncodingException e) {\n            //  // Not likely on modern system.\n            //  throw new Error(\"This system does not support UTF-8.\", e);\n            //} catch (IllegalArgumentException e) {\n            //  // Malformed URI sequence.\n            //  throw new IllegalArgumentException(\n            //      \"Illegal escape in diff_fromDelta: \" + param, e);\n            //}\n            diffs.Add(new Diff(Operation.INSERT, param));\n            break;\n          case '-':\n            // Fall through.\n          case '=':\n            int n;\n            try {\n              n = Convert.ToInt32(param);\n            } catch (FormatException e) {\n              throw new ArgumentException(\n                  \"Invalid number in diff_fromDelta: \" + param, e);\n            }\n            if (n < 0) {\n              throw new ArgumentException(\n                  \"Negative number in diff_fromDelta: \" + param);\n            }\n            string text;\n            try {\n              text = text1.Substring(pointer, n);\n              pointer += n;\n            } catch (ArgumentOutOfRangeException e) {\n              throw new ArgumentException(\"Delta length (\" + pointer\n                  + \") larger than source text length (\" + text1.Length\n                  + \").\", e);\n            }\n            if (token[0] == '=') {\n              diffs.Add(new Diff(Operation.EQUAL, text));\n            } else {\n              diffs.Add(new Diff(Operation.DELETE, text));\n            }\n            break;\n          default:\n            // Anything else is an error.\n            throw new ArgumentException(\n                \"Invalid diff operation in diff_fromDelta: \" + token[0]);\n        }\n      }\n      if (pointer != text1.Length) {\n        throw new ArgumentException(\"Delta length (\" + pointer\n            + \") smaller than source text length (\" + text1.Length + \").\");\n      }\n      return diffs;\n    }\n\n\n    //  MATCH FUNCTIONS\n\n\n    /**\n     * Locate the best instance of 'pattern' in 'text' near 'loc'.\n     * Returns -1 if no match found.\n     * @param text The text to search.\n     * @param pattern The pattern to search for.\n     * @param loc The location to search around.\n     * @return Best match index or -1.\n     */\n    public int match_main(string text, string pattern, int loc) {\n      // Check for null inputs not needed since null can't be passed in C#.\n\n      loc = Math.Max(0, Math.Min(loc, text.Length));\n      if (text == pattern) {\n        // Shortcut (potentially not guaranteed by the algorithm)\n        return 0;\n      } else if (text.Length == 0) {\n        // Nothing to match.\n        return -1;\n      } else if (loc + pattern.Length <= text.Length\n        && text.Substring(loc, pattern.Length) == pattern) {\n        // Perfect match at the perfect spot!  (Includes case of null pattern)\n        return loc;\n      } else {\n        // Do a fuzzy compare.\n        return match_bitap(text, pattern, loc);\n      }\n    }\n\n    /**\n     * Locate the best instance of 'pattern' in 'text' near 'loc' using the\n     * Bitap algorithm.  Returns -1 if no match found.\n     * @param text The text to search.\n     * @param pattern The pattern to search for.\n     * @param loc The location to search around.\n     * @return Best match index or -1.\n     */\n    protected int match_bitap(string text, string pattern, int loc) {\n      // assert (Match_MaxBits == 0 || pattern.Length <= Match_MaxBits)\n      //    : \"Pattern too long for this application.\";\n\n      // Initialise the alphabet.\n      Dictionary<char, int> s = match_alphabet(pattern);\n\n      // Highest score beyond which we give up.\n      double score_threshold = Match_Threshold;\n      // Is there a nearby exact match? (speedup)\n      int best_loc = text.IndexOf(pattern, loc, StringComparison.Ordinal);\n      if (best_loc != -1) {\n        score_threshold = Math.Min(match_bitapScore(0, best_loc, loc,\n            pattern), score_threshold);\n        // What about in the other direction? (speedup)\n        best_loc = text.LastIndexOf(pattern,\n            Math.Min(loc + pattern.Length, text.Length),\n            StringComparison.Ordinal);\n        if (best_loc != -1) {\n          score_threshold = Math.Min(match_bitapScore(0, best_loc, loc,\n              pattern), score_threshold);\n        }\n      }\n\n      // Initialise the bit arrays.\n      int matchmask = 1 << (pattern.Length - 1);\n      best_loc = -1;\n\n      int bin_min, bin_mid;\n      int bin_max = pattern.Length + text.Length;\n      // Empty initialization added to appease C# compiler.\n      int[] last_rd = new int[0];\n      for (int d = 0; d < pattern.Length; d++) {\n        // Scan for the best match; each iteration allows for one more error.\n        // Run a binary search to determine how far from 'loc' we can stray at\n        // this error level.\n        bin_min = 0;\n        bin_mid = bin_max;\n        while (bin_min < bin_mid) {\n          if (match_bitapScore(d, loc + bin_mid, loc, pattern)\n              <= score_threshold) {\n            bin_min = bin_mid;\n          } else {\n            bin_max = bin_mid;\n          }\n          bin_mid = (bin_max - bin_min) / 2 + bin_min;\n        }\n        // Use the result from this iteration as the maximum for the next.\n        bin_max = bin_mid;\n        int start = Math.Max(1, loc - bin_mid + 1);\n        int finish = Math.Min(loc + bin_mid, text.Length) + pattern.Length;\n\n        int[] rd = new int[finish + 2];\n        rd[finish + 1] = (1 << d) - 1;\n        for (int j = finish; j >= start; j--) {\n          int charMatch;\n          if (text.Length <= j - 1 || !s.ContainsKey(text[j - 1])) {\n            // Out of range.\n            charMatch = 0;\n          } else {\n            charMatch = s[text[j - 1]];\n          }\n          if (d == 0) {\n            // First pass: exact match.\n            rd[j] = ((rd[j + 1] << 1) | 1) & charMatch;\n          } else {\n            // Subsequent passes: fuzzy match.\n            rd[j] = ((rd[j + 1] << 1) | 1) & charMatch\n                | (((last_rd[j + 1] | last_rd[j]) << 1) | 1) | last_rd[j + 1];\n          }\n          if ((rd[j] & matchmask) != 0) {\n            double score = match_bitapScore(d, j - 1, loc, pattern);\n            // This match will almost certainly be better than any existing\n            // match.  But check anyway.\n            if (score <= score_threshold) {\n              // Told you so.\n              score_threshold = score;\n              best_loc = j - 1;\n              if (best_loc > loc) {\n                // When passing loc, don't exceed our current distance from loc.\n                start = Math.Max(1, 2 * loc - best_loc);\n              } else {\n                // Already passed loc, downhill from here on in.\n                break;\n              }\n            }\n          }\n        }\n        if (match_bitapScore(d + 1, loc, loc, pattern) > score_threshold) {\n          // No hope for a (better) match at greater error levels.\n          break;\n        }\n        last_rd = rd;\n      }\n      return best_loc;\n    }\n\n    /**\n     * Compute and return the score for a match with e errors and x location.\n     * @param e Number of errors in match.\n     * @param x Location of match.\n     * @param loc Expected location of match.\n     * @param pattern Pattern being sought.\n     * @return Overall score for match (0.0 = good, 1.0 = bad).\n     */\n    private double match_bitapScore(int e, int x, int loc, string pattern) {\n      float accuracy = (float)e / pattern.Length;\n      int proximity = Math.Abs(loc - x);\n      if (Match_Distance == 0) {\n        // Dodge divide by zero error.\n        return proximity == 0 ? accuracy : 1.0;\n      }\n      return accuracy + (proximity / (float) Match_Distance);\n    }\n\n    /**\n     * Initialise the alphabet for the Bitap algorithm.\n     * @param pattern The text to encode.\n     * @return Hash of character locations.\n     */\n    protected Dictionary<char, int> match_alphabet(string pattern) {\n      Dictionary<char, int> s = new Dictionary<char, int>();\n      char[] char_pattern = pattern.ToCharArray();\n      foreach (char c in char_pattern) {\n        if (!s.ContainsKey(c)) {\n          s.Add(c, 0);\n        }\n      }\n      int i = 0;\n      foreach (char c in char_pattern) {\n        int value = s[c] | (1 << (pattern.Length - i - 1));\n        s[c] = value;\n        i++;\n      }\n      return s;\n    }\n\n\n    //  PATCH FUNCTIONS\n\n\n    /**\n     * Increase the context until it is unique,\n     * but don't let the pattern expand beyond Match_MaxBits.\n     * @param patch The patch to grow.\n     * @param text Source text.\n     */\n    protected void patch_addContext(Patch patch, string text) {\n      if (text.Length == 0) {\n        return;\n      }\n      string pattern = text.Substring(patch.start2, patch.length1);\n      int padding = 0;\n\n      // Look for the first and last matches of pattern in text.  If two\n      // different matches are found, increase the pattern length.\n      while (text.IndexOf(pattern, StringComparison.Ordinal)\n          != text.LastIndexOf(pattern, StringComparison.Ordinal)\n          && pattern.Length < Match_MaxBits - Patch_Margin - Patch_Margin) {\n        padding += Patch_Margin;\n        pattern = text.JavaSubstring(Math.Max(0, patch.start2 - padding),\n            Math.Min(text.Length, patch.start2 + patch.length1 + padding));\n      }\n      // Add one chunk for good luck.\n      padding += Patch_Margin;\n\n      // Add the prefix.\n      string prefix = text.JavaSubstring(Math.Max(0, patch.start2 - padding),\n        patch.start2);\n      if (prefix.Length != 0) {\n        patch.diffs.Insert(0, new Diff(Operation.EQUAL, prefix));\n      }\n      // Add the suffix.\n      string suffix = text.JavaSubstring(patch.start2 + patch.length1,\n          Math.Min(text.Length, patch.start2 + patch.length1 + padding));\n      if (suffix.Length != 0) {\n        patch.diffs.Add(new Diff(Operation.EQUAL, suffix));\n      }\n\n      // Roll back the start points.\n      patch.start1 -= prefix.Length;\n      patch.start2 -= prefix.Length;\n      // Extend the lengths.\n      patch.length1 += prefix.Length + suffix.Length;\n      patch.length2 += prefix.Length + suffix.Length;\n    }\n\n    /**\n     * Compute a list of patches to turn text1 into text2.\n     * A set of diffs will be computed.\n     * @param text1 Old text.\n     * @param text2 New text.\n     * @return List of Patch objects.\n     */\n    public List<Patch> patch_make(string text1, string text2) {\n      // Check for null inputs not needed since null can't be passed in C#.\n      // No diffs provided, compute our own.\n      List<Diff> diffs = diff_main(text1, text2, true);\n      if (diffs.Count > 2) {\n        diff_cleanupSemantic(diffs);\n        diff_cleanupEfficiency(diffs);\n      }\n      return patch_make(text1, diffs);\n    }\n\n    /**\n     * Compute a list of patches to turn text1 into text2.\n     * text1 will be derived from the provided diffs.\n     * @param diffs Array of Diff objects for text1 to text2.\n     * @return List of Patch objects.\n     */\n    public List<Patch> patch_make(List<Diff> diffs) {\n      // Check for null inputs not needed since null can't be passed in C#.\n      // No origin string provided, compute our own.\n      string text1 = diff_text1(diffs);\n      return patch_make(text1, diffs);\n    }\n\n    /**\n     * Compute a list of patches to turn text1 into text2.\n     * text2 is ignored, diffs are the delta between text1 and text2.\n     * @param text1 Old text\n     * @param text2 Ignored.\n     * @param diffs Array of Diff objects for text1 to text2.\n     * @return List of Patch objects.\n     * @deprecated Prefer patch_make(string text1, List<Diff> diffs).\n     */\n    public List<Patch> patch_make(string text1, string text2,\n        List<Diff> diffs) {\n      return patch_make(text1, diffs);\n    }\n\n    /**\n     * Compute a list of patches to turn text1 into text2.\n     * text2 is not provided, diffs are the delta between text1 and text2.\n     * @param text1 Old text.\n     * @param diffs Array of Diff objects for text1 to text2.\n     * @return List of Patch objects.\n     */\n    public List<Patch> patch_make(string text1, List<Diff> diffs) {\n      // Check for null inputs not needed since null can't be passed in C#.\n      List<Patch> patches = new List<Patch>();\n      if (diffs.Count == 0) {\n        return patches;  // Get rid of the null case.\n      }\n      Patch patch = new Patch();\n      int char_count1 = 0;  // Number of characters into the text1 string.\n      int char_count2 = 0;  // Number of characters into the text2 string.\n      // Start with text1 (prepatch_text) and apply the diffs until we arrive at\n      // text2 (postpatch_text). We recreate the patches one by one to determine\n      // context info.\n      string prepatch_text = text1;\n      string postpatch_text = text1;\n      foreach (Diff aDiff in diffs) {\n        if (patch.diffs.Count == 0 && aDiff.operation != Operation.EQUAL) {\n          // A new patch starts here.\n          patch.start1 = char_count1;\n          patch.start2 = char_count2;\n        }\n\n        switch (aDiff.operation) {\n          case Operation.INSERT:\n            patch.diffs.Add(aDiff);\n            patch.length2 += aDiff.text.Length;\n            postpatch_text = postpatch_text.Insert(char_count2, aDiff.text);\n            break;\n          case Operation.DELETE:\n            patch.length1 += aDiff.text.Length;\n            patch.diffs.Add(aDiff);\n            postpatch_text = postpatch_text.Remove(char_count2,\n                aDiff.text.Length);\n            break;\n          case Operation.EQUAL:\n            if (aDiff.text.Length <= 2 * Patch_Margin\n                && patch.diffs.Count() != 0 && aDiff != diffs.Last()) {\n              // Small equality inside a patch.\n              patch.diffs.Add(aDiff);\n              patch.length1 += aDiff.text.Length;\n              patch.length2 += aDiff.text.Length;\n            }\n\n            if (aDiff.text.Length >= 2 * Patch_Margin) {\n              // Time for a new patch.\n              if (patch.diffs.Count != 0) {\n                patch_addContext(patch, prepatch_text);\n                patches.Add(patch);\n                patch = new Patch();\n                // Unlike Unidiff, our patch lists have a rolling context.\n                // https://github.com/google/diff-match-patch/wiki/Unidiff\n                // Update prepatch text & pos to reflect the application of the\n                // just completed patch.\n                prepatch_text = postpatch_text;\n                char_count1 = char_count2;\n              }\n            }\n            break;\n        }\n\n        // Update the current character count.\n        if (aDiff.operation != Operation.INSERT) {\n          char_count1 += aDiff.text.Length;\n        }\n        if (aDiff.operation != Operation.DELETE) {\n          char_count2 += aDiff.text.Length;\n        }\n      }\n      // Pick up the leftover patch if not empty.\n      if (patch.diffs.Count != 0) {\n        patch_addContext(patch, prepatch_text);\n        patches.Add(patch);\n      }\n\n      return patches;\n    }\n\n    /**\n     * Given an array of patches, return another array that is identical.\n     * @param patches Array of Patch objects.\n     * @return Array of Patch objects.\n     */\n    public List<Patch> patch_deepCopy(List<Patch> patches) {\n      List<Patch> patchesCopy = new List<Patch>();\n      foreach (Patch aPatch in patches) {\n        Patch patchCopy = new Patch();\n        foreach (Diff aDiff in aPatch.diffs) {\n          Diff diffCopy = new Diff(aDiff.operation, aDiff.text);\n          patchCopy.diffs.Add(diffCopy);\n        }\n        patchCopy.start1 = aPatch.start1;\n        patchCopy.start2 = aPatch.start2;\n        patchCopy.length1 = aPatch.length1;\n        patchCopy.length2 = aPatch.length2;\n        patchesCopy.Add(patchCopy);\n      }\n      return patchesCopy;\n    }\n\n    /**\n     * Merge a set of patches onto the text.  Return a patched text, as well\n     * as an array of true/false values indicating which patches were applied.\n     * @param patches Array of Patch objects\n     * @param text Old text.\n     * @return Two element Object array, containing the new text and an array of\n     *      bool values.\n     */\n    public Object[] patch_apply(List<Patch> patches, string text) {\n      if (patches.Count == 0) {\n        return new Object[] { text, new bool[0] };\n      }\n\n      // Deep copy the patches so that no changes are made to originals.\n      patches = patch_deepCopy(patches);\n\n      string nullPadding = this.patch_addPadding(patches);\n      text = nullPadding + text + nullPadding;\n      patch_splitMax(patches);\n\n      int x = 0;\n      // delta keeps track of the offset between the expected and actual\n      // location of the previous patch.  If there are patches expected at\n      // positions 10 and 20, but the first patch was found at 12, delta is 2\n      // and the second patch has an effective expected position of 22.\n      int delta = 0;\n      bool[] results = new bool[patches.Count];\n      foreach (Patch aPatch in patches) {\n        int expected_loc = aPatch.start2 + delta;\n        string text1 = diff_text1(aPatch.diffs);\n        int start_loc;\n        int end_loc = -1;\n        if (text1.Length > this.Match_MaxBits) {\n          // patch_splitMax will only provide an oversized pattern\n          // in the case of a monster delete.\n          start_loc = match_main(text,\n              text1.Substring(0, this.Match_MaxBits), expected_loc);\n          if (start_loc != -1) {\n            end_loc = match_main(text,\n                text1.Substring(text1.Length - this.Match_MaxBits),\n                expected_loc + text1.Length - this.Match_MaxBits);\n            if (end_loc == -1 || start_loc >= end_loc) {\n              // Can't find valid trailing context.  Drop this patch.\n              start_loc = -1;\n            }\n          }\n        } else {\n          start_loc = this.match_main(text, text1, expected_loc);\n        }\n        if (start_loc == -1) {\n          // No match found.  :(\n          results[x] = false;\n          // Subtract the delta for this failed patch from subsequent patches.\n          delta -= aPatch.length2 - aPatch.length1;\n        } else {\n          // Found a match.  :)\n          results[x] = true;\n          delta = start_loc - expected_loc;\n          string text2;\n          if (end_loc == -1) {\n            text2 = text.JavaSubstring(start_loc,\n                Math.Min(start_loc + text1.Length, text.Length));\n          } else {\n            text2 = text.JavaSubstring(start_loc,\n                Math.Min(end_loc + this.Match_MaxBits, text.Length));\n          }\n          if (text1 == text2) {\n            // Perfect match, just shove the Replacement text in.\n            text = text.Substring(0, start_loc) + diff_text2(aPatch.diffs)\n                + text.Substring(start_loc + text1.Length);\n          } else {\n            // Imperfect match.  Run a diff to get a framework of equivalent\n            // indices.\n            List<Diff> diffs = diff_main(text1, text2, false);\n            if (text1.Length > this.Match_MaxBits\n                && this.diff_levenshtein(diffs) / (float) text1.Length\n                > this.Patch_DeleteThreshold) {\n              // The end points match, but the content is unacceptably bad.\n              results[x] = false;\n            } else {\n              diff_cleanupSemanticLossless(diffs);\n              int index1 = 0;\n              foreach (Diff aDiff in aPatch.diffs) {\n                if (aDiff.operation != Operation.EQUAL) {\n                  int index2 = diff_xIndex(diffs, index1);\n                  if (aDiff.operation == Operation.INSERT) {\n                    // Insertion\n                    text = text.Insert(start_loc + index2, aDiff.text);\n                  } else if (aDiff.operation == Operation.DELETE) {\n                    // Deletion\n                    text = text.Remove(start_loc + index2, diff_xIndex(diffs,\n                        index1 + aDiff.text.Length) - index2);\n                  }\n                }\n                if (aDiff.operation != Operation.DELETE) {\n                  index1 += aDiff.text.Length;\n                }\n              }\n            }\n          }\n        }\n        x++;\n      }\n      // Strip the padding off.\n      text = text.Substring(nullPadding.Length, text.Length\n          - 2 * nullPadding.Length);\n      return new Object[] { text, results };\n    }\n\n    /**\n     * Add some padding on text start and end so that edges can match something.\n     * Intended to be called only from within patch_apply.\n     * @param patches Array of Patch objects.\n     * @return The padding string added to each side.\n     */\n    public string patch_addPadding(List<Patch> patches) {\n      short paddingLength = this.Patch_Margin;\n      string nullPadding = string.Empty;\n      for (short x = 1; x <= paddingLength; x++) {\n        nullPadding += (char)x;\n      }\n\n      // Bump all the patches forward.\n      foreach (Patch aPatch in patches) {\n        aPatch.start1 += paddingLength;\n        aPatch.start2 += paddingLength;\n      }\n\n      // Add some padding on start of first diff.\n      Patch patch = patches.First();\n      List<Diff> diffs = patch.diffs;\n      if (diffs.Count == 0 || diffs.First().operation != Operation.EQUAL) {\n        // Add nullPadding equality.\n        diffs.Insert(0, new Diff(Operation.EQUAL, nullPadding));\n        patch.start1 -= paddingLength;  // Should be 0.\n        patch.start2 -= paddingLength;  // Should be 0.\n        patch.length1 += paddingLength;\n        patch.length2 += paddingLength;\n      } else if (paddingLength > diffs.First().text.Length) {\n        // Grow first equality.\n        Diff firstDiff = diffs.First();\n        int extraLength = paddingLength - firstDiff.text.Length;\n        firstDiff.text = nullPadding.Substring(firstDiff.text.Length)\n            + firstDiff.text;\n        patch.start1 -= extraLength;\n        patch.start2 -= extraLength;\n        patch.length1 += extraLength;\n        patch.length2 += extraLength;\n      }\n\n      // Add some padding on end of last diff.\n      patch = patches.Last();\n      diffs = patch.diffs;\n      if (diffs.Count == 0 || diffs.Last().operation != Operation.EQUAL) {\n        // Add nullPadding equality.\n        diffs.Add(new Diff(Operation.EQUAL, nullPadding));\n        patch.length1 += paddingLength;\n        patch.length2 += paddingLength;\n      } else if (paddingLength > diffs.Last().text.Length) {\n        // Grow last equality.\n        Diff lastDiff = diffs.Last();\n        int extraLength = paddingLength - lastDiff.text.Length;\n        lastDiff.text += nullPadding.Substring(0, extraLength);\n        patch.length1 += extraLength;\n        patch.length2 += extraLength;\n      }\n\n      return nullPadding;\n    }\n\n    /**\n     * Look through the patches and break up any which are longer than the\n     * maximum limit of the match algorithm.\n     * Intended to be called only from within patch_apply.\n     * @param patches List of Patch objects.\n     */\n    public void patch_splitMax(List<Patch> patches) {\n      short patch_size = this.Match_MaxBits;\n      for (int x = 0; x < patches.Count; x++) {\n        if (patches[x].length1 <= patch_size) {\n          continue;\n        }\n        Patch bigpatch = patches[x];\n        // Remove the big old patch.\n        patches.Splice(x--, 1);\n        int start1 = bigpatch.start1;\n        int start2 = bigpatch.start2;\n        string precontext = string.Empty;\n        while (bigpatch.diffs.Count != 0) {\n          // Create one of several smaller patches.\n          Patch patch = new Patch();\n          bool empty = true;\n          patch.start1 = start1 - precontext.Length;\n          patch.start2 = start2 - precontext.Length;\n          if (precontext.Length != 0) {\n            patch.length1 = patch.length2 = precontext.Length;\n            patch.diffs.Add(new Diff(Operation.EQUAL, precontext));\n          }\n          while (bigpatch.diffs.Count != 0\n              && patch.length1 < patch_size - this.Patch_Margin) {\n            Operation diff_type = bigpatch.diffs[0].operation;\n            string diff_text = bigpatch.diffs[0].text;\n            if (diff_type == Operation.INSERT) {\n              // Insertions are harmless.\n              patch.length2 += diff_text.Length;\n              start2 += diff_text.Length;\n              patch.diffs.Add(bigpatch.diffs.First());\n              bigpatch.diffs.RemoveAt(0);\n              empty = false;\n            } else if (diff_type == Operation.DELETE && patch.diffs.Count == 1\n                && patch.diffs.First().operation == Operation.EQUAL\n                && diff_text.Length > 2 * patch_size) {\n              // This is a large deletion.  Let it pass in one chunk.\n              patch.length1 += diff_text.Length;\n              start1 += diff_text.Length;\n              empty = false;\n              patch.diffs.Add(new Diff(diff_type, diff_text));\n              bigpatch.diffs.RemoveAt(0);\n            } else {\n              // Deletion or equality.  Only take as much as we can stomach.\n              diff_text = diff_text.Substring(0, Math.Min(diff_text.Length,\n                  patch_size - patch.length1 - Patch_Margin));\n              patch.length1 += diff_text.Length;\n              start1 += diff_text.Length;\n              if (diff_type == Operation.EQUAL) {\n                patch.length2 += diff_text.Length;\n                start2 += diff_text.Length;\n              } else {\n                empty = false;\n              }\n              patch.diffs.Add(new Diff(diff_type, diff_text));\n              if (diff_text == bigpatch.diffs[0].text) {\n                bigpatch.diffs.RemoveAt(0);\n              } else {\n                bigpatch.diffs[0].text =\n                    bigpatch.diffs[0].text.Substring(diff_text.Length);\n              }\n            }\n          }\n          // Compute the head context for the next patch.\n          precontext = this.diff_text2(patch.diffs);\n          precontext = precontext.Substring(Math.Max(0,\n              precontext.Length - this.Patch_Margin));\n\n          string postcontext = null;\n          // Append the end context for this patch.\n          if (diff_text1(bigpatch.diffs).Length > Patch_Margin) {\n            postcontext = diff_text1(bigpatch.diffs)\n                .Substring(0, Patch_Margin);\n          } else {\n            postcontext = diff_text1(bigpatch.diffs);\n          }\n\n          if (postcontext.Length != 0) {\n            patch.length1 += postcontext.Length;\n            patch.length2 += postcontext.Length;\n            if (patch.diffs.Count != 0\n                && patch.diffs[patch.diffs.Count - 1].operation\n                == Operation.EQUAL) {\n              patch.diffs[patch.diffs.Count - 1].text += postcontext;\n            } else {\n              patch.diffs.Add(new Diff(Operation.EQUAL, postcontext));\n            }\n          }\n          if (!empty) {\n            patches.Splice(++x, 0, patch);\n          }\n        }\n      }\n    }\n\n    /**\n     * Take a list of patches and return a textual representation.\n     * @param patches List of Patch objects.\n     * @return Text representation of patches.\n     */\n    public string patch_toText(List<Patch> patches) {\n      StringBuilder text = new StringBuilder();\n      foreach (Patch aPatch in patches) {\n        text.Append(aPatch);\n      }\n      return text.ToString();\n    }\n\n    /**\n     * Parse a textual representation of patches and return a List of Patch\n     * objects.\n     * @param textline Text representation of patches.\n     * @return List of Patch objects.\n     * @throws ArgumentException If invalid input.\n     */\n    public List<Patch> patch_fromText(string textline) {\n      List<Patch> patches = new List<Patch>();\n      if (textline.Length == 0) {\n        return patches;\n      }\n      string[] text = textline.Split('\\n');\n      int textPointer = 0;\n      Patch patch;\n      Regex patchHeader\n          = new Regex(\"^@@ -(\\\\d+),?(\\\\d*) \\\\+(\\\\d+),?(\\\\d*) @@$\");\n      Match m;\n      char sign;\n      string line;\n      while (textPointer < text.Length) {\n        m = patchHeader.Match(text[textPointer]);\n        if (!m.Success) {\n          throw new ArgumentException(\"Invalid patch string: \"\n              + text[textPointer]);\n        }\n        patch = new Patch();\n        patches.Add(patch);\n        patch.start1 = Convert.ToInt32(m.Groups[1].Value);\n        if (m.Groups[2].Length == 0) {\n          patch.start1--;\n          patch.length1 = 1;\n        } else if (m.Groups[2].Value == \"0\") {\n          patch.length1 = 0;\n        } else {\n          patch.start1--;\n          patch.length1 = Convert.ToInt32(m.Groups[2].Value);\n        }\n\n        patch.start2 = Convert.ToInt32(m.Groups[3].Value);\n        if (m.Groups[4].Length == 0) {\n          patch.start2--;\n          patch.length2 = 1;\n        } else if (m.Groups[4].Value == \"0\") {\n          patch.length2 = 0;\n        } else {\n          patch.start2--;\n          patch.length2 = Convert.ToInt32(m.Groups[4].Value);\n        }\n        textPointer++;\n\n        while (textPointer < text.Length) {\n          try {\n            sign = text[textPointer][0];\n          } catch (IndexOutOfRangeException) {\n            // Blank line?  Whatever.\n            textPointer++;\n            continue;\n          }\n          line = text[textPointer].Substring(1);\n          line = line.Replace(\"+\", \"%2b\");\n          line = HttpUtility.UrlDecode(line);\n          if (sign == '-') {\n            // Deletion.\n            patch.diffs.Add(new Diff(Operation.DELETE, line));\n          } else if (sign == '+') {\n            // Insertion.\n            patch.diffs.Add(new Diff(Operation.INSERT, line));\n          } else if (sign == ' ') {\n            // Minor equality.\n            patch.diffs.Add(new Diff(Operation.EQUAL, line));\n          } else if (sign == '@') {\n            // Start of next patch.\n            break;\n          } else {\n            // WTF?\n            throw new ArgumentException(\n                \"Invalid patch mode '\" + sign + \"' in: \" + line);\n          }\n          textPointer++;\n        }\n      }\n      return patches;\n    }\n\n    /**\n     * Encodes a string with URI-style % escaping.\n     * Compatible with JavaScript's encodeURI function.\n     *\n     * @param str The string to encode.\n     * @return The encoded string.\n     */\n    public static string encodeURI(string str) {\n        // C# is overzealous in the replacements.  Walk back on a few.\n        return new StringBuilder(HttpUtility.UrlEncode(str))\n            .Replace('+', ' ').Replace(\"%20\", \" \").Replace(\"%21\", \"!\")\n            .Replace(\"%2a\", \"*\").Replace(\"%27\", \"'\").Replace(\"%28\", \"(\")\n            .Replace(\"%29\", \")\").Replace(\"%3b\", \";\").Replace(\"%2f\", \"/\")\n            .Replace(\"%3f\", \"?\").Replace(\"%3a\", \":\").Replace(\"%40\", \"@\")\n            .Replace(\"%26\", \"&\").Replace(\"%3d\", \"=\").Replace(\"%2b\", \"+\")\n            .Replace(\"%24\", \"$\").Replace(\"%2c\", \",\").Replace(\"%23\", \"#\")\n            .Replace(\"%7e\", \"~\")\n            .ToString();\n    }\n  }\n}\n"
  },
  {
    "path": "csharp/tests/DiffMatchPatchTest.cs",
    "content": "﻿/*\n * Diff Match and Patch -- Test Harness\n * Copyright 2018 The diff-match-patch Authors.\n * https://github.com/google/diff-match-patch\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *   http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n/*\n * To compile with Mono:\n *   mcs DiffMatchPatchTest.cs ../DiffMatchPatch.cs\n * To run with Mono:\n *   mono DiffMatchPatchTest.exe\n*/\n\nusing DiffMatchPatch;\nusing System.Collections.Generic;\nusing System;\nusing System.Text;\n\npublic class diff_match_patchTest : diff_match_patch {\n  public void diff_commonPrefixTest() {\n    // Detect any common suffix.\n    assertEquals(\"diff_commonPrefix: Null case.\", 0, this.diff_commonPrefix(\"abc\", \"xyz\"));\n\n    assertEquals(\"diff_commonPrefix: Non-null case.\", 4, this.diff_commonPrefix(\"1234abcdef\", \"1234xyz\"));\n\n    assertEquals(\"diff_commonPrefix: Whole case.\", 4, this.diff_commonPrefix(\"1234\", \"1234xyz\"));\n  }\n\n  public void diff_commonSuffixTest() {\n    // Detect any common suffix.\n    assertEquals(\"diff_commonSuffix: Null case.\", 0, this.diff_commonSuffix(\"abc\", \"xyz\"));\n\n    assertEquals(\"diff_commonSuffix: Non-null case.\", 4, this.diff_commonSuffix(\"abcdef1234\", \"xyz1234\"));\n\n    assertEquals(\"diff_commonSuffix: Whole case.\", 4, this.diff_commonSuffix(\"1234\", \"xyz1234\"));\n  }\n\n  public void diff_commonOverlapTest() {\n    // Detect any suffix/prefix overlap.\n    assertEquals(\"diff_commonOverlap: Null case.\", 0, this.diff_commonOverlap(\"\", \"abcd\"));\n\n    assertEquals(\"diff_commonOverlap: Whole case.\", 3, this.diff_commonOverlap(\"abc\", \"abcd\"));\n\n    assertEquals(\"diff_commonOverlap: No overlap.\", 0, this.diff_commonOverlap(\"123456\", \"abcd\"));\n\n    assertEquals(\"diff_commonOverlap: Overlap.\", 3, this.diff_commonOverlap(\"123456xxx\", \"xxxabcd\"));\n\n    // Some overly clever languages (C#) may treat ligatures as equal to their\n    // component letters.  E.g. U+FB01 == 'fi'\n    assertEquals(\"diff_commonOverlap: Unicode.\", 0, this.diff_commonOverlap(\"fi\", \"\\ufb01i\"));\n  }\n\n  public void diff_halfmatchTest() {\n    this.Diff_Timeout = 1;\n    assertNull(\"diff_halfMatch: No match #1.\", this.diff_halfMatch(\"1234567890\", \"abcdef\"));\n\n    assertNull(\"diff_halfMatch: No match #2.\", this.diff_halfMatch(\"12345\", \"23\"));\n\n    assertEquals(\"diff_halfMatch: Single Match #1.\", new string[] { \"12\", \"90\", \"a\", \"z\", \"345678\" }, this.diff_halfMatch(\"1234567890\", \"a345678z\"));\n\n    assertEquals(\"diff_halfMatch: Single Match #2.\", new string[] { \"a\", \"z\", \"12\", \"90\", \"345678\" }, this.diff_halfMatch(\"a345678z\", \"1234567890\"));\n\n    assertEquals(\"diff_halfMatch: Single Match #3.\", new string[] { \"abc\", \"z\", \"1234\", \"0\", \"56789\" }, this.diff_halfMatch(\"abc56789z\", \"1234567890\"));\n\n    assertEquals(\"diff_halfMatch: Single Match #4.\", new string[] { \"a\", \"xyz\", \"1\", \"7890\", \"23456\" }, this.diff_halfMatch(\"a23456xyz\", \"1234567890\"));\n\n    assertEquals(\"diff_halfMatch: Multiple Matches #1.\", new string[] { \"12123\", \"123121\", \"a\", \"z\", \"1234123451234\" }, this.diff_halfMatch(\"121231234123451234123121\", \"a1234123451234z\"));\n\n    assertEquals(\"diff_halfMatch: Multiple Matches #2.\", new string[] { \"\", \"-=-=-=-=-=\", \"x\", \"\", \"x-=-=-=-=-=-=-=\" }, this.diff_halfMatch(\"x-=-=-=-=-=-=-=-=-=-=-=-=\", \"xx-=-=-=-=-=-=-=\"));\n\n    assertEquals(\"diff_halfMatch: Multiple Matches #3.\", new string[] { \"-=-=-=-=-=\", \"\", \"\", \"y\", \"-=-=-=-=-=-=-=y\" }, this.diff_halfMatch(\"-=-=-=-=-=-=-=-=-=-=-=-=y\", \"-=-=-=-=-=-=-=yy\"));\n\n    // Optimal diff would be -q+x=H-i+e=lloHe+Hu=llo-Hew+y not -qHillo+x=HelloHe-w+Hulloy\n    assertEquals(\"diff_halfMatch: Non-optimal halfmatch.\", new string[] { \"qHillo\", \"w\", \"x\", \"Hulloy\", \"HelloHe\" }, this.diff_halfMatch(\"qHilloHelloHew\", \"xHelloHeHulloy\"));\n\n    this.Diff_Timeout = 0;\n    assertNull(\"diff_halfMatch: Optimal no halfmatch.\", this.diff_halfMatch(\"qHilloHelloHew\", \"xHelloHeHulloy\"));\n  }\n\n  public void diff_linesToCharsTest() {\n    // Convert lines down to characters.\n    List<string> tmpVector = new List<string>();\n    tmpVector.Add(\"\");\n    tmpVector.Add(\"alpha\\n\");\n    tmpVector.Add(\"beta\\n\");\n    Object[] result = this.diff_linesToChars(\"alpha\\nbeta\\nalpha\\n\", \"beta\\nalpha\\nbeta\\n\");\n    assertEquals(\"diff_linesToChars: Shared lines #1.\", \"\\u0001\\u0002\\u0001\", (string)result[0]);\n    assertEquals(\"diff_linesToChars: Shared lines #2.\", \"\\u0002\\u0001\\u0002\", (string)result[1]);\n    assertEquals(\"diff_linesToChars: Shared lines #3.\", tmpVector, (List<string>)result[2]);\n\n    tmpVector.Clear();\n    tmpVector.Add(\"\");\n    tmpVector.Add(\"alpha\\r\\n\");\n    tmpVector.Add(\"beta\\r\\n\");\n    tmpVector.Add(\"\\r\\n\");\n    result = this.diff_linesToChars(\"\", \"alpha\\r\\nbeta\\r\\n\\r\\n\\r\\n\");\n    assertEquals(\"diff_linesToChars: Empty string and blank lines #1.\", \"\", (string)result[0]);\n    assertEquals(\"diff_linesToChars: Empty string and blank lines #2.\", \"\\u0001\\u0002\\u0003\\u0003\", (string)result[1]);\n    assertEquals(\"diff_linesToChars: Empty string and blank lines #3.\", tmpVector, (List<string>)result[2]);\n\n    tmpVector.Clear();\n    tmpVector.Add(\"\");\n    tmpVector.Add(\"a\");\n    tmpVector.Add(\"b\");\n    result = this.diff_linesToChars(\"a\", \"b\");\n    assertEquals(\"diff_linesToChars: No linebreaks #1.\", \"\\u0001\", (string)result[0]);\n    assertEquals(\"diff_linesToChars: No linebreaks #2.\", \"\\u0002\", (string)result[1]);\n    assertEquals(\"diff_linesToChars: No linebreaks #3.\", tmpVector, (List<string>)result[2]);\n\n    // More than 256 to reveal any 8-bit limitations.\n    int n = 300;\n    tmpVector.Clear();\n    StringBuilder lineList = new StringBuilder();\n    StringBuilder charList = new StringBuilder();\n    for (int i = 1; i < n + 1; i++) {\n      tmpVector.Add(i + \"\\n\");\n      lineList.Append(i + \"\\n\");\n      charList.Append(Convert.ToChar(i));\n    }\n    assertEquals(\"Test initialization fail #1.\", n, tmpVector.Count);\n    string lines = lineList.ToString();\n    string chars = charList.ToString();\n    assertEquals(\"Test initialization fail #2.\", n, chars.Length);\n    tmpVector.Insert(0, \"\");\n    result = this.diff_linesToChars(lines, \"\");\n    assertEquals(\"diff_linesToChars: More than 256 #1.\", chars, (string)result[0]);\n    assertEquals(\"diff_linesToChars: More than 256 #2.\", \"\", (string)result[1]);\n    assertEquals(\"diff_linesToChars: More than 256 #3.\", tmpVector, (List<string>)result[2]);\n  }\n\n  public void diff_charsToLinesTest() {\n    // First check that Diff equality works.\n    assertTrue(\"diff_charsToLines: Equality #1.\", new Diff(Operation.EQUAL, \"a\").Equals(new Diff(Operation.EQUAL, \"a\")));\n\n    assertEquals(\"diff_charsToLines: Equality #2.\", new Diff(Operation.EQUAL, \"a\"), new Diff(Operation.EQUAL, \"a\"));\n\n    // Convert chars up to lines.\n    List<Diff> diffs = new List<Diff> {\n        new Diff(Operation.EQUAL, \"\\u0001\\u0002\\u0001\"),\n        new Diff(Operation.INSERT, \"\\u0002\\u0001\\u0002\")};\n    List<string> tmpVector = new List<string>();\n    tmpVector.Add(\"\");\n    tmpVector.Add(\"alpha\\n\");\n    tmpVector.Add(\"beta\\n\");\n    this.diff_charsToLines(diffs, tmpVector);\n    assertEquals(\"diff_charsToLines: Shared lines.\", new List<Diff> {\n        new Diff(Operation.EQUAL, \"alpha\\nbeta\\nalpha\\n\"),\n        new Diff(Operation.INSERT, \"beta\\nalpha\\nbeta\\n\")}, diffs);\n\n    // More than 256 to reveal any 8-bit limitations.\n    int n = 300;\n    tmpVector.Clear();\n    StringBuilder lineList = new StringBuilder();\n    StringBuilder charList = new StringBuilder();\n    for (int i = 1; i < n + 1; i++) {\n      tmpVector.Add(i + \"\\n\");\n      lineList.Append(i + \"\\n\");\n      charList.Append(Convert.ToChar (i));\n    }\n    assertEquals(\"Test initialization fail #3.\", n, tmpVector.Count);\n    string lines = lineList.ToString();\n    string chars = charList.ToString();\n    assertEquals(\"Test initialization fail #4.\", n, chars.Length);\n    tmpVector.Insert(0, \"\");\n    diffs = new List<Diff> {new Diff(Operation.DELETE, chars)};\n    this.diff_charsToLines(diffs, tmpVector);\n    assertEquals(\"diff_charsToLines: More than 256.\", new List<Diff>\n        {new Diff(Operation.DELETE, lines)}, diffs);\n\n    // More than 65536 to verify any 16-bit limitation.\n    lineList = new StringBuilder();\n    for (int i = 0; i < 66000; i++) {\n      lineList.Append(i + \"\\n\");\n    }\n    chars = lineList.ToString();\n    Object[] result = this.diff_linesToChars(chars, \"\");\n    diffs = new List<Diff> {new Diff(Operation.INSERT, (string)result[0])};\n    this.diff_charsToLines(diffs, (List<string>)result[2]);\n    assertEquals(\"diff_charsToLines: More than 65536.\", chars, diffs[0].text);\n  }\n\n  public void diff_cleanupMergeTest() {\n    // Cleanup a messy diff.\n    // Null case.\n    List<Diff> diffs = new List<Diff>();\n    assertEquals(\"diff_cleanupMerge: Null case.\", new List<Diff>(), diffs);\n\n    diffs = new List<Diff> {new Diff(Operation.EQUAL, \"a\"), new Diff(Operation.DELETE, \"b\"), new Diff(Operation.INSERT, \"c\")};\n    this.diff_cleanupMerge(diffs);\n    assertEquals(\"diff_cleanupMerge: No change case.\", new List<Diff> {new Diff(Operation.EQUAL, \"a\"), new Diff(Operation.DELETE, \"b\"), new Diff(Operation.INSERT, \"c\")}, diffs);\n\n    diffs = new List<Diff> {new Diff(Operation.EQUAL, \"a\"), new Diff(Operation.EQUAL, \"b\"), new Diff(Operation.EQUAL, \"c\")};\n    this.diff_cleanupMerge(diffs);\n    assertEquals(\"diff_cleanupMerge: Merge equalities.\", new List<Diff> {new Diff(Operation.EQUAL, \"abc\")}, diffs);\n\n    diffs = new List<Diff> {new Diff(Operation.DELETE, \"a\"), new Diff(Operation.DELETE, \"b\"), new Diff(Operation.DELETE, \"c\")};\n    this.diff_cleanupMerge(diffs);\n    assertEquals(\"diff_cleanupMerge: Merge deletions.\", new List<Diff> {new Diff(Operation.DELETE, \"abc\")}, diffs);\n\n    diffs = new List<Diff> {new Diff(Operation.INSERT, \"a\"), new Diff(Operation.INSERT, \"b\"), new Diff(Operation.INSERT, \"c\")};\n    this.diff_cleanupMerge(diffs);\n    assertEquals(\"diff_cleanupMerge: Merge insertions.\", new List<Diff> {new Diff(Operation.INSERT, \"abc\")}, diffs);\n\n    diffs = new List<Diff> {new Diff(Operation.DELETE, \"a\"), new Diff(Operation.INSERT, \"b\"), new Diff(Operation.DELETE, \"c\"), new Diff(Operation.INSERT, \"d\"), new Diff(Operation.EQUAL, \"e\"), new Diff(Operation.EQUAL, \"f\")};\n    this.diff_cleanupMerge(diffs);\n    assertEquals(\"diff_cleanupMerge: Merge interweave.\", new List<Diff> {new Diff(Operation.DELETE, \"ac\"), new Diff(Operation.INSERT, \"bd\"), new Diff(Operation.EQUAL, \"ef\")}, diffs);\n\n    diffs = new List<Diff> {new Diff(Operation.DELETE, \"a\"), new Diff(Operation.INSERT, \"abc\"), new Diff(Operation.DELETE, \"dc\")};\n    this.diff_cleanupMerge(diffs);\n    assertEquals(\"diff_cleanupMerge: Prefix and suffix detection.\", new List<Diff> {new Diff(Operation.EQUAL, \"a\"), new Diff(Operation.DELETE, \"d\"), new Diff(Operation.INSERT, \"b\"), new Diff(Operation.EQUAL, \"c\")}, diffs);\n\n    diffs = new List<Diff> {new Diff(Operation.EQUAL, \"x\"), new Diff(Operation.DELETE, \"a\"), new Diff(Operation.INSERT, \"abc\"), new Diff(Operation.DELETE, \"dc\"), new Diff(Operation.EQUAL, \"y\")};\n    this.diff_cleanupMerge(diffs);\n    assertEquals(\"diff_cleanupMerge: Prefix and suffix detection with equalities.\", new List<Diff> {new Diff(Operation.EQUAL, \"xa\"), new Diff(Operation.DELETE, \"d\"), new Diff(Operation.INSERT, \"b\"), new Diff(Operation.EQUAL, \"cy\")}, diffs);\n\n    diffs = new List<Diff> {new Diff(Operation.EQUAL, \"a\"), new Diff(Operation.INSERT, \"ba\"), new Diff(Operation.EQUAL, \"c\")};\n    this.diff_cleanupMerge(diffs);\n    assertEquals(\"diff_cleanupMerge: Slide edit left.\", new List<Diff> {new Diff(Operation.INSERT, \"ab\"), new Diff(Operation.EQUAL, \"ac\")}, diffs);\n\n    diffs = new List<Diff> {new Diff(Operation.EQUAL, \"c\"), new Diff(Operation.INSERT, \"ab\"), new Diff(Operation.EQUAL, \"a\")};\n    this.diff_cleanupMerge(diffs);\n    assertEquals(\"diff_cleanupMerge: Slide edit right.\", new List<Diff> {new Diff(Operation.EQUAL, \"ca\"), new Diff(Operation.INSERT, \"ba\")}, diffs);\n\n    diffs = new List<Diff> {new Diff(Operation.EQUAL, \"a\"), new Diff(Operation.DELETE, \"b\"), new Diff(Operation.EQUAL, \"c\"), new Diff(Operation.DELETE, \"ac\"), new Diff(Operation.EQUAL, \"x\")};\n    this.diff_cleanupMerge(diffs);\n    assertEquals(\"diff_cleanupMerge: Slide edit left recursive.\", new List<Diff> {new Diff(Operation.DELETE, \"abc\"), new Diff(Operation.EQUAL, \"acx\")}, diffs);\n\n    diffs = new List<Diff> {new Diff(Operation.EQUAL, \"x\"), new Diff(Operation.DELETE, \"ca\"), new Diff(Operation.EQUAL, \"c\"), new Diff(Operation.DELETE, \"b\"), new Diff(Operation.EQUAL, \"a\")};\n    this.diff_cleanupMerge(diffs);\n    assertEquals(\"diff_cleanupMerge: Slide edit right recursive.\", new List<Diff> {new Diff(Operation.EQUAL, \"xca\"), new Diff(Operation.DELETE, \"cba\")}, diffs);\n\n    diffs = new List<Diff> {new Diff(Operation.DELETE, \"b\"), new Diff(Operation.INSERT, \"ab\"), new Diff(Operation.EQUAL, \"c\")};\n    this.diff_cleanupMerge(diffs);\n    assertEquals(\"diff_cleanupMerge: Empty merge.\", new List<Diff> {new Diff(Operation.INSERT, \"a\"), new Diff(Operation.EQUAL, \"bc\")}, diffs);\n\n    diffs = new List<Diff> {new Diff(Operation.EQUAL, \"\"), new Diff(Operation.INSERT, \"a\"), new Diff(Operation.EQUAL, \"b\")};\n    this.diff_cleanupMerge(diffs);\n    assertEquals(\"diff_cleanupMerge: Empty equality.\", new List<Diff> {new Diff(Operation.INSERT, \"a\"), new Diff(Operation.EQUAL, \"b\")}, diffs);\n  }\n\n  public void diff_cleanupSemanticLosslessTest() {\n    // Slide diffs to match logical boundaries.\n    List<Diff> diffs = new List<Diff>();\n    this.diff_cleanupSemanticLossless(diffs);\n    assertEquals(\"diff_cleanupSemanticLossless: Null case.\", new List<Diff>(), diffs);\n\n    diffs = new List<Diff> {\n        new Diff(Operation.EQUAL, \"AAA\\r\\n\\r\\nBBB\"),\n        new Diff(Operation.INSERT, \"\\r\\nDDD\\r\\n\\r\\nBBB\"),\n        new Diff(Operation.EQUAL, \"\\r\\nEEE\")\n    };\n    this.diff_cleanupSemanticLossless(diffs);\n    assertEquals(\"diff_cleanupSemanticLossless: Blank lines.\", new List<Diff> {\n        new Diff(Operation.EQUAL, \"AAA\\r\\n\\r\\n\"),\n        new Diff(Operation.INSERT, \"BBB\\r\\nDDD\\r\\n\\r\\n\"),\n        new Diff(Operation.EQUAL, \"BBB\\r\\nEEE\")}, diffs);\n\n    diffs = new List<Diff> {\n        new Diff(Operation.EQUAL, \"AAA\\r\\nBBB\"),\n        new Diff(Operation.INSERT, \" DDD\\r\\nBBB\"),\n        new Diff(Operation.EQUAL, \" EEE\")};\n    this.diff_cleanupSemanticLossless(diffs);\n    assertEquals(\"diff_cleanupSemanticLossless: Line boundaries.\", new List<Diff> {\n        new Diff(Operation.EQUAL, \"AAA\\r\\n\"),\n        new Diff(Operation.INSERT, \"BBB DDD\\r\\n\"),\n        new Diff(Operation.EQUAL, \"BBB EEE\")}, diffs);\n\n    diffs = new List<Diff> {\n        new Diff(Operation.EQUAL, \"The c\"),\n        new Diff(Operation.INSERT, \"ow and the c\"),\n        new Diff(Operation.EQUAL, \"at.\")};\n    this.diff_cleanupSemanticLossless(diffs);\n    assertEquals(\"diff_cleanupSemanticLossless: Word boundaries.\", new List<Diff> {\n        new Diff(Operation.EQUAL, \"The \"),\n        new Diff(Operation.INSERT, \"cow and the \"),\n        new Diff(Operation.EQUAL, \"cat.\")}, diffs);\n\n    diffs = new List<Diff> {\n        new Diff(Operation.EQUAL, \"The-c\"),\n        new Diff(Operation.INSERT, \"ow-and-the-c\"),\n        new Diff(Operation.EQUAL, \"at.\")};\n    this.diff_cleanupSemanticLossless(diffs);\n    assertEquals(\"diff_cleanupSemanticLossless: Alphanumeric boundaries.\", new List<Diff> {\n        new Diff(Operation.EQUAL, \"The-\"),\n        new Diff(Operation.INSERT, \"cow-and-the-\"),\n        new Diff(Operation.EQUAL, \"cat.\")}, diffs);\n\n    diffs = new List<Diff> {\n        new Diff(Operation.EQUAL, \"a\"),\n        new Diff(Operation.DELETE, \"a\"),\n        new Diff(Operation.EQUAL, \"ax\")};\n    this.diff_cleanupSemanticLossless(diffs);\n    assertEquals(\"diff_cleanupSemanticLossless: Hitting the start.\", new List<Diff> {\n        new Diff(Operation.DELETE, \"a\"),\n        new Diff(Operation.EQUAL, \"aax\")}, diffs);\n\n    diffs = new List<Diff> {\n        new Diff(Operation.EQUAL, \"xa\"),\n        new Diff(Operation.DELETE, \"a\"),\n        new Diff(Operation.EQUAL, \"a\")};\n    this.diff_cleanupSemanticLossless(diffs);\n    assertEquals(\"diff_cleanupSemanticLossless: Hitting the end.\", new List<Diff> {\n        new Diff(Operation.EQUAL, \"xaa\"),\n        new Diff(Operation.DELETE, \"a\")}, diffs);\n\n    diffs = new List<Diff> {\n        new Diff(Operation.EQUAL, \"The xxx. The \"),\n        new Diff(Operation.INSERT, \"zzz. The \"),\n        new Diff(Operation.EQUAL, \"yyy.\")};\n    this.diff_cleanupSemanticLossless(diffs);\n    assertEquals(\"diff_cleanupSemanticLossless: Sentence boundaries.\", new List<Diff> {\n        new Diff(Operation.EQUAL, \"The xxx.\"),\n        new Diff(Operation.INSERT, \" The zzz.\"),\n        new Diff(Operation.EQUAL, \" The yyy.\")}, diffs);\n  }\n\n  public void diff_cleanupSemanticTest() {\n    // Cleanup semantically trivial equalities.\n    // Null case.\n    List<Diff> diffs = new List<Diff>();\n    this.diff_cleanupSemantic(diffs);\n    assertEquals(\"diff_cleanupSemantic: Null case.\", new List<Diff>(), diffs);\n\n    diffs = new List<Diff> {\n        new Diff(Operation.DELETE, \"ab\"),\n        new Diff(Operation.INSERT, \"cd\"),\n        new Diff(Operation.EQUAL, \"12\"),\n        new Diff(Operation.DELETE, \"e\")};\n    this.diff_cleanupSemantic(diffs);\n    assertEquals(\"diff_cleanupSemantic: No elimination #1.\", new List<Diff> {\n        new Diff(Operation.DELETE, \"ab\"),\n        new Diff(Operation.INSERT, \"cd\"),\n        new Diff(Operation.EQUAL, \"12\"),\n        new Diff(Operation.DELETE, \"e\")}, diffs);\n\n    diffs = new List<Diff> {\n        new Diff(Operation.DELETE, \"abc\"),\n        new Diff(Operation.INSERT, \"ABC\"),\n        new Diff(Operation.EQUAL, \"1234\"),\n        new Diff(Operation.DELETE, \"wxyz\")};\n    this.diff_cleanupSemantic(diffs);\n    assertEquals(\"diff_cleanupSemantic: No elimination #2.\", new List<Diff> {\n        new Diff(Operation.DELETE, \"abc\"),\n        new Diff(Operation.INSERT, \"ABC\"),\n        new Diff(Operation.EQUAL, \"1234\"),\n        new Diff(Operation.DELETE, \"wxyz\")}, diffs);\n\n    diffs = new List<Diff> {\n        new Diff(Operation.DELETE, \"a\"),\n        new Diff(Operation.EQUAL, \"b\"),\n        new Diff(Operation.DELETE, \"c\")};\n    this.diff_cleanupSemantic(diffs);\n    assertEquals(\"diff_cleanupSemantic: Simple elimination.\", new List<Diff> {\n        new Diff(Operation.DELETE, \"abc\"),\n        new Diff(Operation.INSERT, \"b\")}, diffs);\n\n    diffs = new List<Diff> {\n        new Diff(Operation.DELETE, \"ab\"),\n        new Diff(Operation.EQUAL, \"cd\"),\n        new Diff(Operation.DELETE, \"e\"),\n        new Diff(Operation.EQUAL, \"f\"),\n        new Diff(Operation.INSERT, \"g\")};\n    this.diff_cleanupSemantic(diffs);\n    assertEquals(\"diff_cleanupSemantic: Backpass elimination.\", new List<Diff> {\n        new Diff(Operation.DELETE, \"abcdef\"),\n        new Diff(Operation.INSERT, \"cdfg\")}, diffs);\n\n    diffs = new List<Diff> {\n        new Diff(Operation.INSERT, \"1\"),\n        new Diff(Operation.EQUAL, \"A\"),\n        new Diff(Operation.DELETE, \"B\"),\n        new Diff(Operation.INSERT, \"2\"),\n        new Diff(Operation.EQUAL, \"_\"),\n        new Diff(Operation.INSERT, \"1\"),\n        new Diff(Operation.EQUAL, \"A\"),\n        new Diff(Operation.DELETE, \"B\"),\n        new Diff(Operation.INSERT, \"2\")};\n    this.diff_cleanupSemantic(diffs);\n    assertEquals(\"diff_cleanupSemantic: Multiple elimination.\", new List<Diff> {\n        new Diff(Operation.DELETE, \"AB_AB\"),\n        new Diff(Operation.INSERT, \"1A2_1A2\")}, diffs);\n\n    diffs = new List<Diff> {\n        new Diff(Operation.EQUAL, \"The c\"),\n        new Diff(Operation.DELETE, \"ow and the c\"),\n        new Diff(Operation.EQUAL, \"at.\")};\n    this.diff_cleanupSemantic(diffs);\n    assertEquals(\"diff_cleanupSemantic: Word boundaries.\", new List<Diff> {\n        new Diff(Operation.EQUAL, \"The \"),\n        new Diff(Operation.DELETE, \"cow and the \"),\n        new Diff(Operation.EQUAL, \"cat.\")}, diffs);\n\n    diffs = new List<Diff> {\n        new Diff(Operation.DELETE, \"abcxx\"),\n        new Diff(Operation.INSERT, \"xxdef\")};\n    this.diff_cleanupSemantic(diffs);\n    assertEquals(\"diff_cleanupSemantic: No overlap elimination.\", new List<Diff> {\n        new Diff(Operation.DELETE, \"abcxx\"),\n        new Diff(Operation.INSERT, \"xxdef\")}, diffs);\n\n    diffs = new List<Diff> {\n        new Diff(Operation.DELETE, \"abcxxx\"),\n        new Diff(Operation.INSERT, \"xxxdef\")};\n    this.diff_cleanupSemantic(diffs);\n    assertEquals(\"diff_cleanupSemantic: Overlap elimination.\", new List<Diff> {\n        new Diff(Operation.DELETE, \"abc\"),\n        new Diff(Operation.EQUAL, \"xxx\"),\n        new Diff(Operation.INSERT, \"def\")}, diffs);\n\n    diffs = new List<Diff> {\n        new Diff(Operation.DELETE, \"xxxabc\"),\n        new Diff(Operation.INSERT, \"defxxx\")};\n    this.diff_cleanupSemantic(diffs);\n    assertEquals(\"diff_cleanupSemantic: Reverse overlap elimination.\", new List<Diff> {\n        new Diff(Operation.INSERT, \"def\"),\n        new Diff(Operation.EQUAL, \"xxx\"),\n        new Diff(Operation.DELETE, \"abc\")}, diffs);\n\n    diffs = new List<Diff> {\n        new Diff(Operation.DELETE, \"abcd1212\"),\n        new Diff(Operation.INSERT, \"1212efghi\"),\n        new Diff(Operation.EQUAL, \"----\"),\n        new Diff(Operation.DELETE, \"A3\"),\n        new Diff(Operation.INSERT, \"3BC\")};\n    this.diff_cleanupSemantic(diffs);\n    assertEquals(\"diff_cleanupSemantic: Two overlap eliminations.\", new List<Diff> {\n        new Diff(Operation.DELETE, \"abcd\"),\n        new Diff(Operation.EQUAL, \"1212\"),\n        new Diff(Operation.INSERT, \"efghi\"),\n        new Diff(Operation.EQUAL, \"----\"),\n        new Diff(Operation.DELETE, \"A\"),\n        new Diff(Operation.EQUAL, \"3\"),\n        new Diff(Operation.INSERT, \"BC\")}, diffs);\n  }\n\n  public void diff_cleanupEfficiencyTest() {\n    // Cleanup operationally trivial equalities.\n    this.Diff_EditCost = 4;\n    List<Diff> diffs = new List<Diff> ();\n    this.diff_cleanupEfficiency(diffs);\n    assertEquals(\"diff_cleanupEfficiency: Null case.\", new List<Diff>(), diffs);\n\n    diffs = new List<Diff> {\n        new Diff(Operation.DELETE, \"ab\"),\n        new Diff(Operation.INSERT, \"12\"),\n        new Diff(Operation.EQUAL, \"wxyz\"),\n        new Diff(Operation.DELETE, \"cd\"),\n        new Diff(Operation.INSERT, \"34\")};\n    this.diff_cleanupEfficiency(diffs);\n    assertEquals(\"diff_cleanupEfficiency: No elimination.\", new List<Diff> {\n        new Diff(Operation.DELETE, \"ab\"),\n        new Diff(Operation.INSERT, \"12\"),\n        new Diff(Operation.EQUAL, \"wxyz\"),\n        new Diff(Operation.DELETE, \"cd\"),\n        new Diff(Operation.INSERT, \"34\")}, diffs);\n\n    diffs = new List<Diff> {\n        new Diff(Operation.DELETE, \"ab\"),\n        new Diff(Operation.INSERT, \"12\"),\n        new Diff(Operation.EQUAL, \"xyz\"),\n        new Diff(Operation.DELETE, \"cd\"),\n        new Diff(Operation.INSERT, \"34\")};\n    this.diff_cleanupEfficiency(diffs);\n    assertEquals(\"diff_cleanupEfficiency: Four-edit elimination.\", new List<Diff> {\n        new Diff(Operation.DELETE, \"abxyzcd\"),\n        new Diff(Operation.INSERT, \"12xyz34\")}, diffs);\n\n    diffs = new List<Diff> {\n        new Diff(Operation.INSERT, \"12\"),\n        new Diff(Operation.EQUAL, \"x\"),\n        new Diff(Operation.DELETE, \"cd\"),\n        new Diff(Operation.INSERT, \"34\")};\n    this.diff_cleanupEfficiency(diffs);\n    assertEquals(\"diff_cleanupEfficiency: Three-edit elimination.\", new List<Diff> {\n        new Diff(Operation.DELETE, \"xcd\"),\n        new Diff(Operation.INSERT, \"12x34\")}, diffs);\n\n    diffs = new List<Diff> {\n        new Diff(Operation.DELETE, \"ab\"),\n        new Diff(Operation.INSERT, \"12\"),\n        new Diff(Operation.EQUAL, \"xy\"),\n        new Diff(Operation.INSERT, \"34\"),\n        new Diff(Operation.EQUAL, \"z\"),\n        new Diff(Operation.DELETE, \"cd\"),\n        new Diff(Operation.INSERT, \"56\")};\n    this.diff_cleanupEfficiency(diffs);\n    assertEquals(\"diff_cleanupEfficiency: Backpass elimination.\", new List<Diff> {\n        new Diff(Operation.DELETE, \"abxyzcd\"),\n        new Diff(Operation.INSERT, \"12xy34z56\")}, diffs);\n\n    this.Diff_EditCost = 5;\n    diffs = new List<Diff> {\n        new Diff(Operation.DELETE, \"ab\"),\n        new Diff(Operation.INSERT, \"12\"),\n        new Diff(Operation.EQUAL, \"wxyz\"),\n        new Diff(Operation.DELETE, \"cd\"),\n        new Diff(Operation.INSERT, \"34\")};\n    this.diff_cleanupEfficiency(diffs);\n    assertEquals(\"diff_cleanupEfficiency: High cost elimination.\", new List<Diff> {\n        new Diff(Operation.DELETE, \"abwxyzcd\"),\n        new Diff(Operation.INSERT, \"12wxyz34\")}, diffs);\n    this.Diff_EditCost = 4;\n  }\n\n  public void diff_prettyHtmlTest() {\n    // Pretty print.\n    List<Diff> diffs = new List<Diff> {\n        new Diff(Operation.EQUAL, \"a\\n\"),\n        new Diff(Operation.DELETE, \"<B>b</B>\"),\n        new Diff(Operation.INSERT, \"c&d\")};\n    assertEquals(\"diff_prettyHtml:\", \"<span>a&para;<br></span><del style=\\\"background:#ffe6e6;\\\">&lt;B&gt;b&lt;/B&gt;</del><ins style=\\\"background:#e6ffe6;\\\">c&amp;d</ins>\",\n        this.diff_prettyHtml(diffs));\n  }\n\n  public void diff_textTest() {\n    // Compute the source and destination texts.\n    List<Diff> diffs = new List<Diff> {\n        new Diff(Operation.EQUAL, \"jump\"),\n        new Diff(Operation.DELETE, \"s\"),\n        new Diff(Operation.INSERT, \"ed\"),\n        new Diff(Operation.EQUAL, \" over \"),\n        new Diff(Operation.DELETE, \"the\"),\n        new Diff(Operation.INSERT, \"a\"),\n        new Diff(Operation.EQUAL, \" lazy\")};\n    assertEquals(\"diff_text1:\", \"jumps over the lazy\", this.diff_text1(diffs));\n\n    assertEquals(\"diff_text2:\", \"jumped over a lazy\", this.diff_text2(diffs));\n  }\n\n  public void diff_deltaTest() {\n    // Convert a diff into delta string.\n    List<Diff> diffs = new List<Diff> {\n        new Diff(Operation.EQUAL, \"jump\"),\n        new Diff(Operation.DELETE, \"s\"),\n        new Diff(Operation.INSERT, \"ed\"),\n        new Diff(Operation.EQUAL, \" over \"),\n        new Diff(Operation.DELETE, \"the\"),\n        new Diff(Operation.INSERT, \"a\"),\n        new Diff(Operation.EQUAL, \" lazy\"),\n        new Diff(Operation.INSERT, \"old dog\")};\n    string text1 = this.diff_text1(diffs);\n    assertEquals(\"diff_text1: Base text.\", \"jumps over the lazy\", text1);\n\n    string delta = this.diff_toDelta(diffs);\n    assertEquals(\"diff_toDelta:\", \"=4\\t-1\\t+ed\\t=6\\t-3\\t+a\\t=5\\t+old dog\", delta);\n\n    // Convert delta string into a diff.\n    assertEquals(\"diff_fromDelta: Normal.\", diffs, this.diff_fromDelta(text1, delta));\n\n    // Generates error (19 < 20).\n    try {\n      this.diff_fromDelta(text1 + \"x\", delta);\n      assertFail(\"diff_fromDelta: Too long.\");\n    } catch (ArgumentException) {\n      // Exception expected.\n    }\n\n    // Generates error (19 > 18).\n    try {\n      this.diff_fromDelta(text1.Substring(1), delta);\n      assertFail(\"diff_fromDelta: Too short.\");\n    } catch (ArgumentException) {\n      // Exception expected.\n    }\n\n    // Generates error (%c3%xy invalid Unicode).\n    try {\n      this.diff_fromDelta(\"\", \"+%c3%xy\");\n      assertFail(\"diff_fromDelta: Invalid character.\");\n    } catch (ArgumentException) {\n      // Exception expected.\n    }\n\n    // Test deltas with special characters.\n    char zero = (char)0;\n    char one = (char)1;\n    char two = (char)2;\n    diffs = new List<Diff> {\n        new Diff(Operation.EQUAL, \"\\u0680 \" + zero + \" \\t %\"),\n        new Diff(Operation.DELETE, \"\\u0681 \" + one + \" \\n ^\"),\n        new Diff(Operation.INSERT, \"\\u0682 \" + two + \" \\\\ |\")};\n    text1 = this.diff_text1(diffs);\n    assertEquals(\"diff_text1: Unicode text.\", \"\\u0680 \" + zero + \" \\t %\\u0681 \" + one + \" \\n ^\", text1);\n\n    delta = this.diff_toDelta(diffs);\n    // Lowercase, due to UrlEncode uses lower.\n    assertEquals(\"diff_toDelta: Unicode.\", \"=7\\t-7\\t+%da%82 %02 %5c %7c\", delta);\n\n    assertEquals(\"diff_fromDelta: Unicode.\", diffs, this.diff_fromDelta(text1, delta));\n\n    // Verify pool of unchanged characters.\n    diffs = new List<Diff> {\n        new Diff(Operation.INSERT, \"A-Z a-z 0-9 - _ . ! ~ * ' ( ) ; / ? : @ & = + $ , # \")};\n    string text2 = this.diff_text2(diffs);\n    assertEquals(\"diff_text2: Unchanged characters.\", \"A-Z a-z 0-9 - _ . ! ~ * \\' ( ) ; / ? : @ & = + $ , # \", text2);\n\n    delta = this.diff_toDelta(diffs);\n    assertEquals(\"diff_toDelta: Unchanged characters.\", \"+A-Z a-z 0-9 - _ . ! ~ * \\' ( ) ; / ? : @ & = + $ , # \", delta);\n\n    // Convert delta string into a diff.\n    assertEquals(\"diff_fromDelta: Unchanged characters.\", diffs, this.diff_fromDelta(\"\", delta));\n\n    // 160 kb string.\n    string a = \"abcdefghij\";\n    for (int i = 0; i < 14; i++) {\n      a += a;\n    }\n    diffs = new List<Diff> {new Diff(Operation.INSERT, a)};\n    delta = this.diff_toDelta(diffs);\n    assertEquals(\"diff_toDelta: 160kb string.\", \"+\" + a, delta);\n\n    // Convert delta string into a diff.\n    assertEquals(\"diff_fromDelta: 160kb string.\", diffs, this.diff_fromDelta(\"\", delta));\n  }\n\n  public void diff_xIndexTest() {\n    // Translate a location in text1 to text2.\n    List<Diff> diffs = new List<Diff> {\n        new Diff(Operation.DELETE, \"a\"),\n        new Diff(Operation.INSERT, \"1234\"),\n        new Diff(Operation.EQUAL, \"xyz\")};\n    assertEquals(\"diff_xIndex: Translation on equality.\", 5, this.diff_xIndex(diffs, 2));\n\n    diffs = new List<Diff> {\n        new Diff(Operation.EQUAL, \"a\"),\n        new Diff(Operation.DELETE, \"1234\"),\n        new Diff(Operation.EQUAL, \"xyz\")};\n    assertEquals(\"diff_xIndex: Translation on deletion.\", 1, this.diff_xIndex(diffs, 3));\n  }\n\n  public void diff_levenshteinTest() {\n    List<Diff> diffs = new List<Diff> {\n        new Diff(Operation.DELETE, \"abc\"),\n        new Diff(Operation.INSERT, \"1234\"),\n        new Diff(Operation.EQUAL, \"xyz\")};\n    assertEquals(\"diff_levenshtein: Levenshtein with trailing equality.\", 4, this.diff_levenshtein(diffs));\n\n    diffs = new List<Diff> {\n        new Diff(Operation.EQUAL, \"xyz\"),\n        new Diff(Operation.DELETE, \"abc\"),\n        new Diff(Operation.INSERT, \"1234\")};\n    assertEquals(\"diff_levenshtein: Levenshtein with leading equality.\", 4, this.diff_levenshtein(diffs));\n\n    diffs = new List<Diff> {\n        new Diff(Operation.DELETE, \"abc\"),\n        new Diff(Operation.EQUAL, \"xyz\"),\n        new Diff(Operation.INSERT, \"1234\")};\n    assertEquals(\"diff_levenshtein: Levenshtein with middle equality.\", 7, this.diff_levenshtein(diffs));\n  }\n\n  public void diff_bisectTest() {\n    // Normal.\n    string a = \"cat\";\n    string b = \"map\";\n    // Since the resulting diff hasn't been normalized, it would be ok if\n    // the insertion and deletion pairs are swapped.\n    // If the order changes, tweak this test as required.\n    List<Diff> diffs = new List<Diff> {new Diff(Operation.DELETE, \"c\"), new Diff(Operation.INSERT, \"m\"), new Diff(Operation.EQUAL, \"a\"), new Diff(Operation.DELETE, \"t\"), new Diff(Operation.INSERT, \"p\")};\n    assertEquals(\"diff_bisect: Normal.\", diffs, this.diff_bisect(a, b, DateTime.MaxValue));\n\n    // Timeout.\n    diffs = new List<Diff> {new Diff(Operation.DELETE, \"cat\"), new Diff(Operation.INSERT, \"map\")};\n    assertEquals(\"diff_bisect: Timeout.\", diffs, this.diff_bisect(a, b, DateTime.MinValue));\n  }\n\n  public void diff_mainTest() {\n    // Perform a trivial diff.\n    List<Diff> diffs = new List<Diff> {};\n    assertEquals(\"diff_main: Null case.\", diffs, this.diff_main(\"\", \"\", false));\n\n    diffs = new List<Diff> {new Diff(Operation.EQUAL, \"abc\")};\n    assertEquals(\"diff_main: Equality.\", diffs, this.diff_main(\"abc\", \"abc\", false));\n\n    diffs = new List<Diff> {new Diff(Operation.EQUAL, \"ab\"), new Diff(Operation.INSERT, \"123\"), new Diff(Operation.EQUAL, \"c\")};\n    assertEquals(\"diff_main: Simple insertion.\", diffs, this.diff_main(\"abc\", \"ab123c\", false));\n\n    diffs = new List<Diff> {new Diff(Operation.EQUAL, \"a\"), new Diff(Operation.DELETE, \"123\"), new Diff(Operation.EQUAL, \"bc\")};\n    assertEquals(\"diff_main: Simple deletion.\", diffs, this.diff_main(\"a123bc\", \"abc\", false));\n\n    diffs = new List<Diff> {new Diff(Operation.EQUAL, \"a\"), new Diff(Operation.INSERT, \"123\"), new Diff(Operation.EQUAL, \"b\"), new Diff(Operation.INSERT, \"456\"), new Diff(Operation.EQUAL, \"c\")};\n    assertEquals(\"diff_main: Two insertions.\", diffs, this.diff_main(\"abc\", \"a123b456c\", false));\n\n    diffs = new List<Diff> {new Diff(Operation.EQUAL, \"a\"), new Diff(Operation.DELETE, \"123\"), new Diff(Operation.EQUAL, \"b\"), new Diff(Operation.DELETE, \"456\"), new Diff(Operation.EQUAL, \"c\")};\n    assertEquals(\"diff_main: Two deletions.\", diffs, this.diff_main(\"a123b456c\", \"abc\", false));\n\n    // Perform a real diff.\n    // Switch off the timeout.\n    this.Diff_Timeout = 0;\n    diffs = new List<Diff> {new Diff(Operation.DELETE, \"a\"), new Diff(Operation.INSERT, \"b\")};\n    assertEquals(\"diff_main: Simple case #1.\", diffs, this.diff_main(\"a\", \"b\", false));\n\n    diffs = new List<Diff> {new Diff(Operation.DELETE, \"Apple\"), new Diff(Operation.INSERT, \"Banana\"), new Diff(Operation.EQUAL, \"s are a\"), new Diff(Operation.INSERT, \"lso\"), new Diff(Operation.EQUAL, \" fruit.\")};\n    assertEquals(\"diff_main: Simple case #2.\", diffs, this.diff_main(\"Apples are a fruit.\", \"Bananas are also fruit.\", false));\n\n    diffs = new List<Diff> {new Diff(Operation.DELETE, \"a\"), new Diff(Operation.INSERT, \"\\u0680\"), new Diff(Operation.EQUAL, \"x\"), new Diff(Operation.DELETE, \"\\t\"), new Diff(Operation.INSERT, new string (new char[]{(char)0}))};\n    assertEquals(\"diff_main: Simple case #3.\", diffs, this.diff_main(\"ax\\t\", \"\\u0680x\" + (char)0, false));\n\n    diffs = new List<Diff> {new Diff(Operation.DELETE, \"1\"), new Diff(Operation.EQUAL, \"a\"), new Diff(Operation.DELETE, \"y\"), new Diff(Operation.EQUAL, \"b\"), new Diff(Operation.DELETE, \"2\"), new Diff(Operation.INSERT, \"xab\")};\n    assertEquals(\"diff_main: Overlap #1.\", diffs, this.diff_main(\"1ayb2\", \"abxab\", false));\n\n    diffs = new List<Diff> {new Diff(Operation.INSERT, \"xaxcx\"), new Diff(Operation.EQUAL, \"abc\"), new Diff(Operation.DELETE, \"y\")};\n    assertEquals(\"diff_main: Overlap #2.\", diffs, this.diff_main(\"abcy\", \"xaxcxabc\", false));\n\n    diffs = new List<Diff> {new Diff(Operation.DELETE, \"ABCD\"), new Diff(Operation.EQUAL, \"a\"), new Diff(Operation.DELETE, \"=\"), new Diff(Operation.INSERT, \"-\"), new Diff(Operation.EQUAL, \"bcd\"), new Diff(Operation.DELETE, \"=\"), new Diff(Operation.INSERT, \"-\"), new Diff(Operation.EQUAL, \"efghijklmnopqrs\"), new Diff(Operation.DELETE, \"EFGHIJKLMNOefg\")};\n    assertEquals(\"diff_main: Overlap #3.\", diffs, this.diff_main(\"ABCDa=bcd=efghijklmnopqrsEFGHIJKLMNOefg\", \"a-bcd-efghijklmnopqrs\", false));\n\n    diffs = new List<Diff> {new Diff(Operation.INSERT, \" \"), new Diff(Operation.EQUAL, \"a\"), new Diff(Operation.INSERT, \"nd\"), new Diff(Operation.EQUAL, \" [[Pennsylvania]]\"), new Diff(Operation.DELETE, \" and [[New\")};\n    assertEquals(\"diff_main: Large equality.\", diffs, this.diff_main(\"a [[Pennsylvania]] and [[New\", \" and [[Pennsylvania]]\", false));\n\n    this.Diff_Timeout = 0.1f;  // 100ms\n    string a = \"`Twas brillig, and the slithy toves\\nDid gyre and gimble in the wabe:\\nAll mimsy were the borogoves,\\nAnd the mome raths outgrabe.\\n\";\n    string b = \"I am the very model of a modern major general,\\nI've information vegetable, animal, and mineral,\\nI know the kings of England, and I quote the fights historical,\\nFrom Marathon to Waterloo, in order categorical.\\n\";\n    // Increase the text lengths by 1024 times to ensure a timeout.\n    for (int i = 0; i < 10; i++) {\n      a += a;\n      b += b;\n    }\n    DateTime startTime = DateTime.Now;\n    this.diff_main(a, b);\n    DateTime endTime = DateTime.Now;\n    // Test that we took at least the timeout period.\n    assertTrue(\"diff_main: Timeout min.\", new TimeSpan(((long)(this.Diff_Timeout * 1000)) * 10000) <= endTime - startTime);\n    // Test that we didn't take forever (be forgiving).\n    // Theoretically this test could fail very occasionally if the\n    // OS task swaps or locks up for a second at the wrong moment.\n    assertTrue(\"diff_main: Timeout max.\", new TimeSpan(((long)(this.Diff_Timeout * 1000)) * 10000 * 2) > endTime - startTime);\n    this.Diff_Timeout = 0;\n\n    // Test the linemode speedup.\n    // Must be long to pass the 100 char cutoff.\n    a = \"1234567890\\n1234567890\\n1234567890\\n1234567890\\n1234567890\\n1234567890\\n1234567890\\n1234567890\\n1234567890\\n1234567890\\n1234567890\\n1234567890\\n1234567890\\n\";\n    b = \"abcdefghij\\nabcdefghij\\nabcdefghij\\nabcdefghij\\nabcdefghij\\nabcdefghij\\nabcdefghij\\nabcdefghij\\nabcdefghij\\nabcdefghij\\nabcdefghij\\nabcdefghij\\nabcdefghij\\n\";\n    assertEquals(\"diff_main: Simple line-mode.\", this.diff_main(a, b, true), this.diff_main(a, b, false));\n\n    a = \"1234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890\";\n    b = \"abcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghij\";\n    assertEquals(\"diff_main: Single line-mode.\", this.diff_main(a, b, true), this.diff_main(a, b, false));\n\n    a = \"1234567890\\n1234567890\\n1234567890\\n1234567890\\n1234567890\\n1234567890\\n1234567890\\n1234567890\\n1234567890\\n1234567890\\n1234567890\\n1234567890\\n1234567890\\n\";\n    b = \"abcdefghij\\n1234567890\\n1234567890\\n1234567890\\nabcdefghij\\n1234567890\\n1234567890\\n1234567890\\nabcdefghij\\n1234567890\\n1234567890\\n1234567890\\nabcdefghij\\n\";\n    string[] texts_linemode = diff_rebuildtexts(this.diff_main(a, b, true));\n    string[] texts_textmode = diff_rebuildtexts(this.diff_main(a, b, false));\n    assertEquals(\"diff_main: Overlap line-mode.\", texts_textmode, texts_linemode);\n\n    // Test null inputs -- not needed because nulls can't be passed in C#.\n  }\n\n  public void match_alphabetTest() {\n    // Initialise the bitmasks for Bitap.\n    Dictionary<char, int> bitmask = new Dictionary<char, int>();\n    bitmask.Add('a', 4); bitmask.Add('b', 2); bitmask.Add('c', 1);\n    assertEquals(\"match_alphabet: Unique.\", bitmask, this.match_alphabet(\"abc\"));\n\n    bitmask.Clear();\n    bitmask.Add('a', 37); bitmask.Add('b', 18); bitmask.Add('c', 8);\n    assertEquals(\"match_alphabet: Duplicates.\", bitmask, this.match_alphabet(\"abcaba\"));\n  }\n\n  public void match_bitapTest() {\n    // Bitap algorithm.\n    this.Match_Distance = 100;\n    this.Match_Threshold = 0.5f;\n    assertEquals(\"match_bitap: Exact match #1.\", 5, this.match_bitap(\"abcdefghijk\", \"fgh\", 5));\n\n    assertEquals(\"match_bitap: Exact match #2.\", 5, this.match_bitap(\"abcdefghijk\", \"fgh\", 0));\n\n    assertEquals(\"match_bitap: Fuzzy match #1.\", 4, this.match_bitap(\"abcdefghijk\", \"efxhi\", 0));\n\n    assertEquals(\"match_bitap: Fuzzy match #2.\", 2, this.match_bitap(\"abcdefghijk\", \"cdefxyhijk\", 5));\n\n    assertEquals(\"match_bitap: Fuzzy match #3.\", -1, this.match_bitap(\"abcdefghijk\", \"bxy\", 1));\n\n    assertEquals(\"match_bitap: Overflow.\", 2, this.match_bitap(\"123456789xx0\", \"3456789x0\", 2));\n\n    assertEquals(\"match_bitap: Before start match.\", 0, this.match_bitap(\"abcdef\", \"xxabc\", 4));\n\n    assertEquals(\"match_bitap: Beyond end match.\", 3, this.match_bitap(\"abcdef\", \"defyy\", 4));\n\n    assertEquals(\"match_bitap: Oversized pattern.\", 0, this.match_bitap(\"abcdef\", \"xabcdefy\", 0));\n\n    this.Match_Threshold = 0.4f;\n    assertEquals(\"match_bitap: Threshold #1.\", 4, this.match_bitap(\"abcdefghijk\", \"efxyhi\", 1));\n\n    this.Match_Threshold = 0.3f;\n    assertEquals(\"match_bitap: Threshold #2.\", -1, this.match_bitap(\"abcdefghijk\", \"efxyhi\", 1));\n\n    this.Match_Threshold = 0.0f;\n    assertEquals(\"match_bitap: Threshold #3.\", 1, this.match_bitap(\"abcdefghijk\", \"bcdef\", 1));\n\n    this.Match_Threshold = 0.5f;\n    assertEquals(\"match_bitap: Multiple select #1.\", 0, this.match_bitap(\"abcdexyzabcde\", \"abccde\", 3));\n\n    assertEquals(\"match_bitap: Multiple select #2.\", 8, this.match_bitap(\"abcdexyzabcde\", \"abccde\", 5));\n\n    this.Match_Distance = 10;  // Strict location.\n    assertEquals(\"match_bitap: Distance test #1.\", -1, this.match_bitap(\"abcdefghijklmnopqrstuvwxyz\", \"abcdefg\", 24));\n\n    assertEquals(\"match_bitap: Distance test #2.\", 0, this.match_bitap(\"abcdefghijklmnopqrstuvwxyz\", \"abcdxxefg\", 1));\n\n    this.Match_Distance = 1000;  // Loose location.\n    assertEquals(\"match_bitap: Distance test #3.\", 0, this.match_bitap(\"abcdefghijklmnopqrstuvwxyz\", \"abcdefg\", 24));\n  }\n\n  public void match_mainTest() {\n    // Full match.\n    assertEquals(\"match_main: Equality.\", 0, this.match_main(\"abcdef\", \"abcdef\", 1000));\n\n    assertEquals(\"match_main: Null text.\", -1, this.match_main(\"\", \"abcdef\", 1));\n\n    assertEquals(\"match_main: Null pattern.\", 3, this.match_main(\"abcdef\", \"\", 3));\n\n    assertEquals(\"match_main: Exact match.\", 3, this.match_main(\"abcdef\", \"de\", 3));\n\n    assertEquals(\"match_main: Beyond end match.\", 3, this.match_main(\"abcdef\", \"defy\", 4));\n\n    assertEquals(\"match_main: Oversized pattern.\", 0, this.match_main(\"abcdef\", \"abcdefy\", 0));\n\n    this.Match_Threshold = 0.7f;\n    assertEquals(\"match_main: Complex match.\", 4, this.match_main(\"I am the very model of a modern major general.\", \" that berry \", 5));\n    this.Match_Threshold = 0.5f;\n\n    // Test null inputs -- not needed because nulls can't be passed in C#.\n  }\n\n  public void patch_patchObjTest() {\n    // Patch Object.\n    Patch p = new Patch();\n    p.start1 = 20;\n    p.start2 = 21;\n    p.length1 = 18;\n    p.length2 = 17;\n    p.diffs = new List<Diff> {\n        new Diff(Operation.EQUAL, \"jump\"),\n        new Diff(Operation.DELETE, \"s\"),\n        new Diff(Operation.INSERT, \"ed\"),\n        new Diff(Operation.EQUAL, \" over \"),\n        new Diff(Operation.DELETE, \"the\"),\n        new Diff(Operation.INSERT, \"a\"),\n        new Diff(Operation.EQUAL, \"\\nlaz\")};\n    string strp = \"@@ -21,18 +22,17 @@\\n jump\\n-s\\n+ed\\n  over \\n-the\\n+a\\n %0alaz\\n\";\n    assertEquals(\"Patch: toString.\", strp, p.ToString());\n  }\n\n  public void patch_fromTextTest() {\n    assertTrue(\"patch_fromText: #0.\", this.patch_fromText(\"\").Count == 0);\n\n    string strp = \"@@ -21,18 +22,17 @@\\n jump\\n-s\\n+ed\\n  over \\n-the\\n+a\\n %0alaz\\n\";\n    assertEquals(\"patch_fromText: #1.\", strp, this.patch_fromText(strp)[0].ToString());\n\n    assertEquals(\"patch_fromText: #2.\", \"@@ -1 +1 @@\\n-a\\n+b\\n\", this.patch_fromText(\"@@ -1 +1 @@\\n-a\\n+b\\n\")[0].ToString());\n\n    assertEquals(\"patch_fromText: #3.\", \"@@ -1,3 +0,0 @@\\n-abc\\n\", this.patch_fromText(\"@@ -1,3 +0,0 @@\\n-abc\\n\") [0].ToString());\n\n    assertEquals(\"patch_fromText: #4.\", \"@@ -0,0 +1,3 @@\\n+abc\\n\", this.patch_fromText(\"@@ -0,0 +1,3 @@\\n+abc\\n\") [0].ToString());\n\n    // Generates error.\n    try {\n      this.patch_fromText(\"Bad\\nPatch\\n\");\n      assertFail(\"patch_fromText: #5.\");\n    } catch (ArgumentException) {\n      // Exception expected.\n    }\n  }\n\n  public void patch_toTextTest() {\n    string strp = \"@@ -21,18 +22,17 @@\\n jump\\n-s\\n+ed\\n  over \\n-the\\n+a\\n  laz\\n\";\n    List<Patch> patches;\n    patches = this.patch_fromText(strp);\n    string result = this.patch_toText(patches);\n    assertEquals(\"patch_toText: Single.\", strp, result);\n\n    strp = \"@@ -1,9 +1,9 @@\\n-f\\n+F\\n oo+fooba\\n@@ -7,9 +7,9 @@\\n obar\\n-,\\n+.\\n  tes\\n\";\n    patches = this.patch_fromText(strp);\n    result = this.patch_toText(patches);\n    assertEquals(\"patch_toText: Dual.\", strp, result);\n  }\n\n  public void patch_addContextTest() {\n    this.Patch_Margin = 4;\n    Patch p;\n    p = this.patch_fromText(\"@@ -21,4 +21,10 @@\\n-jump\\n+somersault\\n\") [0];\n    this.patch_addContext(p, \"The quick brown fox jumps over the lazy dog.\");\n    assertEquals(\"patch_addContext: Simple case.\", \"@@ -17,12 +17,18 @@\\n fox \\n-jump\\n+somersault\\n s ov\\n\", p.ToString());\n\n    p = this.patch_fromText(\"@@ -21,4 +21,10 @@\\n-jump\\n+somersault\\n\")[0];\n    this.patch_addContext(p, \"The quick brown fox jumps.\");\n    assertEquals(\"patch_addContext: Not enough trailing context.\", \"@@ -17,10 +17,16 @@\\n fox \\n-jump\\n+somersault\\n s.\\n\", p.ToString());\n\n    p = this.patch_fromText(\"@@ -3 +3,2 @@\\n-e\\n+at\\n\")[0];\n    this.patch_addContext(p, \"The quick brown fox jumps.\");\n    assertEquals(\"patch_addContext: Not enough leading context.\", \"@@ -1,7 +1,8 @@\\n Th\\n-e\\n+at\\n  qui\\n\", p.ToString());\n\n    p = this.patch_fromText(\"@@ -3 +3,2 @@\\n-e\\n+at\\n\")[0];\n    this.patch_addContext(p, \"The quick brown fox jumps.  The quick brown fox crashes.\");\n    assertEquals(\"patch_addContext: Ambiguity.\", \"@@ -1,27 +1,28 @@\\n Th\\n-e\\n+at\\n  quick brown fox jumps. \\n\", p.ToString());\n  }\n\n  public void patch_makeTest() {\n    List<Patch> patches;\n    patches = this.patch_make(\"\", \"\");\n    assertEquals(\"patch_make: Null case.\", \"\", this.patch_toText(patches));\n\n    string text1 = \"The quick brown fox jumps over the lazy dog.\";\n    string text2 = \"That quick brown fox jumped over a lazy dog.\";\n    string expectedPatch = \"@@ -1,8 +1,7 @@\\n Th\\n-at\\n+e\\n  qui\\n@@ -21,17 +21,18 @@\\n jump\\n-ed\\n+s\\n  over \\n-a\\n+the\\n  laz\\n\";\n    // The second patch must be \"-21,17 +21,18\", not \"-22,17 +21,18\" due to rolling context.\n    patches = this.patch_make(text2, text1);\n    assertEquals(\"patch_make: Text2+Text1 inputs.\", expectedPatch, this.patch_toText(patches));\n\n    expectedPatch = \"@@ -1,11 +1,12 @@\\n Th\\n-e\\n+at\\n  quick b\\n@@ -22,18 +22,17 @@\\n jump\\n-s\\n+ed\\n  over \\n-the\\n+a\\n  laz\\n\";\n    patches = this.patch_make(text1, text2);\n    assertEquals(\"patch_make: Text1+Text2 inputs.\", expectedPatch, this.patch_toText(patches));\n\n    List<Diff> diffs = this.diff_main(text1, text2, false);\n    patches = this.patch_make(diffs);\n    assertEquals(\"patch_make: Diff input.\", expectedPatch, this.patch_toText(patches));\n\n    patches = this.patch_make(text1, diffs);\n    assertEquals(\"patch_make: Text1+Diff inputs.\", expectedPatch, this.patch_toText(patches));\n\n    patches = this.patch_make(text1, text2, diffs);\n    assertEquals(\"patch_make: Text1+Text2+Diff inputs (deprecated).\", expectedPatch, this.patch_toText(patches));\n\n    patches = this.patch_make(\"`1234567890-=[]\\\\;',./\", \"~!@#$%^&*()_+{}|:\\\"<>?\");\n    assertEquals(\"patch_toText: Character encoding.\",\n        \"@@ -1,21 +1,21 @@\\n-%601234567890-=%5b%5d%5c;',./\\n+~!@#$%25%5e&*()_+%7b%7d%7c:%22%3c%3e?\\n\",\n        this.patch_toText(patches));\n\n    diffs = new List<Diff> {\n        new Diff(Operation.DELETE, \"`1234567890-=[]\\\\;',./\"),\n        new Diff(Operation.INSERT, \"~!@#$%^&*()_+{}|:\\\"<>?\")};\n    assertEquals(\"patch_fromText: Character decoding.\",\n        diffs,\n        this.patch_fromText(\"@@ -1,21 +1,21 @@\\n-%601234567890-=%5B%5D%5C;',./\\n+~!@#$%25%5E&*()_+%7B%7D%7C:%22%3C%3E?\\n\") [0].diffs);\n\n    text1 = \"\";\n    for (int x = 0; x < 100; x++) {\n      text1 += \"abcdef\";\n    }\n    text2 = text1 + \"123\";\n    expectedPatch = \"@@ -573,28 +573,31 @@\\n cdefabcdefabcdefabcdefabcdef\\n+123\\n\";\n    patches = this.patch_make(text1, text2);\n    assertEquals(\"patch_make: Long string with repeats.\", expectedPatch, this.patch_toText(patches));\n\n    // Test null inputs -- not needed because nulls can't be passed in C#.\n  }\n\n  public void patch_splitMaxTest() {\n    // Assumes that Match_MaxBits is 32.\n    List<Patch> patches;\n\n    patches = this.patch_make(\"abcdefghijklmnopqrstuvwxyz01234567890\", \"XabXcdXefXghXijXklXmnXopXqrXstXuvXwxXyzX01X23X45X67X89X0\");\n    this.patch_splitMax(patches);\n    assertEquals(\"patch_splitMax: #1.\", \"@@ -1,32 +1,46 @@\\n+X\\n ab\\n+X\\n cd\\n+X\\n ef\\n+X\\n gh\\n+X\\n ij\\n+X\\n kl\\n+X\\n mn\\n+X\\n op\\n+X\\n qr\\n+X\\n st\\n+X\\n uv\\n+X\\n wx\\n+X\\n yz\\n+X\\n 012345\\n@@ -25,13 +39,18 @@\\n zX01\\n+X\\n 23\\n+X\\n 45\\n+X\\n 67\\n+X\\n 89\\n+X\\n 0\\n\", this.patch_toText(patches));\n\n    patches = this.patch_make(\"abcdef1234567890123456789012345678901234567890123456789012345678901234567890uvwxyz\", \"abcdefuvwxyz\");\n    string oldToText = this.patch_toText(patches);\n    this.patch_splitMax(patches);\n    assertEquals(\"patch_splitMax: #2.\", oldToText, this.patch_toText(patches));\n\n    patches = this.patch_make(\"1234567890123456789012345678901234567890123456789012345678901234567890\", \"abc\");\n    this.patch_splitMax(patches);\n    assertEquals(\"patch_splitMax: #3.\", \"@@ -1,32 +1,4 @@\\n-1234567890123456789012345678\\n 9012\\n@@ -29,32 +1,4 @@\\n-9012345678901234567890123456\\n 7890\\n@@ -57,14 +1,3 @@\\n-78901234567890\\n+abc\\n\", this.patch_toText(patches));\n\n    patches = this.patch_make(\"abcdefghij , h : 0 , t : 1 abcdefghij , h : 0 , t : 1 abcdefghij , h : 0 , t : 1\", \"abcdefghij , h : 1 , t : 1 abcdefghij , h : 1 , t : 1 abcdefghij , h : 0 , t : 1\");\n    this.patch_splitMax(patches);\n    assertEquals(\"patch_splitMax: #4.\", \"@@ -2,32 +2,32 @@\\n bcdefghij , h : \\n-0\\n+1\\n  , t : 1 abcdef\\n@@ -29,32 +29,32 @@\\n bcdefghij , h : \\n-0\\n+1\\n  , t : 1 abcdef\\n\", this.patch_toText(patches));\n  }\n\n  public void patch_addPaddingTest() {\n    List<Patch> patches;\n    patches = this.patch_make(\"\", \"test\");\n    assertEquals(\"patch_addPadding: Both edges full.\",\n        \"@@ -0,0 +1,4 @@\\n+test\\n\",\n        this.patch_toText(patches));\n    this.patch_addPadding(patches);\n    assertEquals(\"patch_addPadding: Both edges full.\",\n        \"@@ -1,8 +1,12 @@\\n %01%02%03%04\\n+test\\n %01%02%03%04\\n\",\n        this.patch_toText(patches));\n\n    patches = this.patch_make(\"XY\", \"XtestY\");\n    assertEquals(\"patch_addPadding: Both edges partial.\",\n        \"@@ -1,2 +1,6 @@\\n X\\n+test\\n Y\\n\",\n        this.patch_toText(patches));\n    this.patch_addPadding(patches);\n    assertEquals(\"patch_addPadding: Both edges partial.\",\n        \"@@ -2,8 +2,12 @@\\n %02%03%04X\\n+test\\n Y%01%02%03\\n\",\n        this.patch_toText(patches));\n\n    patches = this.patch_make(\"XXXXYYYY\", \"XXXXtestYYYY\");\n    assertEquals(\"patch_addPadding: Both edges none.\",\n        \"@@ -1,8 +1,12 @@\\n XXXX\\n+test\\n YYYY\\n\",\n        this.patch_toText(patches));\n    this.patch_addPadding(patches);\n    assertEquals(\"patch_addPadding: Both edges none.\",\n        \"@@ -5,8 +5,12 @@\\n XXXX\\n+test\\n YYYY\\n\",\n        this.patch_toText(patches));\n  }\n\n  public void patch_applyTest() {\n    this.Match_Distance = 1000;\n    this.Match_Threshold = 0.5f;\n    this.Patch_DeleteThreshold = 0.5f;\n    List<Patch> patches;\n    patches = this.patch_make(\"\", \"\");\n    Object[] results = this.patch_apply(patches, \"Hello world.\");\n    bool[] boolArray = (bool[])results[1];\n    string resultStr = results[0] + \"\\t\" + boolArray.Length;\n    assertEquals(\"patch_apply: Null case.\", \"Hello world.\\t0\", resultStr);\n\n    patches = this.patch_make(\"The quick brown fox jumps over the lazy dog.\", \"That quick brown fox jumped over a lazy dog.\");\n    results = this.patch_apply(patches, \"The quick brown fox jumps over the lazy dog.\");\n    boolArray = (bool[])results[1];\n    resultStr = results[0] + \"\\t\" + boolArray[0] + \"\\t\" + boolArray[1];\n    assertEquals(\"patch_apply: Exact match.\", \"That quick brown fox jumped over a lazy dog.\\tTrue\\tTrue\", resultStr);\n\n    results = this.patch_apply(patches, \"The quick red rabbit jumps over the tired tiger.\");\n    boolArray = (bool[])results[1];\n    resultStr = results[0] + \"\\t\" + boolArray[0] + \"\\t\" + boolArray[1];\n    assertEquals(\"patch_apply: Partial match.\", \"That quick red rabbit jumped over a tired tiger.\\tTrue\\tTrue\", resultStr);\n\n    results = this.patch_apply(patches, \"I am the very model of a modern major general.\");\n    boolArray = (bool[])results[1];\n    resultStr = results[0] + \"\\t\" + boolArray[0] + \"\\t\" + boolArray[1];\n    assertEquals(\"patch_apply: Failed match.\", \"I am the very model of a modern major general.\\tFalse\\tFalse\", resultStr);\n\n    patches = this.patch_make(\"x1234567890123456789012345678901234567890123456789012345678901234567890y\", \"xabcy\");\n    results = this.patch_apply(patches, \"x123456789012345678901234567890-----++++++++++-----123456789012345678901234567890y\");\n    boolArray = (bool[])results[1];\n    resultStr = results[0] + \"\\t\" + boolArray[0] + \"\\t\" + boolArray[1];\n    assertEquals(\"patch_apply: Big delete, small change.\", \"xabcy\\tTrue\\tTrue\", resultStr);\n\n    patches = this.patch_make(\"x1234567890123456789012345678901234567890123456789012345678901234567890y\", \"xabcy\");\n    results = this.patch_apply(patches, \"x12345678901234567890---------------++++++++++---------------12345678901234567890y\");\n    boolArray = (bool[])results[1];\n    resultStr = results[0] + \"\\t\" + boolArray[0] + \"\\t\" + boolArray[1];\n    assertEquals(\"patch_apply: Big delete, big change 1.\", \"xabc12345678901234567890---------------++++++++++---------------12345678901234567890y\\tFalse\\tTrue\", resultStr);\n\n    this.Patch_DeleteThreshold = 0.6f;\n    patches = this.patch_make(\"x1234567890123456789012345678901234567890123456789012345678901234567890y\", \"xabcy\");\n    results = this.patch_apply(patches, \"x12345678901234567890---------------++++++++++---------------12345678901234567890y\");\n    boolArray = (bool[])results[1];\n    resultStr = results[0] + \"\\t\" + boolArray[0] + \"\\t\" + boolArray[1];\n    assertEquals(\"patch_apply: Big delete, big change 2.\", \"xabcy\\tTrue\\tTrue\", resultStr);\n    this.Patch_DeleteThreshold = 0.5f;\n\n    this.Match_Threshold = 0.0f;\n    this.Match_Distance = 0;\n    patches = this.patch_make(\"abcdefghijklmnopqrstuvwxyz--------------------1234567890\", \"abcXXXXXXXXXXdefghijklmnopqrstuvwxyz--------------------1234567YYYYYYYYYY890\");\n    results = this.patch_apply(patches, \"ABCDEFGHIJKLMNOPQRSTUVWXYZ--------------------1234567890\");\n    boolArray = (bool[])results[1];\n    resultStr = results[0] + \"\\t\" + boolArray[0] + \"\\t\" + boolArray[1];\n    assertEquals(\"patch_apply: Compensate for failed patch.\", \"ABCDEFGHIJKLMNOPQRSTUVWXYZ--------------------1234567YYYYYYYYYY890\\tFalse\\tTrue\", resultStr);\n    this.Match_Threshold = 0.5f;\n    this.Match_Distance = 1000;\n\n    patches = this.patch_make(\"\", \"test\");\n    string patchStr = this.patch_toText(patches);\n    this.patch_apply(patches, \"\");\n    assertEquals(\"patch_apply: No side effects.\", patchStr, this.patch_toText(patches));\n\n    patches = this.patch_make(\"The quick brown fox jumps over the lazy dog.\", \"Woof\");\n    patchStr = this.patch_toText(patches);\n    this.patch_apply(patches, \"The quick brown fox jumps over the lazy dog.\");\n    assertEquals(\"patch_apply: No side effects with major delete.\", patchStr, this.patch_toText(patches));\n\n    patches = this.patch_make(\"\", \"test\");\n    results = this.patch_apply(patches, \"\");\n    boolArray = (bool[])results[1];\n    resultStr = results[0] + \"\\t\" + boolArray[0];\n    assertEquals(\"patch_apply: Edge exact match.\", \"test\\tTrue\", resultStr);\n\n    patches = this.patch_make(\"XY\", \"XtestY\");\n    results = this.patch_apply(patches, \"XY\");\n    boolArray = (bool[])results[1];\n    resultStr = results[0] + \"\\t\" + boolArray[0];\n    assertEquals(\"patch_apply: Near edge exact match.\", \"XtestY\\tTrue\", resultStr);\n\n    patches = this.patch_make(\"y\", \"y123\");\n    results = this.patch_apply(patches, \"x\");\n    boolArray = (bool[])results[1];\n    resultStr = results[0] + \"\\t\" + boolArray[0];\n    assertEquals(\"patch_apply: Edge partial match.\", \"x123\\tTrue\", resultStr);\n  }\n\n  private string[] diff_rebuildtexts(List<Diff> diffs) {\n    string[] text = { \"\", \"\" };\n    foreach (Diff myDiff in diffs) {\n      if (myDiff.operation != Operation.INSERT) {\n        text[0] += myDiff.text;\n      }\n      if (myDiff.operation != Operation.DELETE) {\n        text[1] += myDiff.text;\n      }\n    }\n    return text;\n  }\n\n  private static void assertEquals(string error_msg, string expected, string actual) {\n    if (expected != actual) {\n      throw new ArgumentException(String.Format(\"assertEquals (string, string) fail:\\n Expected: {0}\\n Actual: {1}\\n{2}\", expected, actual, error_msg));\n    }\n  }\n\n  private static void assertEquals(string error_msg, string[] expected, string[] actual) {\n    if (expected.Length != actual.Length) {\n      throw new ArgumentException(String.Format(\"assertEquals (string[], string[]) length fail:\\n Expected: {0}\\n Actual: {1}\\n{2}\", expected, actual, error_msg));\n    }\n    for (int i = 0; i < expected.Length; i++) {\n      if (expected[i] != actual[i]) {\n        throw new ArgumentException(String.Format(\"assertEquals (string[], string[]) index {0} fail:\\n Expected: {1}\\n Actual: {2}\\n{3}\", i, expected, actual, error_msg));\n      }\n    }\n  }\n\n  private static void assertEquals(string error_msg, List<string> expected, List<string> actual) {\n    if (expected.Count != actual.Count) {\n      throw new ArgumentException(String.Format(\"assertEquals (List<string>, List<string>) length fail:\\n Expected: {0}\\n Actual: {1}\\n{2}\", expected, actual, error_msg));\n    }\n    for (int i = 0; i < expected.Count; i++) {\n      if (expected[i] != actual[i]) {\n        throw new ArgumentException(String.Format(\"assertEquals (List<string>, List<string>) index {0} fail:\\n Expected: {1}\\n Actual: {2}\\n{3}\", i, expected, actual, error_msg));\n      }\n    }\n  }\n\n  private static void assertEquals(string error_msg, List<Diff> expected, List<Diff> actual) {\n    if (expected.Count != actual.Count) {\n      throw new ArgumentException(String.Format(\"assertEquals (List<Diff>, List<Diff>) length fail:\\n Expected: {0}\\n Actual: {1}\\n{2}\", expected, actual, error_msg));\n    }\n    for (int i = 0; i < expected.Count; i++) {\n      if (!expected[i].Equals(actual[i])) {\n      throw new ArgumentException(String.Format(\"assertEquals (List<Diff>, List<Diff>) index {0} fail:\\n Expected: {1}\\n Actual: {2}\\n{3}\", i, expected, actual, error_msg));\n      }\n    }\n  }\n\n  private static void assertEquals(string error_msg, Diff expected, Diff actual) {\n    if (!expected.Equals(actual)) {\n      throw new ArgumentException(String.Format(\"assertEquals (Diff, Diff) fail:\\n Expected: {0}\\n Actual: {1}\\n{2}\", expected, actual, error_msg));\n    }\n  }\n\n  private static void assertEquals(string error_msg, Dictionary<char, int> expected, Dictionary<char, int> actual) {\n    foreach(char k in actual.Keys) {\n      if (!expected.ContainsKey(k)) {\n        throw new ArgumentException(String.Format(\"assertEquals (Dictionary<char, int>, Dictionary<char, int>) key {0} fail:\\n Expected: {1}\\n Actual: {2}\\n{3}\", k, expected, actual, error_msg));\n      }\n    }\n    foreach(char k in expected.Keys) {\n      if (!actual.ContainsKey(k)) {\n        throw new ArgumentException(String.Format(\"assertEquals (Dictionary<char, int>, Dictionary<char, int>) key {0} fail:\\n Expected: {1}\\n Actual: {2}\\n{3}\", k, expected, actual, error_msg));\n      }\n      if (actual[k] != expected[k]) {\n        throw new ArgumentException(String.Format(\"assertEquals (Dictionary<char, int>, Dictionary<char, int>) key {0} fail:\\n Expected: {1}\\n Actual: {2}\\n{3}\", k, expected, actual, error_msg));\n      }\n    }\n  }\n\n  private static void assertEquals(string error_msg, int expected, int actual) {\n    if (expected != actual) {\n      throw new ArgumentException(String.Format(\"assertEquals (int, int) fail:\\n Expected: {0}\\n Actual: {1}\\n{2}\", expected, actual, error_msg));\n    }\n  }\n\n  private static void assertTrue(string error_msg, bool expected) {\n    if (!expected) {\n      throw new ArgumentException(String.Format(\"assertTrue fail:\\n{0}\", error_msg));\n    }\n  }\n\n  private static void assertNull(string error_msg, object value) {\n    if (value != null) {\n      throw new ArgumentException(String.Format(\"assertNull fail:\\n{0}\", error_msg));\n    }\n  }\n\n  private static void assertFail(string error_msg) {\n    throw new ArgumentException(String.Format(\"assertFail fail:\\n{0}\", error_msg));\n  }\n\n  public static void Main(string[] args) {\n    diff_match_patchTest dmp = new diff_match_patchTest();\n\n    dmp.diff_commonPrefixTest();\n    dmp.diff_commonSuffixTest();\n    dmp.diff_commonOverlapTest();\n    dmp.diff_halfmatchTest();\n    dmp.diff_linesToCharsTest();\n    dmp.diff_charsToLinesTest();\n    dmp.diff_cleanupMergeTest();\n    dmp.diff_cleanupSemanticLosslessTest();\n    dmp.diff_cleanupSemanticTest();\n    dmp.diff_cleanupEfficiencyTest();\n    dmp.diff_prettyHtmlTest();\n    dmp.diff_textTest();\n    dmp.diff_deltaTest();\n    dmp.diff_xIndexTest();\n    dmp.diff_levenshteinTest();\n    dmp.diff_bisectTest();\n    dmp.diff_mainTest();\n\n    dmp.match_alphabetTest();\n    dmp.match_bitapTest();\n    dmp.match_mainTest();\n\n    dmp.patch_patchObjTest();\n    dmp.patch_fromTextTest();\n    dmp.patch_toTextTest();\n    dmp.patch_addContextTest();\n    dmp.patch_makeTest();\n    dmp.patch_splitMaxTest();\n    dmp.patch_addPaddingTest();\n    dmp.patch_applyTest();\n\n    Console.WriteLine(\"All tests passed.\");\n  }\n}\n"
  },
  {
    "path": "csharp/tests/Speedtest.cs",
    "content": "// Copyright 2010 Google Inc.\n// All Right Reserved.\n\n/*\n * To compile with Mono:\n *   mcs Speedtest.cs ../DiffMatchPatch.cs\n * To run with Mono:\n *   mono Speedtest.exe\n*/\n\nusing DiffMatchPatch;\nusing System;\nusing System.Collections.Generic;\n\npublic class Speedtest {\n  public static void Main(string[] args) {\n    string text1 = System.IO.File.ReadAllText(\"Speedtest1.txt\");\n    string text2 = System.IO.File.ReadAllText(\"Speedtest2.txt\");\n\n    diff_match_patch dmp = new diff_match_patch();\n    dmp.Diff_Timeout = 0;\n\n    // Execute one reverse diff as a warmup.\n    dmp.diff_main(text2, text1);\n    GC.Collect();\n    GC.WaitForPendingFinalizers();\n\n    DateTime ms_start = DateTime.Now;\n    dmp.diff_main(text1, text2);\n    DateTime ms_end = DateTime.Now;\n\n    Console.WriteLine(\"Elapsed time: \" + (ms_end - ms_start));\n  }\n}\n"
  },
  {
    "path": "csharp/tests/Speedtest1.txt",
    "content": "This is a '''list of newspapers published by [[Journal Register Company]]'''.\n\nThe company owns daily and weekly newspapers, other print media properties and newspaper-affiliated local Websites in the [[U.S.]] states of [[Connecticut]], [[Michigan]], [[New York]], [[Ohio]] and [[Pennsylvania]], organized in six geographic \"clusters\":<ref>[http://www.journalregister.com/newspapers.html Journal Register Company: Our Newspapers], accessed February 10, 2008.</ref>\n\n== Capital-Saratoga ==\nThree dailies, associated weeklies and [[pennysaver]]s in greater [[Albany, New York]]; also [http://www.capitalcentral.com capitalcentral.com] and [http://www.jobsinnewyork.com JobsInNewYork.com].\n\n* ''The Oneida Daily Dispatch'' {{WS|oneidadispatch.com}} of [[Oneida, New York]]\n* ''[[The Record (Troy)|The Record]]'' {{WS|troyrecord.com}} of [[Troy, New York]]\n* ''[[The Saratogian]]'' {{WS|saratogian.com}} of [[Saratoga Springs, New York]]\n* Weeklies:\n** ''Community News'' {{WS|cnweekly.com}} weekly of [[Clifton Park, New York]]\n** ''Rome Observer'' of [[Rome, New York]]\n** ''Life & Times of Utica'' of [[Utica, New York]]\n\n== Connecticut ==\nFive dailies, associated weeklies and [[pennysaver]]s in the state of [[Connecticut]]; also [http://www.ctcentral.com CTcentral.com], [http://www.ctcarsandtrucks.com CTCarsAndTrucks.com] and [http://www.jobsinct.com JobsInCT.com].\n\n* ''The Middletown Press'' {{WS|middletownpress.com}} of [[Middletown, Connecticut|Middletown]]\n* ''[[New Haven Register]]'' {{WS|newhavenregister.com}} of [[New Haven, Connecticut|New Haven]]\n* ''The Register Citizen'' {{WS|registercitizen.com}} of [[Torrington, Connecticut|Torrington]]\n\n* [[New Haven Register#Competitors|Elm City Newspapers]] {{WS|ctcentral.com}}\n** ''The Advertiser'' of [[East Haven, Connecticut|East Haven]]\n** ''Hamden Chronicle'' of [[Hamden, Connecticut|Hamden]]\n** ''Milford Weekly'' of [[Milford, Connecticut|Milford]]\n** ''The Orange Bulletin'' of [[Orange, Connecticut|Orange]]\n** ''The Post'' of [[North Haven, Connecticut|North Haven]]\n** ''Shelton Weekly'' of [[Shelton, Connecticut|Shelton]]\n** ''The Stratford Bard'' of [[Stratford, Connecticut|Stratford]]\n** ''Wallingford Voice'' of [[Wallingford, Connecticut|Wallingford]]\n** ''West Haven News'' of [[West Haven, Connecticut|West Haven]]\n* Housatonic Publications \n** ''The New Milford Times'' {{WS|newmilfordtimes.com}} of [[New Milford, Connecticut|New Milford]]\n** ''The Brookfield Journal'' of [[Brookfield, Connecticut|Brookfield]]\n** ''The Kent Good Times Dispatch'' of [[Kent, Connecticut|Kent]]\n** ''The Bethel Beacon'' of [[Bethel, Connecticut|Bethel]]\n** ''The Litchfield Enquirer'' of [[Litchfield, Connecticut|Litchfield]]\n** ''Litchfield County Times'' of [[Litchfield, Connecticut|Litchfield]]\n* Imprint Newspapers {{WS|imprintnewspapers.com}}\n** ''West Hartford News'' of [[West Hartford, Connecticut|West Hartford]]\n** ''Windsor Journal'' of [[Windsor, Connecticut|Windsor]]\n** ''Windsor Locks Journal'' of [[Windsor Locks, Connecticut|Windsor Locks]]\n** ''Avon Post'' of [[Avon, Connecticut|Avon]]\n** ''Farmington Post'' of [[Farmington, Connecticut|Farmington]]\n** ''Simsbury Post'' of [[Simsbury, Connecticut|Simsbury]]\n** ''Tri-Town Post'' of [[Burlington, Connecticut|Burlington]], [[Canton, Connecticut|Canton]] and [[Harwinton, Connecticut|Harwinton]]\n* Minuteman Publications\n** ''[[Fairfield Minuteman]]'' of [[Fairfield, Connecticut|Fairfield]]\n** ''The Westport Minuteman'' {{WS|westportminuteman.com}} of [[Westport, Connecticut|Westport]]\n* Shoreline Newspapers weeklies:\n** ''Branford Review'' of [[Branford, Connecticut|Branford]]\n** ''Clinton Recorder'' of [[Clinton, Connecticut|Clinton]]\n** ''The Dolphin'' of [[Naval Submarine Base New London]] in [[New London, Connecticut|New London]]\n** ''Main Street News'' {{WS|ctmainstreetnews.com}} of [[Essex, Connecticut|Essex]]\n** ''Pictorial Gazette'' of [[Old Saybrook, Connecticut|Old Saybrook]]\n** ''Regional Express'' of [[Colchester, Connecticut|Colchester]]\n** ''Regional Standard'' of [[Colchester, Connecticut|Colchester]]\n** ''Shoreline Times'' {{WS|shorelinetimes.com}} of [[Guilford, Connecticut|Guilford]]\n** ''Shore View East'' of [[Madison, Connecticut|Madison]]\n** ''Shore View West'' of [[Guilford, Connecticut|Guilford]]\n* Other weeklies:\n** ''Registro'' {{WS|registroct.com}} of [[New Haven, Connecticut|New Haven]]\n** ''Thomaston Express'' {{WS|thomastownexpress.com}} of [[Thomaston, Connecticut|Thomaston]]\n** ''Foothills Traders'' {{WS|foothillstrader.com}} of Torrington, Bristol, Canton\n\n== Michigan ==\nFour dailies, associated weeklies and [[pennysaver]]s in the state of [[Michigan]]; also [http://www.micentralhomes.com MIcentralhomes.com] and [http://www.micentralautos.com MIcentralautos.com]\n* ''[[Oakland Press]]'' {{WS|theoaklandpress.com}} of [[Oakland, Michigan|Oakland]]\n* ''Daily Tribune'' {{WS|dailytribune.com}} of [[Royal Oak, Michigan|Royal Oak]]\n* ''Macomb Daily'' {{WS|macombdaily.com}} of [[Mt. Clemens, Michigan|Mt. Clemens]]\n* ''[[Morning Sun]]'' {{WS|themorningsun.com}} of  [[Mount Pleasant, Michigan|Mount Pleasant]]\n* Heritage Newspapers {{WS|heritage.com}}\n** ''Belleville View''\n** ''Ile Camera''\n** ''Monroe Guardian''\n** ''Ypsilanti Courier''\n** ''News-Herald''\n** ''Press & Guide''\n** ''Chelsea Standard & Dexter Leader''\n** ''Manchester Enterprise''\n** ''Milan News-Leader''\n** ''Saline Reporter''\n* Independent Newspapers {{WS|sourcenewspapers.com}}\n** ''Advisor''\n** ''Source''\n* Morning Star {{WS|morningstarpublishing.com}}\n** ''Alma Reminder''\n** ''Alpena Star''\n** ''Antrim County News''\n** ''Carson City Reminder''\n** ''The Leader & Kalkaskian''\n** ''Ogemaw/Oscoda County Star''\n** ''Petoskey/Charlevoix Star''\n** ''Presque Isle Star''\n** ''Preview Community Weekly''\n** ''Roscommon County Star''\n** ''St. Johns Reminder''\n** ''Straits Area Star''\n** ''The (Edmore) Advertiser'' \n* Voice Newspapers {{WS|voicenews.com}}\n** ''Armada Times''\n** ''Bay Voice''\n** ''Blue Water Voice''\n** ''Downriver Voice''\n** ''Macomb Township Voice''\n** ''North Macomb Voice''\n** ''Weekend Voice''\n** ''Suburban Lifestyles'' {{WS|suburbanlifestyles.com}}\n\n== Mid-Hudson ==\nOne daily, associated magazines in the [[Hudson River Valley]] of [[New York]]; also [http://www.midhudsoncentral.com MidHudsonCentral.com] and [http://www.jobsinnewyork.com JobsInNewYork.com].\n\n* ''[[Daily Freeman]]'' {{WS|dailyfreeman.com}} of [[Kingston, New York]]\n\n== Ohio ==\nTwo dailies, associated magazines and three shared Websites, all in the state of [[Ohio]]: [http://www.allaroundcleveland.com AllAroundCleveland.com], [http://www.allaroundclevelandcars.com AllAroundClevelandCars.com] and [http://www.allaroundclevelandjobs.com AllAroundClevelandJobs.com].\n\n* ''[[The News-Herald (Ohio)|The News-Herald]]'' {{WS|news-herald.com}} of [[Willoughby, Ohio|Willoughby]]\n* ''[[The Morning Journal]]'' {{WS|morningjournal.com}} of [[Lorain, Ohio|Lorain]]\n\n== Philadelphia area ==\nSeven dailies and associated weeklies and magazines in [[Pennsylvania]] and [[New Jersey]], and associated Websites: [http://www.allaroundphilly.com AllAroundPhilly.com], [http://www.jobsinnj.com JobsInNJ.com], [http://www.jobsinpa.com JobsInPA.com], and [http://www.phillycarsearch.com PhillyCarSearch.com].\n\n* ''The Daily Local'' {{WS|dailylocal.com}} of [[West Chester, Pennsylvania|West Chester]]\n* ''[[Delaware County Daily and Sunday Times]] {{WS|delcotimes.com}} of Primos\n* ''[[The Mercury (Pennsylvania)|The Mercury]]'' {{WS|pottstownmercury.com}} of [[Pottstown, Pennsylvania|Pottstown]]\n* ''The Phoenix'' {{WS|phoenixvillenews.com}} of [[Phoenixville, Pennsylvania|Phoenixville]]\n* ''[[The Reporter (Lansdale)|The Reporter]]'' {{WS|thereporteronline.com}} of [[Lansdale, Pennsylvania|Lansdale]]\n* ''The Times Herald'' {{WS|timesherald.com}} of [[Norristown, Pennsylvania|Norristown]]\n* ''[[The Trentonian]]'' {{WS|trentonian.com}} of [[Trenton, New Jersey]]\n\n* Weeklies\n** ''El Latino Expreso'' of [[Trenton, New Jersey]]\n** ''La Voz'' of [[Norristown, Pennsylvania]]\n** ''The Village News'' of [[Downingtown, Pennsylvania]]\n** ''The Times Record'' of [[Kennett Square, Pennsylvania]]\n** ''The Tri-County Record'' {{WS|tricountyrecord.com}} of [[Morgantown, Pennsylvania]]\n** ''News of Delaware County'' {{WS|newsofdelawarecounty.com}}of [[Havertown, Pennsylvania]]\n** ''Main Line Times'' {{WS|mainlinetimes.com}}of [[Ardmore, Pennsylvania]]\n** ''Penny Pincher'' of [[Pottstown, Pennsylvania]]\n** ''Town Talk'' {{WS|towntalknews.com}} of [[Ridley, Pennsylvania]]\n* Chesapeake Publishing {{WS|pa8newsgroup.com}} \n** ''Solanco Sun Ledger'' of [[Quarryville, Pennsylvania]]\n** ''Columbia Ledger'' of [[Columbia, Pennsylvania]]\n** ''Coatesville Ledger'' of [[Downingtown, Pennsylvania]]\n** ''Parkesburg Post Ledger'' of [[Quarryville, Pennsylvania]]\n** ''Downingtown Ledger'' of [[Downingtown, Pennsylvania]]\n** ''The Kennett Paper'' of [[Kennett Square, Pennsylvania]]\n** ''Avon Grove Sun'' of [[West Grove, Pennsylvania]]\n** ''Oxford Tribune'' of [[Oxford, Pennsylvania]]\n** ''Elizabethtown Chronicle'' of [[Elizabethtown, Pennsylvania]]\n** ''Donegal Ledger'' of [[Donegal, Pennsylvania]]\n** ''Chadds Ford Post'' of [[Chadds Ford, Pennsylvania]]\n** ''The Central Record'' of [[Medford, New Jersey]]\n** ''Maple Shade Progress'' of [[Maple Shade, New Jersey]]\n* Intercounty Newspapers {{WS|buckslocalnews.com}} \n** ''The Review'' of Roxborough, Pennsylvania\n** ''The Recorder'' of [[Conshohocken, Pennsylvania]]\n** ''The Leader'' of [[Mount Airy, Pennsylvania|Mount Airy]] and West Oak Lake, Pennsylvania\n** ''The Pennington Post'' of [[Pennington, New Jersey]]\n** ''The Bristol Pilot'' of [[Bristol, Pennsylvania]]\n** ''Yardley News'' of [[Yardley, Pennsylvania]]\n** ''New Hope Gazette'' of [[New Hope, Pennsylvania]]\n** ''Doylestown Patriot'' of [[Doylestown, Pennsylvania]]\n** ''Newtown Advance'' of [[Newtown, Pennsylvania]]\n** ''The Plain Dealer'' of [[Williamstown, New Jersey]]\n** ''News Report'' of [[Sewell, New Jersey]]\n** ''Record Breeze'' of [[Berlin, New Jersey]]\n** ''Newsweekly'' of [[Moorestown, New Jersey]]\n** ''Haddon Herald'' of [[Haddonfield, New Jersey]]\n** ''New Egypt Press'' of [[New Egypt, New Jersey]]\n** ''Community News'' of [[Pemberton, New Jersey]]\n** ''Plymouth Meeting Journal'' of [[Plymouth Meeting, Pennsylvania]]\n** ''Lafayette Hill Journal'' of [[Lafayette Hill, Pennsylvania]]\n* Montgomery Newspapers {{WS|montgomerynews.com}} \n** ''Ambler Gazette'' of [[Ambler, Pennsylvania]]\n** ''Central Bucks Life'' of [[Bucks County, Pennsylvania]]\n** ''The Colonial'' of [[Plymouth Meeting, Pennsylvania]]\n** ''Glenside News'' of [[Glenside, Pennsylvania]]\n** ''The Globe'' of [[Lower Moreland Township, Pennsylvania]]\n** ''Main Line Life'' of [[Ardmore, Pennsylvania]]\n** ''Montgomery Life'' of [[Fort Washington, Pennsylvania]]\n** ''North Penn Life'' of [[Lansdale, Pennsylvania]]\n** ''Perkasie News Herald'' of [[Perkasie, Pennsylvania]]\n** ''Public Spirit'' of [[Hatboro, Pennsylvania]]\n** ''Souderton Independent'' of [[Souderton, Pennsylvania]]\n** ''Springfield Sun'' of [[Springfield, Pennsylvania]]\n** ''Spring-Ford Reporter'' of [[Royersford, Pennsylvania]]\n** ''Times Chronicle'' of [[Jenkintown, Pennsylvania]]\n** ''Valley Item'' of [[Perkiomenville, Pennsylvania]]\n** ''Willow Grove Guide'' of [[Willow Grove, Pennsylvania]]\n* News Gleaner Publications (closed December 2008) {{WS|newsgleaner.com}} \n** ''Life Newspapers'' of [[Philadelphia, Pennsylvania]]\n* Suburban Publications\n** ''The Suburban & Wayne Times'' {{WS|waynesuburban.com}} of [[Wayne, Pennsylvania]]\n** ''The Suburban Advertiser'' of [[Exton, Pennsylvania]]\n** ''The King of Prussia Courier'' of [[King of Prussia, Pennsylvania]]\n* Press Newspapers {{WS|countypressonline.com}} \n** ''County Press'' of [[Newtown Square, Pennsylvania]]\n** ''Garnet Valley Press'' of [[Glen Mills, Pennsylvania]]\n** ''Haverford Press'' of [[Newtown Square, Pennsylvania]] (closed January 2009)\n** ''Hometown Press'' of [[Glen Mills, Pennsylvania]] (closed January 2009)\n** ''Media Press'' of [[Newtown Square, Pennsylvania]] (closed January 2009)\n** ''Springfield Press'' of [[Springfield, Pennsylvania]]\n* Berks-Mont Newspapers {{WS|berksmontnews.com}} \n** ''The Boyertown Area Times'' of [[Boyertown, Pennsylvania]]\n** ''The Kutztown Area Patriot'' of [[Kutztown, Pennsylvania]]\n** ''The Hamburg Area Item'' of [[Hamburg, Pennsylvania]]\n** ''The Southern Berks News'' of [[Exeter Township, Berks County, Pennsylvania]]\n** ''The Free Press'' of [[Quakertown, Pennsylvania]]\n** ''The Saucon News'' of [[Quakertown, Pennsylvania]]\n** ''Westside Weekly'' of [[Reading, Pennsylvania]]\n\n* Magazines\n** ''Bucks Co. Town & Country Living''\n** ''Chester Co. Town & Country Living''\n** ''Montomgery Co. Town & Country Living''\n** ''Garden State Town & Country Living''\n** ''Montgomery Homes''\n** ''Philadelphia Golfer''\n** ''Parents Express''\n** ''Art Matters''\n\n{{JRC}}\n\n==References==\n<references />\n\n[[Category:Journal Register publications|*]]\n"
  },
  {
    "path": "csharp/tests/Speedtest2.txt",
    "content": "This is a '''list of newspapers published by [[Journal Register Company]]'''.\n\nThe company owns daily and weekly newspapers, other print media properties and newspaper-affiliated local Websites in the [[U.S.]] states of [[Connecticut]], [[Michigan]], [[New York]], [[Ohio]], [[Pennsylvania]] and [[New Jersey]], organized in six geographic \"clusters\":<ref>[http://www.journalregister.com/publications.html Journal Register Company: Our Publications], accessed April 21, 2010.</ref>\n\n== Capital-Saratoga ==\nThree dailies, associated weeklies and [[pennysaver]]s in greater [[Albany, New York]]; also [http://www.capitalcentral.com capitalcentral.com] and [http://www.jobsinnewyork.com JobsInNewYork.com].\n\n* ''The Oneida Daily Dispatch'' {{WS|oneidadispatch.com}} of [[Oneida, New York]]\n* ''[[The Record (Troy)|The Record]]'' {{WS|troyrecord.com}} of [[Troy, New York]]\n* ''[[The Saratogian]]'' {{WS|saratogian.com}} of [[Saratoga Springs, New York]]\n* Weeklies:\n** ''Community News'' {{WS|cnweekly.com}} weekly of [[Clifton Park, New York]]\n** ''Rome Observer'' {{WS|romeobserver.com}} of [[Rome, New York]]\n** ''WG Life '' {{WS|saratogian.com/wglife/}} of [[Wilton, New York]]\n** ''Ballston Spa Life '' {{WS|saratogian.com/bspalife}} of [[Ballston Spa, New York]]\n** ''Greenbush Life'' {{WS|troyrecord.com/greenbush}} of [[Troy, New York]]\n** ''Latham Life'' {{WS|troyrecord.com/latham}} of [[Latham, New York]]\n** ''River Life'' {{WS|troyrecord.com/river}} of [[Troy, New York]]\n\n== Connecticut ==\nThree dailies, associated weeklies and [[pennysaver]]s in the state of [[Connecticut]]; also [http://www.ctcentral.com CTcentral.com], [http://www.ctcarsandtrucks.com CTCarsAndTrucks.com] and [http://www.jobsinct.com JobsInCT.com].\n\n* ''The Middletown Press'' {{WS|middletownpress.com}} of [[Middletown, Connecticut|Middletown]]\n* ''[[New Haven Register]]'' {{WS|newhavenregister.com}} of [[New Haven, Connecticut|New Haven]]\n* ''The Register Citizen'' {{WS|registercitizen.com}} of [[Torrington, Connecticut|Torrington]]\n\n* Housatonic Publications \n** ''The Housatonic Times'' {{WS|housatonictimes.com}} of [[New Milford, Connecticut|New Milford]]\n** ''Litchfield County Times'' {{WS|countytimes.com}} of [[Litchfield, Connecticut|Litchfield]]\n\n* Minuteman Publications\n** ''[[Fairfield Minuteman]]'' {{WS|fairfieldminuteman.com}}of [[Fairfield, Connecticut|Fairfield]]\n** ''The Westport Minuteman'' {{WS|westportminuteman.com}} of [[Westport, Connecticut|Westport]]\n\n* Shoreline Newspapers \n** ''The Dolphin'' {{WS|dolphin-news.com}} of [[Naval Submarine Base New London]] in [[New London, Connecticut|New London]]\n** ''Shoreline Times'' {{WS|shorelinetimes.com}} of [[Guilford, Connecticut|Guilford]]\n\n* Foothills Media Group {{WS|foothillsmediagroup.com}}\n** ''Thomaston Express'' {{WS|thomastonexpress.com}} of [[Thomaston, Connecticut|Thomaston]]\n** ''Good News About Torrington'' {{WS|goodnewsabouttorrington.com}} of [[Torrington, Connecticut|Torrington]]\n** ''Granby News'' {{WS|foothillsmediagroup.com/granby}} of [[Granby, Connecticut|Granby]]\n** ''Canton News'' {{WS|foothillsmediagroup.com/canton}} of [[Canton, Connecticut|Canton]]\n** ''Avon News'' {{WS|foothillsmediagroup.com/avon}} of [[Avon, Connecticut|Avon]]\n** ''Simsbury News'' {{WS|foothillsmediagroup.com/simsbury}} of [[Simsbury, Connecticut|Simsbury]]\n** ''Litchfield News'' {{WS|foothillsmediagroup.com/litchfield}} of [[Litchfield, Connecticut|Litchfield]]\n** ''Foothills Trader'' {{WS|foothillstrader.com}} of Torrington, Bristol, Canton\n\n* Other weeklies\n** ''The Milford-Orange Bulletin'' {{WS|ctbulletin.com}} of [[Orange, Connecticut|Orange]]\n** ''The Post-Chronicle'' {{WS|ctpostchronicle.com}} of [[North Haven, Connecticut|North Haven]]\n** ''West Hartford News'' {{WS|westhartfordnews.com}} of [[West Hartford, Connecticut|West Hartford]]\n\n* Magazines\n** ''The Connecticut Bride'' {{WS|connecticutmag.com}}\n** ''Connecticut Magazine'' {{WS|theconnecticutbride.com}}\n** ''Passport Magazine'' {{WS|passport-mag.com}}\n\n== Michigan ==\nFour dailies, associated weeklies and [[pennysaver]]s in the state of [[Michigan]]; also [http://www.micentralhomes.com MIcentralhomes.com] and [http://www.micentralautos.com MIcentralautos.com]\n* ''[[Oakland Press]]'' {{WS|theoaklandpress.com}} of [[Oakland, Michigan|Oakland]]\n* ''Daily Tribune'' {{WS|dailytribune.com}} of [[Royal Oak, Michigan|Royal Oak]]\n* ''Macomb Daily'' {{WS|macombdaily.com}} of [[Mt. Clemens, Michigan|Mt. Clemens]]\n* ''[[Morning Sun]]'' {{WS|themorningsun.com}} of  [[Mount Pleasant, Michigan|Mount Pleasant]]\n\n* Heritage Newspapers {{WS|heritage.com}}\n** ''Belleville View'' {{WS|bellevilleview.com}}\n** ''Ile Camera'' {{WS|thenewsherald.com/ile_camera}}\n** ''Monroe Guardian''  {{WS|monreguardian.com}}\n** ''Ypsilanti Courier'' {{WS|ypsilanticourier.com}}\n** ''News-Herald'' {{WS|thenewsherald.com}}\n** ''Press & Guide'' {{WS|pressandguide.com}}\n** ''Chelsea Standard & Dexter Leader'' {{WS|chelseastandard.com}}\n** ''Manchester Enterprise'' {{WS|manchesterguardian.com}}\n** ''Milan News-Leader'' {{WS|milannews.com}}\n** ''Saline Reporter'' {{WS|salinereporter.com}}\n* Independent Newspapers \n** ''Advisor'' {{WS|sourcenewspapers.com}}\n** ''Source'' {{WS|sourcenewspapers.com}}\n* Morning Star {{WS|morningstarpublishing.com}}\n** ''The Leader & Kalkaskian'' {{WS|leaderandkalkaskian.com}}\n** ''Grand Traverse Insider'' {{WS|grandtraverseinsider.com}}\n** ''Alma Reminder''\n** ''Alpena Star''\n** ''Ogemaw/Oscoda County Star''\n** ''Presque Isle Star''\n** ''St. Johns Reminder''\n\n* Voice Newspapers {{WS|voicenews.com}}\n** ''Armada Times''\n** ''Bay Voice''\n** ''Blue Water Voice''\n** ''Downriver Voice''\n** ''Macomb Township Voice''\n** ''North Macomb Voice''\n** ''Weekend Voice''\n\n== Mid-Hudson ==\nOne daily, associated magazines in the [[Hudson River Valley]] of [[New York]]; also [http://www.midhudsoncentral.com MidHudsonCentral.com] and [http://www.jobsinnewyork.com JobsInNewYork.com].\n\n* ''[[Daily Freeman]]'' {{WS|dailyfreeman.com}} of [[Kingston, New York]]\n* ''Las Noticias'' {{WS|lasnoticiasny.com}} of [[Kingston, New York]]\n\n== Ohio ==\nTwo dailies, associated magazines and three shared Websites, all in the state of [[Ohio]]: [http://www.allaroundcleveland.com AllAroundCleveland.com], [http://www.allaroundclevelandcars.com AllAroundClevelandCars.com] and [http://www.allaroundclevelandjobs.com AllAroundClevelandJobs.com].\n\n* ''[[The News-Herald (Ohio)|The News-Herald]]'' {{WS|news-herald.com}} of [[Willoughby, Ohio|Willoughby]]\n* ''[[The Morning Journal]]'' {{WS|morningjournal.com}} of [[Lorain, Ohio|Lorain]]\n* ''El Latino Expreso'' {{WS|lorainlatino.com}} of [[Lorain, Ohio|Lorain]]\n\n== Philadelphia area ==\nSeven dailies and associated weeklies and magazines in [[Pennsylvania]] and [[New Jersey]], and associated Websites: [http://www.allaroundphilly.com AllAroundPhilly.com], [http://www.jobsinnj.com JobsInNJ.com], [http://www.jobsinpa.com JobsInPA.com], and [http://www.phillycarsearch.com PhillyCarSearch.com].\n\n* ''[[The Daily Local News]]'' {{WS|dailylocal.com}} of [[West Chester, Pennsylvania|West Chester]]\n* ''[[Delaware County Daily and Sunday Times]] {{WS|delcotimes.com}} of Primos [[Upper Darby Township, Pennsylvania]]\n* ''[[The Mercury (Pennsylvania)|The Mercury]]'' {{WS|pottstownmercury.com}} of [[Pottstown, Pennsylvania|Pottstown]]\n* ''[[The Reporter (Lansdale)|The Reporter]]'' {{WS|thereporteronline.com}} of [[Lansdale, Pennsylvania|Lansdale]]\n* ''The Times Herald'' {{WS|timesherald.com}} of [[Norristown, Pennsylvania|Norristown]]\n* ''[[The Trentonian]]'' {{WS|trentonian.com}} of [[Trenton, New Jersey]]\n\n* Weeklies\n* ''The Phoenix'' {{WS|phoenixvillenews.com}} of [[Phoenixville, Pennsylvania]]\n** ''El Latino Expreso'' {{WS|njexpreso.com}} of [[Trenton, New Jersey]]\n** ''La Voz'' {{WS|lavozpa.com}} of [[Norristown, Pennsylvania]]\n** ''The Tri County Record'' {{WS|tricountyrecord.com}} of [[Morgantown, Pennsylvania]]\n** ''Penny Pincher'' {{WS|pennypincherpa.com}}of [[Pottstown, Pennsylvania]]\n\n* Chesapeake Publishing  {{WS|southernchestercountyweeklies.com}}\n** ''The Kennett Paper'' {{WS|kennettpaper.com}} of [[Kennett Square, Pennsylvania]]\n** ''Avon Grove Sun'' {{WS|avongrovesun.com}} of [[West Grove, Pennsylvania]]\n** ''The Central Record'' {{WS|medfordcentralrecord.com}} of [[Medford, New Jersey]]\n** ''Maple Shade Progress'' {{WS|mapleshadeprogress.com}} of [[Maple Shade, New Jersey]]\n\n* Intercounty Newspapers {{WS|buckslocalnews.com}} {{WS|southjerseylocalnews.com}} \n** ''The Pennington Post'' {{WS|penningtonpost.com}} of [[Pennington, New Jersey]]\n** ''The Bristol Pilot'' {{WS|bristolpilot.com}} of [[Bristol, Pennsylvania]]\n** ''Yardley News'' {{WS|yardleynews.com}} of [[Yardley, Pennsylvania]]\n** ''Advance of Bucks County'' {{WS|advanceofbucks.com}} of [[Newtown, Pennsylvania]]\n** ''Record Breeze'' {{WS|recordbreeze.com}} of [[Berlin, New Jersey]]\n** ''Community News'' {{WS|sjcommunitynews.com}} of [[Pemberton, New Jersey]]\n\n* Montgomery Newspapers {{WS|montgomerynews.com}} \n** ''Ambler Gazette'' {{WS|amblergazette.com}} of [[Ambler, Pennsylvania]]\n** ''The Colonial'' {{WS|colonialnews.com}} of [[Plymouth Meeting, Pennsylvania]]\n** ''Glenside News'' {{WS|glensidenews.com}} of [[Glenside, Pennsylvania]]\n** ''The Globe'' {{WS|globenewspaper.com}} of [[Lower Moreland Township, Pennsylvania]]\n** ''Montgomery Life'' {{WS|montgomerylife.com}} of [[Fort Washington, Pennsylvania]]\n** ''North Penn Life'' {{WS|northpennlife.com}} of [[Lansdale, Pennsylvania]]\n** ''Perkasie News Herald'' {{WS|perkasienewsherald.com}} of [[Perkasie, Pennsylvania]]\n** ''Public Spirit'' {{WS|thepublicspirit.com}} of [[Hatboro, Pennsylvania]]\n** ''Souderton Independent'' {{WS|soudertonindependent.com}} of [[Souderton, Pennsylvania]]\n** ''Springfield Sun'' {{WS|springfieldsun.com}} of [[Springfield, Pennsylvania]]\n** ''Spring-Ford Reporter'' {{WS|springfordreporter.com}} of [[Royersford, Pennsylvania]]\n** ''Times Chronicle'' {{WS|thetimeschronicle.com}} of [[Jenkintown, Pennsylvania]]\n** ''Valley Item'' {{WS|valleyitem.com}} of [[Perkiomenville, Pennsylvania]]\n** ''Willow Grove Guide'' {{WS|willowgroveguide.com}} of [[Willow Grove, Pennsylvania]]\n** ''The Review'' {{WS|roxreview.com}} of [[Roxborough, Philadelphia, Pennsylvania]]\n\n* Main Line Media News {{WS|mainlinemedianews.com}}\n** ''Main Line Times'' {{WS|mainlinetimes.com}} of [[Ardmore, Pennsylvania]]\n** ''Main Line Life'' {{WS|mainlinelife.com}} of [[Ardmore, Pennsylvania]]\n** ''The King of Prussia Courier'' {{WS|kingofprussiacourier.com}} of [[King of Prussia, Pennsylvania]]\n\n* Delaware County News Network {{WS|delconewsnetwork.com}} \n** ''News of Delaware County'' {{WS|newsofdelawarecounty.com}} of [[Havertown, Pennsylvania]]\n** ''County Press'' {{WS|countypressonline.com}} of [[Newtown Square, Pennsylvania]]\n** ''Garnet Valley Press'' {{WS|countypressonline.com}} of [[Glen Mills, Pennsylvania]]\n** ''Springfield Press'' {{WS|countypressonline.com}} of [[Springfield, Pennsylvania]]\n** ''Town Talk'' {{WS|towntalknews.com}} of [[Ridley, Pennsylvania]]\n\n* Berks-Mont Newspapers {{WS|berksmontnews.com}} \n** ''The Boyertown Area Times'' {{WS|berksmontnews.com/boyertown_area_times}} of [[Boyertown, Pennsylvania]]\n** ''The Kutztown Area Patriot'' {{WS|berksmontnews.com/kutztown_area_patriot}} of [[Kutztown, Pennsylvania]]\n** ''The Hamburg Area Item'' {{WS|berksmontnews.com/hamburg_area_item}} of [[Hamburg, Pennsylvania]]\n** ''The Southern Berks News'' {{WS|berksmontnews.com/southern_berks_news}} of [[Exeter Township, Berks County, Pennsylvania]]\n** ''Community Connection'' {{WS|berksmontnews.com/community_connection}} of [[Boyertown, Pennsylvania]]\n\n* Magazines\n** ''Bucks Co. Town & Country Living'' {{WS|buckscountymagazine.com}} \n** ''Parents Express'' {{WS|parents-express.com}} \n** ''Real Men, Rednecks'' {{WS|realmenredneck.com}} \n\n{{JRC}}\n\n==References==\n<references />\n\n[[Category:Journal Register publications|*]]\n"
  },
  {
    "path": "dart/DMPClass.dart",
    "content": "/*\n * Diff Match and Patch\n * Copyright 2018 The diff-match-patch Authors.\n * https://github.com/google/diff-match-patch\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *   http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n/*\n * Functions for diff, match and patch.\n * Computes the difference between two texts to create a patch.\n * Applies the patch onto another text, allowing for errors.\n *\n * @author fraser@google.com (Neil Fraser)\n */\n\npart of DiffMatchPatch;\n\n/**\n * The data structure representing a diff is a List of Diff objects:\n * {Diff(Operation.delete, 'Hello'), Diff(Operation.insert, 'Goodbye'),\n *  Diff(Operation.equal, ' world.')}\n * which means: delete 'Hello', add 'Goodbye' and keep ' world.'\n */\nenum Operation { delete, insert, equal }\n\n/**\n * Class containing the diff, match and patch methods.\n * Also contains the behaviour settings.\n */\nclass DiffMatchPatch {\n  // Defaults.\n  // Set these on your diff_match_patch instance to override the defaults.\n\n  /**\n   * Number of seconds to map a diff before giving up (0 for infinity).\n   */\n  double Diff_Timeout = 1.0;\n  /**\n   * Cost of an empty edit operation in terms of edit characters.\n   */\n  int Diff_EditCost = 4;\n  /**\n   * At what point is no match declared (0.0 = perfection, 1.0 = very loose).\n   */\n  double Match_Threshold = 0.5;\n  /**\n   * How far to search for a match (0 = exact location, 1000+ = broad match).\n   * A match this many characters away from the expected location will add\n   * 1.0 to the score (0.0 is a perfect match).\n   */\n  int Match_Distance = 1000;\n  /**\n   * When deleting a large block of text (over ~64 characters), how close do\n   * the contents have to be to match the expected contents. (0.0 = perfection,\n   * 1.0 = very loose).  Note that Match_Threshold controls how closely the\n   * end points of a delete need to match.\n   */\n  double Patch_DeleteThreshold = 0.5;\n  /**\n   * Chunk size for context length.\n   */\n  int Patch_Margin = 4;\n\n  /**\n   * The number of bits in an int.\n   */\n  int Match_MaxBits = 32;\n\n  //  DIFF FUNCTIONS\n\n  /**\n   * Find the differences between two texts.  Simplifies the problem by\n   * stripping any common prefix or suffix off the texts before diffing.\n   * [text1] is the old string to be diffed.\n   * [text2] is the new string to be diffed.\n   * [checklines] is an optional speedup flag.  If present and false, then don't\n   *     run a line-level diff first to identify the changed areas.\n   *     Defaults to true, which does a faster, slightly less optimal diff.\n   * [deadline] is an optional time when the diff should be complete by.  Used\n   *     internally for recursive calls.  Users should set DiffTimeout instead.\n   * Returns a List of Diff objects.\n   */\n  List<Diff> diff_main(String text1, String text2,\n      [bool checklines = true, DateTime deadline]) {\n    // Set a deadline by which time the diff must be complete.\n    if (deadline == null) {\n      deadline = new DateTime.now();\n      if (Diff_Timeout <= 0) {\n        // One year should be sufficient for 'infinity'.\n        deadline = deadline.add(new Duration(days: 365));\n      } else {\n        deadline = deadline\n            .add(new Duration(milliseconds: (Diff_Timeout * 1000).toInt()));\n      }\n    }\n    // Check for null inputs.\n    if (text1 == null || text2 == null) {\n      throw new ArgumentError('Null inputs. (diff_main)');\n    }\n\n    // Check for equality (speedup).\n    List<Diff> diffs;\n    if (text1 == text2) {\n      diffs = [];\n      if (!text1.isEmpty) {\n        diffs.add(new Diff(Operation.equal, text1));\n      }\n      return diffs;\n    }\n\n    // Trim off common prefix (speedup).\n    int commonlength = diff_commonPrefix(text1, text2);\n    String commonprefix = text1.substring(0, commonlength);\n    text1 = text1.substring(commonlength);\n    text2 = text2.substring(commonlength);\n\n    // Trim off common suffix (speedup).\n    commonlength = diff_commonSuffix(text1, text2);\n    String commonsuffix = text1.substring(text1.length - commonlength);\n    text1 = text1.substring(0, text1.length - commonlength);\n    text2 = text2.substring(0, text2.length - commonlength);\n\n    // Compute the diff on the middle block.\n    diffs = _diff_compute(text1, text2, checklines, deadline);\n\n    // Restore the prefix and suffix.\n    if (!commonprefix.isEmpty) {\n      diffs.insert(0, new Diff(Operation.equal, commonprefix));\n    }\n    if (!commonsuffix.isEmpty) {\n      diffs.add(new Diff(Operation.equal, commonsuffix));\n    }\n\n    diff_cleanupMerge(diffs);\n    return diffs;\n  }\n\n  /**\n   * Find the differences between two texts.  Assumes that the texts do not\n   * have any common prefix or suffix.\n   * [text1] is the old string to be diffed.\n   * [text2] is the new string to be diffed.\n   * [checklines] is a speedup flag.  If false, then don't run a\n   *     line-level diff first to identify the changed areas.\n   *     If true, then run a faster slightly less optimal diff.\n   * [deadline] is the time when the diff should be complete by.\n   * Returns a List of Diff objects.\n   */\n  List<Diff> _diff_compute(\n      String text1, String text2, bool checklines, DateTime deadline) {\n    List<Diff> diffs = <Diff>[];\n\n    if (text1.length == 0) {\n      // Just add some text (speedup).\n      diffs.add(new Diff(Operation.insert, text2));\n      return diffs;\n    }\n\n    if (text2.length == 0) {\n      // Just delete some text (speedup).\n      diffs.add(new Diff(Operation.delete, text1));\n      return diffs;\n    }\n\n    String longtext = text1.length > text2.length ? text1 : text2;\n    String shorttext = text1.length > text2.length ? text2 : text1;\n    int i = longtext.indexOf(shorttext);\n    if (i != -1) {\n      // Shorter text is inside the longer text (speedup).\n      Operation op =\n          (text1.length > text2.length) ? Operation.delete : Operation.insert;\n      diffs.add(new Diff(op, longtext.substring(0, i)));\n      diffs.add(new Diff(Operation.equal, shorttext));\n      diffs.add(new Diff(op, longtext.substring(i + shorttext.length)));\n      return diffs;\n    }\n\n    if (shorttext.length == 1) {\n      // Single character string.\n      // After the previous speedup, the character can't be an equality.\n      diffs.add(new Diff(Operation.delete, text1));\n      diffs.add(new Diff(Operation.insert, text2));\n      return diffs;\n    }\n\n    // Check to see if the problem can be split in two.\n    final hm = _diff_halfMatch(text1, text2);\n    if (hm != null) {\n      // A half-match was found, sort out the return data.\n      final text1_a = hm[0];\n      final text1_b = hm[1];\n      final text2_a = hm[2];\n      final text2_b = hm[3];\n      final mid_common = hm[4];\n      // Send both pairs off for separate processing.\n      final diffs_a = diff_main(text1_a, text2_a, checklines, deadline);\n      final diffs_b = diff_main(text1_b, text2_b, checklines, deadline);\n      // Merge the results.\n      diffs = diffs_a;\n      diffs.add(new Diff(Operation.equal, mid_common));\n      diffs.addAll(diffs_b);\n      return diffs;\n    }\n\n    if (checklines && text1.length > 100 && text2.length > 100) {\n      return _diff_lineMode(text1, text2, deadline);\n    }\n\n    return _diff_bisect(text1, text2, deadline);\n  }\n\n  /**\n   * Do a quick line-level diff on both strings, then rediff the parts for\n   * greater accuracy.\n   * This speedup can produce non-minimal diffs.\n   * [text1] is the old string to be diffed.\n   * [text2] is the new string to be diffed.\n   * [deadline] is the time when the diff should be complete by.\n   * Returns a List of Diff objects.\n   */\n  List<Diff> _diff_lineMode(String text1, String text2, DateTime deadline) {\n    // Scan the text on a line-by-line basis first.\n    final a = _diff_linesToChars(text1, text2);\n    text1 = a['chars1'];\n    text2 = a['chars2'];\n    final linearray = a['lineArray'];\n\n    final diffs = diff_main(text1, text2, false, deadline);\n\n    // Convert the diff back to original text.\n    _diff_charsToLines(diffs, linearray);\n    // Eliminate freak matches (e.g. blank lines)\n    diff_cleanupSemantic(diffs);\n\n    // Rediff any replacement blocks, this time character-by-character.\n    // Add a dummy entry at the end.\n    diffs.add(new Diff(Operation.equal, ''));\n    int pointer = 0;\n    int count_delete = 0;\n    int count_insert = 0;\n    final text_delete = new StringBuffer();\n    final text_insert = new StringBuffer();\n    while (pointer < diffs.length) {\n      switch (diffs[pointer].operation) {\n        case Operation.insert:\n          count_insert++;\n          text_insert.write(diffs[pointer].text);\n          break;\n        case Operation.delete:\n          count_delete++;\n          text_delete.write(diffs[pointer].text);\n          break;\n        case Operation.equal:\n          // Upon reaching an equality, check for prior redundancies.\n          if (count_delete >= 1 && count_insert >= 1) {\n            // Delete the offending records and add the merged ones.\n            diffs.removeRange(pointer - count_delete - count_insert, pointer);\n            pointer = pointer - count_delete - count_insert;\n            final subDiff = diff_main(text_delete.toString(),\n                text_insert.toString(), false, deadline);\n            for (int j = subDiff.length - 1; j >= 0; j--) {\n              diffs.insert(pointer, subDiff[j]);\n            }\n            pointer = pointer + subDiff.length;\n          }\n          count_insert = 0;\n          count_delete = 0;\n          text_delete.clear();\n          text_insert.clear();\n          break;\n      }\n      pointer++;\n    }\n    diffs.removeLast(); // Remove the dummy entry at the end.\n\n    return diffs;\n  }\n\n  /**\n   * Find the 'middle snake' of a diff, split the problem in two\n   * and return the recursively constructed diff.\n   * See Myers 1986 paper: An O(ND) Difference Algorithm and Its Variations.\n   * [text1] is the old string to be diffed.\n   * [text2] is the new string to be diffed.\n   * [deadline] is the time at which to bail if not yet complete.\n   * Returns a List of Diff objects.\n   */\n  List<Diff> _diff_bisect(String text1, String text2, DateTime deadline) {\n    // Cache the text lengths to prevent multiple calls.\n    final text1_length = text1.length;\n    final text2_length = text2.length;\n    final max_d = (text1_length + text2_length + 1) ~/ 2;\n    final v_offset = max_d;\n    final v_length = 2 * max_d;\n    final v1 = new List<int>(v_length);\n    final v2 = new List<int>(v_length);\n    for (int x = 0; x < v_length; x++) {\n      v1[x] = -1;\n      v2[x] = -1;\n    }\n    v1[v_offset + 1] = 0;\n    v2[v_offset + 1] = 0;\n    final delta = text1_length - text2_length;\n    // If the total number of characters is odd, then the front path will\n    // collide with the reverse path.\n    final front = (delta % 2 != 0);\n    // Offsets for start and end of k loop.\n    // Prevents mapping of space beyond the grid.\n    int k1start = 0;\n    int k1end = 0;\n    int k2start = 0;\n    int k2end = 0;\n    for (int d = 0; d < max_d; d++) {\n      // Bail out if deadline is reached.\n      if ((new DateTime.now()).compareTo(deadline) == 1) {\n        break;\n      }\n\n      // Walk the front path one step.\n      for (int k1 = -d + k1start; k1 <= d - k1end; k1 += 2) {\n        int k1_offset = v_offset + k1;\n        int x1;\n        if (k1 == -d || k1 != d && v1[k1_offset - 1] < v1[k1_offset + 1]) {\n          x1 = v1[k1_offset + 1];\n        } else {\n          x1 = v1[k1_offset - 1] + 1;\n        }\n        int y1 = x1 - k1;\n        while (\n            x1 < text1_length && y1 < text2_length && text1[x1] == text2[y1]) {\n          x1++;\n          y1++;\n        }\n        v1[k1_offset] = x1;\n        if (x1 > text1_length) {\n          // Ran off the right of the graph.\n          k1end += 2;\n        } else if (y1 > text2_length) {\n          // Ran off the bottom of the graph.\n          k1start += 2;\n        } else if (front) {\n          int k2_offset = v_offset + delta - k1;\n          if (k2_offset >= 0 && k2_offset < v_length && v2[k2_offset] != -1) {\n            // Mirror x2 onto top-left coordinate system.\n            int x2 = text1_length - v2[k2_offset];\n            if (x1 >= x2) {\n              // Overlap detected.\n              return _diff_bisectSplit(text1, text2, x1, y1, deadline);\n            }\n          }\n        }\n      }\n\n      // Walk the reverse path one step.\n      for (int k2 = -d + k2start; k2 <= d - k2end; k2 += 2) {\n        int k2_offset = v_offset + k2;\n        int x2;\n        if (k2 == -d || k2 != d && v2[k2_offset - 1] < v2[k2_offset + 1]) {\n          x2 = v2[k2_offset + 1];\n        } else {\n          x2 = v2[k2_offset - 1] + 1;\n        }\n        int y2 = x2 - k2;\n        while (x2 < text1_length &&\n            y2 < text2_length &&\n            text1[text1_length - x2 - 1] == text2[text2_length - y2 - 1]) {\n          x2++;\n          y2++;\n        }\n        v2[k2_offset] = x2;\n        if (x2 > text1_length) {\n          // Ran off the left of the graph.\n          k2end += 2;\n        } else if (y2 > text2_length) {\n          // Ran off the top of the graph.\n          k2start += 2;\n        } else if (!front) {\n          int k1_offset = v_offset + delta - k2;\n          if (k1_offset >= 0 && k1_offset < v_length && v1[k1_offset] != -1) {\n            int x1 = v1[k1_offset];\n            int y1 = v_offset + x1 - k1_offset;\n            // Mirror x2 onto top-left coordinate system.\n            x2 = text1_length - x2;\n            if (x1 >= x2) {\n              // Overlap detected.\n              return _diff_bisectSplit(text1, text2, x1, y1, deadline);\n            }\n          }\n        }\n      }\n    }\n    // Diff took too long and hit the deadline or\n    // number of diffs equals number of characters, no commonality at all.\n    return [\n      new Diff(Operation.delete, text1),\n      new Diff(Operation.insert, text2)\n    ];\n  }\n\n  /**\n   * Hack to allow unit tests to call private method.  Do not use.\n   */\n  List<Diff> test_diff_bisect(String text1, String text2, DateTime deadline) {\n    return _diff_bisect(text1, text2, deadline);\n  }\n\n  /**\n   * Given the location of the 'middle snake', split the diff in two parts\n   * and recurse.\n   * [text1] is the old string to be diffed.\n   * [text2] is the new string to be diffed.\n   * [x] is the index of split point in text1.\n   * [y] is the index of split point in text2.\n   * [deadline] is the time at which to bail if not yet complete.\n   * Returns a List of Diff objects.\n   */\n  List<Diff> _diff_bisectSplit(\n      String text1, String text2, int x, int y, DateTime deadline) {\n    final text1a = text1.substring(0, x);\n    final text2a = text2.substring(0, y);\n    final text1b = text1.substring(x);\n    final text2b = text2.substring(y);\n\n    // Compute both diffs serially.\n    final diffs = diff_main(text1a, text2a, false, deadline);\n    final diffsb = diff_main(text1b, text2b, false, deadline);\n\n    diffs.addAll(diffsb);\n    return diffs;\n  }\n\n  /**\n   * Split two texts into a list of strings.  Reduce the texts to a string of\n   * hashes where each Unicode character represents one line.\n   * [text1] is the first string.\n   * [text2] is the second string.\n   * Returns a Map containing the encoded text1, the encoded text2 and\n   *     the List of unique strings.  The zeroth element of the List of\n   *     unique strings is intentionally blank.\n   */\n  Map<String, dynamic> _diff_linesToChars(String text1, String text2) {\n    final lineArray = <String>[];\n    final lineHash = new HashMap<String, int>();\n    // e.g. linearray[4] == 'Hello\\n'\n    // e.g. linehash['Hello\\n'] == 4\n\n    // '\\x00' is a valid character, but various debuggers don't like it.\n    // So we'll insert a junk entry to avoid generating a null character.\n    lineArray.add('');\n\n    // Allocate 2/3rds of the space for text1, the rest for text2.\n    String chars1 = _diff_linesToCharsMunge(text1, lineArray, lineHash, 40000);\n    String chars2 = _diff_linesToCharsMunge(text2, lineArray, lineHash, 65535);\n    return {'chars1': chars1, 'chars2': chars2, 'lineArray': lineArray};\n  }\n\n  /**\n   * Hack to allow unit tests to call private method.  Do not use.\n   */\n  Map<String, dynamic> test_diff_linesToChars(String text1, String text2) {\n    return _diff_linesToChars(text1, text2);\n  }\n\n  /**\n   * Split a text into a list of strings.  Reduce the texts to a string of\n   * hashes where each Unicode character represents one line.\n   * [text] is the string to encode.\n   * [lineArray] is a List of unique strings.\n   * [lineHash] is a Map of strings to indices.\n   * [maxLines] is the maximum length for lineArray.\n   * Returns an encoded string.\n   */\n  String _diff_linesToCharsMunge(String text, List<String> lineArray,\n      Map<String, int> lineHash, int maxLines) {\n    int lineStart = 0;\n    int lineEnd = -1;\n    String line;\n    final chars = new StringBuffer();\n    // Walk the text, pulling out a substring for each line.\n    // text.split('\\n') would would temporarily double our memory footprint.\n    // Modifying text would create many large strings to garbage collect.\n    while (lineEnd < text.length - 1) {\n      lineEnd = text.indexOf('\\n', lineStart);\n      if (lineEnd == -1) {\n        lineEnd = text.length - 1;\n      }\n      line = text.substring(lineStart, lineEnd + 1);\n\n      if (lineHash.containsKey(line)) {\n        chars.writeCharCode(lineHash[line]);\n      } else {\n        if (lineArray.length == maxLines) {\n          // Bail out at 65535 because\n          // final chars1 = new StringBuffer();\n          // chars1.writeCharCode(65536);\n          // chars1.toString().codeUnitAt(0) == 55296;\n          line = text.substring(lineStart);\n          lineEnd = text.length;\n        }\n        lineArray.add(line);\n        lineHash[line] = lineArray.length - 1;\n        chars.writeCharCode(lineArray.length - 1);\n      }\n      lineStart = lineEnd + 1;\n    }\n    return chars.toString();\n  }\n\n  /**\n   * Rehydrate the text in a diff from a string of line hashes to real lines of\n   * text.\n   * [diffs] is a List of Diff objects.\n   * [lineArray] is a List of unique strings.\n   */\n  void _diff_charsToLines(List<Diff> diffs, List<String> lineArray) {\n    final text = new StringBuffer();\n    for (Diff diff in diffs) {\n      for (int j = 0; j < diff.text.length; j++) {\n        text.write(lineArray[diff.text.codeUnitAt(j)]);\n      }\n      diff.text = text.toString();\n      text.clear();\n    }\n  }\n\n  /**\n   * Hack to allow unit tests to call private method.  Do not use.\n   */\n  void test_diff_charsToLines(List<Diff> diffs, List<String> lineArray) {\n     _diff_charsToLines(diffs, lineArray);\n  }\n\n  /**\n   * Determine the common prefix of two strings\n   * [text1] is the first string.\n   * [text2] is the second string.\n   * Returns the number of characters common to the start of each string.\n   */\n  int diff_commonPrefix(String text1, String text2) {\n    // TODO: Once Dart's performance stabilizes, determine if linear or binary\n    // search is better.\n    // Performance analysis: https://neil.fraser.name/news/2007/10/09/\n    final n = min(text1.length, text2.length);\n    for (int i = 0; i < n; i++) {\n      if (text1[i] != text2[i]) {\n        return i;\n      }\n    }\n    return n;\n  }\n\n  /**\n   * Determine the common suffix of two strings\n   * [text1] is the first string.\n   * [text2] is the second string.\n   * Returns the number of characters common to the end of each string.\n   */\n  int diff_commonSuffix(String text1, String text2) {\n    // TODO: Once Dart's performance stabilizes, determine if linear or binary\n    // search is better.\n    // Performance analysis: https://neil.fraser.name/news/2007/10/09/\n    final text1_length = text1.length;\n    final text2_length = text2.length;\n    final n = min(text1_length, text2_length);\n    for (int i = 1; i <= n; i++) {\n      if (text1[text1_length - i] != text2[text2_length - i]) {\n        return i - 1;\n      }\n    }\n    return n;\n  }\n\n  /**\n   * Determine if the suffix of one string is the prefix of another.\n   * [text1] is the first string.\n   * [text2] is the second string.\n   * Returns the number of characters common to the end of the first\n   *     string and the start of the second string.\n   */\n  int _diff_commonOverlap(String text1, String text2) {\n    // Eliminate the null case.\n    if (text1.isEmpty || text2.isEmpty) {\n      return 0;\n    }\n    // Cache the text lengths to prevent multiple calls.\n    final text1_length = text1.length;\n    final text2_length = text2.length;\n    // Truncate the longer string.\n    if (text1_length > text2_length) {\n      text1 = text1.substring(text1_length - text2_length);\n    } else if (text1_length < text2_length) {\n      text2 = text2.substring(0, text1_length);\n    }\n    final text_length = min(text1_length, text2_length);\n    // Quick check for the worst case.\n    if (text1 == text2) {\n      return text_length;\n    }\n\n    // Start by looking for a single character match\n    // and increase length until no match is found.\n    // Performance analysis: https://neil.fraser.name/news/2010/11/04/\n    int best = 0;\n    int length = 1;\n    while (true) {\n      String pattern = text1.substring(text_length - length);\n      int found = text2.indexOf(pattern);\n      if (found == -1) {\n        return best;\n      }\n      length += found;\n      if (found == 0 ||\n          text1.substring(text_length - length) == text2.substring(0, length)) {\n        best = length;\n        length++;\n      }\n    }\n  }\n\n  /**\n   * Hack to allow unit tests to call private method.  Do not use.\n   */\n  int test_diff_commonOverlap(String text1, String text2) {\n    return _diff_commonOverlap(text1, text2);\n  }\n\n  /**\n   * Do the two texts share a substring which is at least half the length of\n   * the longer text?\n   * This speedup can produce non-minimal diffs.\n   * [text1] is the first string.\n   * [text2] is the second string.\n   * Returns a five element List of Strings, containing the prefix of text1,\n   *     the suffix of text1, the prefix of text2, the suffix of text2 and the\n   *     common middle.  Or null if there was no match.\n   */\n  List<String> _diff_halfMatch(String text1, String text2) {\n    if (Diff_Timeout <= 0) {\n      // Don't risk returning a non-optimal diff if we have unlimited time.\n      return null;\n    }\n    final longtext = text1.length > text2.length ? text1 : text2;\n    final shorttext = text1.length > text2.length ? text2 : text1;\n    if (longtext.length < 4 || shorttext.length * 2 < longtext.length) {\n      return null; // Pointless.\n    }\n\n    // First check if the second quarter is the seed for a half-match.\n    final hm1 = _diff_halfMatchI(\n        longtext, shorttext, ((longtext.length + 3) / 4).ceil().toInt());\n    // Check again based on the third quarter.\n    final hm2 = _diff_halfMatchI(\n        longtext, shorttext, ((longtext.length + 1) / 2).ceil().toInt());\n    List<String> hm;\n    if (hm1 == null && hm2 == null) {\n      return null;\n    } else if (hm2 == null) {\n      hm = hm1;\n    } else if (hm1 == null) {\n      hm = hm2;\n    } else {\n      // Both matched.  Select the longest.\n      hm = hm1[4].length > hm2[4].length ? hm1 : hm2;\n    }\n\n    // A half-match was found, sort out the return data.\n    if (text1.length > text2.length) {\n      return hm;\n      //return [hm[0], hm[1], hm[2], hm[3], hm[4]];\n    } else {\n      return [hm[2], hm[3], hm[0], hm[1], hm[4]];\n    }\n  }\n\n  /**\n   * Hack to allow unit tests to call private method.  Do not use.\n   */\n  List<String> test_diff_halfMatch(String text1, String text2) {\n    return _diff_halfMatch(text1, text2);\n  }\n\n  /**\n   * Does a substring of shorttext exist within longtext such that the\n   * substring is at least half the length of longtext?\n   * [longtext] is the longer string.\n   * [shorttext is the shorter string.\n   * [i] Start index of quarter length substring within longtext.\n   * Returns a five element String array, containing the prefix of longtext,\n   *     the suffix of longtext, the prefix of shorttext, the suffix of\n   *     shorttext and the common middle.  Or null if there was no match.\n   */\n  List<String> _diff_halfMatchI(String longtext, String shorttext, int i) {\n    // Start with a 1/4 length substring at position i as a seed.\n    final seed =\n        longtext.substring(i, i + (longtext.length / 4).floor().toInt());\n    int j = -1;\n    String best_common = '';\n    String best_longtext_a = '', best_longtext_b = '';\n    String best_shorttext_a = '', best_shorttext_b = '';\n    while ((j = shorttext.indexOf(seed, j + 1)) != -1) {\n      int prefixLength =\n          diff_commonPrefix(longtext.substring(i), shorttext.substring(j));\n      int suffixLength = diff_commonSuffix(\n          longtext.substring(0, i), shorttext.substring(0, j));\n      if (best_common.length < suffixLength + prefixLength) {\n        best_common = shorttext.substring(j - suffixLength, j) +\n            shorttext.substring(j, j + prefixLength);\n        best_longtext_a = longtext.substring(0, i - suffixLength);\n        best_longtext_b = longtext.substring(i + prefixLength);\n        best_shorttext_a = shorttext.substring(0, j - suffixLength);\n        best_shorttext_b = shorttext.substring(j + prefixLength);\n      }\n    }\n    if (best_common.length * 2 >= longtext.length) {\n      return [\n        best_longtext_a,\n        best_longtext_b,\n        best_shorttext_a,\n        best_shorttext_b,\n        best_common\n      ];\n    } else {\n      return null;\n    }\n  }\n\n  /**\n   * Reduce the number of edits by eliminating semantically trivial equalities.\n   * [diffs] is a List of Diff objects.\n   */\n  void diff_cleanupSemantic(List<Diff> diffs) {\n    bool changes = false;\n    // Stack of indices where equalities are found.\n    final equalities = <int>[];\n    // Always equal to diffs[equalities.last()].text\n    String lastEquality = null;\n    int pointer = 0; // Index of current position.\n    // Number of characters that changed prior to the equality.\n    int length_insertions1 = 0;\n    int length_deletions1 = 0;\n    // Number of characters that changed after the equality.\n    int length_insertions2 = 0;\n    int length_deletions2 = 0;\n    while (pointer < diffs.length) {\n      if (diffs[pointer].operation == Operation.equal) {\n        // Equality found.\n        equalities.add(pointer);\n        length_insertions1 = length_insertions2;\n        length_deletions1 = length_deletions2;\n        length_insertions2 = 0;\n        length_deletions2 = 0;\n        lastEquality = diffs[pointer].text;\n      } else {\n        // An insertion or deletion.\n        if (diffs[pointer].operation == Operation.insert) {\n          length_insertions2 += diffs[pointer].text.length;\n        } else {\n          length_deletions2 += diffs[pointer].text.length;\n        }\n        // Eliminate an equality that is smaller or equal to the edits on both\n        // sides of it.\n        if (lastEquality != null &&\n            (lastEquality.length <=\n                max(length_insertions1, length_deletions1)) &&\n            (lastEquality.length <=\n                max(length_insertions2, length_deletions2))) {\n          // Duplicate record.\n          diffs.insert(\n              equalities.last, new Diff(Operation.delete, lastEquality));\n          // Change second copy to insert.\n          diffs[equalities.last + 1].operation = Operation.insert;\n          // Throw away the equality we just deleted.\n          equalities.removeLast();\n          // Throw away the previous equality (it needs to be reevaluated).\n          if (!equalities.isEmpty) {\n            equalities.removeLast();\n          }\n          pointer = equalities.isEmpty ? -1 : equalities.last;\n          length_insertions1 = 0; // Reset the counters.\n          length_deletions1 = 0;\n          length_insertions2 = 0;\n          length_deletions2 = 0;\n          lastEquality = null;\n          changes = true;\n        }\n      }\n      pointer++;\n    }\n\n    // Normalize the diff.\n    if (changes) {\n      diff_cleanupMerge(diffs);\n    }\n    _diff_cleanupSemanticLossless(diffs);\n\n    // Find any overlaps between deletions and insertions.\n    // e.g: <del>abcxxx</del><ins>xxxdef</ins>\n    //   -> <del>abc</del>xxx<ins>def</ins>\n    // e.g: <del>xxxabc</del><ins>defxxx</ins>\n    //   -> <ins>def</ins>xxx<del>abc</del>\n    // Only extract an overlap if it is as big as the edit ahead or behind it.\n    pointer = 1;\n    while (pointer < diffs.length) {\n      if (diffs[pointer - 1].operation == Operation.delete &&\n          diffs[pointer].operation == Operation.insert) {\n        String deletion = diffs[pointer - 1].text;\n        String insertion = diffs[pointer].text;\n        int overlap_length1 = _diff_commonOverlap(deletion, insertion);\n        int overlap_length2 = _diff_commonOverlap(insertion, deletion);\n        if (overlap_length1 >= overlap_length2) {\n          if (overlap_length1 >= deletion.length / 2 ||\n              overlap_length1 >= insertion.length / 2) {\n            // Overlap found.\n            // Insert an equality and trim the surrounding edits.\n            diffs.insert(\n                pointer,\n                new Diff(\n                    Operation.equal, insertion.substring(0, overlap_length1)));\n            diffs[pointer - 1].text =\n                deletion.substring(0, deletion.length - overlap_length1);\n            diffs[pointer + 1].text = insertion.substring(overlap_length1);\n            pointer++;\n          }\n        } else {\n          if (overlap_length2 >= deletion.length / 2 ||\n              overlap_length2 >= insertion.length / 2) {\n            // Reverse overlap found.\n            // Insert an equality and swap and trim the surrounding edits.\n            diffs.insert(\n                pointer,\n                new Diff(\n                    Operation.equal, deletion.substring(0, overlap_length2)));\n            diffs[pointer - 1] = new Diff(Operation.insert,\n                insertion.substring(0, insertion.length - overlap_length2));\n            diffs[pointer + 1] =\n                new Diff(Operation.delete, deletion.substring(overlap_length2));\n            pointer++;\n          }\n        }\n        pointer++;\n      }\n      pointer++;\n    }\n  }\n\n  /**\n   * Look for single edits surrounded on both sides by equalities\n   * which can be shifted sideways to align the edit to a word boundary.\n   * e.g: The c<ins>at c</ins>ame. -> The <ins>cat </ins>came.\n   * [diffs] is a List of Diff objects.\n   */\n  void _diff_cleanupSemanticLossless(List<Diff> diffs) {\n    /**\n     * Given two strings, compute a score representing whether the internal\n     * boundary falls on logical boundaries.\n     * Scores range from 6 (best) to 0 (worst).\n     * Closure, but does not reference any external variables.\n     * [one] the first string.\n     * [two] the second string.\n     * Returns the score.\n     */\n    int _diff_cleanupSemanticScore(String one, String two) {\n      if (one.isEmpty || two.isEmpty) {\n        // Edges are the best.\n        return 6;\n      }\n\n      // Each port of this function behaves slightly differently due to\n      // subtle differences in each language's definition of things like\n      // 'whitespace'.  Since this function's purpose is largely cosmetic,\n      // the choice has been made to use each language's native features\n      // rather than force total conformity.\n      String char1 = one[one.length - 1];\n      String char2 = two[0];\n      bool nonAlphaNumeric1 = char1.contains(nonAlphaNumericRegex_);\n      bool nonAlphaNumeric2 = char2.contains(nonAlphaNumericRegex_);\n      bool whitespace1 = nonAlphaNumeric1 && char1.contains(whitespaceRegex_);\n      bool whitespace2 = nonAlphaNumeric2 && char2.contains(whitespaceRegex_);\n      bool lineBreak1 = whitespace1 && char1.contains(linebreakRegex_);\n      bool lineBreak2 = whitespace2 && char2.contains(linebreakRegex_);\n      bool blankLine1 = lineBreak1 && one.contains(blanklineEndRegex_);\n      bool blankLine2 = lineBreak2 && two.contains(blanklineStartRegex_);\n\n      if (blankLine1 || blankLine2) {\n        // Five points for blank lines.\n        return 5;\n      } else if (lineBreak1 || lineBreak2) {\n        // Four points for line breaks.\n        return 4;\n      } else if (nonAlphaNumeric1 && !whitespace1 && whitespace2) {\n        // Three points for end of sentences.\n        return 3;\n      } else if (whitespace1 || whitespace2) {\n        // Two points for whitespace.\n        return 2;\n      } else if (nonAlphaNumeric1 || nonAlphaNumeric2) {\n        // One point for non-alphanumeric.\n        return 1;\n      }\n      return 0;\n    }\n\n    int pointer = 1;\n    // Intentionally ignore the first and last element (don't need checking).\n    while (pointer < diffs.length - 1) {\n      if (diffs[pointer - 1].operation == Operation.equal &&\n          diffs[pointer + 1].operation == Operation.equal) {\n        // This is a single edit surrounded by equalities.\n        String equality1 = diffs[pointer - 1].text;\n        String edit = diffs[pointer].text;\n        String equality2 = diffs[pointer + 1].text;\n\n        // First, shift the edit as far left as possible.\n        int commonOffset = diff_commonSuffix(equality1, edit);\n        if (commonOffset != 0) {\n          String commonString = edit.substring(edit.length - commonOffset);\n          equality1 = equality1.substring(0, equality1.length - commonOffset);\n          edit = commonString + edit.substring(0, edit.length - commonOffset);\n          equality2 = commonString + equality2;\n        }\n\n        // Second, step character by character right, looking for the best fit.\n        String bestEquality1 = equality1;\n        String bestEdit = edit;\n        String bestEquality2 = equality2;\n        int bestScore = _diff_cleanupSemanticScore(equality1, edit) +\n            _diff_cleanupSemanticScore(edit, equality2);\n        while (!edit.isEmpty && !equality2.isEmpty && edit[0] == equality2[0]) {\n          equality1 = equality1 + edit[0];\n          edit = edit.substring(1) + equality2[0];\n          equality2 = equality2.substring(1);\n          int score = _diff_cleanupSemanticScore(equality1, edit) +\n              _diff_cleanupSemanticScore(edit, equality2);\n          // The >= encourages trailing rather than leading whitespace on edits.\n          if (score >= bestScore) {\n            bestScore = score;\n            bestEquality1 = equality1;\n            bestEdit = edit;\n            bestEquality2 = equality2;\n          }\n        }\n\n        if (diffs[pointer - 1].text != bestEquality1) {\n          // We have an improvement, save it back to the diff.\n          if (!bestEquality1.isEmpty) {\n            diffs[pointer - 1].text = bestEquality1;\n          } else {\n            diffs.removeAt(pointer - 1);\n            pointer--;\n          }\n          diffs[pointer].text = bestEdit;\n          if (!bestEquality2.isEmpty) {\n            diffs[pointer + 1].text = bestEquality2;\n          } else {\n            diffs.removeAt(pointer + 1);\n            pointer--;\n          }\n        }\n      }\n      pointer++;\n    }\n  }\n\n  /**\n   * Hack to allow unit tests to call private method.  Do not use.\n   */\n  void test_diff_cleanupSemanticLossless(List<Diff> diffs) {\n    _diff_cleanupSemanticLossless(diffs);\n  }\n\n  // Define some regex patterns for matching boundaries.\n  RegExp nonAlphaNumericRegex_ = new RegExp(r'[^a-zA-Z0-9]');\n  RegExp whitespaceRegex_ = new RegExp(r'\\s');\n  RegExp linebreakRegex_ = new RegExp(r'[\\r\\n]');\n  RegExp blanklineEndRegex_ = new RegExp(r'\\n\\r?\\n$');\n  RegExp blanklineStartRegex_ = new RegExp(r'^\\r?\\n\\r?\\n');\n\n  /**\n   * Reduce the number of edits by eliminating operationally trivial equalities.\n   * [diffs] is a List of Diff objects.\n   */\n  void diff_cleanupEfficiency(List<Diff> diffs) {\n    bool changes = false;\n    // Stack of indices where equalities are found.\n    final equalities = <int>[];\n    // Always equal to diffs[equalities.last()].text\n    String lastEquality = null;\n    int pointer = 0; // Index of current position.\n    // Is there an insertion operation before the last equality.\n    bool pre_ins = false;\n    // Is there a deletion operation before the last equality.\n    bool pre_del = false;\n    // Is there an insertion operation after the last equality.\n    bool post_ins = false;\n    // Is there a deletion operation after the last equality.\n    bool post_del = false;\n    while (pointer < diffs.length) {\n      if (diffs[pointer].operation == Operation.equal) {\n        // Equality found.\n        if (diffs[pointer].text.length < Diff_EditCost &&\n            (post_ins || post_del)) {\n          // Candidate found.\n          equalities.add(pointer);\n          pre_ins = post_ins;\n          pre_del = post_del;\n          lastEquality = diffs[pointer].text;\n        } else {\n          // Not a candidate, and can never become one.\n          equalities.clear();\n          lastEquality = null;\n        }\n        post_ins = post_del = false;\n      } else {\n        // An insertion or deletion.\n        if (diffs[pointer].operation == Operation.delete) {\n          post_del = true;\n        } else {\n          post_ins = true;\n        }\n        /*\n         * Five types to be split:\n         * <ins>A</ins><del>B</del>XY<ins>C</ins><del>D</del>\n         * <ins>A</ins>X<ins>C</ins><del>D</del>\n         * <ins>A</ins><del>B</del>X<ins>C</ins>\n         * <ins>A</del>X<ins>C</ins><del>D</del>\n         * <ins>A</ins><del>B</del>X<del>C</del>\n         */\n        if (lastEquality != null &&\n            ((pre_ins && pre_del && post_ins && post_del) ||\n                ((lastEquality.length < Diff_EditCost / 2) &&\n                    ((pre_ins ? 1 : 0) +\n                            (pre_del ? 1 : 0) +\n                            (post_ins ? 1 : 0) +\n                            (post_del ? 1 : 0)) ==\n                        3))) {\n          // Duplicate record.\n          diffs.insert(\n              equalities.last, new Diff(Operation.delete, lastEquality));\n          // Change second copy to insert.\n          diffs[equalities.last + 1].operation = Operation.insert;\n          equalities.removeLast(); // Throw away the equality we just deleted.\n          lastEquality = null;\n          if (pre_ins && pre_del) {\n            // No changes made which could affect previous entry, keep going.\n            post_ins = post_del = true;\n            equalities.clear();\n          } else {\n            if (!equalities.isEmpty) {\n              equalities.removeLast();\n            }\n            pointer = equalities.isEmpty ? -1 : equalities.last;\n            post_ins = post_del = false;\n          }\n          changes = true;\n        }\n      }\n      pointer++;\n    }\n\n    if (changes) {\n      diff_cleanupMerge(diffs);\n    }\n  }\n\n  /**\n   * Reorder and merge like edit sections.  Merge equalities.\n   * Any edit section can move as long as it doesn't cross an equality.\n   * [diffs] is a List of Diff objects.\n   */\n  void diff_cleanupMerge(List<Diff> diffs) {\n    diffs.add(new Diff(Operation.equal, '')); // Add a dummy entry at the end.\n    int pointer = 0;\n    int count_delete = 0;\n    int count_insert = 0;\n    String text_delete = '';\n    String text_insert = '';\n    int commonlength;\n    while (pointer < diffs.length) {\n      switch (diffs[pointer].operation) {\n        case Operation.insert:\n          count_insert++;\n          text_insert += diffs[pointer].text;\n          pointer++;\n          break;\n        case Operation.delete:\n          count_delete++;\n          text_delete += diffs[pointer].text;\n          pointer++;\n          break;\n        case Operation.equal:\n          // Upon reaching an equality, check for prior redundancies.\n          if (count_delete + count_insert > 1) {\n            if (count_delete != 0 && count_insert != 0) {\n              // Factor out any common prefixies.\n              commonlength = diff_commonPrefix(text_insert, text_delete);\n              if (commonlength != 0) {\n                if ((pointer - count_delete - count_insert) > 0 &&\n                    diffs[pointer - count_delete - count_insert - 1]\n                            .operation ==\n                        Operation.equal) {\n                  final i = pointer - count_delete - count_insert - 1;\n                  diffs[i].text =\n                      diffs[i].text + text_insert.substring(0, commonlength);\n                } else {\n                  diffs.insert(\n                      0,\n                      new Diff(Operation.equal,\n                          text_insert.substring(0, commonlength)));\n                  pointer++;\n                }\n                text_insert = text_insert.substring(commonlength);\n                text_delete = text_delete.substring(commonlength);\n              }\n\n              // Factor out any common suffixies.\n              commonlength = diff_commonSuffix(text_insert, text_delete);\n              if (commonlength != 0) {\n                diffs[pointer].text =\n                    text_insert.substring(text_insert.length - commonlength) +\n                        diffs[pointer].text;\n                text_insert =\n                    text_insert.substring(0, text_insert.length - commonlength);\n                text_delete =\n                    text_delete.substring(0, text_delete.length - commonlength);\n              }\n            }\n            // Delete the offending records and add the merged ones.\n            pointer -= count_delete + count_insert;\n            diffs.removeRange(pointer, pointer + count_delete + count_insert);\n            if (!text_delete.isEmpty) {\n              diffs.insert(pointer, new Diff(Operation.delete, text_delete));\n              pointer++;\n            }\n            if (!text_insert.isEmpty) {\n              diffs.insert(pointer, new Diff(Operation.insert, text_insert));\n              pointer++;\n            }\n            pointer++;\n          } else if (pointer != 0 &&\n              diffs[pointer - 1].operation == Operation.equal) {\n            // Merge this equality with the previous one.\n            diffs[pointer - 1].text =\n                diffs[pointer - 1].text + diffs[pointer].text;\n            diffs.removeAt(pointer);\n          } else {\n            pointer++;\n          }\n          count_insert = 0;\n          count_delete = 0;\n          text_delete = '';\n          text_insert = '';\n          break;\n      }\n    }\n    if (diffs.last.text.isEmpty) {\n      diffs.removeLast(); // Remove the dummy entry at the end.\n    }\n\n    // Second pass: look for single edits surrounded on both sides by equalities\n    // which can be shifted sideways to eliminate an equality.\n    // e.g: A<ins>BA</ins>C -> <ins>AB</ins>AC\n    bool changes = false;\n    pointer = 1;\n    // Intentionally ignore the first and last element (don't need checking).\n    while (pointer < diffs.length - 1) {\n      if (diffs[pointer - 1].operation == Operation.equal &&\n          diffs[pointer + 1].operation == Operation.equal) {\n        // This is a single edit surrounded by equalities.\n        if (diffs[pointer].text.endsWith(diffs[pointer - 1].text)) {\n          // Shift the edit over the previous equality.\n          diffs[pointer].text = diffs[pointer - 1].text +\n              diffs[pointer].text.substring(0,\n                  diffs[pointer].text.length - diffs[pointer - 1].text.length);\n          diffs[pointer + 1].text =\n              diffs[pointer - 1].text + diffs[pointer + 1].text;\n          diffs.removeAt(pointer - 1);\n          changes = true;\n        } else if (diffs[pointer].text.startsWith(diffs[pointer + 1].text)) {\n          // Shift the edit over the next equality.\n          diffs[pointer - 1].text =\n              diffs[pointer - 1].text + diffs[pointer + 1].text;\n          diffs[pointer].text =\n              diffs[pointer].text.substring(diffs[pointer + 1].text.length) +\n                  diffs[pointer + 1].text;\n          diffs.removeAt(pointer + 1);\n          changes = true;\n        }\n      }\n      pointer++;\n    }\n    // If shifts were made, the diff needs reordering and another shift sweep.\n    if (changes) {\n      diff_cleanupMerge(diffs);\n    }\n  }\n\n  /**\n   * loc is a location in text1, compute and return the equivalent location in\n   * text2.\n   * e.g. \"The cat\" vs \"The big cat\", 1->1, 5->8\n   * [diffs] is a List of Diff objects.\n   * [loc] is the location within text1.\n   * Returns the location within text2.\n   */\n  int diff_xIndex(List<Diff> diffs, int loc) {\n    int chars1 = 0;\n    int chars2 = 0;\n    int last_chars1 = 0;\n    int last_chars2 = 0;\n    Diff lastDiff = null;\n    for (Diff aDiff in diffs) {\n      if (aDiff.operation != Operation.insert) {\n        // Equality or deletion.\n        chars1 += aDiff.text.length;\n      }\n      if (aDiff.operation != Operation.delete) {\n        // Equality or insertion.\n        chars2 += aDiff.text.length;\n      }\n      if (chars1 > loc) {\n        // Overshot the location.\n        lastDiff = aDiff;\n        break;\n      }\n      last_chars1 = chars1;\n      last_chars2 = chars2;\n    }\n    if (lastDiff != null && lastDiff.operation == Operation.delete) {\n      // The location was deleted.\n      return last_chars2;\n    }\n    // Add the remaining character length.\n    return last_chars2 + (loc - last_chars1);\n  }\n\n  /**\n   * Convert a Diff list into a pretty HTML report.\n   * [diffs] is a List of Diff objects.\n   * Returns an HTML representation.\n   */\n  String diff_prettyHtml(List<Diff> diffs) {\n    final html = new StringBuffer();\n    for (Diff aDiff in diffs) {\n      String text = aDiff.text\n          .replaceAll('&', '&amp;')\n          .replaceAll('<', '&lt;')\n          .replaceAll('>', '&gt;')\n          .replaceAll('\\n', '&para;<br>');\n      switch (aDiff.operation) {\n        case Operation.insert:\n          html.write('<ins style=\"background:#e6ffe6;\">');\n          html.write(text);\n          html.write('</ins>');\n          break;\n        case Operation.delete:\n          html.write('<del style=\"background:#ffe6e6;\">');\n          html.write(text);\n          html.write('</del>');\n          break;\n        case Operation.equal:\n          html.write('<span>');\n          html.write(text);\n          html.write('</span>');\n          break;\n      }\n    }\n    return html.toString();\n  }\n\n  /**\n   * Compute and return the source text (all equalities and deletions).\n   * [diffs] is a List of Diff objects.\n   * Returns the source text.\n   */\n  String diff_text1(List<Diff> diffs) {\n    final text = new StringBuffer();\n    for (Diff aDiff in diffs) {\n      if (aDiff.operation != Operation.insert) {\n        text.write(aDiff.text);\n      }\n    }\n    return text.toString();\n  }\n\n  /**\n   * Compute and return the destination text (all equalities and insertions).\n   * [diffs] is a List of Diff objects.\n   * Returns the destination text.\n   */\n  String diff_text2(List<Diff> diffs) {\n    final text = new StringBuffer();\n    for (Diff aDiff in diffs) {\n      if (aDiff.operation != Operation.delete) {\n        text.write(aDiff.text);\n      }\n    }\n    return text.toString();\n  }\n\n  /**\n   * Compute the Levenshtein distance; the number of inserted, deleted or\n   * substituted characters.\n   * [diffs] is a List of Diff objects.\n   * Returns the number of changes.\n   */\n  int diff_levenshtein(List<Diff> diffs) {\n    int levenshtein = 0;\n    int insertions = 0;\n    int deletions = 0;\n    for (Diff aDiff in diffs) {\n      switch (aDiff.operation) {\n        case Operation.insert:\n          insertions += aDiff.text.length;\n          break;\n        case Operation.delete:\n          deletions += aDiff.text.length;\n          break;\n        case Operation.equal:\n          // A deletion and an insertion is one substitution.\n          levenshtein += max(insertions, deletions);\n          insertions = 0;\n          deletions = 0;\n          break;\n      }\n    }\n    levenshtein += max(insertions, deletions);\n    return levenshtein;\n  }\n\n  /**\n   * Crush the diff into an encoded string which describes the operations\n   * required to transform text1 into text2.\n   * E.g. =3\\t-2\\t+ing  -> Keep 3 chars, delete 2 chars, insert 'ing'.\n   * Operations are tab-separated.  Inserted text is escaped using %xx notation.\n   * [diffs] is a List of Diff objects.\n   * Returns the delta text.\n   */\n  String diff_toDelta(List<Diff> diffs) {\n    final text = new StringBuffer();\n    for (Diff aDiff in diffs) {\n      switch (aDiff.operation) {\n        case Operation.insert:\n          text.write('+');\n          text.write(Uri.encodeFull(aDiff.text));\n          text.write('\\t');\n          break;\n        case Operation.delete:\n          text.write('-');\n          text.write(aDiff.text.length);\n          text.write('\\t');\n          break;\n        case Operation.equal:\n          text.write('=');\n          text.write(aDiff.text.length);\n          text.write('\\t');\n          break;\n      }\n    }\n    String delta = text.toString();\n    if (!delta.isEmpty) {\n      // Strip off trailing tab character.\n      delta = delta.substring(0, delta.length - 1);\n    }\n    return delta.replaceAll('%20', ' ');\n  }\n\n  /**\n   * Given the original text1, and an encoded string which describes the\n   * operations required to transform text1 into text2, compute the full diff.\n   * [text1] is the source string for the diff.\n   * [delta] is the delta text.\n   * Returns a List of Diff objects or null if invalid.\n   * Throws ArgumentError if invalid input.\n   */\n  List<Diff> diff_fromDelta(String text1, String delta) {\n    final diffs = <Diff>[];\n    int pointer = 0; // Cursor in text1\n    final tokens = delta.split('\\t');\n    for (String token in tokens) {\n      if (token.length == 0) {\n        // Blank tokens are ok (from a trailing \\t).\n        continue;\n      }\n      // Each token begins with a one character parameter which specifies the\n      // operation of this token (delete, insert, equality).\n      String param = token.substring(1);\n      switch (token[0]) {\n        case '+':\n          // decode would change all \"+\" to \" \"\n          param = param.replaceAll('+', '%2B');\n          try {\n            param = Uri.decodeFull(param);\n          } on ArgumentError {\n            // Malformed URI sequence.\n            throw new ArgumentError('Illegal escape in diff_fromDelta: $param');\n          }\n          diffs.add(new Diff(Operation.insert, param));\n          break;\n        case '-':\n        // Fall through.\n        case '=':\n          int n;\n          try {\n            n = int.parse(param);\n          } on FormatException {\n            throw new ArgumentError('Invalid number in diff_fromDelta: $param');\n          }\n          if (n < 0) {\n            throw new ArgumentError(\n                'Negative number in diff_fromDelta: $param');\n          }\n          String text;\n          try {\n            text = text1.substring(pointer, pointer += n);\n          } on RangeError {\n            throw new ArgumentError('Delta length ($pointer)'\n                ' larger than source text length (${text1.length}).');\n          }\n          if (token[0] == '=') {\n            diffs.add(new Diff(Operation.equal, text));\n          } else {\n            diffs.add(new Diff(Operation.delete, text));\n          }\n          break;\n        default:\n          // Anything else is an error.\n          throw new ArgumentError(\n              'Invalid diff operation in diff_fromDelta: ${token[0]}');\n      }\n    }\n    if (pointer != text1.length) {\n      throw new ArgumentError('Delta length ($pointer)'\n          ' smaller than source text length (${text1.length}).');\n    }\n    return diffs;\n  }\n\n  //  MATCH FUNCTIONS\n\n  /**\n   * Locate the best instance of 'pattern' in 'text' near 'loc'.\n   * Returns -1 if no match found.\n   * [text] is the text to search.\n   * [pattern] is the pattern to search for.\n   * [loc] is the location to search around.\n   * Returns the best match index or -1.\n   */\n  int match_main(String text, String pattern, int loc) {\n    // Check for null inputs.\n    if (text == null || pattern == null) {\n      throw new ArgumentError('Null inputs. (match_main)');\n    }\n\n    loc = max(0, min(loc, text.length));\n    if (text == pattern) {\n      // Shortcut (potentially not guaranteed by the algorithm)\n      return 0;\n    } else if (text.length == 0) {\n      // Nothing to match.\n      return -1;\n    } else if (loc + pattern.length <= text.length &&\n        text.substring(loc, loc + pattern.length) == pattern) {\n      // Perfect match at the perfect spot!  (Includes case of null pattern)\n      return loc;\n    } else {\n      // Do a fuzzy compare.\n      return _match_bitap(text, pattern, loc);\n    }\n  }\n\n  /**\n   * Locate the best instance of 'pattern' in 'text' near 'loc' using the\n   * Bitap algorithm.  Returns -1 if no match found.\n   * [text] is the the text to search.\n   * [pattern] is the pattern to search for.\n   * [loc] is the location to search around.\n   * Returns the best match index or -1.\n   */\n  int _match_bitap(String text, String pattern, int loc) {\n    if (Match_MaxBits != 0 && pattern.length > Match_MaxBits) {\n      throw new Exception('Pattern too long for this application.');\n    }\n    // Initialise the alphabet.\n    Map<String, int> s = _match_alphabet(pattern);\n\n    // Highest score beyond which we give up.\n    double score_threshold = Match_Threshold;\n    // Is there a nearby exact match? (speedup)\n    int best_loc = text.indexOf(pattern, loc);\n    if (best_loc != -1) {\n      score_threshold =\n          min(_match_bitapScore(0, best_loc, loc, pattern), score_threshold);\n      // What about in the other direction? (speedup)\n      best_loc = text.lastIndexOf(pattern, loc + pattern.length);\n      if (best_loc != -1) {\n        score_threshold =\n            min(_match_bitapScore(0, best_loc, loc, pattern), score_threshold);\n      }\n    }\n\n    // Initialise the bit arrays.\n    final matchmask = 1 << (pattern.length - 1);\n    best_loc = -1;\n\n    int bin_min, bin_mid;\n    int bin_max = pattern.length + text.length;\n    List<int> last_rd;\n    for (int d = 0; d < pattern.length; d++) {\n      // Scan for the best match; each iteration allows for one more error.\n      // Run a binary search to determine how far from 'loc' we can stray at\n      // this error level.\n      bin_min = 0;\n      bin_mid = bin_max;\n      while (bin_min < bin_mid) {\n        if (_match_bitapScore(d, loc + bin_mid, loc, pattern) <=\n            score_threshold) {\n          bin_min = bin_mid;\n        } else {\n          bin_max = bin_mid;\n        }\n        bin_mid = ((bin_max - bin_min) / 2 + bin_min).toInt();\n      }\n      // Use the result from this iteration as the maximum for the next.\n      bin_max = bin_mid;\n      int start = max(1, loc - bin_mid + 1);\n      int finish = min(loc + bin_mid, text.length) + pattern.length;\n\n      final rd = new List<int>(finish + 2);\n      rd[finish + 1] = (1 << d) - 1;\n      for (int j = finish; j >= start; j--) {\n        int charMatch;\n        if (text.length <= j - 1 || !s.containsKey(text[j - 1])) {\n          // Out of range.\n          charMatch = 0;\n        } else {\n          charMatch = s[text[j - 1]];\n        }\n        if (d == 0) {\n          // First pass: exact match.\n          rd[j] = ((rd[j + 1] << 1) | 1) & charMatch;\n        } else {\n          // Subsequent passes: fuzzy match.\n          rd[j] = ((rd[j + 1] << 1) | 1) & charMatch |\n              (((last_rd[j + 1] | last_rd[j]) << 1) | 1) |\n              last_rd[j + 1];\n        }\n        if ((rd[j] & matchmask) != 0) {\n          double score = _match_bitapScore(d, j - 1, loc, pattern);\n          // This match will almost certainly be better than any existing\n          // match.  But check anyway.\n          if (score <= score_threshold) {\n            // Told you so.\n            score_threshold = score;\n            best_loc = j - 1;\n            if (best_loc > loc) {\n              // When passing loc, don't exceed our current distance from loc.\n              start = max(1, 2 * loc - best_loc);\n            } else {\n              // Already passed loc, downhill from here on in.\n              break;\n            }\n          }\n        }\n      }\n      if (_match_bitapScore(d + 1, loc, loc, pattern) > score_threshold) {\n        // No hope for a (better) match at greater error levels.\n        break;\n      }\n      last_rd = rd;\n    }\n    return best_loc;\n  }\n\n  /**\n   * Hack to allow unit tests to call private method.  Do not use.\n   */\n  int test_match_bitap(String text, String pattern, int loc) {\n    return _match_bitap(text, pattern, loc);\n  }\n\n  /**\n   * Compute and return the score for a match with e errors and x location.\n   * [e] is the number of errors in match.\n   * [x] is the location of match.\n   * [loc] is the expected location of match.\n   * [pattern] is the pattern being sought.\n   * Returns the overall score for match (0.0 = good, 1.0 = bad).\n   */\n  double _match_bitapScore(int e, int x, int loc, String pattern) {\n    final accuracy = e / pattern.length;\n    final proximity = (loc - x).abs();\n    if (Match_Distance == 0) {\n      // Dodge divide by zero error.\n      return proximity == 0 ? accuracy : 1.0;\n    }\n    return accuracy + proximity / Match_Distance;\n  }\n\n  /**\n   * Initialise the alphabet for the Bitap algorithm.\n   * [pattern] is the the text to encode.\n   * Returns a Map of character locations.\n   */\n  Map<String, int> _match_alphabet(String pattern) {\n    final s = new HashMap<String, int>();\n    for (int i = 0; i < pattern.length; i++) {\n      s[pattern[i]] = 0;\n    }\n    for (int i = 0; i < pattern.length; i++) {\n      s[pattern[i]] = s[pattern[i]] | (1 << (pattern.length - i - 1));\n    }\n    return s;\n  }\n\n  /**\n   * Hack to allow unit tests to call private method.  Do not use.\n   */\n  Map<String, int> test_match_alphabet(String pattern) {\n    return _match_alphabet(pattern);\n  }\n\n  //  PATCH FUNCTIONS\n\n  /**\n   * Increase the context until it is unique,\n   * but don't let the pattern expand beyond Match_MaxBits.\n   * [patch] is the phe patch to grow.\n   * [text] is the source text.\n   */\n  void _patch_addContext(Patch patch, String text) {\n    if (text.isEmpty) {\n      return;\n    }\n    String pattern = text.substring(patch.start2, patch.start2 + patch.length1);\n    int padding = 0;\n\n    // Look for the first and last matches of pattern in text.  If two different\n    // matches are found, increase the pattern length.\n    while (text.indexOf(pattern) != text.lastIndexOf(pattern) &&\n        pattern.length < Match_MaxBits - Patch_Margin - Patch_Margin) {\n      padding += Patch_Margin;\n      pattern = text.substring(max(0, patch.start2 - padding),\n          min(text.length, patch.start2 + patch.length1 + padding));\n    }\n    // Add one chunk for good luck.\n    padding += Patch_Margin;\n\n    // Add the prefix.\n    final prefix = text.substring(max(0, patch.start2 - padding), patch.start2);\n    if (!prefix.isEmpty) {\n      patch.diffs.insert(0, new Diff(Operation.equal, prefix));\n    }\n    // Add the suffix.\n    final suffix = text.substring(patch.start2 + patch.length1,\n        min(text.length, patch.start2 + patch.length1 + padding));\n    if (!suffix.isEmpty) {\n      patch.diffs.add(new Diff(Operation.equal, suffix));\n    }\n\n    // Roll back the start points.\n    patch.start1 -= prefix.length;\n    patch.start2 -= prefix.length;\n    // Extend the lengths.\n    patch.length1 += prefix.length + suffix.length;\n    patch.length2 += prefix.length + suffix.length;\n  }\n\n  /**\n   * Hack to allow unit tests to call private method.  Do not use.\n   */\n  void test_patch_addContext(Patch patch, String text) {\n    _patch_addContext(patch, text);\n  }\n\n  /**\n   * Compute a list of patches to turn text1 into text2.\n   * Use diffs if provided, otherwise compute it ourselves.\n   * There are four ways to call this function, depending on what data is\n   * available to the caller:\n   * Method 1:\n   * [a] = text1, [opt_b] = text2\n   * Method 2:\n   * [a] = diffs\n   * Method 3 (optimal):\n   * [a] = text1, [opt_b] = diffs\n   * Method 4 (deprecated, use method 3):\n   * [a] = text1, [opt_b] = text2, [opt_c] = diffs\n   * Returns a List of Patch objects.\n   */\n  List<Patch> patch_make(a, [opt_b, opt_c]) {\n    String text1;\n    List<Diff> diffs;\n    if (a is String && opt_b is String && opt_c == null) {\n      // Method 1: text1, text2\n      // Compute diffs from text1 and text2.\n      text1 = a;\n      diffs = diff_main(text1, opt_b, true);\n      if (diffs.length > 2) {\n        diff_cleanupSemantic(diffs);\n        diff_cleanupEfficiency(diffs);\n      }\n    } else if (a is List && opt_b == null && opt_c == null) {\n      // Method 2: diffs\n      // Compute text1 from diffs.\n      diffs = a;\n      text1 = diff_text1(diffs);\n    } else if (a is String && opt_b is List && opt_c == null) {\n      // Method 3: text1, diffs\n      text1 = a;\n      diffs = opt_b;\n    } else if (a is String && opt_b is String && opt_c is List) {\n      // Method 4: text1, text2, diffs\n      // text2 is not used.\n      text1 = a;\n      diffs = opt_c;\n    } else {\n      throw new ArgumentError('Unknown call format to patch_make.');\n    }\n\n    final patches = <Patch>[];\n    if (diffs.isEmpty) {\n      return patches; // Get rid of the null case.\n    }\n    Patch patch = new Patch();\n    int char_count1 = 0; // Number of characters into the text1 string.\n    int char_count2 = 0; // Number of characters into the text2 string.\n    // Start with text1 (prepatch_text) and apply the diffs until we arrive at\n    // text2 (postpatch_text). We recreate the patches one by one to determine\n    // context info.\n    String prepatch_text = text1;\n    String postpatch_text = text1;\n    for (Diff aDiff in diffs) {\n      if (patch.diffs.isEmpty && aDiff.operation != Operation.equal) {\n        // A new patch starts here.\n        patch.start1 = char_count1;\n        patch.start2 = char_count2;\n      }\n\n      switch (aDiff.operation) {\n        case Operation.insert:\n          patch.diffs.add(aDiff);\n          patch.length2 += aDiff.text.length;\n          postpatch_text = postpatch_text.substring(0, char_count2) +\n              aDiff.text +\n              postpatch_text.substring(char_count2);\n          break;\n        case Operation.delete:\n          patch.length1 += aDiff.text.length;\n          patch.diffs.add(aDiff);\n          postpatch_text = postpatch_text.substring(0, char_count2) +\n              postpatch_text.substring(char_count2 + aDiff.text.length);\n          break;\n        case Operation.equal:\n          if (aDiff.text.length <= 2 * Patch_Margin &&\n              !patch.diffs.isEmpty &&\n              aDiff != diffs.last) {\n            // Small equality inside a patch.\n            patch.diffs.add(aDiff);\n            patch.length1 += aDiff.text.length;\n            patch.length2 += aDiff.text.length;\n          }\n\n          if (aDiff.text.length >= 2 * Patch_Margin) {\n            // Time for a new patch.\n            if (!patch.diffs.isEmpty) {\n              _patch_addContext(patch, prepatch_text);\n              patches.add(patch);\n              patch = new Patch();\n              // Unlike Unidiff, our patch lists have a rolling context.\n              // https://github.com/google/diff-match-patch/wiki/Unidiff\n              // Update prepatch text & pos to reflect the application of the\n              // just completed patch.\n              prepatch_text = postpatch_text;\n              char_count1 = char_count2;\n            }\n          }\n          break;\n      }\n\n      // Update the current character count.\n      if (aDiff.operation != Operation.insert) {\n        char_count1 += aDiff.text.length;\n      }\n      if (aDiff.operation != Operation.delete) {\n        char_count2 += aDiff.text.length;\n      }\n    }\n    // Pick up the leftover patch if not empty.\n    if (!patch.diffs.isEmpty) {\n      _patch_addContext(patch, prepatch_text);\n      patches.add(patch);\n    }\n\n    return patches;\n  }\n\n  /**\n   * Given an array of patches, return another array that is identical.\n   * [patches] is a List of Patch objects.\n   * Returns a List of Patch objects.\n   */\n  List<Patch> patch_deepCopy(List<Patch> patches) {\n    final patchesCopy = <Patch>[];\n    for (Patch aPatch in patches) {\n      final patchCopy = new Patch();\n      for (Diff aDiff in aPatch.diffs) {\n        patchCopy.diffs.add(new Diff(aDiff.operation, aDiff.text));\n      }\n      patchCopy.start1 = aPatch.start1;\n      patchCopy.start2 = aPatch.start2;\n      patchCopy.length1 = aPatch.length1;\n      patchCopy.length2 = aPatch.length2;\n      patchesCopy.add(patchCopy);\n    }\n    return patchesCopy;\n  }\n\n  /**\n   * Merge a set of patches onto the text.  Return a patched text, as well\n   * as an array of true/false values indicating which patches were applied.\n   * [patches] is a List of Patch objects\n   * [text] is the old text.\n   * Returns a two element List, containing the new text and a List of\n   *      bool values.\n   */\n  List patch_apply(List<Patch> patches, String text) {\n    if (patches.isEmpty) {\n      return [text, []];\n    }\n\n    // Deep copy the patches so that no changes are made to originals.\n    patches = patch_deepCopy(patches);\n\n    final nullPadding = patch_addPadding(patches);\n    text = nullPadding + text + nullPadding;\n    patch_splitMax(patches);\n\n    int x = 0;\n    // delta keeps track of the offset between the expected and actual location\n    // of the previous patch.  If there are patches expected at positions 10 and\n    // 20, but the first patch was found at 12, delta is 2 and the second patch\n    // has an effective expected position of 22.\n    int delta = 0;\n    final results = new List<bool>(patches.length);\n    for (Patch aPatch in patches) {\n      int expected_loc = aPatch.start2 + delta;\n      String text1 = diff_text1(aPatch.diffs);\n      int start_loc;\n      int end_loc = -1;\n      if (text1.length > Match_MaxBits) {\n        // patch_splitMax will only provide an oversized pattern in the case of\n        // a monster delete.\n        start_loc =\n            match_main(text, text1.substring(0, Match_MaxBits), expected_loc);\n        if (start_loc != -1) {\n          end_loc = match_main(\n              text,\n              text1.substring(text1.length - Match_MaxBits),\n              expected_loc + text1.length - Match_MaxBits);\n          if (end_loc == -1 || start_loc >= end_loc) {\n            // Can't find valid trailing context.  Drop this patch.\n            start_loc = -1;\n          }\n        }\n      } else {\n        start_loc = match_main(text, text1, expected_loc);\n      }\n      if (start_loc == -1) {\n        // No match found.  :(\n        results[x] = false;\n        // Subtract the delta for this failed patch from subsequent patches.\n        delta -= aPatch.length2 - aPatch.length1;\n      } else {\n        // Found a match.  :)\n        results[x] = true;\n        delta = start_loc - expected_loc;\n        String text2;\n        if (end_loc == -1) {\n          text2 = text.substring(\n              start_loc, min(start_loc + text1.length, text.length));\n        } else {\n          text2 = text.substring(\n              start_loc, min(end_loc + Match_MaxBits, text.length));\n        }\n        if (text1 == text2) {\n          // Perfect match, just shove the replacement text in.\n          text = text.substring(0, start_loc) +\n              diff_text2(aPatch.diffs) +\n              text.substring(start_loc + text1.length);\n        } else {\n          // Imperfect match.  Run a diff to get a framework of equivalent\n          // indices.\n          final diffs = diff_main(text1, text2, false);\n          if (text1.length > Match_MaxBits &&\n              diff_levenshtein(diffs) / text1.length > Patch_DeleteThreshold) {\n            // The end points match, but the content is unacceptably bad.\n            results[x] = false;\n          } else {\n            _diff_cleanupSemanticLossless(diffs);\n            int index1 = 0;\n            for (Diff aDiff in aPatch.diffs) {\n              if (aDiff.operation != Operation.equal) {\n                int index2 = diff_xIndex(diffs, index1);\n                if (aDiff.operation == Operation.insert) {\n                  // Insertion\n                  text = text.substring(0, start_loc + index2) +\n                      aDiff.text +\n                      text.substring(start_loc + index2);\n                } else if (aDiff.operation == Operation.delete) {\n                  // Deletion\n                  text = text.substring(0, start_loc + index2) +\n                      text.substring(start_loc +\n                          diff_xIndex(diffs, index1 + aDiff.text.length));\n                }\n              }\n              if (aDiff.operation != Operation.delete) {\n                index1 += aDiff.text.length;\n              }\n            }\n          }\n        }\n      }\n      x++;\n    }\n    // Strip the padding off.\n    text = text.substring(nullPadding.length, text.length - nullPadding.length);\n    return [text, results];\n  }\n\n  /**\n   * Add some padding on text start and end so that edges can match something.\n   * Intended to be called only from within patch_apply.\n   * [patches] is a List of Patch objects.\n   * Returns the padding string added to each side.\n   */\n  String patch_addPadding(List<Patch> patches) {\n    final paddingLength = Patch_Margin;\n    final paddingCodes = <int>[];\n    for (int x = 1; x <= paddingLength; x++) {\n      paddingCodes.add(x);\n    }\n    String nullPadding = new String.fromCharCodes(paddingCodes);\n\n    // Bump all the patches forward.\n    for (Patch aPatch in patches) {\n      aPatch.start1 += paddingLength;\n      aPatch.start2 += paddingLength;\n    }\n\n    // Add some padding on start of first diff.\n    Patch patch = patches[0];\n    List<Diff> diffs = patch.diffs;\n    if (diffs.isEmpty || diffs[0].operation != Operation.equal) {\n      // Add nullPadding equality.\n      diffs.insert(0, new Diff(Operation.equal, nullPadding));\n      patch.start1 -= paddingLength; // Should be 0.\n      patch.start2 -= paddingLength; // Should be 0.\n      patch.length1 += paddingLength;\n      patch.length2 += paddingLength;\n    } else if (paddingLength > diffs[0].text.length) {\n      // Grow first equality.\n      Diff firstDiff = diffs[0];\n      int extraLength = paddingLength - firstDiff.text.length;\n      firstDiff.text =\n          nullPadding.substring(firstDiff.text.length) + firstDiff.text;\n      patch.start1 -= extraLength;\n      patch.start2 -= extraLength;\n      patch.length1 += extraLength;\n      patch.length2 += extraLength;\n    }\n\n    // Add some padding on end of last diff.\n    patch = patches.last;\n    diffs = patch.diffs;\n    if (diffs.isEmpty || diffs.last.operation != Operation.equal) {\n      // Add nullPadding equality.\n      diffs.add(new Diff(Operation.equal, nullPadding));\n      patch.length1 += paddingLength;\n      patch.length2 += paddingLength;\n    } else if (paddingLength > diffs.last.text.length) {\n      // Grow last equality.\n      Diff lastDiff = diffs.last;\n      int extraLength = paddingLength - lastDiff.text.length;\n      lastDiff.text = lastDiff.text + nullPadding.substring(0, extraLength);\n      patch.length1 += extraLength;\n      patch.length2 += extraLength;\n    }\n\n    return nullPadding;\n  }\n\n  /**\n   * Look through the patches and break up any which are longer than the\n   * maximum limit of the match algorithm.\n   * Intended to be called only from within patch_apply.\n   * [patches] is a List of Patch objects.\n   */\n  patch_splitMax(List<Patch> patches) {\n    final patch_size = Match_MaxBits;\n    for (var x = 0; x < patches.length; x++) {\n      if (patches[x].length1 <= patch_size) {\n        continue;\n      }\n      Patch bigpatch = patches[x];\n      // Remove the big old patch.\n      patches.removeAt(x--);\n      int start1 = bigpatch.start1;\n      int start2 = bigpatch.start2;\n      String precontext = '';\n      while (!bigpatch.diffs.isEmpty) {\n        // Create one of several smaller patches.\n        final patch = new Patch();\n        bool empty = true;\n        patch.start1 = start1 - precontext.length;\n        patch.start2 = start2 - precontext.length;\n        if (!precontext.isEmpty) {\n          patch.length1 = patch.length2 = precontext.length;\n          patch.diffs.add(new Diff(Operation.equal, precontext));\n        }\n        while (!bigpatch.diffs.isEmpty &&\n            patch.length1 < patch_size - Patch_Margin) {\n          Operation diff_type = bigpatch.diffs[0].operation;\n          String diff_text = bigpatch.diffs[0].text;\n          if (diff_type == Operation.insert) {\n            // Insertions are harmless.\n            patch.length2 += diff_text.length;\n            start2 += diff_text.length;\n            patch.diffs.add(bigpatch.diffs[0]);\n            bigpatch.diffs.removeAt(0);\n            empty = false;\n          } else if (diff_type == Operation.delete &&\n              patch.diffs.length == 1 &&\n              patch.diffs[0].operation == Operation.equal &&\n              diff_text.length > 2 * patch_size) {\n            // This is a large deletion.  Let it pass in one chunk.\n            patch.length1 += diff_text.length;\n            start1 += diff_text.length;\n            empty = false;\n            patch.diffs.add(new Diff(diff_type, diff_text));\n            bigpatch.diffs.removeAt(0);\n          } else {\n            // Deletion or equality.  Only take as much as we can stomach.\n            diff_text = diff_text.substring(\n                0,\n                min(diff_text.length,\n                    patch_size - patch.length1 - Patch_Margin));\n            patch.length1 += diff_text.length;\n            start1 += diff_text.length;\n            if (diff_type == Operation.equal) {\n              patch.length2 += diff_text.length;\n              start2 += diff_text.length;\n            } else {\n              empty = false;\n            }\n            patch.diffs.add(new Diff(diff_type, diff_text));\n            if (diff_text == bigpatch.diffs[0].text) {\n              bigpatch.diffs.removeAt(0);\n            } else {\n              bigpatch.diffs[0].text =\n                  bigpatch.diffs[0].text.substring(diff_text.length);\n            }\n          }\n        }\n        // Compute the head context for the next patch.\n        precontext = diff_text2(patch.diffs);\n        precontext =\n            precontext.substring(max(0, precontext.length - Patch_Margin));\n        // Append the end context for this patch.\n        String postcontext;\n        if (diff_text1(bigpatch.diffs).length > Patch_Margin) {\n          postcontext = diff_text1(bigpatch.diffs).substring(0, Patch_Margin);\n        } else {\n          postcontext = diff_text1(bigpatch.diffs);\n        }\n        if (!postcontext.isEmpty) {\n          patch.length1 += postcontext.length;\n          patch.length2 += postcontext.length;\n          if (!patch.diffs.isEmpty &&\n              patch.diffs.last.operation == Operation.equal) {\n            patch.diffs.last.text = patch.diffs.last.text + postcontext;\n          } else {\n            patch.diffs.add(new Diff(Operation.equal, postcontext));\n          }\n        }\n        if (!empty) {\n          patches.insert(++x, patch);\n        }\n      }\n    }\n  }\n\n  /**\n   * Take a list of patches and return a textual representation.\n   * [patches] is a List of Patch objects.\n   * Returns a text representation of patches.\n   */\n  String patch_toText(List<Patch> patches) {\n    final text = new StringBuffer();\n    text.writeAll(patches);\n    return text.toString();\n  }\n\n  /**\n   * Parse a textual representation of patches and return a List of Patch\n   * objects.\n   * [textline] is a text representation of patches.\n   * Returns a List of Patch objects.\n   * Throws ArgumentError if invalid input.\n   */\n  List<Patch> patch_fromText(String textline) {\n    final patches = <Patch>[];\n    if (textline.isEmpty) {\n      return patches;\n    }\n    final text = textline.split('\\n');\n    int textPointer = 0;\n    final patchHeader =\n        new RegExp('^@@ -(\\\\d+),?(\\\\d*) \\\\+(\\\\d+),?(\\\\d*) @@\\$');\n    while (textPointer < text.length) {\n      Match m = patchHeader.firstMatch(text[textPointer]);\n      if (m == null) {\n        throw new ArgumentError('Invalid patch string: ${text[textPointer]}');\n      }\n      final patch = new Patch();\n      patches.add(patch);\n      patch.start1 = int.parse(m.group(1));\n      if (m.group(2).isEmpty) {\n        patch.start1--;\n        patch.length1 = 1;\n      } else if (m.group(2) == '0') {\n        patch.length1 = 0;\n      } else {\n        patch.start1--;\n        patch.length1 = int.parse(m.group(2));\n      }\n\n      patch.start2 = int.parse(m.group(3));\n      if (m.group(4).isEmpty) {\n        patch.start2--;\n        patch.length2 = 1;\n      } else if (m.group(4) == '0') {\n        patch.length2 = 0;\n      } else {\n        patch.start2--;\n        patch.length2 = int.parse(m.group(4));\n      }\n      textPointer++;\n\n      while (textPointer < text.length) {\n        if (!text[textPointer].isEmpty) {\n          final sign = text[textPointer][0];\n          String line;\n          try {\n            line = Uri.decodeFull(text[textPointer].substring(1));\n          } on ArgumentError {\n            // Malformed URI sequence.\n            throw new ArgumentError('Illegal escape in patch_fromText: $line');\n          }\n          if (sign == '-') {\n            // Deletion.\n            patch.diffs.add(new Diff(Operation.delete, line));\n          } else if (sign == '+') {\n            // Insertion.\n            patch.diffs.add(new Diff(Operation.insert, line));\n          } else if (sign == ' ') {\n            // Minor equality.\n            patch.diffs.add(new Diff(Operation.equal, line));\n          } else if (sign == '@') {\n            // Start of next patch.\n            break;\n          } else {\n            // WTF?\n            throw new ArgumentError('Invalid patch mode \"$sign\" in: $line');\n          }\n        }\n        textPointer++;\n      }\n    }\n    return patches;\n  }\n}\n"
  },
  {
    "path": "dart/DiffClass.dart",
    "content": "/*\n * Diff Match and Patch\n * Copyright 2018 The diff-match-patch Authors.\n * https://github.com/google/diff-match-patch\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *   http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npart of DiffMatchPatch;\n\n/**\n * Class representing one diff operation.\n */\nclass Diff {\n  /**\n   * One of: Operation.insert, Operation.delete or Operation.equal.\n   */\n  Operation operation;\n  /**\n   * The text associated with this diff operation.\n   */\n  String text;\n\n  /**\n   * Constructor.  Initializes the diff with the provided values.\n   * [operation] is one of Operation.insert, Operation.delete or Operation.equal.\n   * [text] is the text being applied.\n   */\n  Diff(this.operation, this.text);\n\n  /**\n   * Display a human-readable version of this Diff.\n   * Returns a text version.\n   */\n  String toString() {\n    String prettyText = this.text.replaceAll('\\n', '\\u00b6');\n    return 'Diff(${this.operation},\"$prettyText\")';\n  }\n\n  /**\n   * Is this Diff equivalent to another Diff?\n   * [other] is another Diff to compare against.\n   * Returns true or false.\n   */\n  @override\n  bool operator ==(Object other) =>\n  \t      identical(this, other) ||\n          other is Diff &&\n              runtimeType == other.runtimeType &&\n              operation == other.operation &&\n              text == other.text;\n\n  /**\n   * Generate a uniquely identifiable hashcode for this Diff.\n   * Returns numeric hashcode.\n   */\n  @override\n  int get hashCode =>\n      operation.hashCode ^ text.hashCode;\n}\n"
  },
  {
    "path": "dart/DiffMatchPatch.dart",
    "content": "/*\n * Diff Match and Patch\n * Copyright 2018 The diff-match-patch Authors.\n * https://github.com/google/diff-match-patch\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *   http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nlibrary DiffMatchPatch;\n\nimport 'dart:math';\nimport 'dart:collection';\n\npart 'DMPClass.dart';\npart 'DiffClass.dart';\npart 'PatchClass.dart';\n"
  },
  {
    "path": "dart/PatchClass.dart",
    "content": "/*\n * Diff Match and Patch\n * Copyright 2018 The diff-match-patch Authors.\n * https://github.com/google/diff-match-patch\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *   http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npart of DiffMatchPatch;\n\n/**\n * Class representing one patch operation.\n */\nclass Patch {\n  List<Diff> diffs;\n  int start1;\n  int start2;\n  int length1 = 0;\n  int length2 = 0;\n\n  /**\n   * Constructor.  Initializes with an empty list of diffs.\n   */\n  Patch() {\n    this.diffs = <Diff>[];\n  }\n\n  /**\n   * Emulate GNU diff's format.\n   * Header: @@ -382,8 +481,9 @@\n   * Indices are printed as 1-based, not 0-based.\n   * Returns the GNU diff string.\n   */\n  String toString() {\n    String coords1, coords2;\n    if (this.length1 == 0) {\n      coords1 = '${this.start1},0';\n    } else if (this.length1 == 1) {\n      coords1 = (this.start1 + 1).toString();\n    } else {\n      coords1 = '${this.start1 + 1},${this.length1}';\n    }\n    if (this.length2 == 0) {\n      coords2 = '${this.start2},0';\n    } else if (this.length2 == 1) {\n      coords2 = (this.start2 + 1).toString();\n    } else {\n      coords2 = '${this.start2 + 1},${this.length2}';\n    }\n    final text = new StringBuffer('@@ -$coords1 +$coords2 @@\\n');\n    // Escape the body of the patch with %xx notation.\n    for (Diff aDiff in this.diffs) {\n      switch (aDiff.operation) {\n        case Operation.insert:\n          text.write('+');\n          break;\n        case Operation.delete:\n          text.write('-');\n          break;\n        case Operation.equal:\n          text.write(' ');\n          break;\n      }\n      text.write(Uri.encodeFull(aDiff.text));\n      text.write('\\n');\n    }\n    return text.toString().replaceAll('%20', ' ');\n  }\n}\n"
  },
  {
    "path": "dart/tests/DiffMatchPatchTest.dart",
    "content": "/**\n * Diff Match and Patch -- Test Harness\n * Copyright 2018 The diff-match-patch Authors.\n * https://github.com/google/diff-match-patch\n *\n * Licensed under the Apache License, Version 2.0 (the 'License');\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *   http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an 'AS IS' BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport '../DiffMatchPatch.dart';\n\n// Expect class disappeared from Dart unexpectedly.  Here's a minimal shim.\nclass Expect {\n  static void equals(var expected, var actual, String msg) {\n    if (expected == actual) return;\n    throw new Exception(\n        'Expect.equals(expected: <$expected>, actual: <$actual> $msg) fails.');\n  }\n\n  static void isNull(actual, String msg) {\n    if (null == actual) return;\n    throw new Exception('Expect.isNull(actual: <$actual>$msg) fails.');\n  }\n\n  static void isTrue(var actual, String msg) {\n    if (identical(actual, true)) return;\n    throw new Exception('Expect.isTrue($actual, $msg) fails.');\n  }\n\n  static void throws(void f(), String msg) {\n    try {\n      f();\n    } catch (e) {\n      return;\n    }\n    throw new Exception('Expect.throws($msg) fails');\n  }\n\n  static void listEquals(List expected, List actual, String msg) {\n    int n = (expected.length < actual.length) ? expected.length : actual.length;\n    for (int i = 0; i < n; i++) {\n      if (expected[i] != actual[i]) {\n        throw new Exception('Expect.listEquals(at index $i, '\n            'expected: <${expected[i]}>, actual: <${actual[i]}> $msg) fails');\n      }\n    }\n    // We check on length at the end in order to provide better error\n    // messages when an unexpected item is inserted in a list.\n    if (expected.length != actual.length) {\n      throw new Exception('Expect.listEquals(list length, '\n        'expected: <${expected.length}>, actual: <${actual.length}> $msg) '\n        'fails: Next element <'\n        '${expected.length > n ? expected[n] : actual[n]}>');\n    }\n  }\n\n  static void mapEquals(Map expected, Map actual, String msg) {\n    for (var k in actual.keys) {\n      if (!expected.containsKey(k)) {\n        throw new Exception('Expect.mapEquals(unexpected key <$k> found '\n            'expected: <$expected>, actual: <$actual> $msg) fails');\n      }\n    }\n    for (var k in expected.keys) {\n      if (!actual.containsKey(k)) {\n        throw new Exception('Expect.mapEquals(key <$k> not found '\n            'expected: <$expected>, actual: <$actual> $msg) fails');\n      }\n      Expect.equals(actual[k], expected[k], \"$msg [Key: $k]\");\n    }\n  }\n}\n\nList<String> _diff_rebuildtexts(diffs) {\n  // Construct the two texts which made up the diff originally.\n  final text1 = new StringBuffer();\n  final text2 = new StringBuffer();\n  for (int x = 0; x < diffs.length; x++) {\n    if (diffs[x].operation != Operation.insert) {\n      text1.write(diffs[x].text);\n    }\n    if (diffs[x].operation != Operation.delete) {\n      text2.write(diffs[x].text);\n    }\n  }\n  return [text1.toString(), text2.toString()];\n}\n\nDiffMatchPatch dmp;\n\n// DIFF TEST FUNCTIONS\n\n\nvoid testDiffCommonPrefix() {\n  // Detect any common prefix.\n  Expect.equals(0, dmp.diff_commonPrefix('abc', 'xyz'), 'diff_commonPrefix: Null case.');\n\n  Expect.equals(4, dmp.diff_commonPrefix('1234abcdef', '1234xyz'), 'diff_commonPrefix: Non-null case.');\n\n  Expect.equals(4, dmp.diff_commonPrefix('1234', '1234xyz'), 'diff_commonPrefix: Whole case.');\n}\n\nvoid testDiffCommonSuffix() {\n  // Detect any common suffix.\n  Expect.equals(0, dmp.diff_commonSuffix('abc', 'xyz'), 'diff_commonSuffix: Null case.');\n\n  Expect.equals(4, dmp.diff_commonSuffix('abcdef1234', 'xyz1234'), 'diff_commonSuffix: Non-null case.');\n\n  Expect.equals(4, dmp.diff_commonSuffix('1234', 'xyz1234'), 'diff_commonSuffix: Whole case.');\n}\n\nvoid testDiffCommonOverlap() {\n  // Detect any suffix/prefix overlap.\n  Expect.equals(0, dmp.test_diff_commonOverlap('', 'abcd'), 'diff_commonOverlap: Null case.');\n\n  Expect.equals(3, dmp.test_diff_commonOverlap('abc', 'abcd'), 'diff_commonOverlap: Whole case.');\n\n  Expect.equals(0, dmp.test_diff_commonOverlap('123456', 'abcd'), 'diff_commonOverlap: No overlap.');\n\n  Expect.equals(3, dmp.test_diff_commonOverlap('123456xxx', 'xxxabcd'), 'diff_commonOverlap: Overlap.');\n\n  // Some overly clever languages (C#) may treat ligatures as equal to their\n  // component letters.  E.g. U+FB01 == 'fi'\n  Expect.equals(0, dmp.test_diff_commonOverlap('fi', '\\ufb01i'), 'diff_commonOverlap: Unicode.');\n}\n\nvoid testDiffHalfmatch() {\n  // Detect a halfmatch.\n  dmp.Diff_Timeout = 1.0;\n  Expect.isNull(dmp.test_diff_halfMatch('1234567890', 'abcdef'), 'diff_halfMatch: No match #1.');\n\n  Expect.isNull(dmp.test_diff_halfMatch('12345', '23'), 'diff_halfMatch: No match #2.');\n\n  Expect.listEquals(['12', '90', 'a', 'z', '345678'], dmp.test_diff_halfMatch('1234567890', 'a345678z'), 'diff_halfMatch: Single Match #1.');\n\n  Expect.listEquals(['a', 'z', '12', '90', '345678'], dmp.test_diff_halfMatch('a345678z', '1234567890'), 'diff_halfMatch: Single Match #2.');\n\n  Expect.listEquals(['abc', 'z', '1234', '0', '56789'], dmp.test_diff_halfMatch('abc56789z', '1234567890'), 'diff_halfMatch: Single Match #3.');\n\n  Expect.listEquals(['a', 'xyz', '1', '7890', '23456'], dmp.test_diff_halfMatch('a23456xyz', '1234567890'), 'diff_halfMatch: Single Match #4.');\n\n  Expect.listEquals(['12123', '123121', 'a', 'z', '1234123451234'], dmp.test_diff_halfMatch('121231234123451234123121', 'a1234123451234z'), 'diff_halfMatch: Multiple Matches #1.');\n\n  Expect.listEquals(['', '-=-=-=-=-=', 'x', '', 'x-=-=-=-=-=-=-='], dmp.test_diff_halfMatch('x-=-=-=-=-=-=-=-=-=-=-=-=', 'xx-=-=-=-=-=-=-='), 'diff_halfMatch: Multiple Matches #2.');\n\n  Expect.listEquals(['-=-=-=-=-=', '', '', 'y', '-=-=-=-=-=-=-=y'], dmp.test_diff_halfMatch('-=-=-=-=-=-=-=-=-=-=-=-=y', '-=-=-=-=-=-=-=yy'), 'diff_halfMatch: Multiple Matches #3.');\n\n  // Optimal diff would be -q+x=H-i+e=lloHe+Hu=llo-Hew+y not -qHillo+x=HelloHe-w+Hulloy\n  Expect.listEquals(['qHillo', 'w', 'x', 'Hulloy', 'HelloHe'], dmp.test_diff_halfMatch('qHilloHelloHew', 'xHelloHeHulloy'), 'diff_halfMatch: Non-optimal halfmatch.');\n\n  dmp.Diff_Timeout = 0.0;\n  Expect.isNull(dmp.test_diff_halfMatch('qHilloHelloHew', 'xHelloHeHulloy'), 'diff_halfMatch: Optimal no halfmatch.');\n}\n\nvoid testDiffLinesToChars() {\n  void assertLinesToCharsResultEquals(Map<String, dynamic> a, Map<String, dynamic> b, String error_msg) {\n    Expect.equals(a['chars1'], b['chars1'], error_msg);\n    Expect.equals(a['chars2'], b['chars2'], error_msg);\n    Expect.listEquals(a['lineArray'], b['lineArray'], error_msg);\n  }\n\n  // Convert lines down to characters.\n  assertLinesToCharsResultEquals({'chars1': '\\u0001\\u0002\\u0001', 'chars2': '\\u0002\\u0001\\u0002', 'lineArray': ['', 'alpha\\n', 'beta\\n']}, dmp.test_diff_linesToChars('alpha\\nbeta\\nalpha\\n', 'beta\\nalpha\\nbeta\\n'), 'diff_linesToChars: Shared lines.');\n\n  assertLinesToCharsResultEquals({'chars1': '', 'chars2': '\\u0001\\u0002\\u0003\\u0003', 'lineArray': ['', 'alpha\\r\\n', 'beta\\r\\n', '\\r\\n']}, dmp.test_diff_linesToChars('', 'alpha\\r\\nbeta\\r\\n\\r\\n\\r\\n'), 'diff_linesToChars: Empty string and blank lines.');\n\n  assertLinesToCharsResultEquals({'chars1': '\\u0001', 'chars2': '\\u0002', 'lineArray': ['', 'a', 'b']}, dmp.test_diff_linesToChars('a', 'b'), 'diff_linesToChars: No linebreaks.');\n\n  // More than 256 to reveal any 8-bit limitations.\n  int n = 300;\n  List<String> lineList = [];\n  StringBuffer charList = new StringBuffer();\n  for (int i = 1; i < n + 1; i++) {\n    lineList.add('$i\\n');\n    charList.writeCharCode(i);\n  }\n  Expect.equals(n, lineList.length, 'Test initialization fail #1.');\n  String lines = lineList.join();\n  String chars = charList.toString();\n  Expect.equals(n, chars.length, 'Test initialization fail #2.');\n  lineList.insert(0, '');\n  assertLinesToCharsResultEquals({'chars1': chars, 'chars2': '', 'lineArray': lineList}, dmp.test_diff_linesToChars(lines, ''), 'diff_linesToChars: More than 256.');\n}\n\nvoid testDiffCharsToLines() {\n  // First check that Diff equality works.\n  Expect.isTrue(new Diff(Operation.equal, 'a') == new Diff(Operation.equal, 'a'), 'diff_charsToLines: Equality #1.');\n\n  Expect.equals(new Diff(Operation.equal, 'a'), new Diff(Operation.equal, 'a'), 'diff_charsToLines: Equality #2.');\n\n  // Convert chars up to lines.\n  List<Diff> diffs = [new Diff(Operation.equal, '\\u0001\\u0002\\u0001'), new Diff(Operation.insert, '\\u0002\\u0001\\u0002')];\n  dmp.test_diff_charsToLines(diffs, ['', 'alpha\\n', 'beta\\n']);\n  Expect.listEquals([new Diff(Operation.equal, 'alpha\\nbeta\\nalpha\\n'), new Diff(Operation.insert, 'beta\\nalpha\\nbeta\\n')], diffs, 'diff_charsToLines: Shared lines.');\n\n  // More than 256 to reveal any 8-bit limitations.\n  int n = 300;\n  List<String> lineList = [];\n  StringBuffer charList = new StringBuffer();\n  for (int i = 1; i < n + 1; i++) {\n    lineList.add('$i\\n');\n    charList.writeCharCode(i);\n  }\n  Expect.equals(n, lineList.length, 'Test initialization fail #3.');\n  String lines = lineList.join();\n  String chars = charList.toString();\n  Expect.equals(n, chars.length, 'Test initialization fail #4.');\n  lineList.insert(0, '');\n  diffs = [new Diff(Operation.delete, chars)];\n  dmp.test_diff_charsToLines(diffs, lineList);\n  Expect.listEquals([new Diff(Operation.delete, lines)], diffs, 'diff_charsToLines: More than 256.');\n\n  // More than 65536 to verify any 16-bit limitation.\n  lineList = [];\n  for (int i = 0; i < 66000; i++) {\n    lineList.add('$i\\n');\n  }\n  chars = lineList.join();\n  final results = dmp.test_diff_linesToChars(chars, '');\n  diffs = [new Diff(Operation.insert, results['chars1'])];\n  dmp.test_diff_charsToLines(diffs, results['lineArray']);\n  Expect.equals(chars, diffs[0].text, 'diff_charsToLines: More than 65536.');\n}\n\nvoid testDiffCleanupMerge() {\n  // Cleanup a messy diff.\n  List<Diff> diffs = [];\n  dmp.diff_cleanupMerge(diffs);\n  Expect.listEquals([], diffs, 'diff_cleanupMerge: Null case.');\n\n  diffs = [new Diff(Operation.equal, 'a'), new Diff(Operation.delete, 'b'), new Diff(Operation.insert, 'c')];\n  dmp.diff_cleanupMerge(diffs);\n  Expect.listEquals([new Diff(Operation.equal, 'a'), new Diff(Operation.delete, 'b'), new Diff(Operation.insert, 'c')], diffs, 'diff_cleanupMerge: No change case.');\n\n  diffs = [new Diff(Operation.equal, 'a'), new Diff(Operation.equal, 'b'), new Diff(Operation.equal, 'c')];\n  dmp.diff_cleanupMerge(diffs);\n  Expect.listEquals([new Diff(Operation.equal, 'abc')], diffs, 'diff_cleanupMerge: Merge equalities.');\n\n  diffs = [new Diff(Operation.delete, 'a'), new Diff(Operation.delete, 'b'), new Diff(Operation.delete, 'c')];\n  dmp.diff_cleanupMerge(diffs);\n  Expect.listEquals([new Diff(Operation.delete, 'abc')], diffs, 'diff_cleanupMerge: Merge deletions.');\n\n  diffs = [new Diff(Operation.insert, 'a'), new Diff(Operation.insert, 'b'), new Diff(Operation.insert, 'c')];\n  dmp.diff_cleanupMerge(diffs);\n  Expect.listEquals([new Diff(Operation.insert, 'abc')], diffs, 'diff_cleanupMerge: Merge insertions.');\n\n  diffs = [new Diff(Operation.delete, 'a'), new Diff(Operation.insert, 'b'), new Diff(Operation.delete, 'c'), new Diff(Operation.insert, 'd'), new Diff(Operation.equal, 'e'), new Diff(Operation.equal, 'f')];\n  dmp.diff_cleanupMerge(diffs);\n  Expect.listEquals([new Diff(Operation.delete, 'ac'), new Diff(Operation.insert, 'bd'), new Diff(Operation.equal, 'ef')], diffs, 'diff_cleanupMerge: Merge interweave.');\n\n  diffs = [new Diff(Operation.delete, 'a'), new Diff(Operation.insert, 'abc'), new Diff(Operation.delete, 'dc')];\n  dmp.diff_cleanupMerge(diffs);\n  Expect.listEquals([new Diff(Operation.equal, 'a'), new Diff(Operation.delete, 'd'), new Diff(Operation.insert, 'b'), new Diff(Operation.equal, 'c')], diffs, 'diff_cleanupMerge: Prefix and suffix detection.');\n\n  diffs = [new Diff(Operation.equal, 'x'), new Diff(Operation.delete, 'a'), new Diff(Operation.insert, 'abc'), new Diff(Operation.delete, 'dc'), new Diff(Operation.equal, 'y')];\n  dmp.diff_cleanupMerge(diffs);\n  Expect.listEquals([new Diff(Operation.equal, 'xa'), new Diff(Operation.delete, 'd'), new Diff(Operation.insert, 'b'), new Diff(Operation.equal, 'cy')], diffs, 'diff_cleanupMerge: Prefix and suffix detection with equalities.');\n\n  diffs = [new Diff(Operation.equal, 'a'), new Diff(Operation.insert, 'ba'), new Diff(Operation.equal, 'c')];\n  dmp.diff_cleanupMerge(diffs);\n  Expect.listEquals([new Diff(Operation.insert, 'ab'), new Diff(Operation.equal, 'ac')], diffs, 'diff_cleanupMerge: Slide edit left.');\n\n  diffs = [new Diff(Operation.equal, 'c'), new Diff(Operation.insert, 'ab'), new Diff(Operation.equal, 'a')];\n  dmp.diff_cleanupMerge(diffs);\n  Expect.listEquals([new Diff(Operation.equal, 'ca'), new Diff(Operation.insert, 'ba')], diffs, 'diff_cleanupMerge: Slide edit right.');\n\n  diffs = [new Diff(Operation.equal, 'a'), new Diff(Operation.delete, 'b'), new Diff(Operation.equal, 'c'), new Diff(Operation.delete, 'ac'), new Diff(Operation.equal, 'x')];\n  dmp.diff_cleanupMerge(diffs);\n  Expect.listEquals([new Diff(Operation.delete, 'abc'), new Diff(Operation.equal, 'acx')], diffs, 'diff_cleanupMerge: Slide edit left recursive.');\n\n  diffs = [new Diff(Operation.equal, 'x'), new Diff(Operation.delete, 'ca'), new Diff(Operation.equal, 'c'), new Diff(Operation.delete, 'b'), new Diff(Operation.equal, 'a')];\n  dmp.diff_cleanupMerge(diffs);\n  Expect.listEquals([new Diff(Operation.equal, 'xca'), new Diff(Operation.delete, 'cba')], diffs, 'diff_cleanupMerge: Slide edit right recursive.');\n\n  diffs = [new Diff(Operation.delete, 'b'), new Diff(Operation.insert, 'ab'), new Diff(Operation.equal, 'c')];\n  dmp.diff_cleanupMerge(diffs);\n  Expect.listEquals([new Diff(Operation.insert, 'a'), new Diff(Operation.equal, 'bc')], diffs, 'diff_cleanupMerge: Empty merge.');\n\n  diffs = [new Diff(Operation.equal, ''), new Diff(Operation.insert, 'a'), new Diff(Operation.equal, 'b')];\n  dmp.diff_cleanupMerge(diffs);\n  Expect.listEquals([new Diff(Operation.insert, 'a'), new Diff(Operation.equal, 'b')], diffs, 'diff_cleanupMerge: Empty equality.');\n}\n\nvoid testDiffCleanupSemanticLossless() {\n  // Slide diffs to match logical boundaries.\n  List<Diff> diffs = [];\n  dmp.test_diff_cleanupSemanticLossless(diffs);\n  Expect.listEquals([], diffs, 'diff_cleanupSemanticLossless: Null case.');\n\n  diffs = [new Diff(Operation.equal, 'AAA\\r\\n\\r\\nBBB'), new Diff(Operation.insert, '\\r\\nDDD\\r\\n\\r\\nBBB'), new Diff(Operation.equal, '\\r\\nEEE')];\n  dmp.test_diff_cleanupSemanticLossless(diffs);\n  Expect.listEquals([new Diff(Operation.equal, 'AAA\\r\\n\\r\\n'), new Diff(Operation.insert, 'BBB\\r\\nDDD\\r\\n\\r\\n'), new Diff(Operation.equal, 'BBB\\r\\nEEE')], diffs, 'diff_cleanupSemanticLossless: Blank lines.');\n\n  diffs = [new Diff(Operation.equal, 'AAA\\r\\nBBB'), new Diff(Operation.insert, ' DDD\\r\\nBBB'), new Diff(Operation.equal, ' EEE')];\n  dmp.test_diff_cleanupSemanticLossless(diffs);\n  Expect.listEquals([new Diff(Operation.equal, 'AAA\\r\\n'), new Diff(Operation.insert, 'BBB DDD\\r\\n'), new Diff(Operation.equal, 'BBB EEE')], diffs, 'diff_cleanupSemanticLossless: Line boundaries.');\n\n  diffs = [new Diff(Operation.equal, 'The c'), new Diff(Operation.insert, 'ow and the c'), new Diff(Operation.equal, 'at.')];\n  dmp.test_diff_cleanupSemanticLossless(diffs);\n  Expect.listEquals([new Diff(Operation.equal, 'The '), new Diff(Operation.insert, 'cow and the '), new Diff(Operation.equal, 'cat.')], diffs, 'diff_cleanupSemanticLossless: Word boundaries.');\n\n  diffs = [new Diff(Operation.equal, 'The-c'), new Diff(Operation.insert, 'ow-and-the-c'), new Diff(Operation.equal, 'at.')];\n  dmp.test_diff_cleanupSemanticLossless(diffs);\n  Expect.listEquals([new Diff(Operation.equal, 'The-'), new Diff(Operation.insert, 'cow-and-the-'), new Diff(Operation.equal, 'cat.')], diffs, 'diff_cleanupSemanticLossless: Alphanumeric boundaries.');\n\n  diffs = [new Diff(Operation.equal, 'a'), new Diff(Operation.delete, 'a'), new Diff(Operation.equal, 'ax')];\n  dmp.test_diff_cleanupSemanticLossless(diffs);\n  Expect.listEquals([new Diff(Operation.delete, 'a'), new Diff(Operation.equal, 'aax')], diffs, 'diff_cleanupSemanticLossless: Hitting the start.');\n\n  diffs = [new Diff(Operation.equal, 'xa'), new Diff(Operation.delete, 'a'), new Diff(Operation.equal, 'a')];\n  dmp.test_diff_cleanupSemanticLossless(diffs);\n  Expect.listEquals([new Diff(Operation.equal, 'xaa'), new Diff(Operation.delete, 'a')], diffs, 'diff_cleanupSemanticLossless: Hitting the end.');\n\n  diffs = [new Diff(Operation.equal, 'The xxx. The '), new Diff(Operation.insert, 'zzz. The '), new Diff(Operation.equal, 'yyy.')];\n  dmp.test_diff_cleanupSemanticLossless(diffs);\n  Expect.listEquals([new Diff(Operation.equal, 'The xxx.'), new Diff(Operation.insert, ' The zzz.'), new Diff(Operation.equal, ' The yyy.')], diffs, 'diff_cleanupSemanticLossless: Sentence boundaries.');\n}\n\nvoid testDiffCleanupSemantic() {\n  // Cleanup semantically trivial equalities.\n  List<Diff> diffs = [];\n  dmp.diff_cleanupSemantic(diffs);\n  Expect.listEquals([], diffs, 'diff_cleanupSemantic: Null case.');\n\n  diffs = [new Diff(Operation.delete, 'ab'), new Diff(Operation.insert, 'cd'), new Diff(Operation.equal, '12'), new Diff(Operation.delete, 'e')];\n  dmp.diff_cleanupSemantic(diffs);\n  Expect.listEquals([new Diff(Operation.delete, 'ab'), new Diff(Operation.insert, 'cd'), new Diff(Operation.equal, '12'), new Diff(Operation.delete, 'e')], diffs, 'diff_cleanupSemantic: No elimination #1.');\n\n  diffs = [new Diff(Operation.delete, 'abc'), new Diff(Operation.insert, 'ABC'), new Diff(Operation.equal, '1234'), new Diff(Operation.delete, 'wxyz')];\n  dmp.diff_cleanupSemantic(diffs);\n  Expect.listEquals([new Diff(Operation.delete, 'abc'), new Diff(Operation.insert, 'ABC'), new Diff(Operation.equal, '1234'), new Diff(Operation.delete, 'wxyz')], diffs, 'diff_cleanupSemantic: No elimination #2.');\n\n  diffs = [new Diff(Operation.delete, 'a'), new Diff(Operation.equal, 'b'), new Diff(Operation.delete, 'c')];\n  dmp.diff_cleanupSemantic(diffs);\n  Expect.listEquals([new Diff(Operation.delete, 'abc'), new Diff(Operation.insert, 'b')], diffs, 'diff_cleanupSemantic: Simple elimination.');\n\n  diffs = [new Diff(Operation.delete, 'ab'), new Diff(Operation.equal, 'cd'), new Diff(Operation.delete, 'e'), new Diff(Operation.equal, 'f'), new Diff(Operation.insert, 'g')];\n  dmp.diff_cleanupSemantic(diffs);\n  Expect.listEquals([new Diff(Operation.delete, 'abcdef'), new Diff(Operation.insert, 'cdfg')], diffs, 'diff_cleanupSemantic: Backpass elimination.');\n\n  diffs = [new Diff(Operation.insert, '1'), new Diff(Operation.equal, 'A'), new Diff(Operation.delete, 'B'), new Diff(Operation.insert, '2'), new Diff(Operation.equal, '_'), new Diff(Operation.insert, '1'), new Diff(Operation.equal, 'A'), new Diff(Operation.delete, 'B'), new Diff(Operation.insert, '2')];\n  dmp.diff_cleanupSemantic(diffs);\n  Expect.listEquals([new Diff(Operation.delete, 'AB_AB'), new Diff(Operation.insert, '1A2_1A2')], diffs, 'diff_cleanupSemantic: Multiple elimination.');\n\n  diffs = [new Diff(Operation.equal, 'The c'), new Diff(Operation.delete, 'ow and the c'), new Diff(Operation.equal, 'at.')];\n  dmp.diff_cleanupSemantic(diffs);\n  Expect.listEquals([new Diff(Operation.equal, 'The '), new Diff(Operation.delete, 'cow and the '), new Diff(Operation.equal, 'cat.')], diffs, 'diff_cleanupSemantic: Word boundaries.');\n\n  diffs = [new Diff(Operation.delete, 'abcxx'), new Diff(Operation.insert, 'xxdef')];\n  dmp.diff_cleanupSemantic(diffs);\n  Expect.listEquals([new Diff(Operation.delete, 'abcxx'), new Diff(Operation.insert, 'xxdef')], diffs, 'diff_cleanupSemantic: No overlap elimination.');\n\n  diffs = [new Diff(Operation.delete, 'abcxxx'), new Diff(Operation.insert, 'xxxdef')];\n  dmp.diff_cleanupSemantic(diffs);\n  Expect.listEquals([new Diff(Operation.delete, 'abc'), new Diff(Operation.equal, 'xxx'), new Diff(Operation.insert, 'def')], diffs, 'diff_cleanupSemantic: Overlap elimination.');\n\n  diffs = [new Diff(Operation.delete, 'xxxabc'), new Diff(Operation.insert, 'defxxx')];\n  dmp.diff_cleanupSemantic(diffs);\n  Expect.listEquals([new Diff(Operation.insert, 'def'), new Diff(Operation.equal, 'xxx'), new Diff(Operation.delete, 'abc')], diffs, 'diff_cleanupSemantic: Reverse overlap elimination.');\n\n  diffs = [new Diff(Operation.delete, 'abcd1212'), new Diff(Operation.insert, '1212efghi'), new Diff(Operation.equal, '----'), new Diff(Operation.delete, 'A3'), new Diff(Operation.insert, '3BC')];\n  dmp.diff_cleanupSemantic(diffs);\n  Expect.listEquals([new Diff(Operation.delete, 'abcd'), new Diff(Operation.equal, '1212'), new Diff(Operation.insert, 'efghi'), new Diff(Operation.equal, '----'), new Diff(Operation.delete, 'A'), new Diff(Operation.equal, '3'), new Diff(Operation.insert, 'BC')], diffs, 'diff_cleanupSemantic: Two overlap eliminations.');\n}\n\nvoid testDiffCleanupEfficiency() {\n  // Cleanup operationally trivial equalities.\n  dmp.Diff_EditCost = 4;\n  List<Diff> diffs = [];\n  dmp.diff_cleanupEfficiency(diffs);\n  Expect.listEquals([], diffs, 'diff_cleanupEfficiency: Null case.');\n\n  diffs = [new Diff(Operation.delete, 'ab'), new Diff(Operation.insert, '12'), new Diff(Operation.equal, 'wxyz'), new Diff(Operation.delete, 'cd'), new Diff(Operation.insert, '34')];\n  dmp.diff_cleanupEfficiency(diffs);\n  Expect.listEquals([new Diff(Operation.delete, 'ab'), new Diff(Operation.insert, '12'), new Diff(Operation.equal, 'wxyz'), new Diff(Operation.delete, 'cd'), new Diff(Operation.insert, '34')], diffs, 'diff_cleanupEfficiency: No elimination.');\n\n  diffs = [new Diff(Operation.delete, 'ab'), new Diff(Operation.insert, '12'), new Diff(Operation.equal, 'xyz'), new Diff(Operation.delete, 'cd'), new Diff(Operation.insert, '34')];\n  dmp.diff_cleanupEfficiency(diffs);\n  Expect.listEquals([new Diff(Operation.delete, 'abxyzcd'), new Diff(Operation.insert, '12xyz34')], diffs, 'diff_cleanupEfficiency: Four-edit elimination.');\n\n  diffs = [new Diff(Operation.insert, '12'), new Diff(Operation.equal, 'x'), new Diff(Operation.delete, 'cd'), new Diff(Operation.insert, '34')];\n  dmp.diff_cleanupEfficiency(diffs);\n  Expect.listEquals([new Diff(Operation.delete, 'xcd'), new Diff(Operation.insert, '12x34')], diffs, 'diff_cleanupEfficiency: Three-edit elimination.');\n\n  diffs = [new Diff(Operation.delete, 'ab'), new Diff(Operation.insert, '12'), new Diff(Operation.equal, 'xy'), new Diff(Operation.insert, '34'), new Diff(Operation.equal, 'z'), new Diff(Operation.delete, 'cd'), new Diff(Operation.insert, '56')];\n  dmp.diff_cleanupEfficiency(diffs);\n  Expect.listEquals([new Diff(Operation.delete, 'abxyzcd'), new Diff(Operation.insert, '12xy34z56')], diffs, 'diff_cleanupEfficiency: Backpass elimination.');\n\n  dmp.Diff_EditCost = 5;\n  diffs = [new Diff(Operation.delete, 'ab'), new Diff(Operation.insert, '12'), new Diff(Operation.equal, 'wxyz'), new Diff(Operation.delete, 'cd'), new Diff(Operation.insert, '34')];\n  dmp.diff_cleanupEfficiency(diffs);\n  Expect.listEquals([new Diff(Operation.delete, 'abwxyzcd'), new Diff(Operation.insert, '12wxyz34')], diffs, 'diff_cleanupEfficiency: High cost elimination.');\n  dmp.Diff_EditCost = 4;\n}\n\nvoid testDiffPrettyHtml() {\n  // Pretty print.\n  List<Diff> diffs = [new Diff(Operation.equal, 'a\\n'), new Diff(Operation.delete, '<B>b</B>'), new Diff(Operation.insert, 'c&d')];\n  Expect.equals('<span>a&para;<br></span><del style=\"background:#ffe6e6;\">&lt;B&gt;b&lt;/B&gt;</del><ins style=\"background:#e6ffe6;\">c&amp;d</ins>', dmp.diff_prettyHtml(diffs), 'diff_prettyHtml:');\n}\n\nvoid testDiffText() {\n  // Compute the source and destination texts.\n  List<Diff> diffs = [new Diff(Operation.equal, 'jump'), new Diff(Operation.delete, 's'), new Diff(Operation.insert, 'ed'), new Diff(Operation.equal, ' over '), new Diff(Operation.delete, 'the'), new Diff(Operation.insert, 'a'), new Diff(Operation.equal, ' lazy')];\n  Expect.equals('jumps over the lazy', dmp.diff_text1(diffs), 'diff_text1:');\n  Expect.equals('jumped over a lazy', dmp.diff_text2(diffs), 'diff_text2:');\n}\n\nvoid testDiffDelta() {\n  // Convert a diff into delta string.\n  List<Diff> diffs = [new Diff(Operation.equal, 'jump'), new Diff(Operation.delete, 's'), new Diff(Operation.insert, 'ed'), new Diff(Operation.equal, ' over '), new Diff(Operation.delete, 'the'), new Diff(Operation.insert, 'a'), new Diff(Operation.equal, ' lazy'), new Diff(Operation.insert, 'old dog')];\n  String text1 = dmp.diff_text1(diffs);\n  Expect.equals('jumps over the lazy', text1, 'diff_text1: Base text.');\n\n  String delta = dmp.diff_toDelta(diffs);\n  Expect.equals('=4\\t-1\\t+ed\\t=6\\t-3\\t+a\\t=5\\t+old dog', delta, 'diff_toDelta:');\n\n  // Convert delta string into a diff.\n  Expect.listEquals(diffs, dmp.diff_fromDelta(text1, delta), 'diff_fromDelta: Normal.');\n\n  // Generates error (19 < 20).\n  Expect.throws(() => dmp.diff_fromDelta(text1 + 'x', delta), 'diff_fromDelta: Too long.');\n\n  // Generates error (19 > 18).\n  Expect.throws(() => dmp.diff_fromDelta(text1.substring(1), delta), 'diff_fromDelta: Too short.');\n\n  // Generates error (%c3%xy invalid Unicode).\n  Expect.throws(() => dmp.diff_fromDelta('', '+%c3%xy'), 'diff_fromDelta: Invalid character.');\n\n  // Test deltas with special characters.\n  diffs = [new Diff(Operation.equal, '\\u0680 \\x00 \\t %'), new Diff(Operation.delete, '\\u0681 \\x01 \\n ^'), new Diff(Operation.insert, '\\u0682 \\x02 \\\\ |')];\n  text1 = dmp.diff_text1(diffs);\n  Expect.equals('\\u0680 \\x00 \\t %\\u0681 \\x01 \\n ^', text1, 'diff_text1: Unicode text.');\n\n  delta = dmp.diff_toDelta(diffs);\n  Expect.equals('=7\\t-7\\t+%DA%82 %02 %5C %7C', delta, 'diff_toDelta: Unicode.');\n\n  Expect.listEquals(diffs, dmp.diff_fromDelta(text1, delta), 'diff_fromDelta: Unicode.');\n\n  // Verify pool of unchanged characters.\n  diffs = [new Diff(Operation.insert, 'A-Z a-z 0-9 - _ . ! ~ * \\' ( ) ; / ? : @ & = + \\$ , # ')];\n  String text2 = dmp.diff_text2(diffs);\n  Expect.equals('A-Z a-z 0-9 - _ . ! ~ * \\' ( ) ; / ? : @ & = + \\$ , # ', text2, 'diff_text2: Unchanged characters.');\n\n  delta = dmp.diff_toDelta(diffs);\n  Expect.equals('+A-Z a-z 0-9 - _ . ! ~ * \\' ( ) ; / ? : @ & = + \\$ , # ', delta, 'diff_toDelta: Unchanged characters.');\n\n  // Convert delta string into a diff.\n  Expect.listEquals(diffs, dmp.diff_fromDelta('', delta), 'diff_fromDelta: Unchanged characters.');\n\n  // 160 kb string.\n  var a = 'abcdefghij';\n  for (var i = 0; i < 14; i++) {\n    a += a;\n  }\n  diffs = [new Diff(Operation.insert, a)];\n  delta = dmp.diff_toDelta(diffs);\n  Expect.equals('+' + a, delta, 'diff_toDelta: 160kb string.');\n\n  // Convert delta string into a diff.\n  Expect.listEquals(diffs, dmp.diff_fromDelta('', delta), 'diff_fromDelta: 160kb string.');\n}\n\nvoid testDiffXIndex() {\n  // Translate a location in text1 to text2.\n  List<Diff> diffs = [new Diff(Operation.delete, 'a'), new Diff(Operation.insert, '1234'), new Diff(Operation.equal, 'xyz')];\n  Expect.equals(5, dmp.diff_xIndex(diffs, 2), 'diff_xIndex: Translation on equality.');\n\n  diffs = [new Diff(Operation.equal, 'a'), new Diff(Operation.delete, '1234'), new Diff(Operation.equal, 'xyz')];\n  Expect.equals(1, dmp.diff_xIndex(diffs, 3), 'diff_xIndex: Translation on deletion.');\n}\n\nvoid testDiffLevenshtein() {\n  List<Diff> diffs = [new Diff(Operation.delete, 'abc'), new Diff(Operation.insert, '1234'), new Diff(Operation.equal, 'xyz')];\n  Expect.equals(4, dmp.diff_levenshtein(diffs), 'Levenshtein with trailing equality.');\n\n  diffs = [new Diff(Operation.equal, 'xyz'), new Diff(Operation.delete, 'abc'), new Diff(Operation.insert, '1234')];\n  Expect.equals(4, dmp.diff_levenshtein(diffs), 'Levenshtein with leading equality.');\n\n  diffs = [new Diff(Operation.delete, 'abc'), new Diff(Operation.equal, 'xyz'), new Diff(Operation.insert, '1234')];\n  Expect.equals(7, dmp.diff_levenshtein(diffs), 'Levenshtein with middle equality.');\n}\n\nvoid testDiffBisect() {\n  // Normal.\n  String a = 'cat';\n  String b = 'map';\n  // Since the resulting diff hasn't been normalized, it would be ok if\n  // the insertion and deletion pairs are swapped.\n  // If the order changes, tweak this test as required.\n  List<Diff> diffs = [new Diff(Operation.delete, 'c'), new Diff(Operation.insert, 'm'), new Diff(Operation.equal, 'a'), new Diff(Operation.delete, 't'), new Diff(Operation.insert, 'p')];\n  // One year should be sufficient.\n  DateTime deadline = new DateTime.now().add(new Duration(days : 365));\n  Expect.listEquals(diffs, dmp.test_diff_bisect(a, b, deadline), 'diff_bisect: Normal.');\n\n  // Timeout.\n  diffs = [new Diff(Operation.delete, 'cat'), new Diff(Operation.insert, 'map')];\n  // Set deadline to one year ago.\n  deadline = new DateTime.now().subtract(new Duration(days : 365));\n  Expect.listEquals(diffs, dmp.test_diff_bisect(a, b, deadline), 'diff_bisect: Timeout.');\n}\n\nvoid testDiffMain() {\n  // Perform a trivial diff.\n  List<Diff> diffs = [];\n  Expect.listEquals(diffs, dmp.diff_main('', '', false), 'diff_main: Null case.');\n\n  diffs = [new Diff(Operation.equal, 'abc')];\n  Expect.listEquals(diffs, dmp.diff_main('abc', 'abc', false), 'diff_main: Equality.');\n\n  diffs = [new Diff(Operation.equal, 'ab'), new Diff(Operation.insert, '123'), new Diff(Operation.equal, 'c')];\n  Expect.listEquals(diffs, dmp.diff_main('abc', 'ab123c', false), 'diff_main: Simple insertion.');\n\n  diffs = [new Diff(Operation.equal, 'a'), new Diff(Operation.delete, '123'), new Diff(Operation.equal, 'bc')];\n  Expect.listEquals(diffs, dmp.diff_main('a123bc', 'abc', false), 'diff_main: Simple deletion.');\n\n  diffs = [new Diff(Operation.equal, 'a'), new Diff(Operation.insert, '123'), new Diff(Operation.equal, 'b'), new Diff(Operation.insert, '456'), new Diff(Operation.equal, 'c')];\n  Expect.listEquals(diffs, dmp.diff_main('abc', 'a123b456c', false), 'diff_main: Two insertions.');\n\n  diffs = [new Diff(Operation.equal, 'a'), new Diff(Operation.delete, '123'), new Diff(Operation.equal, 'b'), new Diff(Operation.delete, '456'), new Diff(Operation.equal, 'c')];\n  Expect.listEquals(diffs, dmp.diff_main('a123b456c', 'abc', false), 'diff_main: Two deletions.');\n\n  // Perform a real diff.\n  // Switch off the timeout.\n  dmp.Diff_Timeout = 0.0;\n  diffs = [new Diff(Operation.delete, 'a'), new Diff(Operation.insert, 'b')];\n  Expect.listEquals(diffs, dmp.diff_main('a', 'b', false), 'diff_main: Simple case #1.');\n\n  diffs = [new Diff(Operation.delete, 'Apple'), new Diff(Operation.insert, 'Banana'), new Diff(Operation.equal, 's are a'), new Diff(Operation.insert, 'lso'), new Diff(Operation.equal, ' fruit.')];\n  Expect.listEquals(diffs, dmp.diff_main('Apples are a fruit.', 'Bananas are also fruit.', false), 'diff_main: Simple case #2.');\n\n  diffs = [new Diff(Operation.delete, 'a'), new Diff(Operation.insert, '\\u0680'), new Diff(Operation.equal, 'x'), new Diff(Operation.delete, '\\t'), new Diff(Operation.insert, '\\000')];\n  Expect.listEquals(diffs, dmp.diff_main('ax\\t', '\\u0680x\\000', false), 'diff_main: Simple case #3.');\n\n  diffs = [new Diff(Operation.delete, '1'), new Diff(Operation.equal, 'a'), new Diff(Operation.delete, 'y'), new Diff(Operation.equal, 'b'), new Diff(Operation.delete, '2'), new Diff(Operation.insert, 'xab')];\n  Expect.listEquals(diffs, dmp.diff_main('1ayb2', 'abxab', false), 'diff_main: Overlap #1.');\n\n  diffs = [new Diff(Operation.insert, 'xaxcx'), new Diff(Operation.equal, 'abc'), new Diff(Operation.delete, 'y')];\n  Expect.listEquals(diffs, dmp.diff_main('abcy', 'xaxcxabc', false), 'diff_main: Overlap #2.');\n\n  diffs = [new Diff(Operation.delete, 'ABCD'), new Diff(Operation.equal, 'a'), new Diff(Operation.delete, '='), new Diff(Operation.insert, '-'), new Diff(Operation.equal, 'bcd'), new Diff(Operation.delete, '='), new Diff(Operation.insert, '-'), new Diff(Operation.equal, 'efghijklmnopqrs'), new Diff(Operation.delete, 'EFGHIJKLMNOefg')];\n  Expect.listEquals(diffs, dmp.diff_main('ABCDa=bcd=efghijklmnopqrsEFGHIJKLMNOefg', 'a-bcd-efghijklmnopqrs', false), 'diff_main: Overlap #3.');\n\n  diffs = [new Diff(Operation.insert, ' '), new Diff(Operation.equal, 'a'), new Diff(Operation.insert, 'nd'), new Diff(Operation.equal, ' [[Pennsylvania]]'), new Diff(Operation.delete, ' and [[New')];\n  Expect.listEquals(diffs, dmp.diff_main('a [[Pennsylvania]] and [[New', ' and [[Pennsylvania]]', false), 'diff_main: Large equality.');\n\n  dmp.Diff_Timeout = 0.1;  // 100ms\n  String a = '`Twas brillig, and the slithy toves\\nDid gyre and gimble in the wabe:\\nAll mimsy were the borogoves,\\nAnd the mome raths outgrabe.\\n';\n  String b = 'I am the very model of a modern major general,\\nI\\'ve information vegetable, animal, and mineral,\\nI know the kings of England, and I quote the fights historical,\\nFrom Marathon to Waterloo, in order categorical.\\n';\n  // Increase the text lengths by 1024 times to ensure a timeout.\n  for (int i = 0; i < 10; i++) {\n    a += a;\n    b += b;\n  }\n  DateTime startTime = new DateTime.now();\n  dmp.diff_main(a, b);\n  DateTime endTime = new DateTime.now();\n  double elapsedSeconds = endTime.difference(startTime).inMilliseconds / 1000;\n  // Test that we took at least the timeout period.\n  Expect.isTrue(dmp.Diff_Timeout <= elapsedSeconds, 'diff_main: Timeout min.');\n  // Test that we didn't take forever (be forgiving).\n  // Theoretically this test could fail very occasionally if the\n  // OS task swaps or locks up for a second at the wrong moment.\n  Expect.isTrue(dmp.Diff_Timeout * 2 > elapsedSeconds, 'diff_main: Timeout max.');\n  dmp.Diff_Timeout = 0.0;\n\n  // Test the linemode speedup.\n  // Must be long to pass the 100 char cutoff.\n  a = '1234567890\\n1234567890\\n1234567890\\n1234567890\\n1234567890\\n1234567890\\n1234567890\\n1234567890\\n1234567890\\n1234567890\\n1234567890\\n1234567890\\n1234567890\\n';\n  b = 'abcdefghij\\nabcdefghij\\nabcdefghij\\nabcdefghij\\nabcdefghij\\nabcdefghij\\nabcdefghij\\nabcdefghij\\nabcdefghij\\nabcdefghij\\nabcdefghij\\nabcdefghij\\nabcdefghij\\n';\n  Expect.listEquals(dmp.diff_main(a, b, true), dmp.diff_main(a, b, false), 'diff_main: Simple line-mode.');\n\n  a = '1234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890';\n  b = 'abcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghij';\n  Expect.listEquals(dmp.diff_main(a, b, true), dmp.diff_main(a, b, false), 'diff_main: Single line-mode.');\n\n  a = '1234567890\\n1234567890\\n1234567890\\n1234567890\\n1234567890\\n1234567890\\n1234567890\\n1234567890\\n1234567890\\n1234567890\\n1234567890\\n1234567890\\n1234567890\\n';\n  b = 'abcdefghij\\n1234567890\\n1234567890\\n1234567890\\nabcdefghij\\n1234567890\\n1234567890\\n1234567890\\nabcdefghij\\n1234567890\\n1234567890\\n1234567890\\nabcdefghij\\n';\n  List<String> texts_linemode = _diff_rebuildtexts(dmp.diff_main(a, b, true));\n  List<String> texts_textmode = _diff_rebuildtexts(dmp.diff_main(a, b, false));\n  Expect.listEquals(texts_textmode, texts_linemode, 'diff_main: Overlap line-mode.');\n\n  // Test null inputs.\n  Expect.throws(() => dmp.diff_main(null, null), 'diff_main: Null inputs.');\n}\n\n\n//  MATCH TEST FUNCTIONS\n\nvoid testMatchAlphabet() {\n  // Initialise the bitmasks for Bitap.\n  Map<String, int> bitmask = {'a': 4, 'b': 2, 'c': 1};\n  Expect.mapEquals(bitmask, dmp.test_match_alphabet('abc'), 'match_alphabet: Unique.');\n\n  bitmask = {'a': 37, 'b': 18, 'c': 8};\n  Expect.mapEquals(bitmask, dmp.test_match_alphabet('abcaba'), 'match_alphabet: Duplicates.');\n}\n\nvoid testMatchBitap() {\n  // Bitap algorithm.\n  dmp.Match_Distance = 100;\n  dmp.Match_Threshold = 0.5;\n  Expect.equals(5, dmp.test_match_bitap('abcdefghijk', 'fgh', 5), 'match_bitap: Exact match #1.');\n\n  Expect.equals(5, dmp.test_match_bitap('abcdefghijk', 'fgh', 0), 'match_bitap: Exact match #2.');\n\n  Expect.equals(4, dmp.test_match_bitap('abcdefghijk', 'efxhi', 0), 'match_bitap: Fuzzy match #1.');\n\n  Expect.equals(2, dmp.test_match_bitap('abcdefghijk', 'cdefxyhijk', 5), 'match_bitap: Fuzzy match #2.');\n\n  Expect.equals(-1, dmp.test_match_bitap('abcdefghijk', 'bxy', 1), 'match_bitap: Fuzzy match #3.');\n\n  Expect.equals(2, dmp.test_match_bitap('123456789xx0', '3456789x0', 2), 'match_bitap: Overflow.');\n\n  Expect.equals(0, dmp.test_match_bitap('abcdef', 'xxabc', 4), 'match_bitap: Before start match.');\n\n  Expect.equals(3, dmp.test_match_bitap('abcdef', 'defyy', 4), 'match_bitap: Beyond end match.');\n\n  Expect.equals(0, dmp.test_match_bitap('abcdef', 'xabcdefy', 0), 'match_bitap: Oversized pattern.');\n\n  dmp.Match_Threshold = 0.4;\n  Expect.equals(4, dmp.test_match_bitap('abcdefghijk', 'efxyhi', 1), 'match_bitap: Threshold #1.');\n\n  dmp.Match_Threshold = 0.3;\n  Expect.equals(-1, dmp.test_match_bitap('abcdefghijk', 'efxyhi', 1), 'match_bitap: Threshold #2.');\n\n  dmp.Match_Threshold = 0.0;\n  Expect.equals(1, dmp.test_match_bitap('abcdefghijk', 'bcdef', 1), 'match_bitap: Threshold #3.');\n\n  dmp.Match_Threshold = 0.5;\n  Expect.equals(0, dmp.test_match_bitap('abcdexyzabcde', 'abccde', 3), 'match_bitap: Multiple select #1.');\n\n  Expect.equals(8, dmp.test_match_bitap('abcdexyzabcde', 'abccde', 5), 'match_bitap: Multiple select #2.');\n\n  dmp.Match_Distance = 10;  // Strict location.\n  Expect.equals(-1, dmp.test_match_bitap('abcdefghijklmnopqrstuvwxyz', 'abcdefg', 24), 'match_bitap: Distance test #1.');\n\n  Expect.equals(0, dmp.test_match_bitap('abcdefghijklmnopqrstuvwxyz', 'abcdxxefg', 1), 'match_bitap: Distance test #2.');\n\n  dmp.Match_Distance = 1000;  // Loose location.\n  Expect.equals(0, dmp.test_match_bitap('abcdefghijklmnopqrstuvwxyz', 'abcdefg', 24), 'match_bitap: Distance test #3.');\n}\n\nvoid testMatchMain() {\n  // Full match.\n  Expect.equals(0, dmp.match_main('abcdef', 'abcdef', 1000), 'match_main: Equality.');\n\n  Expect.equals(-1, dmp.match_main('', 'abcdef', 1), 'match_main: Null text.');\n\n  Expect.equals(3, dmp.match_main('abcdef', '', 3), 'match_main: Null pattern.');\n\n  Expect.equals(3, dmp.match_main('abcdef', 'de', 3), 'match_main: Exact match.');\n\n  Expect.equals(3, dmp.match_main('abcdef', 'defy', 4), 'match_main: Beyond end match.');\n\n  Expect.equals(0, dmp.match_main('abcdef', 'abcdefy', 0), 'match_main: Oversized pattern.');\n\n  dmp.Match_Threshold = 0.7;\n  Expect.equals(4, dmp.match_main('I am the very model of a modern major general.', ' that berry ', 5), 'match_main: Complex match.');\n  dmp.Match_Threshold = 0.5;\n\n  // Test null inputs.\n  Expect.throws(() => dmp.match_main(null, null, 0), 'match_main: Null inputs.');\n}\n\n\n//  PATCH TEST FUNCTIONS\n\n\nvoid testPatchObj() {\n  // Patch Object.\n  Patch p = new Patch();\n  p.start1 = 20;\n  p.start2 = 21;\n  p.length1 = 18;\n  p.length2 = 17;\n  p.diffs = [new Diff(Operation.equal, 'jump'), new Diff(Operation.delete, 's'), new Diff(Operation.insert, 'ed'), new Diff(Operation.equal, ' over '), new Diff(Operation.delete, 'the'), new Diff(Operation.insert, 'a'), new Diff(Operation.equal, '\\nlaz')];\n  String strp = '@@ -21,18 +22,17 @@\\n jump\\n-s\\n+ed\\n  over \\n-the\\n+a\\n %0Alaz\\n';\n  Expect.equals(strp, p.toString(), 'Patch: toString.');\n}\n\nvoid testPatchFromText() {\n  Expect.isTrue(dmp.patch_fromText('').isEmpty, 'patch_fromText: #0.');\n\n  String strp = '@@ -21,18 +22,17 @@\\n jump\\n-s\\n+ed\\n  over \\n-the\\n+a\\n %0Alaz\\n';\n  Expect.equals(strp, dmp.patch_fromText(strp)[0].toString(), 'patch_fromText: #1.');\n\n  Expect.equals('@@ -1 +1 @@\\n-a\\n+b\\n', dmp.patch_fromText('@@ -1 +1 @@\\n-a\\n+b\\n')[0].toString(), 'patch_fromText: #2.');\n\n  Expect.equals('@@ -1,3 +0,0 @@\\n-abc\\n', dmp.patch_fromText('@@ -1,3 +0,0 @@\\n-abc\\n')[0].toString(), 'patch_fromText: #3.');\n\n  Expect.equals('@@ -0,0 +1,3 @@\\n+abc\\n', dmp.patch_fromText('@@ -0,0 +1,3 @@\\n+abc\\n')[0].toString(), 'patch_fromText: #4.');\n\n  // Generates error.\n  Expect.throws(() => dmp.patch_fromText('Bad\\nPatch\\n'), 'patch_fromText: #5.');\n}\n\nvoid testPatchToText() {\n  String strp = '@@ -21,18 +22,17 @@\\n jump\\n-s\\n+ed\\n  over \\n-the\\n+a\\n  laz\\n';\n  List<Patch> patches;\n  patches = dmp.patch_fromText(strp);\n  Expect.equals(strp, dmp.patch_toText(patches), 'patch_toText: Single.');\n\n  strp = '@@ -1,9 +1,9 @@\\n-f\\n+F\\n oo+fooba\\n@@ -7,9 +7,9 @@\\n obar\\n-,\\n+.\\n  tes\\n';\n  patches = dmp.patch_fromText(strp);\n  Expect.equals(strp, dmp.patch_toText(patches), 'patch_toText: Dual.');\n}\n\nvoid testPatchAddContext() {\n  dmp.Patch_Margin = 4;\n  Patch p;\n  p = dmp.patch_fromText('@@ -21,4 +21,10 @@\\n-jump\\n+somersault\\n')[0];\n  dmp.test_patch_addContext(p, 'The quick brown fox jumps over the lazy dog.');\n  Expect.equals('@@ -17,12 +17,18 @@\\n fox \\n-jump\\n+somersault\\n s ov\\n', p.toString(), 'patch_addContext: Simple case.');\n\n  p = dmp.patch_fromText('@@ -21,4 +21,10 @@\\n-jump\\n+somersault\\n')[0];\n  dmp.test_patch_addContext(p, 'The quick brown fox jumps.');\n  Expect.equals('@@ -17,10 +17,16 @@\\n fox \\n-jump\\n+somersault\\n s.\\n', p.toString(), 'patch_addContext: Not enough trailing context.');\n\n  p = dmp.patch_fromText('@@ -3 +3,2 @@\\n-e\\n+at\\n')[0];\n  dmp.test_patch_addContext(p, 'The quick brown fox jumps.');\n  Expect.equals('@@ -1,7 +1,8 @@\\n Th\\n-e\\n+at\\n  qui\\n', p.toString(), 'patch_addContext: Not enough leading context.');\n\n  p = dmp.patch_fromText('@@ -3 +3,2 @@\\n-e\\n+at\\n')[0];\n  dmp.test_patch_addContext(p, 'The quick brown fox jumps.  The quick brown fox crashes.');\n  Expect.equals('@@ -1,27 +1,28 @@\\n Th\\n-e\\n+at\\n  quick brown fox jumps. \\n', p.toString(), 'patch_addContext: Ambiguity.');\n}\n\nvoid testPatchMake() {\n  List<Patch> patches;\n  patches = dmp.patch_make('', '');\n  Expect.equals('', dmp.patch_toText(patches), 'patch_make: Null case.');\n\n  String text1 = 'The quick brown fox jumps over the lazy dog.';\n  String text2 = 'That quick brown fox jumped over a lazy dog.';\n  String expectedPatch = '@@ -1,8 +1,7 @@\\n Th\\n-at\\n+e\\n  qui\\n@@ -21,17 +21,18 @@\\n jump\\n-ed\\n+s\\n  over \\n-a\\n+the\\n  laz\\n';\n  // The second patch must be '-21,17 +21,18', not '-22,17 +21,18' due to rolling context.\n  patches = dmp.patch_make(text2, text1);\n  Expect.equals(expectedPatch, dmp.patch_toText(patches), 'patch_make: Text2+Text1 inputs.');\n\n  expectedPatch = '@@ -1,11 +1,12 @@\\n Th\\n-e\\n+at\\n  quick b\\n@@ -22,18 +22,17 @@\\n jump\\n-s\\n+ed\\n  over \\n-the\\n+a\\n  laz\\n';\n  patches = dmp.patch_make(text1, text2);\n  Expect.equals(expectedPatch, dmp.patch_toText(patches), 'patch_make: Text1+Text2 inputs.');\n\n  List<Diff> diffs = dmp.diff_main(text1, text2, false);\n  patches = dmp.patch_make(diffs);\n  Expect.equals(expectedPatch, dmp.patch_toText(patches), 'patch_make: Diff input.');\n\n  patches = dmp.patch_make(text1, diffs);\n  Expect.equals(expectedPatch, dmp.patch_toText(patches), 'patch_make: Text1+Diff inputs.');\n\n  patches = dmp.patch_make(text1, text2, diffs);\n  Expect.equals(expectedPatch, dmp.patch_toText(patches), 'patch_make: Text1+Text2+Diff inputs (deprecated).');\n\n  patches = dmp.patch_make('`1234567890-=[]\\\\;\\',./', '~!@#\\$%^&*()_+{}|:\"<>?');\n  Expect.equals('@@ -1,21 +1,21 @@\\n-%601234567890-=%5B%5D%5C;\\',./\\n+~!@#\\$%25%5E&*()_+%7B%7D%7C:%22%3C%3E?\\n', dmp.patch_toText(patches), 'patch_toText: Character encoding.');\n\n  diffs = [new Diff(Operation.delete, '`1234567890-=[]\\\\;\\',./'), new Diff(Operation.insert, '~!@#\\$%^&*()_+{}|:\"<>?')];\n  Expect.listEquals(diffs, dmp.patch_fromText('@@ -1,21 +1,21 @@\\n-%601234567890-=%5B%5D%5C;\\',./\\n+~!@#\\$%25%5E&*()_+%7B%7D%7C:%22%3C%3E?\\n')[0].diffs, 'patch_fromText: Character decoding.');\n\n  final sb = new StringBuffer();\n  for (int x = 0; x < 100; x++) {\n    sb.write('abcdef');\n  }\n  text1 = sb.toString();\n  text2 = text1 + '123';\n  expectedPatch = '@@ -573,28 +573,31 @@\\n cdefabcdefabcdefabcdefabcdef\\n+123\\n';\n  patches = dmp.patch_make(text1, text2);\n  Expect.equals(expectedPatch, dmp.patch_toText(patches), 'patch_make: Long string with repeats.');\n\n  // Test null inputs.\n  Expect.throws(() => dmp.patch_make(null), 'patch_make: Null inputs.');\n}\n\nvoid testPatchSplitMax() {\n  // Assumes that Match_MaxBits is 32.\n  List<Patch> patches;\n  patches = dmp.patch_make('abcdefghijklmnopqrstuvwxyz01234567890', 'XabXcdXefXghXijXklXmnXopXqrXstXuvXwxXyzX01X23X45X67X89X0');\n  dmp.patch_splitMax(patches);\n  Expect.equals('@@ -1,32 +1,46 @@\\n+X\\n ab\\n+X\\n cd\\n+X\\n ef\\n+X\\n gh\\n+X\\n ij\\n+X\\n kl\\n+X\\n mn\\n+X\\n op\\n+X\\n qr\\n+X\\n st\\n+X\\n uv\\n+X\\n wx\\n+X\\n yz\\n+X\\n 012345\\n@@ -25,13 +39,18 @@\\n zX01\\n+X\\n 23\\n+X\\n 45\\n+X\\n 67\\n+X\\n 89\\n+X\\n 0\\n', dmp.patch_toText(patches), 'patch_splitMax: #1.');\n\n  patches = dmp.patch_make('abcdef1234567890123456789012345678901234567890123456789012345678901234567890uvwxyz', 'abcdefuvwxyz');\n  String oldToText = dmp.patch_toText(patches);\n  dmp.patch_splitMax(patches);\n  Expect.equals(oldToText, dmp.patch_toText(patches), 'patch_splitMax: #2.');\n\n  patches = dmp.patch_make('1234567890123456789012345678901234567890123456789012345678901234567890', 'abc');\n  dmp.patch_splitMax(patches);\n  Expect.equals('@@ -1,32 +1,4 @@\\n-1234567890123456789012345678\\n 9012\\n@@ -29,32 +1,4 @@\\n-9012345678901234567890123456\\n 7890\\n@@ -57,14 +1,3 @@\\n-78901234567890\\n+abc\\n', dmp.patch_toText(patches), 'patch_splitMax: #3.');\n\n  patches = dmp.patch_make('abcdefghij , h : 0 , t : 1 abcdefghij , h : 0 , t : 1 abcdefghij , h : 0 , t : 1', 'abcdefghij , h : 1 , t : 1 abcdefghij , h : 1 , t : 1 abcdefghij , h : 0 , t : 1');\n  dmp.patch_splitMax(patches);\n  Expect.equals('@@ -2,32 +2,32 @@\\n bcdefghij , h : \\n-0\\n+1\\n  , t : 1 abcdef\\n@@ -29,32 +29,32 @@\\n bcdefghij , h : \\n-0\\n+1\\n  , t : 1 abcdef\\n', dmp.patch_toText(patches), 'patch_splitMax: #4.');\n}\n\nvoid testPatchAddPadding() {\n  List<Patch> patches;\n  patches = dmp.patch_make('', 'test');\n  Expect.equals('@@ -0,0 +1,4 @@\\n+test\\n', dmp.patch_toText(patches), 'patch_addPadding: Both edges full.');\n  dmp.patch_addPadding(patches);\n  Expect.equals('@@ -1,8 +1,12 @@\\n %01%02%03%04\\n+test\\n %01%02%03%04\\n', dmp.patch_toText(patches), 'patch_addPadding: Both edges full.');\n\n  patches = dmp.patch_make('XY', 'XtestY');\n  Expect.equals('@@ -1,2 +1,6 @@\\n X\\n+test\\n Y\\n', dmp.patch_toText(patches), 'patch_addPadding: Both edges partial.');\n  dmp.patch_addPadding(patches);\n  Expect.equals('@@ -2,8 +2,12 @@\\n %02%03%04X\\n+test\\n Y%01%02%03\\n', dmp.patch_toText(patches), 'patch_addPadding: Both edges partial.');\n\n  patches = dmp.patch_make('XXXXYYYY', 'XXXXtestYYYY');\n  Expect.equals('@@ -1,8 +1,12 @@\\n XXXX\\n+test\\n YYYY\\n', dmp.patch_toText(patches), 'patch_addPadding: Both edges none.');\n  dmp.patch_addPadding(patches);\n  Expect.equals('@@ -5,8 +5,12 @@\\n XXXX\\n+test\\n YYYY\\n', dmp.patch_toText(patches), 'patch_addPadding: Both edges none.');\n}\n\nvoid testPatchApply() {\n  dmp.Match_Distance = 1000;\n  dmp.Match_Threshold = 0.5;\n  dmp.Patch_DeleteThreshold = 0.5;\n  List<Patch> patches;\n  patches = dmp.patch_make('', '');\n  List results = dmp.patch_apply(patches, 'Hello world.');\n  List boolArray = results[1];\n  String resultStr = '${results[0]}\\t${boolArray.length}';\n  Expect.equals('Hello world.\\t0', resultStr, 'patch_apply: Null case.');\n\n  patches = dmp.patch_make('The quick brown fox jumps over the lazy dog.', 'That quick brown fox jumped over a lazy dog.');\n  results = dmp.patch_apply(patches, 'The quick brown fox jumps over the lazy dog.');\n  boolArray = results[1];\n  resultStr = '${results[0]}\\t${boolArray[0]}\\t${boolArray[1]}';\n  Expect.equals('That quick brown fox jumped over a lazy dog.\\ttrue\\ttrue', resultStr, 'patch_apply: Exact match.');\n\n  results = dmp.patch_apply(patches, 'The quick red rabbit jumps over the tired tiger.');\n  boolArray = results[1];\n  resultStr = '${results[0]}\\t${boolArray[0]}\\t${boolArray[1]}';\n  Expect.equals('That quick red rabbit jumped over a tired tiger.\\ttrue\\ttrue', resultStr, 'patch_apply: Partial match.');\n\n  results = dmp.patch_apply(patches, 'I am the very model of a modern major general.');\n  boolArray = results[1];\n  resultStr = '${results[0]}\\t${boolArray[0]}\\t${boolArray[1]}';\n  Expect.equals('I am the very model of a modern major general.\\tfalse\\tfalse', resultStr, 'patch_apply: Failed match.');\n\n  patches = dmp.patch_make('x1234567890123456789012345678901234567890123456789012345678901234567890y', 'xabcy');\n  results = dmp.patch_apply(patches, 'x123456789012345678901234567890-----++++++++++-----123456789012345678901234567890y');\n  boolArray = results[1];\n  resultStr = '${results[0]}\\t${boolArray[0]}\\t${boolArray[1]}';\n  Expect.equals('xabcy\\ttrue\\ttrue', resultStr, 'patch_apply: Big delete, small change.');\n\n  patches = dmp.patch_make('x1234567890123456789012345678901234567890123456789012345678901234567890y', 'xabcy');\n  results = dmp.patch_apply(patches, 'x12345678901234567890---------------++++++++++---------------12345678901234567890y');\n  boolArray = results[1];\n  resultStr = '${results[0]}\\t${boolArray[0]}\\t${boolArray[1]}';\n  Expect.equals('xabc12345678901234567890---------------++++++++++---------------12345678901234567890y\\tfalse\\ttrue', resultStr, 'patch_apply: Big delete, big change 1.');\n\n  dmp.Patch_DeleteThreshold = 0.6;\n  patches = dmp.patch_make('x1234567890123456789012345678901234567890123456789012345678901234567890y', 'xabcy');\n  results = dmp.patch_apply(patches, 'x12345678901234567890---------------++++++++++---------------12345678901234567890y');\n  boolArray = results[1];\n  resultStr = '${results[0]}\\t${boolArray[0]}\\t${boolArray[1]}';\n  Expect.equals('xabcy\\ttrue\\ttrue', resultStr, 'patch_apply: Big delete, big change 2.');\n  dmp.Patch_DeleteThreshold = 0.5;\n\n  // Compensate for failed patch.\n  dmp.Match_Threshold = 0.0;\n  dmp.Match_Distance = 0;\n  patches = dmp.patch_make('abcdefghijklmnopqrstuvwxyz--------------------1234567890', 'abcXXXXXXXXXXdefghijklmnopqrstuvwxyz--------------------1234567YYYYYYYYYY890');\n  results = dmp.patch_apply(patches, 'ABCDEFGHIJKLMNOPQRSTUVWXYZ--------------------1234567890');\n  boolArray = results[1];\n  resultStr = '${results[0]}\\t${boolArray[0]}\\t${boolArray[1]}';\n  Expect.equals('ABCDEFGHIJKLMNOPQRSTUVWXYZ--------------------1234567YYYYYYYYYY890\\tfalse\\ttrue', resultStr, 'patch_apply: Compensate for failed patch.');\n  dmp.Match_Threshold = 0.5;\n  dmp.Match_Distance = 1000;\n\n  patches = dmp.patch_make('', 'test');\n  String patchStr = dmp.patch_toText(patches);\n  dmp.patch_apply(patches, '');\n  Expect.equals(patchStr, dmp.patch_toText(patches), 'patch_apply: No side effects.');\n\n  patches = dmp.patch_make('The quick brown fox jumps over the lazy dog.', 'Woof');\n  patchStr = dmp.patch_toText(patches);\n  dmp.patch_apply(patches, 'The quick brown fox jumps over the lazy dog.');\n  Expect.equals(patchStr, dmp.patch_toText(patches), 'patch_apply: No side effects with major delete.');\n\n  patches = dmp.patch_make('', 'test');\n  results = dmp.patch_apply(patches, '');\n  boolArray = results[1];\n  resultStr = '${results[0]}\\t${boolArray[0]}';\n  Expect.equals('test\\ttrue', resultStr, 'patch_apply: Edge exact match.');\n\n  patches = dmp.patch_make('XY', 'XtestY');\n  results = dmp.patch_apply(patches, 'XY');\n  boolArray = results[1];\n  resultStr = '${results[0]}\\t${boolArray[0]}';\n  Expect.equals('XtestY\\ttrue', resultStr, 'patch_apply: Near edge exact match.');\n\n  patches = dmp.patch_make('y', 'y123');\n  results = dmp.patch_apply(patches, 'x');\n  boolArray = results[1];\n  resultStr = '${results[0]}\\t${boolArray[0]}';\n  Expect.equals('x123\\ttrue', resultStr, 'patch_apply: Edge partial match.');\n}\n\n// Run each test.\nmain() {\n  dmp = new DiffMatchPatch();\n\n  testDiffCommonPrefix();\n  testDiffCommonSuffix();\n  testDiffCommonOverlap();\n  testDiffHalfmatch();\n  testDiffLinesToChars();\n  testDiffCharsToLines();\n  testDiffCleanupMerge();\n  testDiffCleanupSemanticLossless();\n  testDiffCleanupSemantic();\n  testDiffCleanupEfficiency();\n  testDiffPrettyHtml();\n  testDiffText();\n  testDiffDelta();\n  testDiffXIndex();\n  testDiffLevenshtein();\n  testDiffBisect();\n  testDiffMain();\n\n  testMatchAlphabet();\n  testMatchBitap();\n  testMatchMain();\n\n  testPatchObj();\n  testPatchFromText();\n  testPatchToText();\n  testPatchAddContext();\n  testPatchMake();\n  testPatchSplitMax();\n  testPatchAddPadding();\n  testPatchApply();\n\n  print('All tests passed.');\n}\n"
  },
  {
    "path": "dart/tests/Speedtest.dart",
    "content": "import 'dart:html';\nimport '../DiffMatchPatch.dart';\n\n// Compile with:\n// dart2js -O4 --out=Speedtest.dart.js Speedtest.dart\n\nvoid launch(Event e) {\n  HtmlElement input1 = document.getElementById('text1');\n  HtmlElement input2 = document.getElementById('text2');\n  String text1 = input1.text;\n  String text2 = input2.text;\n\n  DiffMatchPatch dmp = new DiffMatchPatch();\n  dmp.Diff_Timeout = 0.0;\n\n  // No warmup loop since it risks triggering an 'unresponsive script' dialog.\n  DateTime date_start = new DateTime.now();\n  List<Diff> d = dmp.diff_main(text1, text2, false);\n  DateTime date_end = new DateTime.now();\n\n  var ds = dmp.diff_prettyHtml(d);\n  document.getElementById('outputdiv').setInnerHtml(\n      '$ds<BR>Time: ${date_end.difference(date_start)} (h:mm:ss.mmm)',\n      validator: new TrustedNodeValidator());\n}\n\nvoid main() {\n  document.getElementById('launch').addEventListener('click', launch);\n  document.getElementById('outputdiv').setInnerHtml('');\n}\n\n/// A NodeValidator which allows any contents.\n/// The default validator strips 'style' attributes.\nclass TrustedNodeValidator implements NodeValidator {\n  bool allowsElement(Element element) => true;\n  bool allowsAttribute(Element element, String attributeName, String value)\n      => true;\n}\n"
  },
  {
    "path": "dart/tests/Speedtest.dart.js",
    "content": "{}(function dartProgram(){function copyProperties(a,b){var u=Object.keys(a)\nfor(var t=0;t<u.length;t++){var s=u[t]\nb[s]=a[s]}}var z=function(){var u=function(){}\nu.prototype={p:{}}\nvar t=new u()\nif(!(t.__proto__&&t.__proto__.p===u.prototype.p))return false\ntry{if(typeof navigator!=\"undefined\"&&typeof navigator.userAgent==\"string\"&&navigator.userAgent.indexOf(\"Chrome/\")>=0)return true\nif(typeof version==\"function\"&&version.length==0){var s=version()\nif(/^\\d+\\.\\d+\\.\\d+\\.\\d+$/.test(s))return true}}catch(r){}return false}()\nfunction setFunctionNamesIfNecessary(a){function t(){};if(typeof t.name==\"string\")return\nfor(var u=0;u<a.length;u++){var t=a[u]\nvar s=Object.keys(t)\nfor(var r=0;r<s.length;r++){var q=s[r]\nvar p=t[q]\nif(typeof p=='function')p.name=q}}}function inherit(a,b){a.prototype.constructor=a\na.prototype[\"$i\"+a.name]=a\nif(b!=null){if(z){a.prototype.__proto__=b.prototype\nreturn}var u=Object.create(b.prototype)\ncopyProperties(a.prototype,u)\na.prototype=u}}function inheritMany(a,b){for(var u=0;u<b.length;u++)inherit(b[u],a)}function mixin(a,b){copyProperties(b.prototype,a.prototype)\na.prototype.constructor=a}function lazy(a,b,c,d){var u=a\na[b]=u\na[c]=function(){a[c]=function(){H.dI(b)}\nvar t\nvar s=d\ntry{if(a[b]===u){t=a[b]=s\nt=a[b]=d()}else t=a[b]}finally{if(t===s)a[b]=null\na[c]=function(){return this[b]}}return t}}function makeConstList(a){a.immutable$list=Array\na.fixed$length=Array\nreturn a}function convertToFastObject(a){function t(){}t.prototype=a\nnew t()\nreturn a}function convertAllToFastObject(a){for(var u=0;u<a.length;++u)convertToFastObject(a[u])}var y=0\nfunction tearOffGetter(a,b,c,d,e){return e?new Function(\"funcs\",\"applyTrampolineIndex\",\"reflectionInfo\",\"name\",\"H\",\"c\",\"return function tearOff_\"+d+y+++\"(receiver) {\"+\"if (c === null) c = \"+\"H.c1\"+\"(\"+\"this, funcs, applyTrampolineIndex, reflectionInfo, false, true, name);\"+\"return new c(this, funcs[0], receiver, name);\"+\"}\")(a,b,c,d,H,null):new Function(\"funcs\",\"applyTrampolineIndex\",\"reflectionInfo\",\"name\",\"H\",\"c\",\"return function tearOff_\"+d+y+++\"() {\"+\"if (c === null) c = \"+\"H.c1\"+\"(\"+\"this, funcs, applyTrampolineIndex, reflectionInfo, false, false, name);\"+\"return new c(this, funcs[0], null, name);\"+\"}\")(a,b,c,d,H,null)}function tearOff(a,b,c,d,e,f){var u=null\nreturn d?function(){if(u===null)u=H.c1(this,a,b,c,true,false,e).prototype\nreturn u}:tearOffGetter(a,b,c,e,f)}var x=0\nfunction installTearOff(a,b,c,d,e,f,g,h,i,j){var u=[]\nfor(var t=0;t<h.length;t++){var s=h[t]\nif(typeof s=='string')s=a[s]\ns.$callName=g[t]\nu.push(s)}var s=u[0]\ns.$R=e\ns.$D=f\nvar r=i\nif(typeof r==\"number\")r=r+x\nvar q=h[0]\ns.$stubName=q\nvar p=tearOff(u,j||0,r,c,q,d)\na[b]=p\nif(c)s.$tearOff=p}function installStaticTearOff(a,b,c,d,e,f,g,h){return installTearOff(a,b,true,false,c,d,e,f,g,h)}function installInstanceTearOff(a,b,c,d,e,f,g,h,i){return installTearOff(a,b,false,c,d,e,f,g,h,i)}function setOrUpdateInterceptorsByTag(a){var u=v.interceptorsByTag\nif(!u){v.interceptorsByTag=a\nreturn}copyProperties(a,u)}function setOrUpdateLeafTags(a){var u=v.leafTags\nif(!u){v.leafTags=a\nreturn}copyProperties(a,u)}function updateTypes(a){var u=v.types\nvar t=u.length\nu.push.apply(u,a)\nreturn t}function updateHolder(a,b){copyProperties(b,a)\nreturn a}var hunkHelpers=function(){var u=function(a,b,c,d,e){return function(f,g,h,i){return installInstanceTearOff(f,g,a,b,c,d,[h],i,e)}},t=function(a,b,c,d){return function(e,f,g,h){return installStaticTearOff(e,f,a,b,c,[g],h,d)}}\nreturn{inherit:inherit,inheritMany:inheritMany,mixin:mixin,installStaticTearOff:installStaticTearOff,installInstanceTearOff:installInstanceTearOff,_instance_0u:u(0,0,null,[\"$0\"],0),_instance_1u:u(0,1,null,[\"$1\"],0),_instance_2u:u(0,2,null,[\"$2\"],0),_instance_0i:u(1,0,null,[\"$0\"],0),_instance_1i:u(1,1,null,[\"$1\"],0),_instance_2i:u(1,2,null,[\"$2\"],0),_static_0:t(0,null,[\"$0\"],0),_static_1:t(1,null,[\"$1\"],0),_static_2:t(2,null,[\"$2\"],0),makeConstList:makeConstList,lazy:lazy,updateHolder:updateHolder,convertToFastObject:convertToFastObject,setFunctionNamesIfNecessary:setFunctionNamesIfNecessary,updateTypes:updateTypes,setOrUpdateInterceptorsByTag:setOrUpdateInterceptorsByTag,setOrUpdateLeafTags:setOrUpdateLeafTags}}()\nfunction initializeDeferredHunk(a){x=v.types.length\na(hunkHelpers,v,w,$)}function getGlobalFromName(a){for(var u=0;u<w.length;u++){if(w[u]==C)continue\nif(w[u][a])return w[u][a]}}var C={},H={bY:function bY(){},\ncf:function(){return new P.V(\"No element\")},\nd5:function(){return new P.V(\"Too many elements\")},\naL:function aL(){},\nac:function ac(){},\nad:function ad(a,b,c){var _=this\n_.a=a\n_.b=b\n_.c=c\n_.d=null},\nb8:function b8(a,b,c){this.a=a\nthis.b=b\nthis.$ti=c},\nam:function am(a,b,c){this.a=a\nthis.b=b\nthis.$ti=c},\nbr:function br(a,b){this.a=a\nthis.b=b},\nbP:function(a){var u=v.mangledGlobalNames[a]\nif(typeof u===\"string\")return u\nu=\"minified:\"+a\nreturn u},\ndv:function(a){return v.types[a]},\ndC:function(a,b){var u\nif(b!=null){u=b.x\nif(u!=null)return u}return!!J.t(a).$iaa},\nd:function(a){var u\nif(typeof a===\"string\")return a\nif(typeof a===\"number\"){if(a!==0)return\"\"+a}else if(!0===a)return\"true\"\nelse if(!1===a)return\"false\"\nelse if(a==null)return\"null\"\nu=J.a4(a)\nif(typeof u!==\"string\")throw H.e(H.dp(a))\nreturn u},\nF:function(a){var u=a.$identityHash\nif(u==null){u=Math.random()*0x3fffffff|0\na.$identityHash=u}return u},\nah:function(a){return H.d9(a)+H.cq(H.bJ(a),0,null)},\nd9:function(a){var u,t,s,r,q,p,o,n,m\nu=J.t(a)\nt=u.constructor\nif(typeof t==\"function\"){s=t.name\nr=typeof s===\"string\"?s:null}else r=null\nq=r==null\nif(q||u===C.x||!!u.$iY){p=C.m(a)\nif(q)r=p\nif(p===\"Object\"){o=a.constructor\nif(typeof o==\"function\"){n=String(o).match(/^\\s*function\\s*([\\w$]*)\\s*\\(/)\nm=n==null?null:n[1]\nif(typeof m===\"string\"&&/^\\w+$/.test(m))r=m}}return r}r=r\nreturn H.bP(r.length>1&&C.a.at(r,0)===36?C.a.n(r,1):r)},\nE:function(a){if(a.date===void 0)a.date=new Date(a.a)\nreturn a.date},\ndg:function(a){var u=H.E(a).getFullYear()+0\nreturn u},\nde:function(a){var u=H.E(a).getMonth()+1\nreturn u},\nda:function(a){var u=H.E(a).getDate()+0\nreturn u},\ndb:function(a){var u=H.E(a).getHours()+0\nreturn u},\ndd:function(a){var u=H.E(a).getMinutes()+0\nreturn u},\ndf:function(a){var u=H.E(a).getSeconds()+0\nreturn u},\ndc:function(a){var u=H.E(a).getMilliseconds()+0\nreturn u},\ndr:function(a,b){var u\nif(typeof b!==\"number\"||Math.floor(b)!==b)return new P.n(!0,b,\"index\",null)\nu=J.aA(a)\nif(b<0||b>=u)return P.aR(b,a,\"index\",null,u)\nreturn P.T(b,\"index\")},\ndp:function(a){return new P.n(!0,a,null,null)},\ne:function(a){var u\nif(a==null)a=new P.bc()\nu=new Error()\nu.dartException=a\nif(\"defineProperty\" in Object){Object.defineProperty(u,\"message\",{get:H.cA})\nu.name=\"\"}else u.toString=H.cA\nreturn u},\ncA:function(){return J.a4(this.dartException)},\na2:function(a){throw H.e(a)},\naw:function(a){throw H.e(P.N(a))},\nr:function(a){var u,t,s,r,q,p\na=a.replace(String({}),'$receiver$').replace(/[[\\]{}()*+?.\\\\^$|]/g,\"\\\\$&\")\nu=a.match(/\\\\\\$[a-zA-Z]+\\\\\\$/g)\nif(u==null)u=H.i([],[P.h])\nt=u.indexOf(\"\\\\$arguments\\\\$\")\ns=u.indexOf(\"\\\\$argumentsExpr\\\\$\")\nr=u.indexOf(\"\\\\$expr\\\\$\")\nq=u.indexOf(\"\\\\$method\\\\$\")\np=u.indexOf(\"\\\\$receiver\\\\$\")\nreturn new H.bm(a.replace(new RegExp('\\\\\\\\\\\\$arguments\\\\\\\\\\\\$','g'),'((?:x|[^x])*)').replace(new RegExp('\\\\\\\\\\\\$argumentsExpr\\\\\\\\\\\\$','g'),'((?:x|[^x])*)').replace(new RegExp('\\\\\\\\\\\\$expr\\\\\\\\\\\\$','g'),'((?:x|[^x])*)').replace(new RegExp('\\\\\\\\\\\\$method\\\\\\\\\\\\$','g'),'((?:x|[^x])*)').replace(new RegExp('\\\\\\\\\\\\$receiver\\\\\\\\\\\\$','g'),'((?:x|[^x])*)'),t,s,r,q,p)},\nbn:function(a){return function($expr$){var $argumentsExpr$='$arguments$'\ntry{$expr$.$method$($argumentsExpr$)}catch(u){return u.message}}(a)},\ncm:function(a){return function($expr$){try{$expr$.$method$}catch(u){return u.message}}(a)},\ncj:function(a,b){return new H.bb(a,b==null?null:b.method)},\nbZ:function(a,b){var u,t\nu=b==null\nt=u?null:b.method\nreturn new H.aY(a,t,u?null:b.receiver)},\nax:function(a){var u,t,s,r,q,p,o,n,m,l,k,j,i,h,g\nu=new H.bQ(a)\nif(a==null)return\nif(typeof a!==\"object\")return a\nif(\"dartException\" in a)return u.$1(a.dartException)\nelse if(!(\"message\" in a))return a\nt=a.message\nif(\"number\" in a&&typeof a.number==\"number\"){s=a.number\nr=s&65535\nif((C.c.ad(s,16)&8191)===10)switch(r){case 438:return u.$1(H.bZ(H.d(t)+\" (Error \"+r+\")\",null))\ncase 445:case 5007:return u.$1(H.cj(H.d(t)+\" (Error \"+r+\")\",null))}}if(a instanceof TypeError){q=$.cC()\np=$.cD()\no=$.cE()\nn=$.cF()\nm=$.cI()\nl=$.cJ()\nk=$.cH()\n$.cG()\nj=$.cL()\ni=$.cK()\nh=q.v(t)\nif(h!=null)return u.$1(H.bZ(t,h))\nelse{h=p.v(t)\nif(h!=null){h.method=\"call\"\nreturn u.$1(H.bZ(t,h))}else{h=o.v(t)\nif(h==null){h=n.v(t)\nif(h==null){h=m.v(t)\nif(h==null){h=l.v(t)\nif(h==null){h=k.v(t)\nif(h==null){h=n.v(t)\nif(h==null){h=j.v(t)\nif(h==null){h=i.v(t)\ng=h!=null}else g=!0}else g=!0}else g=!0}else g=!0}else g=!0}else g=!0}else g=!0\nif(g)return u.$1(H.cj(t,h))}}return u.$1(new H.bp(typeof t===\"string\"?t:\"\"))}if(a instanceof RangeError){if(typeof t===\"string\"&&t.indexOf(\"call stack\")!==-1)return new P.ak()\nt=function(b){try{return String(b)}catch(f){}return null}(a)\nreturn u.$1(new P.n(!1,null,null,typeof t===\"string\"?t.replace(/^RangeError:\\s*/,\"\"):t))}if(typeof InternalError==\"function\"&&a instanceof InternalError)if(typeof t===\"string\"&&t===\"too much recursion\")return new P.ak()\nreturn a},\ndB:function(a,b,c,d,e,f){switch(b){case 0:return a.$0()\ncase 1:return a.$1(c)\ncase 2:return a.$2(c,d)\ncase 3:return a.$3(c,d,e)\ncase 4:return a.$4(c,d,e,f)}throw H.e(new P.bu(\"Unsupported number of arguments for wrapped closure\"))},\ndq:function(a,b){var u\nif(a==null)return\nu=a.$identity\nif(!!u)return u\nu=function(c,d,e){return function(f,g,h,i){return e(c,d,f,g,h,i)}}(a,b,H.dB)\na.$identity=u\nreturn u},\nd_:function(a,b,c,d,e,f,g){var u,t,s,r,q,p,o,n,m,l,k,j\nu=b[0]\nt=u.$callName\ns=e?Object.create(new H.bh().constructor.prototype):Object.create(new H.K(null,null,null,null).constructor.prototype)\ns.$initialize=s.constructor\nif(e)r=function static_tear_off(){this.$initialize()}\nelse{q=$.o\n$.o=q+1\nq=new Function(\"a,b,c,d\"+q,\"this.$initialize(a,b,c,d\"+q+\")\")\nr=q}s.constructor=r\nr.prototype=s\nif(!e){p=H.cb(a,u,f)\np.$reflectionInfo=d}else{s.$static_name=g\np=u}if(typeof d==\"number\")o=function(h,i){return function(){return h(i)}}(H.dv,d)\nelse if(typeof d==\"function\")if(e)o=d\nelse{n=f?H.ca:H.bT\no=function(h,i){return function(){return h.apply({$receiver:i(this)},arguments)}}(d,n)}else throw H.e(\"Error in reflectionInfo.\")\ns.$S=o\ns[t]=p\nfor(m=p,l=1;l<b.length;++l){k=b[l]\nj=k.$callName\nif(j!=null){k=e?k:H.cb(a,k,f)\ns[j]=k}if(l===c){k.$reflectionInfo=d\nm=k}}s.$C=m\ns.$R=u.$R\ns.$D=u.$D\nreturn r},\ncX:function(a,b,c,d){var u=H.bT\nswitch(b?-1:a){case 0:return function(e,f){return function(){return f(this)[e]()}}(c,u)\ncase 1:return function(e,f){return function(g){return f(this)[e](g)}}(c,u)\ncase 2:return function(e,f){return function(g,h){return f(this)[e](g,h)}}(c,u)\ncase 3:return function(e,f){return function(g,h,i){return f(this)[e](g,h,i)}}(c,u)\ncase 4:return function(e,f){return function(g,h,i,j){return f(this)[e](g,h,i,j)}}(c,u)\ncase 5:return function(e,f){return function(g,h,i,j,k){return f(this)[e](g,h,i,j,k)}}(c,u)\ndefault:return function(e,f){return function(){return e.apply(f(this),arguments)}}(d,u)}},\ncb:function(a,b,c){var u,t,s,r,q,p,o\nif(c)return H.cZ(a,b)\nu=b.$stubName\nt=b.length\ns=a[u]\nr=b==null?s==null:b===s\nq=!r||t>=27\nif(q)return H.cX(t,!r,u,b)\nif(t===0){r=$.o\n$.o=r+1\np=\"self\"+H.d(r)\nr=\"return function(){var \"+p+\" = this.\"\nq=$.L\nif(q==null){q=H.aE(\"self\")\n$.L=q}return new Function(r+H.d(q)+\";return \"+p+\".\"+H.d(u)+\"();}\")()}o=\"abcdefghijklmnopqrstuvwxyz\".split(\"\").splice(0,t).join(\",\")\nr=$.o\n$.o=r+1\no+=H.d(r)\nr=\"return function(\"+o+\"){return this.\"\nq=$.L\nif(q==null){q=H.aE(\"self\")\n$.L=q}return new Function(r+H.d(q)+\".\"+H.d(u)+\"(\"+o+\");}\")()},\ncY:function(a,b,c,d){var u,t\nu=H.bT\nt=H.ca\nswitch(b?-1:a){case 0:throw H.e(new H.bf(\"Intercepted function with no arguments.\"))\ncase 1:return function(e,f,g){return function(){return f(this)[e](g(this))}}(c,u,t)\ncase 2:return function(e,f,g){return function(h){return f(this)[e](g(this),h)}}(c,u,t)\ncase 3:return function(e,f,g){return function(h,i){return f(this)[e](g(this),h,i)}}(c,u,t)\ncase 4:return function(e,f,g){return function(h,i,j){return f(this)[e](g(this),h,i,j)}}(c,u,t)\ncase 5:return function(e,f,g){return function(h,i,j,k){return f(this)[e](g(this),h,i,j,k)}}(c,u,t)\ncase 6:return function(e,f,g){return function(h,i,j,k,l){return f(this)[e](g(this),h,i,j,k,l)}}(c,u,t)\ndefault:return function(e,f,g,h){return function(){h=[g(this)]\nArray.prototype.push.apply(h,arguments)\nreturn e.apply(f(this),h)}}(d,u,t)}},\ncZ:function(a,b){var u,t,s,r,q,p,o,n\nu=$.L\nif(u==null){u=H.aE(\"self\")\n$.L=u}t=$.c9\nif(t==null){t=H.aE(\"receiver\")\n$.c9=t}s=b.$stubName\nr=b.length\nq=a[s]\np=b==null?q==null:b===q\no=!p||r>=28\nif(o)return H.cY(r,!p,s,b)\nif(r===1){u=\"return function(){return this.\"+H.d(u)+\".\"+H.d(s)+\"(this.\"+H.d(t)+\");\"\nt=$.o\n$.o=t+1\nreturn new Function(u+H.d(t)+\"}\")()}n=\"abcdefghijklmnopqrstuvwxyz\".split(\"\").splice(0,r-1).join(\",\")\nu=\"return function(\"+n+\"){return this.\"+H.d(u)+\".\"+H.d(s)+\"(this.\"+H.d(t)+\", \"+n+\");\"\nt=$.o\n$.o=t+1\nreturn new Function(u+H.d(t)+\"}\")()},\nc1:function(a,b,c,d,e,f,g){return H.d_(a,b,c,d,!!e,!!f,g)},\nbT:function(a){return a.a},\nca:function(a){return a.c},\naE:function(a){var u,t,s,r,q\nu=new H.K(\"self\",\"target\",\"receiver\",\"name\")\nt=J.cg(Object.getOwnPropertyNames(u))\nfor(s=t.length,r=0;r<s;++r){q=t[r]\nif(u[q]===a)return q}},\nds:function(a){var u\nif(\"$S\" in a){u=a.$S\nif(typeof u==\"number\")return v.types[u]\nelse return a.$S()}return},\ndI:function(a){throw H.e(new P.aG(a))},\ncu:function(a){return v.getIsolateTag(a)},\ni:function(a,b){a.$ti=b\nreturn a},\nbJ:function(a){if(a==null)return\nreturn a.$ti},\ndu:function(a,b,c){var u=H.dH(a[\"$a\"+H.d(b)],H.bJ(a))\nreturn u==null?null:u[c]},\nc3:function(a,b){var u=H.bJ(a)\nreturn u==null?null:u[b]},\ndG:function(a){return H.z(a,null)},\nz:function(a,b){if(a==null)return\"dynamic\"\nif(a===-1)return\"void\"\nif(typeof a===\"object\"&&a!==null&&a.constructor===Array)return H.bP(a[0].name)+H.cq(a,1,b)\nif(typeof a==\"function\")return H.bP(a.name)\nif(a===-2)return\"dynamic\"\nif(typeof a===\"number\"){if(b==null||a<0||a>=b.length)return\"unexpected-generic-index:\"+H.d(a)\nreturn H.d(b[b.length-a-1])}if('func' in a)return H.dm(a,b)\nif('futureOr' in a)return\"FutureOr<\"+H.z(\"type\" in a?a.type:null,b)+\">\"\nreturn\"unknown-reified-type\"},\ndm:function(a,b){var u,t,s,r,q,p,o,n,m,l,k,j,i,h,g,f,e,d,c\nif(\"bounds\" in a){u=a.bounds\nif(b==null){b=H.i([],[P.h])\nt=null}else t=b.length\ns=b.length\nfor(r=u.length,q=r;q>0;--q)b.push(\"T\"+(s+q))\nfor(p=\"<\",o=\"\",q=0;q<r;++q,o=\", \"){p=C.a.aj(p+o,b[b.length-q-1])\nn=u[q]\nif(n!=null&&n!==P.m)p+=\" extends \"+H.z(n,b)}p+=\">\"}else{p=\"\"\nt=null}m=!!a.v?\"void\":H.z(a.ret,b)\nif(\"args\" in a){l=a.args\nfor(k=l.length,j=\"\",i=\"\",h=0;h<k;++h,i=\", \"){g=l[h]\nj=j+i+H.z(g,b)}}else{j=\"\"\ni=\"\"}if(\"opt\" in a){f=a.opt\nj+=i+\"[\"\nfor(k=f.length,i=\"\",h=0;h<k;++h,i=\", \"){g=f[h]\nj=j+i+H.z(g,b)}j+=\"]\"}if(\"named\" in a){e=a.named\nj+=i+\"{\"\nfor(k=H.dt(e),d=k.length,i=\"\",h=0;h<d;++h,i=\", \"){c=k[h]\nj=j+i+H.z(e[c],b)+(\" \"+H.d(c))}j+=\"}\"}if(t!=null)b.length=t\nreturn p+\"(\"+j+\") => \"+m},\ncq:function(a,b,c){var u,t,s,r,q,p\nif(a==null)return\"\"\nu=new P.G(\"\")\nfor(t=b,s=\"\",r=!0,q=\"\";t<a.length;++t,s=\", \"){u.a=q+s\np=a[t]\nif(p!=null)r=!1\nq=u.a+=H.z(p,c)}return\"<\"+u.h(0)+\">\"},\ncv:function(a){var u,t,s,r\nu=J.t(a)\nif(!!u.$iM){t=H.ds(u)\nif(t!=null)return t}s=u.constructor\nif(a==null)return s\nif(typeof a!=\"object\")return s\nr=H.bJ(a)\nif(r!=null){r=r.slice()\nr.splice(0,0,s)\ns=r}return s},\ndH:function(a,b){if(a==null)return b\na=a.apply(null,b)\nif(a==null)return\nif(typeof a===\"object\"&&a!==null&&a.constructor===Array)return a\nif(typeof a==\"function\")return a.apply(null,b)\nreturn b},\ndY:function(a,b,c){Object.defineProperty(a,b,{value:c,enumerable:false,writable:true,configurable:true})},\ndE:function(a){var u,t,s,r,q,p\nu=$.cw.$1(a)\nt=$.bG[u]\nif(t!=null){Object.defineProperty(a,v.dispatchPropertyName,{value:t,enumerable:false,writable:true,configurable:true})\nreturn t.i}s=$.bN[u]\nif(s!=null)return s\nr=v.interceptorsByTag[u]\nif(r==null){u=$.cr.$2(a,u)\nif(u!=null){t=$.bG[u]\nif(t!=null){Object.defineProperty(a,v.dispatchPropertyName,{value:t,enumerable:false,writable:true,configurable:true})\nreturn t.i}s=$.bN[u]\nif(s!=null)return s\nr=v.interceptorsByTag[u]}}if(r==null)return\ns=r.prototype\nq=u[0]\nif(q===\"!\"){t=H.bO(s)\n$.bG[u]=t\nObject.defineProperty(a,v.dispatchPropertyName,{value:t,enumerable:false,writable:true,configurable:true})\nreturn t.i}if(q===\"~\"){$.bN[u]=s\nreturn s}if(q===\"-\"){p=H.bO(s)\nObject.defineProperty(Object.getPrototypeOf(a),v.dispatchPropertyName,{value:p,enumerable:false,writable:true,configurable:true})\nreturn p.i}if(q===\"+\")return H.cy(a,s)\nif(q===\"*\")throw H.e(P.cn(u))\nif(v.leafTags[u]===true){p=H.bO(s)\nObject.defineProperty(Object.getPrototypeOf(a),v.dispatchPropertyName,{value:p,enumerable:false,writable:true,configurable:true})\nreturn p.i}else return H.cy(a,s)},\ncy:function(a,b){var u=Object.getPrototypeOf(a)\nObject.defineProperty(u,v.dispatchPropertyName,{value:J.c5(b,u,null,null),enumerable:false,writable:true,configurable:true})\nreturn b},\nbO:function(a){return J.c5(a,!1,null,!!a.$iaa)},\ndF:function(a,b,c){var u=b.prototype\nif(v.leafTags[a]===true)return H.bO(u)\nelse return J.c5(u,c,null,null)},\ndz:function(){if(!0===$.c4)return\n$.c4=!0\nH.dA()},\ndA:function(){var u,t,s,r,q,p,o,n\n$.bG=Object.create(null)\n$.bN=Object.create(null)\nH.dy()\nu=v.interceptorsByTag\nt=Object.getOwnPropertyNames(u)\nif(typeof window!=\"undefined\"){window\ns=function(){}\nfor(r=0;r<t.length;++r){q=t[r]\np=$.cz.$1(q)\nif(p!=null){o=H.dF(q,u[q],p)\nif(o!=null){Object.defineProperty(p,v.dispatchPropertyName,{value:o,enumerable:false,writable:true,configurable:true})\ns.prototype=p}}}}for(r=0;r<t.length;++r){q=t[r]\nif(/^[A-Za-z_]/.test(q)){n=u[q]\nu[\"!\"+q]=n\nu[\"~\"+q]=n\nu[\"-\"+q]=n\nu[\"+\"+q]=n\nu[\"*\"+q]=n}}},\ndy:function(){var u,t,s,r,q,p,o\nu=C.q()\nu=H.I(C.r,H.I(C.t,H.I(C.n,H.I(C.n,H.I(C.u,H.I(C.v,H.I(C.w(C.m),u)))))))\nif(typeof dartNativeDispatchHooksTransformer!=\"undefined\"){t=dartNativeDispatchHooksTransformer\nif(typeof t==\"function\")t=[t]\nif(t.constructor==Array)for(s=0;s<t.length;++s){r=t[s]\nif(typeof r==\"function\")u=r(u)||u}}q=u.getTag\np=u.getUnknownTag\no=u.prototypeForTag\n$.cw=new H.bK(q)\n$.cr=new H.bL(p)\n$.cz=new H.bM(o)},\nI:function(a,b){return a(b)||b},\nd7:function(a,b,c,d){var u,t,s,r\nu=b?\"m\":\"\"\nt=c?\"\":\"i\"\ns=d?\"g\":\"\"\nr=function(e,f){try{return new RegExp(e,f)}catch(q){return q}}(a,u+t+s)\nif(r instanceof RegExp)return r\nthrow H.e(new P.aO(\"Illegal RegExp pattern (\"+String(r)+\")\",a,null))},\nav:function(a,b,c){var u,t,s\nif(b===\"\")if(a===\"\")return c\nelse{u=a.length\nfor(t=c,s=0;s<u;++s)t=t+a[s]+c\nreturn t.charCodeAt(0)==0?t:t}else return a.replace(new RegExp(b.replace(/[[\\]{}()*+?.\\\\^$|]/g,\"\\\\$&\"),'g'),c.replace(/\\$/g,\"$$$$\"))},\nbm:function bm(a,b,c,d,e,f){var _=this\n_.a=a\n_.b=b\n_.c=c\n_.d=d\n_.e=e\n_.f=f},\nbb:function bb(a,b){this.a=a\nthis.b=b},\naY:function aY(a,b,c){this.a=a\nthis.b=b\nthis.c=c},\nbp:function bp(a){this.a=a},\nbQ:function bQ(a){this.a=a},\nM:function M(){},\nbk:function bk(){},\nbh:function bh(){},\nK:function K(a,b,c,d){var _=this\n_.a=a\n_.b=b\n_.c=c\n_.d=d},\nbf:function bf(a){this.a=a},\nX:function X(a){this.a=a\nthis.d=this.b=null},\naX:function aX(a){var _=this\n_.a=0\n_.f=_.e=_.d=_.c=_.b=null\n_.r=0\n_.$ti=a},\naZ:function aZ(a,b){var _=this\n_.a=a\n_.b=b\n_.d=_.c=null},\nb_:function b_(a,b){this.a=a\nthis.$ti=b},\nb0:function b0(a,b){var _=this\n_.a=a\n_.b=b\n_.d=_.c=null},\nbK:function bK(a){this.a=a},\nbL:function bL(a){this.a=a},\nbM:function bM(a){this.a=a},\naW:function aW(a,b){var _=this\n_.a=a\n_.b=b\n_.d=_.c=null},\ndt:function(a){return J.d6(a?Object.keys(a):[],null)}},J={\nc5:function(a,b,c,d){return{i:a,p:b,e:c,x:d}},\nbI:function(a){var u,t,s,r,q\nu=a[v.dispatchPropertyName]\nif(u==null)if($.c4==null){H.dz()\nu=a[v.dispatchPropertyName]}if(u!=null){t=u.p\nif(!1===t)return u.i\nif(!0===t)return a\ns=Object.getPrototypeOf(a)\nif(t===s)return u.i\nif(u.e===s)throw H.e(P.cn(\"Return interceptor for \"+H.d(t(a,u))))}r=a.constructor\nq=r==null?null:r[$.c6()]\nif(q!=null)return q\nq=H.dE(a)\nif(q!=null)return q\nif(typeof a==\"function\")return C.y\nt=Object.getPrototypeOf(a)\nif(t==null)return C.o\nif(t===Object.prototype)return C.o\nif(typeof r==\"function\"){Object.defineProperty(r,$.c6(),{value:C.k,enumerable:false,writable:true,configurable:true})\nreturn C.k}return C.k},\nd6:function(a,b){return J.cg(H.i(a,[b]))},\ncg:function(a){a.fixed$length=Array\nreturn a},\nt:function(a){if(typeof a==\"number\"){if(Math.floor(a)==a)return J.a9.prototype\nreturn J.a8.prototype}if(typeof a==\"string\")return J.D.prototype\nif(a==null)return J.aU.prototype\nif(typeof a==\"boolean\")return J.aT.prototype\nif(a.constructor==Array)return J.x.prototype\nif(typeof a!=\"object\"){if(typeof a==\"function\")return J.y.prototype\nreturn a}if(a instanceof P.m)return a\nreturn J.bI(a)},\nc2:function(a){if(typeof a==\"string\")return J.D.prototype\nif(a==null)return a\nif(a.constructor==Array)return J.x.prototype\nif(typeof a!=\"object\"){if(typeof a==\"function\")return J.y.prototype\nreturn a}if(a instanceof P.m)return a\nreturn J.bI(a)},\ncs:function(a){if(a==null)return a\nif(a.constructor==Array)return J.x.prototype\nif(typeof a!=\"object\"){if(typeof a==\"function\")return J.y.prototype\nreturn a}if(a instanceof P.m)return a\nreturn J.bI(a)},\nct:function(a){if(typeof a==\"string\")return J.D.prototype\nif(a==null)return a\nif(!(a instanceof P.m))return J.Y.prototype\nreturn a},\na_:function(a){if(a==null)return a\nif(typeof a!=\"object\"){if(typeof a==\"function\")return J.y.prototype\nreturn a}if(a instanceof P.m)return a\nreturn J.bI(a)},\nbR:function(a,b){if(a==null)return b==null\nif(typeof a!=\"object\")return b!=null&&a===b\nreturn J.t(a).w(a,b)},\ncN:function(a,b){if(typeof b===\"number\")if(a.constructor==Array||typeof a==\"string\"||H.dC(a,a[v.dispatchPropertyName]))if(b>>>0===b&&b<a.length)return a[b]\nreturn J.c2(a).A(a,b)},\ncO:function(a,b,c,d){return J.a_(a).as(a,b,c,d)},\ncP:function(a,b){return J.cs(a).D(a,b)},\ncQ:function(a){return J.a_(a).gaF(a)},\nay:function(a){return J.t(a).gm(a)},\naz:function(a){return J.cs(a).gq(a)},\naA:function(a){return J.c2(a).gi(a)},\ncR:function(a){return J.a_(a).gaQ(a)},\nc8:function(a){return J.a_(a).aP(a)},\ncS:function(a,b){return J.a_(a).a4(a,b)},\ncT:function(a,b,c){return J.a_(a).J(a,b,c)},\ncU:function(a,b,c){return J.ct(a).j(a,b,c)},\ncV:function(a){return J.ct(a).aR(a)},\na4:function(a){return J.t(a).h(a)},\nk:function k(){},\naT:function aT(){},\naU:function aU(){},\nab:function ab(){},\nbd:function bd(){},\nY:function Y(){},\ny:function y(){},\nx:function x(a){this.$ti=a},\nbX:function bX(a){this.$ti=a},\naD:function aD(a,b,c){var _=this\n_.a=a\n_.b=b\n_.c=c\n_.d=null},\naV:function aV(){},\na9:function a9(){},\na8:function a8(){},\nD:function D(){}},P={\nd8:function(a,b){return new H.aX([a,b])},\nb1:function(a){return new P.bv([a])},\nc_:function(){var u=Object.create(null)\nu[\"<non-identifier-key>\"]=u\ndelete u[\"<non-identifier-key>\"]\nreturn u},\nd4:function(a,b,c){var u,t\nif(P.c0(a)){if(b===\"(\"&&c===\")\")return\"(...)\"\nreturn b+\"...\"+c}u=H.i([],[P.h])\nt=$.a3()\nt.push(a)\ntry{P.dn(a,u)}finally{t.pop()}t=P.cl(b,u,\", \")+c\nreturn t.charCodeAt(0)==0?t:t},\nbW:function(a,b,c){var u,t,s\nif(P.c0(a))return b+\"...\"+c\nu=new P.G(b)\nt=$.a3()\nt.push(a)\ntry{s=u\ns.a=P.cl(s.a,a,\", \")}finally{t.pop()}u.a+=c\nt=u.a\nreturn t.charCodeAt(0)==0?t:t},\nc0:function(a){var u,t\nfor(u=0;t=$.a3(),u<t.length;++u)if(a===t[u])return!0\nreturn!1},\ndn:function(a,b){var u,t,s,r,q,p,o,n,m,l\nu=a.gq(a)\nt=0\ns=0\nwhile(!0){if(!(t<80||s<3))break\nif(!u.k())return\nr=H.d(u.gl())\nb.push(r)\nt+=r.length+2;++s}if(!u.k()){if(s<=5)return\nq=b.pop()\np=b.pop()}else{o=u.gl();++s\nif(!u.k()){if(s<=4){b.push(H.d(o))\nreturn}q=H.d(o)\np=b.pop()\nt+=q.length+2}else{n=u.gl();++s\nfor(;u.k();o=n,n=m){m=u.gl();++s\nif(s>100){while(!0){if(!(t>75&&s>3))break\nt-=b.pop().length+2;--s}b.push(\"...\")\nreturn}}p=H.d(o)\nq=H.d(n)\nt+=q.length+p.length+4}}if(s>b.length+2){t+=5\nl=\"...\"}else l=null\nwhile(!0){if(!(t>80&&b.length>3))break\nt-=b.pop().length+2\nif(l==null){t+=5\nl=\"...\"}}if(l!=null)b.push(l)\nb.push(p)\nb.push(q)},\nch:function(a,b){var u,t,s\nu=P.b1(b)\nfor(t=a.length,s=0;s<a.length;a.length===t||(0,H.aw)(a),++s)u.H(0,a[s])\nreturn u},\nci:function(a){var u,t\nt={}\nif(P.c0(a))return\"{...}\"\nu=new P.G(\"\")\ntry{$.a3().push(a)\nu.a+=\"{\"\nt.a=!0\na.a_(0,new P.b6(t,u))\nu.a+=\"}\"}finally{$.a3().pop()}t=u.a\nreturn t.charCodeAt(0)==0?t:t},\nbv:function bv(a){var _=this\n_.a=0\n_.f=_.e=_.d=_.c=_.b=null\n_.r=0\n_.$ti=a},\nbw:function bw(a){this.a=a\nthis.b=null},\nbx:function bx(a,b){var _=this\n_.a=a\n_.b=b\n_.d=_.c=null},\nb3:function b3(){},\nv:function v(){},\nb5:function b5(){},\nb6:function b6(a,b){this.a=a\nthis.b=b},\nb7:function b7(){},\nbz:function bz(){},\nan:function an(){},\nd3:function(a){if(a instanceof H.M)return a.h(0)\nreturn\"Instance of '\"+H.ah(a)+\"'\"},\naj:function(a){return new H.aW(a,H.d7(a,!1,!0,!1))},\ncl:function(a,b,c){var u=J.az(b)\nif(!u.k())return a\nif(c.length===0){do a+=H.d(u.gl())\nwhile(u.k())}else{a+=H.d(u.gl())\nfor(;u.k();)a=a+c+H.d(u.gl())}return a},\nd0:function(a){var u,t\nu=Math.abs(a)\nt=a<0?\"-\":\"\"\nif(u>=1000)return\"\"+a\nif(u>=100)return t+\"0\"+u\nif(u>=10)return t+\"00\"+u\nreturn t+\"000\"+u},\nd1:function(a){if(a>=100)return\"\"+a\nif(a>=10)return\"0\"+a\nreturn\"00\"+a},\na5:function(a){if(a>=10)return\"\"+a\nreturn\"0\"+a},\nbU:function(a,b){return new P.P(864e8*a+1000*b)},\nce:function(a){if(typeof a===\"number\"||typeof a===\"boolean\"||null==a)return J.a4(a)\nif(typeof a===\"string\")return JSON.stringify(a)\nreturn P.d3(a)},\nbS:function(a){return new P.n(!1,null,null,a)},\ncW:function(a,b,c){return new P.n(!0,a,b,c)},\nT:function(a,b){return new P.ai(null,null,!0,a,b,\"Value not in range\")},\nbe:function(a,b,c,d,e){return new P.ai(b,c,!0,a,d,\"Invalid value\")},\ndi:function(a,b,c){if(0>a||a>c)throw H.e(P.be(a,0,c,\"start\",null))\nif(a>b||b>c)throw H.e(P.be(b,a,c,\"end\",null))\nreturn b},\ndh:function(a,b){if(a<0)throw H.e(P.be(a,0,null,b,null))},\naR:function(a,b,c,d,e){var u=e==null?J.aA(b):e\nreturn new P.aQ(u,!0,a,c,\"Index out of range\")},\nH:function(a){return new P.bq(a)},\ncn:function(a){return new P.bo(a)},\nck:function(a){return new P.V(a)},\nN:function(a){return new P.aF(a)},\nJ:function J(){},\nO:function O(a,b){this.a=a\nthis.b=b},\nbH:function bH(){},\nP:function P(a){this.a=a},\naJ:function aJ(){},\naK:function aK(){},\nR:function R(){},\nbc:function bc(){},\nn:function n(a,b,c,d){var _=this\n_.a=a\n_.b=b\n_.c=c\n_.d=d},\nai:function ai(a,b,c,d,e,f){var _=this\n_.e=a\n_.f=b\n_.a=c\n_.b=d\n_.c=e\n_.d=f},\naQ:function aQ(a,b,c,d,e){var _=this\n_.f=a\n_.a=b\n_.b=c\n_.c=d\n_.d=e},\nbq:function bq(a){this.a=a},\nbo:function bo(a){this.a=a},\nV:function V(a){this.a=a},\naF:function aF(a){this.a=a},\nak:function ak(){},\naG:function aG(a){this.a=a},\nbu:function bu(a){this.a=a},\naO:function aO(a,b,c){this.a=a\nthis.b=b\nthis.c=c},\naP:function aP(){},\na0:function a0(){},\nC:function C(){},\naS:function aS(){},\nb2:function b2(){},\nag:function ag(){},\na1:function a1(){},\nm:function m(){},\nh:function h(){},\nG:function G(a){this.a=a},\nU:function U(){},\nb:function b(){}},W={\nd2:function(a,b,c){var u,t,s,r\nu=document.body\nt=(u&&C.l).u(u,a,b,c)\nt.toString\nu=new H.am(new W.l(t),new W.aM(),[W.j])\ns=u.gq(u)\nif(!s.k())H.a2(H.cf())\nr=s.gl()\nif(s.k())H.a2(H.d5())\nreturn r},\nQ:function(a){var u,t,s\nu=\"element tag unavailable\"\ntry{t=J.cR(a)\nif(typeof t===\"string\")u=a.tagName}catch(s){H.ax(s)}return u},\nco:function(a){var u,t\nu=document.createElement(\"a\")\nt=new W.by(u,window.location)\nt=new W.Z(t)\nt.ap(a)\nreturn t},\ndk:function(a,b,c,d){return!0},\ndl:function(a,b,c,d){var u,t,s\nu=d.a\nt=u.a\nt.href=c\ns=t.hostname\nu=u.b\nif(!(s==u.hostname&&t.port==u.port&&t.protocol==u.protocol))if(s===\"\")if(t.port===\"\"){u=t.protocol\nu=u===\":\"||u===\"\"}else u=!1\nelse u=!1\nelse u=!0\nreturn u},\ncp:function(){var u,t,s\nu=P.h\nt=P.ch(C.i,u)\ns=H.i([\"TEMPLATE\"],[u])\nt=new W.bD(t,P.b1(u),P.b1(u),P.b1(u),null)\nt.aq(null,new H.b8(C.i,new W.bE(),[H.c3(C.i,0),u]),s,null)\nreturn t},\nc:function c(){},\naB:function aB(){},\naC:function aC(){},\nA:function A(){},\nw:function w(){},\naI:function aI(){},\np:function p(){},\naM:function aM(){},\na:function a(){},\nB:function B(){},\naN:function aN(){},\nb4:function b4(){},\nl:function l(a){this.a=a},\nj:function j(){},\nae:function ae(){},\nbg:function bg(){},\nal:function al(){},\nbi:function bi(){},\nbj:function bj(){},\nW:function W(){},\nao:function ao(){},\nbs:function bs(){},\nbt:function bt(a){this.a=a},\nZ:function Z(a){this.a=a},\na7:function a7(){},\naf:function af(a){this.a=a},\nba:function ba(a){this.a=a},\nb9:function b9(a,b,c){this.a=a\nthis.b=b\nthis.c=c},\nar:function ar(){},\nbA:function bA(){},\nbB:function bB(){},\nbD:function bD(a,b,c,d,e){var _=this\n_.e=a\n_.a=b\n_.b=c\n_.c=d\n_.d=e},\nbE:function bE(){},\nbC:function bC(){},\na6:function a6(a,b,c){var _=this\n_.a=a\n_.b=b\n_.c=c\n_.d=null},\nq:function q(){},\nby:function by(a,b){this.a=a\nthis.b=b},\nas:function as(a){this.a=a},\nbF:function bF(a){this.a=a},\nap:function ap(){},\naq:function aq(){},\nat:function at(){},\nau:function au(){}},M={S:function S(a){this.b=a},aH:function aH(a,b,c,d,e,f){var _=this\n_.a=a\n_.x=b\n_.y=c\n_.z=d\n_.Q=e\n_.ch=f},f:function f(a,b){this.a=a\nthis.b=b}},O={\ndD:function(a){var u,t,s,r,q,p,o,n\nu=document\nt=u.getElementById(\"text1\")\ns=u.getElementById(\"text2\")\nr=t.textContent\nq=s.textContent\np=new M.aH(1,P.aj(\"[^a-zA-Z0-9]\"),P.aj(\"\\\\s\"),P.aj(\"[\\\\r\\\\n]\"),P.aj(\"\\\\n\\\\r?\\\\n$\"),P.aj(\"^\\\\r?\\\\n\\\\r?\\\\n\"))\np.a=0\ns=Date.now()\no=p.aI(r,q,!1)\nq=Date.now()\nn=p.aJ(o)\nJ.cT(u.getElementById(\"outputdiv\"),n+\"<BR>Time: \"+P.bU(0,q-s).h(0)+\" (h:mm:ss.mmm)\",new O.bl())},\ncx:function(){var u=document\nJ.cO(u.getElementById(\"launch\"),\"click\",O.dj(),null)\nJ.cS(u.getElementById(\"outputdiv\"),\"\")},\nbl:function bl(){}}\nvar w=[C,H,J,P,W,M,O]\nhunkHelpers.setFunctionNamesIfNecessary(w)\nvar $={}\nH.bY.prototype={}\nJ.k.prototype={\nw:function(a,b){return a===b},\ngm:function(a){return H.F(a)},\nh:function(a){return\"Instance of '\"+H.ah(a)+\"'\"}}\nJ.aT.prototype={\nh:function(a){return String(a)},\ngm:function(a){return a?519018:218159},\n$iJ:1}\nJ.aU.prototype={\nw:function(a,b){return null==b},\nh:function(a){return\"null\"},\ngm:function(a){return 0},\n$iag:1}\nJ.ab.prototype={\ngm:function(a){return 0},\nh:function(a){return String(a)}}\nJ.bd.prototype={}\nJ.Y.prototype={}\nJ.y.prototype={\nh:function(a){var u=a[$.cB()]\nif(u==null)return this.an(a)\nreturn\"JavaScript function for \"+H.d(J.a4(u))},\n$S:function(){return{func:1,opt:[,,,,,,,,,,,,,,,,]}}}\nJ.x.prototype={\na1:function(a,b){if(!!a.fixed$length)H.a2(P.H(\"removeAt\"))\nif(b<0||b>=a.length)throw H.e(P.T(b,null))\nreturn a.splice(b,1)[0]},\nM:function(a,b,c){if(!!a.fixed$length)H.a2(P.H(\"insert\"))\nif(b<0||b>a.length)throw H.e(P.T(b,null))\na.splice(b,0,c)},\nt:function(a,b){var u,t\nif(!!a.fixed$length)H.a2(P.H(\"addAll\"))\nfor(u=b.length,t=0;t<b.length;b.length===u||(0,H.aw)(b),++t)a.push(b[t])},\nD:function(a,b){return a[b]},\ngaO:function(a){var u=a.length\nif(u>0)return a[u-1]\nthrow H.e(H.cf())},\nae:function(a,b){var u,t\nu=a.length\nfor(t=0;t<u;++t){if(b.$1(a[t]))return!0\nif(a.length!==u)throw H.e(P.N(a))}return!1},\np:function(a,b){var u\nfor(u=0;u<a.length;++u)if(J.bR(a[u],b))return!0\nreturn!1},\nh:function(a){return P.bW(a,\"[\",\"]\")},\ngq:function(a){return new J.aD(a,a.length,0)},\ngm:function(a){return H.F(a)},\ngi:function(a){return a.length}}\nJ.bX.prototype={}\nJ.aD.prototype={\ngl:function(){return this.d},\nk:function(){var u,t,s\nu=this.a\nt=u.length\nif(this.b!==t)throw H.e(H.aw(u))\ns=this.c\nif(s>=t){this.d=null\nreturn!1}this.d=u[s]\nthis.c=s+1\nreturn!0}}\nJ.aV.prototype={\naG:function(a,b){var u\nif(a<b)return-1\nelse if(a>b)return 1\nelse if(a===b){if(a===0){u=C.c.ga0(b)\nif(this.ga0(a)===u)return 0\nif(this.ga0(a))return-1\nreturn 1}return 0}else if(isNaN(a)){if(isNaN(b))return 0\nreturn 1}else return-1},\nga0:function(a){return a===0?1/a<0:a<0},\nN:function(a){var u\nif(a>=-2147483648&&a<=2147483647)return a|0\nif(isFinite(a)){u=a<0?Math.ceil(a):Math.floor(a)\nreturn u+0}throw H.e(P.H(\"\"+a+\".toInt()\"))},\naf:function(a){var u,t\nif(a>=0){if(a<=2147483647){u=a|0\nreturn a===u?u:u+1}}else if(a>=-2147483648)return a|0\nt=Math.ceil(a)\nif(isFinite(t))return t\nthrow H.e(P.H(\"\"+a+\".ceil()\"))},\naL:function(a){var u,t\nif(a>=0){if(a<=2147483647)return a|0}else if(a>=-2147483648){u=a|0\nreturn a===u?u:u-1}t=Math.floor(a)\nif(isFinite(t))return t\nthrow H.e(P.H(\"\"+a+\".floor()\"))},\nh:function(a){if(a===0&&1/a<0)return\"-0.0\"\nelse return\"\"+a},\ngm:function(a){var u,t,s,r,q\nu=a|0\nif(a===u)return 536870911&u\nt=Math.abs(a)\ns=Math.log(t)/0.6931471805599453|0\nr=Math.pow(2,s)\nq=t<1?t/r:r/t\nreturn 536870911&((q*9007199254740992|0)+(q*3542243181176521|0))*599197+s*1259},\nak:function(a,b){var u=a%b\nif(u===0)return 0\nif(u>0)return u\nif(b<0)return u-b\nelse return u+b},\nG:function(a,b){return(a|0)===a?a/b|0:this.aD(a,b)},\naD:function(a,b){var u=a/b\nif(u>=-2147483648&&u<=2147483647)return u|0\nif(u>0){if(u!==1/0)return Math.floor(u)}else if(u>-1/0)return Math.ceil(u)\nthrow H.e(P.H(\"Result of truncating division is \"+H.d(u)+\": \"+H.d(a)+\" ~/ \"+b))},\nad:function(a,b){var u\nif(a>0)u=this.aC(a,b)\nelse{u=b>31?31:b\nu=a>>u>>>0}return u},\naC:function(a,b){return b>31?0:a>>>b},\n$ia1:1}\nJ.a9.prototype={$ia0:1}\nJ.a8.prototype={}\nJ.D.prototype={\nat:function(a,b){if(b>=a.length)throw H.e(H.dr(a,b))\nreturn a.charCodeAt(b)},\naj:function(a,b){if(typeof b!==\"string\")throw H.e(P.cW(b,null,null))\nreturn a+b},\naK:function(a,b){var u,t\nu=b.length\nt=a.length\nif(u>t)return!1\nreturn b===this.n(a,t-u)},\na5:function(a,b){var u=b.length\nif(u>a.length)return!1\nreturn b===a.substring(0,u)},\nj:function(a,b,c){if(c==null)c=a.length\nif(b<0)throw H.e(P.T(b,null))\nif(b>c)throw H.e(P.T(b,null))\nif(c>a.length)throw H.e(P.T(c,null))\nreturn a.substring(b,c)},\nn:function(a,b){return this.j(a,b,null)},\naR:function(a){return a.toLowerCase()},\nah:function(a,b,c){var u\nif(c<0||c>a.length)throw H.e(P.be(c,0,a.length,null,null))\nu=a.indexOf(b,c)\nreturn u},\naM:function(a,b){return this.ah(a,b,0)},\nh:function(a){return a},\ngm:function(a){var u,t,s\nfor(u=a.length,t=0,s=0;s<u;++s){t=536870911&t+a.charCodeAt(s)\nt=536870911&t+((524287&t)<<10)\nt^=t>>6}t=536870911&t+((67108863&t)<<3)\nt^=t>>11\nreturn 536870911&t+((16383&t)<<15)},\ngi:function(a){return a.length},\n$ih:1}\nH.aL.prototype={}\nH.ac.prototype={\ngq:function(a){return new H.ad(this,this.gi(this),0)},\nO:function(a,b){return this.am(0,b)}}\nH.ad.prototype={\ngl:function(){return this.d},\nk:function(){var u,t,s,r\nu=this.a\nt=J.c2(u)\ns=t.gi(u)\nif(this.b!==s)throw H.e(P.N(u))\nr=this.c\nif(r>=s){this.d=null\nreturn!1}this.d=t.D(u,r);++this.c\nreturn!0}}\nH.b8.prototype={\ngi:function(a){return J.aA(this.a)},\nD:function(a,b){return this.b.$1(J.cP(this.a,b))},\n$aac:function(a,b){return[b]},\n$aC:function(a,b){return[b]}}\nH.am.prototype={\ngq:function(a){return new H.br(J.az(this.a),this.b)}}\nH.br.prototype={\nk:function(){var u,t\nfor(u=this.a,t=this.b;u.k();)if(t.$1(u.gl()))return!0\nreturn!1},\ngl:function(){return this.a.gl()}}\nH.bm.prototype={\nv:function(a){var u,t,s\nu=new RegExp(this.a).exec(a)\nif(u==null)return\nt=Object.create(null)\ns=this.b\nif(s!==-1)t.arguments=u[s+1]\ns=this.c\nif(s!==-1)t.argumentsExpr=u[s+1]\ns=this.d\nif(s!==-1)t.expr=u[s+1]\ns=this.e\nif(s!==-1)t.method=u[s+1]\ns=this.f\nif(s!==-1)t.receiver=u[s+1]\nreturn t}}\nH.bb.prototype={\nh:function(a){var u=this.b\nif(u==null)return\"NoSuchMethodError: \"+H.d(this.a)\nreturn\"NoSuchMethodError: method not found: '\"+u+\"' on null\"}}\nH.aY.prototype={\nh:function(a){var u,t\nu=this.b\nif(u==null)return\"NoSuchMethodError: \"+H.d(this.a)\nt=this.c\nif(t==null)return\"NoSuchMethodError: method not found: '\"+u+\"' (\"+H.d(this.a)+\")\"\nreturn\"NoSuchMethodError: method not found: '\"+u+\"' on '\"+t+\"' (\"+H.d(this.a)+\")\"}}\nH.bp.prototype={\nh:function(a){var u=this.a\nreturn u.length===0?\"Error\":\"Error: \"+u}}\nH.bQ.prototype={\n$1:function(a){if(!!J.t(a).$iR)if(a.$thrownJsError==null)a.$thrownJsError=this.a\nreturn a}}\nH.M.prototype={\nh:function(a){return\"Closure '\"+H.ah(this).trim()+\"'\"},\ngaS:function(){return this},\n$C:\"$1\",\n$R:1,\n$D:null}\nH.bk.prototype={}\nH.bh.prototype={\nh:function(a){var u=this.$static_name\nif(u==null)return\"Closure of unknown static method\"\nreturn\"Closure '\"+H.bP(u)+\"'\"}}\nH.K.prototype={\nw:function(a,b){if(b==null)return!1\nif(this===b)return!0\nif(!(b instanceof H.K))return!1\nreturn this.a===b.a&&this.b===b.b&&this.c===b.c},\ngm:function(a){var u,t\nu=this.c\nif(u==null)t=H.F(this.a)\nelse t=typeof u!==\"object\"?J.ay(u):H.F(u)\nreturn(t^H.F(this.b))>>>0},\nh:function(a){var u=this.c\nif(u==null)u=this.a\nreturn\"Closure '\"+H.d(this.d)+\"' of \"+(\"Instance of '\"+H.ah(u)+\"'\")}}\nH.bf.prototype={\nh:function(a){return\"RuntimeError: \"+this.a}}\nH.X.prototype={\ngL:function(){var u=this.b\nif(u==null){u=H.dG(this.a)\nthis.b=u}return u},\nh:function(a){return this.gL()},\ngm:function(a){var u=this.d\nif(u==null){u=C.a.gm(this.gL())\nthis.d=u}return u},\nw:function(a,b){if(b==null)return!1\nreturn b instanceof H.X&&this.gL()===b.gL()}}\nH.aX.prototype={\ngi:function(a){return this.a},\ngE:function(){return new H.b_(this,[H.c3(this,0)])},\nA:function(a,b){var u,t,s,r\nif(typeof b===\"string\"){u=this.b\nif(u==null)return\nt=this.U(u,b)\ns=t==null?null:t.b\nreturn s}else if(typeof b===\"number\"&&(b&0x3ffffff)===b){r=this.c\nif(r==null)return\nt=this.U(r,b)\ns=t==null?null:t.b\nreturn s}else return this.aN(b)},\naN:function(a){var u,t,s\nu=this.d\nif(u==null)return\nt=this.ac(u,J.ay(a)&0x3ffffff)\ns=this.ai(t,a)\nif(s<0)return\nreturn t[s].b},\na2:function(a,b,c){var u,t,s,r,q,p\nif(typeof b===\"string\"){u=this.b\nif(u==null){u=this.V()\nthis.b=u}this.a6(u,b,c)}else if(typeof b===\"number\"&&(b&0x3ffffff)===b){t=this.c\nif(t==null){t=this.V()\nthis.c=t}this.a6(t,b,c)}else{s=this.d\nif(s==null){s=this.V()\nthis.d=s}r=J.ay(b)&0x3ffffff\nq=this.ac(s,r)\nif(q==null)this.X(s,r,[this.R(b,c)])\nelse{p=this.ai(q,b)\nif(p>=0)q[p].b=c\nelse q.push(this.R(b,c))}}},\na_:function(a,b){var u,t\nu=this.e\nt=this.r\nfor(;u!=null;){b.$2(u.a,u.b)\nif(t!==this.r)throw H.e(P.N(this))\nu=u.c}},\na6:function(a,b,c){var u=this.U(a,b)\nif(u==null)this.X(a,b,this.R(b,c))\nelse u.b=c},\naz:function(){this.r=this.r+1&67108863},\nR:function(a,b){var u,t\nu=new H.aZ(a,b)\nif(this.e==null){this.f=u\nthis.e=u}else{t=this.f\nu.d=t\nt.c=u\nthis.f=u}++this.a\nthis.az()\nreturn u},\nai:function(a,b){var u,t\nif(a==null)return-1\nu=a.length\nfor(t=0;t<u;++t)if(J.bR(a[t].a,b))return t\nreturn-1},\nh:function(a){return P.ci(this)},\nU:function(a,b){return a[b]},\nac:function(a,b){return a[b]},\nX:function(a,b,c){a[b]=c},\nav:function(a,b){delete a[b]},\nV:function(){var u=Object.create(null)\nthis.X(u,\"<non-identifier-key>\",u)\nthis.av(u,\"<non-identifier-key>\")\nreturn u}}\nH.aZ.prototype={}\nH.b_.prototype={\ngi:function(a){return this.a.a},\ngq:function(a){var u,t\nu=this.a\nt=new H.b0(u,u.r)\nt.c=u.e\nreturn t}}\nH.b0.prototype={\ngl:function(){return this.d},\nk:function(){var u=this.a\nif(this.b!==u.r)throw H.e(P.N(u))\nelse{u=this.c\nif(u==null){this.d=null\nreturn!1}else{this.d=u.a\nthis.c=u.c\nreturn!0}}}}\nH.bK.prototype={\n$1:function(a){return this.a(a)}}\nH.bL.prototype={\n$2:function(a,b){return this.a(a,b)}}\nH.bM.prototype={\n$1:function(a){return this.a(a)}}\nH.aW.prototype={\nh:function(a){return\"RegExp/\"+this.a+\"/\"}}\nP.bv.prototype={\ngq:function(a){var u=new P.bx(this,this.r)\nu.c=this.e\nreturn u},\ngi:function(a){return this.a},\np:function(a,b){var u,t\nif(typeof b===\"string\"&&b!==\"__proto__\"){u=this.b\nif(u==null)return!1\nreturn u[b]!=null}else{t=this.au(b)\nreturn t}},\nau:function(a){var u=this.d\nif(u==null)return!1\nreturn this.ab(u[this.a8(a)],a)>=0},\nH:function(a,b){var u,t\nif(typeof b===\"string\"&&b!==\"__proto__\"){u=this.b\nif(u==null){u=P.c_()\nthis.b=u}return this.a7(u,b)}else if(typeof b===\"number\"&&(b&1073741823)===b){t=this.c\nif(t==null){t=P.c_()\nthis.c=t}return this.a7(t,b)}else return this.ar(b)},\nar:function(a){var u,t,s\nu=this.d\nif(u==null){u=P.c_()\nthis.d=u}t=this.a8(a)\ns=u[t]\nif(s==null)u[t]=[this.W(a)]\nelse{if(this.ab(s,a)>=0)return!1\ns.push(this.W(a))}return!0},\na7:function(a,b){if(a[b]!=null)return!1\na[b]=this.W(b)\nreturn!0},\nW:function(a){var u=new P.bw(a)\nif(this.e==null){this.f=u\nthis.e=u}else{this.f.b=u\nthis.f=u}++this.a\nthis.r=1073741823&this.r+1\nreturn u},\na8:function(a){return J.ay(a)&1073741823},\nab:function(a,b){var u,t\nif(a==null)return-1\nu=a.length\nfor(t=0;t<u;++t)if(J.bR(a[t].a,b))return t\nreturn-1}}\nP.bw.prototype={}\nP.bx.prototype={\ngl:function(){return this.d},\nk:function(){var u=this.a\nif(this.b!==u.r)throw H.e(P.N(u))\nelse{u=this.c\nif(u==null){this.d=null\nreturn!1}else{this.d=u.a\nthis.c=u.b\nreturn!0}}}}\nP.b3.prototype={}\nP.v.prototype={\ngq:function(a){return new H.ad(a,this.gi(a),0)},\nD:function(a,b){return this.A(a,b)},\nh:function(a){return P.bW(a,\"[\",\"]\")}}\nP.b5.prototype={}\nP.b6.prototype={\n$2:function(a,b){var u,t\nu=this.a\nif(!u.a)this.b.a+=\", \"\nu.a=!1\nu=this.b\nt=u.a+=H.d(a)\nu.a=t+\": \"\nu.a+=H.d(b)}}\nP.b7.prototype={\na_:function(a,b){var u,t\nfor(u=J.az(this.gE());u.k();){t=u.gl()\nb.$2(t,this.A(0,t))}},\ngi:function(a){return J.aA(this.gE())},\nh:function(a){return P.ci(this)}}\nP.bz.prototype={\nt:function(a,b){var u\nfor(u=J.az(b);u.k();)this.H(0,u.gl())},\nh:function(a){return P.bW(this,\"{\",\"}\")}}\nP.an.prototype={}\nP.J.prototype={}\nP.O.prototype={\nH:function(a,b){var u,t\nu=this.a+C.c.G(b.a,1000)\nif(Math.abs(u)<=864e13)t=!1\nelse t=!0\nif(t)H.a2(P.bS(\"DateTime is outside valid range: \"+u))\nreturn new P.O(u,!1)},\nw:function(a,b){if(b==null)return!1\nreturn b instanceof P.O&&this.a===b.a&&!0},\ngm:function(a){var u=this.a\nreturn(u^C.c.ad(u,30))&1073741823},\nh:function(a){var u,t,s,r,q,p,o,n\nu=P.d0(H.dg(this))\nt=P.a5(H.de(this))\ns=P.a5(H.da(this))\nr=P.a5(H.db(this))\nq=P.a5(H.dd(this))\np=P.a5(H.df(this))\no=P.d1(H.dc(this))\nn=u+\"-\"+t+\"-\"+s+\" \"+r+\":\"+q+\":\"+p+\".\"+o\nreturn n}}\nP.bH.prototype={}\nP.P.prototype={\nw:function(a,b){if(b==null)return!1\nreturn b instanceof P.P&&this.a===b.a},\ngm:function(a){return C.c.gm(this.a)},\nh:function(a){var u,t,s,r,q\nu=new P.aK()\nt=this.a\nif(t<0)return\"-\"+new P.P(0-t).h(0)\ns=u.$1(C.c.G(t,6e7)%60)\nr=u.$1(C.c.G(t,1e6)%60)\nq=new P.aJ().$1(t%1e6)\nreturn\"\"+C.c.G(t,36e8)+\":\"+H.d(s)+\":\"+H.d(r)+\".\"+H.d(q)}}\nP.aJ.prototype={\n$1:function(a){if(a>=1e5)return\"\"+a\nif(a>=1e4)return\"0\"+a\nif(a>=1000)return\"00\"+a\nif(a>=100)return\"000\"+a\nif(a>=10)return\"0000\"+a\nreturn\"00000\"+a}}\nP.aK.prototype={\n$1:function(a){if(a>=10)return\"\"+a\nreturn\"0\"+a}}\nP.R.prototype={}\nP.bc.prototype={\nh:function(a){return\"Throw of null.\"}}\nP.n.prototype={\ngT:function(){return\"Invalid argument\"+(!this.a?\"(s)\":\"\")},\ngS:function(){return\"\"},\nh:function(a){var u,t,s,r,q,p\nu=this.c\nt=u!=null?\" (\"+u+\")\":\"\"\nu=this.d\ns=u==null?\"\":\": \"+u\nr=this.gT()+t+s\nif(!this.a)return r\nq=this.gS()\np=P.ce(this.b)\nreturn r+q+\": \"+p}}\nP.ai.prototype={\ngT:function(){return\"RangeError\"},\ngS:function(){var u,t,s\nu=this.e\nif(u==null){u=this.f\nt=u!=null?\": Not less than or equal to \"+H.d(u):\"\"}else{s=this.f\nif(s==null)t=\": Not greater than or equal to \"+H.d(u)\nelse if(s>u)t=\": Not in range \"+H.d(u)+\"..\"+H.d(s)+\", inclusive\"\nelse t=s<u?\": Valid value range is empty\":\": Only valid value is \"+H.d(u)}return t}}\nP.aQ.prototype={\ngT:function(){return\"RangeError\"},\ngS:function(){if(this.b<0)return\": index must not be negative\"\nvar u=this.f\nif(u===0)return\": no indices are valid\"\nreturn\": index should be less than \"+H.d(u)},\ngi:function(a){return this.f}}\nP.bq.prototype={\nh:function(a){return\"Unsupported operation: \"+this.a}}\nP.bo.prototype={\nh:function(a){var u=this.a\nreturn u!=null?\"UnimplementedError: \"+u:\"UnimplementedError\"}}\nP.V.prototype={\nh:function(a){return\"Bad state: \"+this.a}}\nP.aF.prototype={\nh:function(a){var u=this.a\nif(u==null)return\"Concurrent modification during iteration.\"\nreturn\"Concurrent modification during iteration: \"+P.ce(u)+\".\"}}\nP.ak.prototype={\nh:function(a){return\"Stack Overflow\"},\n$iR:1}\nP.aG.prototype={\nh:function(a){var u=this.a\nreturn u==null?\"Reading static variable during its initialization\":\"Reading static variable '\"+u+\"' during its initialization\"}}\nP.bu.prototype={\nh:function(a){return\"Exception: \"+this.a}}\nP.aO.prototype={\nh:function(a){var u,t,s,r\nu=this.a\nt=\"\"!==u?\"FormatException: \"+u:\"FormatException\"\ns=this.b\nr=s.length>78?C.a.j(s,0,75)+\"...\":s\nreturn t+\"\\n\"+r}}\nP.aP.prototype={}\nP.a0.prototype={}\nP.C.prototype={\nO:function(a,b){return new H.am(this,b,[H.du(this,\"C\",0)])},\ngi:function(a){var u,t\nu=this.gq(this)\nfor(t=0;u.k();)++t\nreturn t},\nD:function(a,b){var u,t,s\nP.dh(b,\"index\")\nfor(u=this.gq(this),t=0;u.k();){s=u.gl()\nif(b===t)return s;++t}throw H.e(P.aR(b,this,\"index\",null,t))},\nh:function(a){return P.d4(this,\"(\",\")\")}}\nP.aS.prototype={}\nP.b2.prototype={}\nP.ag.prototype={\ngm:function(a){return P.m.prototype.gm.call(this,this)},\nh:function(a){return\"null\"}}\nP.a1.prototype={}\nP.m.prototype={constructor:P.m,$im:1,\nw:function(a,b){return this===b},\ngm:function(a){return H.F(this)},\nh:function(a){return\"Instance of '\"+H.ah(this)+\"'\"},\ntoString:function(){return this.h(this)}}\nP.h.prototype={}\nP.G.prototype={\ngi:function(a){return this.a.length},\nh:function(a){var u=this.a\nreturn u.charCodeAt(0)==0?u:u}}\nW.c.prototype={}\nW.aB.prototype={\nh:function(a){return String(a)}}\nW.aC.prototype={\nh:function(a){return String(a)}}\nW.A.prototype={$iA:1}\nW.w.prototype={\ngi:function(a){return a.length}}\nW.aI.prototype={\nh:function(a){return String(a)}}\nW.p.prototype={\ngaF:function(a){return new W.bt(a)},\nh:function(a){return a.localName},\nu:function(a,b,c,d){var u,t,s,r,q\nif(c==null){if(d==null){u=$.cd\nif(u==null){u=H.i([],[W.q])\nt=new W.af(u)\nu.push(W.co(null))\nu.push(W.cp())\n$.cd=t\nd=t}else d=u}u=$.cc\nif(u==null){u=new W.as(d)\n$.cc=u\nc=u}else{u.a=d\nc=u}}else if(d!=null)throw H.e(P.bS(\"validator can only be passed if treeSanitizer is null\"))\nif($.u==null){u=document\nt=u.implementation.createHTMLDocument(\"\")\n$.u=t\n$.bV=t.createRange()\ns=$.u.createElement(\"base\")\ns.href=u.baseURI\n$.u.head.appendChild(s)}u=$.u\nif(u.body==null){t=u.createElement(\"body\")\nu.body=t}u=$.u\nif(!!this.$iA)r=u.body\nelse{r=u.createElement(a.tagName)\n$.u.body.appendChild(r)}if(\"createContextualFragment\" in window.Range.prototype&&!C.b.p(C.A,a.tagName)){$.bV.selectNodeContents(r)\nq=$.bV.createContextualFragment(b)}else{r.innerHTML=b\nq=$.u.createDocumentFragment()\nfor(;u=r.firstChild,u!=null;)q.appendChild(u)}u=$.u.body\nif(r==null?u!=null:r!==u)J.c8(r)\nc.a3(q)\ndocument.adoptNode(q)\nreturn q},\naH:function(a,b,c){return this.u(a,b,c,null)},\nJ:function(a,b,c){a.textContent=null\na.appendChild(this.u(a,b,null,c))},\na4:function(a,b){return this.J(a,b,null)},\n$ip:1,\ngaQ:function(a){return a.tagName}}\nW.aM.prototype={\n$1:function(a){return!!J.t(a).$ip}}\nW.a.prototype={$ia:1}\nW.B.prototype={\nas:function(a,b,c,d){return a.addEventListener(b,H.dq(c,1),d)}}\nW.aN.prototype={\ngi:function(a){return a.length}}\nW.b4.prototype={\nh:function(a){return String(a)}}\nW.l.prototype={\ngK:function(a){var u,t\nu=this.a\nt=u.childNodes.length\nif(t===0)throw H.e(P.ck(\"No elements\"))\nif(t>1)throw H.e(P.ck(\"More than one element\"))\nreturn u.firstChild},\nt:function(a,b){var u,t,s,r\nu=b.a\nt=this.a\nif(u!==t)for(s=u.childNodes.length,r=0;r<s;++r)t.appendChild(u.firstChild)\nreturn},\ngq:function(a){var u=this.a.childNodes\nreturn new W.a6(u,u.length,-1)},\ngi:function(a){return this.a.childNodes.length},\nA:function(a,b){return this.a.childNodes[b]},\n$av:function(){return[W.j]}}\nW.j.prototype={\naP:function(a){var u=a.parentNode\nif(u!=null)u.removeChild(a)},\nh:function(a){var u=a.nodeValue\nreturn u==null?this.al(a):u},\n$ij:1}\nW.ae.prototype={\ngi:function(a){return a.length},\nA:function(a,b){if(b>>>0!==b||b>=a.length)throw H.e(P.aR(b,a,null,null,null))\nreturn a[b]},\nD:function(a,b){return a[b]},\n$iaa:1,\n$aaa:function(){return[W.j]},\n$av:function(){return[W.j]}}\nW.bg.prototype={\ngi:function(a){return a.length}}\nW.al.prototype={\nu:function(a,b,c,d){var u,t\nif(\"createContextualFragment\" in window.Range.prototype)return this.P(a,b,c,d)\nu=W.d2(\"<table>\"+b+\"</table>\",c,d)\nt=document.createDocumentFragment()\nt.toString\nu.toString\nnew W.l(t).t(0,new W.l(u))\nreturn t}}\nW.bi.prototype={\nu:function(a,b,c,d){var u,t,s,r\nif(\"createContextualFragment\" in window.Range.prototype)return this.P(a,b,c,d)\nu=document\nt=u.createDocumentFragment()\nu=C.p.u(u.createElement(\"table\"),b,c,d)\nu.toString\nu=new W.l(u)\ns=u.gK(u)\ns.toString\nu=new W.l(s)\nr=u.gK(u)\nt.toString\nr.toString\nnew W.l(t).t(0,new W.l(r))\nreturn t}}\nW.bj.prototype={\nu:function(a,b,c,d){var u,t,s\nif(\"createContextualFragment\" in window.Range.prototype)return this.P(a,b,c,d)\nu=document\nt=u.createDocumentFragment()\nu=C.p.u(u.createElement(\"table\"),b,c,d)\nu.toString\nu=new W.l(u)\ns=u.gK(u)\nt.toString\ns.toString\nnew W.l(t).t(0,new W.l(s))\nreturn t}}\nW.W.prototype={\nJ:function(a,b,c){var u\na.textContent=null\nu=this.u(a,b,null,c)\na.content.appendChild(u)},\na4:function(a,b){return this.J(a,b,null)},\n$iW:1}\nW.ao.prototype={\ngi:function(a){return a.length},\nA:function(a,b){if(b>>>0!==b||b>=a.length)throw H.e(P.aR(b,a,null,null,null))\nreturn a[b]},\nD:function(a,b){return a[b]},\n$iaa:1,\n$aaa:function(){return[W.j]},\n$av:function(){return[W.j]}}\nW.bs.prototype={\na_:function(a,b){var u,t,s,r,q\nfor(u=this.gE(),t=u.length,s=this.a,r=0;r<u.length;u.length===t||(0,H.aw)(u),++r){q=u[r]\nb.$2(q,s.getAttribute(q))}},\ngE:function(){var u,t,s,r,q\nu=this.a.attributes\nt=H.i([],[P.h])\nfor(s=u.length,r=0;r<s;++r){q=u[r]\nif(q.namespaceURI==null)t.push(q.name)}return t}}\nW.bt.prototype={\nA:function(a,b){return this.a.getAttribute(b)},\ngi:function(a){return this.gE().length}}\nW.Z.prototype={\nap:function(a){var u,t\nu=$.c7()\nif(u.a===0){for(t=0;t<262;++t)u.a2(0,C.z[t],W.dw())\nfor(t=0;t<12;++t)u.a2(0,C.j[t],W.dx())}},\nC:function(a){return $.cM().p(0,W.Q(a))},\nB:function(a,b,c){var u,t,s\nu=W.Q(a)\nt=$.c7()\ns=t.A(0,H.d(u)+\"::\"+b)\nif(s==null)s=t.A(0,\"*::\"+b)\nif(s==null)return!1\nreturn s.$4(a,b,c,this)},\n$iq:1}\nW.a7.prototype={\ngq:function(a){return new W.a6(a,this.gi(a),-1)}}\nW.af.prototype={\nC:function(a){return C.b.ae(this.a,new W.ba(a))},\nB:function(a,b,c){return C.b.ae(this.a,new W.b9(a,b,c))},\n$iq:1}\nW.ba.prototype={\n$1:function(a){return a.C(this.a)}}\nW.b9.prototype={\n$1:function(a){return a.B(this.a,this.b,this.c)}}\nW.ar.prototype={\naq:function(a,b,c,d){var u,t,s\nthis.a.t(0,c)\nu=b.O(0,new W.bA())\nt=b.O(0,new W.bB())\nthis.b.t(0,u)\ns=this.c\ns.t(0,C.B)\ns.t(0,t)},\nC:function(a){return this.a.p(0,W.Q(a))},\nB:function(a,b,c){var u,t\nu=W.Q(a)\nt=this.c\nif(t.p(0,H.d(u)+\"::\"+b))return this.d.aE(c)\nelse if(t.p(0,\"*::\"+b))return this.d.aE(c)\nelse{t=this.b\nif(t.p(0,H.d(u)+\"::\"+b))return!0\nelse if(t.p(0,\"*::\"+b))return!0\nelse if(t.p(0,H.d(u)+\"::*\"))return!0\nelse if(t.p(0,\"*::*\"))return!0}return!1},\n$iq:1}\nW.bA.prototype={\n$1:function(a){return!C.b.p(C.j,a)}}\nW.bB.prototype={\n$1:function(a){return C.b.p(C.j,a)}}\nW.bD.prototype={\nB:function(a,b,c){if(this.ao(a,b,c))return!0\nif(b===\"template\"&&c===\"\")return!0\nif(a.getAttribute(\"template\")===\"\")return this.e.p(0,b)\nreturn!1}}\nW.bE.prototype={\n$1:function(a){return\"TEMPLATE::\"+H.d(a)}}\nW.bC.prototype={\nC:function(a){var u=J.t(a)\nif(!!u.$iU)return!1\nu=!!u.$ib\nif(u&&W.Q(a)===\"foreignObject\")return!1\nif(u)return!0\nreturn!1},\nB:function(a,b,c){if(b===\"is\"||C.a.a5(b,\"on\"))return!1\nreturn this.C(a)},\n$iq:1}\nW.a6.prototype={\nk:function(){var u,t\nu=this.c+1\nt=this.b\nif(u<t){this.d=J.cN(this.a,u)\nthis.c=u\nreturn!0}this.d=null\nthis.c=t\nreturn!1},\ngl:function(){return this.d}}\nW.q.prototype={}\nW.by.prototype={}\nW.as.prototype={\na3:function(a){new W.bF(this).$2(a,null)},\nF:function(a,b){if(b==null)J.c8(a)\nelse b.removeChild(a)},\naB:function(a,b){var u,t,s,r,q,p,o,n\nu=!0\nt=null\ns=null\ntry{t=J.cQ(a)\ns=t.a.getAttribute(\"is\")\nr=function(c){if(!(c.attributes instanceof NamedNodeMap))return true\nvar m=c.childNodes\nif(c.lastChild&&c.lastChild!==m[m.length-1])return true\nif(c.children)if(!(c.children instanceof HTMLCollection||c.children instanceof NodeList))return true\nvar l=0\nif(c.children)l=c.children.length\nfor(var k=0;k<l;k++){var j=c.children[k]\nif(j.id=='attributes'||j.name=='attributes'||j.id=='lastChild'||j.name=='lastChild'||j.id=='children'||j.name=='children')return true}return false}(a)\nu=r?!0:!(a.attributes instanceof NamedNodeMap)}catch(o){H.ax(o)}q=\"element unprintable\"\ntry{q=J.a4(a)}catch(o){H.ax(o)}try{p=W.Q(a)\nthis.aA(a,b,u,q,p,t,s)}catch(o){if(H.ax(o) instanceof P.n)throw o\nelse{this.F(a,b)\nwindow\nn=\"Removing corrupted element \"+H.d(q)\nif(typeof console!=\"undefined\")window.console.warn(n)}}},\naA:function(a,b,c,d,e,f,g){var u,t,s,r,q\nif(c){this.F(a,b)\nwindow\nu=\"Removing element due to corrupted attributes on <\"+d+\">\"\nif(typeof console!=\"undefined\")window.console.warn(u)\nreturn}if(!this.a.C(a)){this.F(a,b)\nwindow\nu=\"Removing disallowed element <\"+H.d(e)+\"> from \"+H.d(b)\nif(typeof console!=\"undefined\")window.console.warn(u)\nreturn}if(g!=null)if(!this.a.B(a,\"is\",g)){this.F(a,b)\nwindow\nu=\"Removing disallowed type extension <\"+H.d(e)+' is=\"'+g+'\">'\nif(typeof console!=\"undefined\")window.console.warn(u)\nreturn}u=f.gE()\nt=H.i(u.slice(0),[H.c3(u,0)])\nfor(s=f.gE().length-1,u=f.a;s>=0;--s){r=t[s]\nif(!this.a.B(a,J.cV(r),u.getAttribute(r))){window\nq=\"Removing disallowed attribute <\"+H.d(e)+\" \"+r+'=\"'+H.d(u.getAttribute(r))+'\">'\nif(typeof console!=\"undefined\")window.console.warn(q)\nu.removeAttribute(r)}}if(!!J.t(a).$iW)this.a3(a.content)}}\nW.bF.prototype={\n$2:function(a,b){var u,t,s,r,q,p\ns=this.a\nswitch(a.nodeType){case 1:s.aB(a,b)\nbreak\ncase 8:case 11:case 3:case 4:break\ndefault:s.F(a,b)}u=a.lastChild\nfor(s=a==null;null!=u;){t=null\ntry{t=u.previousSibling}catch(r){H.ax(r)\nq=u\nif(s){p=q.parentNode\nif(p!=null)p.removeChild(q)}else a.removeChild(q)\nu=null\nt=a.lastChild}if(u!=null)this.$2(u,a)\nu=t}}}\nW.ap.prototype={}\nW.aq.prototype={}\nW.at.prototype={}\nW.au.prototype={}\nP.U.prototype={$iU:1}\nP.b.prototype={\nu:function(a,b,c,d){var u,t,s,r,q,p\nif(d==null){u=H.i([],[W.q])\nd=new W.af(u)\nu.push(W.co(null))\nu.push(W.cp())\nu.push(new W.bC())}c=new W.as(d)\nt='<svg version=\"1.1\">'+b+\"</svg>\"\nu=document\ns=u.body\nr=(s&&C.l).aH(s,t,c)\nq=u.createDocumentFragment()\nr.toString\nu=new W.l(r)\np=u.gK(u)\nfor(;u=p.firstChild,u!=null;)q.appendChild(u)\nreturn q},\n$ib:1}\nM.S.prototype={\nh:function(a){return this.b}}\nM.aH.prototype={\nI:function(a,b,c,d){var u,t,s,r,q\nif(d==null){d=new P.O(Date.now(),!1)\nu=this.a\nd=u<=0?d.H(0,P.bU(365,0)):d.H(0,P.bU(0,C.c.N(u*1000)))}if(a==null||b==null)throw H.e(P.bS(\"Null inputs. (diff_main)\"))\nif(a===b){t=H.i([],[M.f])\nif(a.length!==0)t.push(new M.f(C.d,a))\nreturn t}s=this.Y(a,b)\nr=J.cU(a,0,s)\na=C.a.n(a,s)\nb=C.a.n(b,s)\ns=this.Z(a,b)\nu=a.length-s\nq=C.a.n(a,u)\nt=this.ax(C.a.j(a,0,u),C.a.j(b,0,b.length-s),!1,d)\nif(r.length!==0)C.b.M(t,0,new M.f(C.d,r))\nif(q.length!==0)t.push(new M.f(C.d,q))\nthis.ag(t)\nreturn t},\naI:function(a,b,c){return this.I(a,b,c,null)},\nax:function(a,b,c,d){var u,t,s,r,q,p,o,n,m,l,k,j,i,h,g\nu=H.i([],[M.f])\nt=a.length\nif(t===0){u.push(new M.f(C.f,b))\nreturn u}s=b.length\nif(s===0){u.push(new M.f(C.e,a))\nreturn u}t=t>s\nr=t?a:b\nq=t?b:a\np=C.a.aM(r,q)\nif(p!==-1){o=t?C.e:C.f\nu.push(new M.f(o,C.a.j(r,0,p)))\nu.push(new M.f(C.d,q))\nu.push(new M.f(o,C.a.n(r,p+q.length)))\nreturn u}if(q.length===1){u.push(new M.f(C.e,a))\nu.push(new M.f(C.f,b))\nreturn u}n=this.ay(a,b)\nif(n!=null){m=n[0]\nl=n[1]\nk=n[2]\nj=n[3]\ni=n[4]\nh=this.I(m,k,!1,d)\ng=this.I(l,j,!1,d)\nh.push(new M.f(C.d,i))\nC.b.t(h,g)\nreturn h}return this.aw(a,b,d)},\naw:function(a6,a7,a8){var u,t,s,r,q,p,o,n,m,l,k,j,i,h,g,f,e,d,c,b,a,a0,a1,a2,a3,a4,a5\nu=a6.length\nt=a7.length\ns=C.c.G(u+t+1,2)\nr=2*s\nq=new Array(r)\nq.fixed$length=Array\np=[P.a0]\no=H.i(q,p)\nq=new Array(r)\nq.fixed$length=Array\nn=H.i(q,p)\nfor(m=0;m<r;++m){o[m]=-1\nn[m]=-1}q=s+1\no[q]=0\nn[q]=0\nl=u-t\nq=C.c.ak(l,2)===0\nk=!q\nfor(p=s+l,j=a8.a,i=0,h=0,g=0,f=0,e=0;e<s;++e){if(C.c.aG(Date.now(),j)===1)break\nfor(d=-e,c=d+i;c<=e-h;c+=2){b=s+c\nif(c!==d)a=c!==e&&o[b-1]<o[b+1]\nelse a=!0\na0=a?o[b+1]:o[b-1]+1\na1=a0-c\nwhile(!0){if(!(a0<u&&a1<t&&a6[a0]===a7[a1]))break;++a0;++a1}o[b]=a0\nif(a0>u)h+=2\nelse if(a1>t)i+=2\nelse if(k){a2=p-c\nif(a2>=0&&a2<r&&n[a2]!==-1)if(a0>=u-n[a2])return this.a9(a6,a7,a0,a1,a8)}}for(a3=d+g;a3<=e-f;a3+=2){a2=s+a3\nif(a3!==d)a=a3!==e&&n[a2-1]<n[a2+1]\nelse a=!0\na4=a?n[a2+1]:n[a2-1]+1\na5=a4-a3\nwhile(!0){if(!(a4<u&&a5<t&&a6[u-a4-1]===a7[t-a5-1]))break;++a4;++a5}n[a2]=a4\nif(a4>u)f+=2\nelse if(a5>t)g+=2\nelse if(q){b=p-a3\nif(b>=0&&b<r&&o[b]!==-1){a0=o[b]\na1=s+a0-b\nif(a0>=u-a4)return this.a9(a6,a7,a0,a1,a8)}}}}return H.i([new M.f(C.e,a6),new M.f(C.f,a7)],[M.f])},\na9:function(a,b,c,d,e){var u,t,s,r,q\nu=C.a.j(a,0,c)\nt=C.a.j(b,0,d)\ns=C.a.n(a,c)\nr=C.a.n(b,d)\nq=this.I(u,t,!1,e)\nC.b.t(q,this.I(s,r,!1,e))\nreturn q},\nY:function(a,b){var u,t\nu=Math.min(a.length,b.length)\nfor(t=0;t<u;++t)if(a[t]!==b[t])return t\nreturn u},\nZ:function(a,b){var u,t,s,r\nu=a.length\nt=b.length\ns=Math.min(u,t)\nfor(r=1;r<=s;++r)if(a[u-r]!==b[t-r])return r-1\nreturn s},\nay:function(a,b){var u,t,s,r,q,p,o\nif(this.a<=0)return\nu=a.length>b.length\nt=u?a:b\ns=u?b:a\nr=t.length\nif(r<4||s.length*2<r)return\nq=this.aa(t,s,C.c.N(C.h.af((r+3)/4)))\np=this.aa(t,s,C.c.N(C.h.af((r+1)/2)))\nr=q==null\nif(r&&p==null)return\nelse if(p==null)o=q\nelse if(r)o=p\nelse o=q[4].length>p[4].length?q:p\nif(u)return o\nelse return H.i([o[2],o[3],o[0],o[1],o[4]],[P.h])},\naa:function(a,b,c){var u,t,s,r,q,p,o,n,m,l,k,j\nu=a.length\nt=C.a.j(a,c,c+C.c.N(C.h.aL(u/4)))\nfor(s=-1,r=\"\",q=\"\",p=\"\",o=\"\",n=\"\";s=C.a.ah(b,t,s+1),s!==-1;){m=this.Y(C.a.n(a,c),C.a.n(b,s))\nl=this.Z(C.a.j(a,0,c),C.a.j(b,0,s))\nif(r.length<l+m){k=s-l\nj=s+m\nr=C.a.j(b,k,s)+C.a.j(b,s,j)\nq=C.a.j(a,0,c-l)\np=C.a.n(a,c+m)\no=C.a.j(b,0,k)\nn=C.a.n(b,j)}}if(r.length*2>=u)return H.i([q,p,o,n,r],[P.h])\nelse return},\nag:function(a){var u,t,s,r,q,p,o,n,m,l,k,j,i,h\na.push(new M.f(C.d,\"\"))\nfor(u=0,t=0,s=0,r=\"\",q=\"\",p=null;u<a.length;){o=a[u]\nswitch(o.a){case C.f:++s\nq+=o.b;++u\nbreak\ncase C.e:++t\nr+=o.b;++u\nbreak\ncase C.d:n=t+s\nif(n>1){if(t!==0&&s!==0){p=this.Y(q,r)\nif(p!==0){o=u-t-s\nif(o>0&&a[o-1].a===C.d){o=a[o-1]\no.b=o.b+C.a.j(q,0,p)}else{C.b.M(a,0,new M.f(C.d,C.a.j(q,0,p)));++u}q=C.a.n(q,p)\nr=C.a.n(r,p)}p=this.Z(q,r)\nif(p!==0){o=a[u]\nm=q.length-p\no.b=C.a.n(q,m)+o.b\nq=C.a.j(q,0,m)\nr=C.a.j(r,0,r.length-p)}}u-=n\no=u+t+s\nP.di(u,o,a.length)\na.splice(u,o-u)\nif(r.length!==0){C.b.M(a,u,new M.f(C.e,r));++u}if(q.length!==0){C.b.M(a,u,new M.f(C.f,q));++u}++u}else if(u!==0&&a[u-1].a===C.d){n=a[u-1]\nn.b=n.b+o.b\nC.b.a1(a,u)}else ++u\nt=0\ns=0\nr=\"\"\nq=\"\"\nbreak}}if(C.b.gaO(a).b.length===0)a.pop()\nfor(u=1,l=!1;u<a.length-1;){o=u-1\nn=a[o]\nif(n.a===C.d&&a[u+1].a===C.d){m=a[u]\nk=m.b\nj=n.b\nif(C.a.aK(k,j)){m.b=j+C.a.j(k,0,k.length-j.length)\nm=a[u+1]\nm.b=n.b+m.b\nC.b.a1(a,o)\nl=!0}else{o=u+1\ni=a[o]\nh=i.b\nif(C.a.a5(k,h)){n.b=j+h\nn=m.b\ni=i.b\nm.b=C.a.n(n,i.length)+i\nC.b.a1(a,o)\nl=!0}}}++u}if(l)this.ag(a)},\naJ:function(a){var u,t,s,r,q,p\nu=new P.G(\"\")\nfor(t=a.length,s=0;s<a.length;a.length===t||(0,H.aw)(a),++s){r=a[s]\nq=r.b\nq=H.av(q,\"&\",\"&amp;\")\nq=H.av(q,\"<\",\"&lt;\")\nq=H.av(q,\">\",\"&gt;\")\np=H.av(q,\"\\n\",\"&para;<br>\")\nswitch(r.a){case C.f:q=u.a+='<ins style=\"background:#e6ffe6;\">'\nq+=p\nu.a=q\nu.a=q+\"</ins>\"\nbreak\ncase C.e:q=u.a+='<del style=\"background:#ffe6e6;\">'\nq+=p\nu.a=q\nu.a=q+\"</del>\"\nbreak\ncase C.d:q=u.a+=\"<span>\"\nq+=p\nu.a=q\nu.a=q+\"</span>\"\nbreak}}t=u.a\nreturn t.charCodeAt(0)==0?t:t}}\nM.f.prototype={\nh:function(a){var u,t\nu=this.b\nt=H.av(u,\"\\n\",\"\\xb6\")\nreturn\"Diff(\"+this.a.h(0)+',\"'+t+'\")'},\nw:function(a,b){var u\nif(b==null)return!1\nif(this!==b)u=b instanceof M.f&&new H.X(H.cv(this)).w(0,new H.X(H.cv(b)))&&this.a===b.a&&this.b===b.b\nelse u=!0\nreturn u},\ngm:function(a){return(H.F(this.a)^C.a.gm(this.b))>>>0}}\nO.bl.prototype={\nC:function(a){return!0},\nB:function(a,b,c){return!0},\n$iq:1};(function aliases(){var u=J.k.prototype\nu.al=u.h\nu=J.ab.prototype\nu.an=u.h\nu=P.C.prototype\nu.am=u.O\nu=W.p.prototype\nu.P=u.u\nu=W.ar.prototype\nu.ao=u.B})();(function installTearOffs(){var u=hunkHelpers.installStaticTearOff,t=hunkHelpers._static_1\nu(W,\"dw\",4,null,[\"$4\"],[\"dk\"],0,0)\nu(W,\"dx\",4,null,[\"$4\"],[\"dl\"],0,0)\nt(O,\"dj\",\"dD\",1)})();(function inheritance(){var u=hunkHelpers.mixin,t=hunkHelpers.inherit,s=hunkHelpers.inheritMany\nt(P.m,null)\ns(P.m,[H.bY,J.k,J.aD,P.C,H.ad,P.aS,H.bm,P.R,H.M,H.X,P.b7,H.aZ,H.b0,H.aW,P.bz,P.bw,P.bx,P.an,P.v,P.J,P.O,P.a1,P.P,P.ak,P.bu,P.aO,P.aP,P.b2,P.ag,P.h,P.G,W.Z,W.a7,W.af,W.ar,W.bC,W.a6,W.q,W.by,W.as,M.S,M.aH,M.f,O.bl])\ns(J.k,[J.aT,J.aU,J.ab,J.x,J.aV,J.D,W.B,W.aI,W.a,W.b4,W.ap,W.at])\ns(J.ab,[J.bd,J.Y,J.y])\nt(J.bX,J.x)\ns(J.aV,[J.a9,J.a8])\ns(P.C,[H.aL,H.am])\ns(H.aL,[H.ac,H.b_])\nt(H.b8,H.ac)\nt(H.br,P.aS)\ns(P.R,[H.bb,H.aY,H.bp,H.bf,P.bc,P.n,P.bq,P.bo,P.V,P.aF,P.aG])\ns(H.M,[H.bQ,H.bk,H.bK,H.bL,H.bM,P.b6,P.aJ,P.aK,W.aM,W.ba,W.b9,W.bA,W.bB,W.bE,W.bF])\ns(H.bk,[H.bh,H.K])\nt(P.b5,P.b7)\ns(P.b5,[H.aX,W.bs])\nt(P.bv,P.bz)\nt(P.b3,P.an)\ns(P.a1,[P.bH,P.a0])\ns(P.n,[P.ai,P.aQ])\nt(W.j,W.B)\ns(W.j,[W.p,W.w])\ns(W.p,[W.c,P.b])\ns(W.c,[W.aB,W.aC,W.A,W.aN,W.bg,W.al,W.bi,W.bj,W.W])\nt(W.l,P.b3)\nt(W.aq,W.ap)\nt(W.ae,W.aq)\nt(W.au,W.at)\nt(W.ao,W.au)\nt(W.bt,W.bs)\nt(W.bD,W.ar)\nt(P.U,P.b)\nu(P.an,P.v)\nu(W.ap,P.v)\nu(W.aq,W.a7)\nu(W.at,P.v)\nu(W.au,W.a7)})();(function constants(){var u=hunkHelpers.makeConstList\nC.l=W.A.prototype\nC.x=J.k.prototype\nC.b=J.x.prototype\nC.h=J.a8.prototype\nC.c=J.a9.prototype\nC.a=J.D.prototype\nC.y=J.y.prototype\nC.o=J.bd.prototype\nC.p=W.al.prototype\nC.k=J.Y.prototype\nC.m=function getTagFallback(o) {\n  var s = Object.prototype.toString.call(o);\n  return s.substring(8, s.length - 1);\n}\nC.q=function() {\n  var toStringFunction = Object.prototype.toString;\n  function getTag(o) {\n    var s = toStringFunction.call(o);\n    return s.substring(8, s.length - 1);\n  }\n  function getUnknownTag(object, tag) {\n    if (/^HTML[A-Z].*Element$/.test(tag)) {\n      var name = toStringFunction.call(object);\n      if (name == \"[object Object]\") return null;\n      return \"HTMLElement\";\n    }\n  }\n  function getUnknownTagGenericBrowser(object, tag) {\n    if (self.HTMLElement && object instanceof HTMLElement) return \"HTMLElement\";\n    return getUnknownTag(object, tag);\n  }\n  function prototypeForTag(tag) {\n    if (typeof window == \"undefined\") return null;\n    if (typeof window[tag] == \"undefined\") return null;\n    var constructor = window[tag];\n    if (typeof constructor != \"function\") return null;\n    return constructor.prototype;\n  }\n  function discriminator(tag) { return null; }\n  var isBrowser = typeof navigator == \"object\";\n  return {\n    getTag: getTag,\n    getUnknownTag: isBrowser ? getUnknownTagGenericBrowser : getUnknownTag,\n    prototypeForTag: prototypeForTag,\n    discriminator: discriminator };\n}\nC.w=function(getTagFallback) {\n  return function(hooks) {\n    if (typeof navigator != \"object\") return hooks;\n    var ua = navigator.userAgent;\n    if (ua.indexOf(\"DumpRenderTree\") >= 0) return hooks;\n    if (ua.indexOf(\"Chrome\") >= 0) {\n      function confirm(p) {\n        return typeof window == \"object\" && window[p] && window[p].name == p;\n      }\n      if (confirm(\"Window\") && confirm(\"HTMLElement\")) return hooks;\n    }\n    hooks.getTag = getTagFallback;\n  };\n}\nC.r=function(hooks) {\n  if (typeof dartExperimentalFixupGetTag != \"function\") return hooks;\n  hooks.getTag = dartExperimentalFixupGetTag(hooks.getTag);\n}\nC.t=function(hooks) {\n  var getTag = hooks.getTag;\n  var prototypeForTag = hooks.prototypeForTag;\n  function getTagFixed(o) {\n    var tag = getTag(o);\n    if (tag == \"Document\") {\n      if (!!o.xmlVersion) return \"!Document\";\n      return \"!HTMLDocument\";\n    }\n    return tag;\n  }\n  function prototypeForTagFixed(tag) {\n    if (tag == \"Document\") return null;\n    return prototypeForTag(tag);\n  }\n  hooks.getTag = getTagFixed;\n  hooks.prototypeForTag = prototypeForTagFixed;\n}\nC.v=function(hooks) {\n  var userAgent = typeof navigator == \"object\" ? navigator.userAgent : \"\";\n  if (userAgent.indexOf(\"Firefox\") == -1) return hooks;\n  var getTag = hooks.getTag;\n  var quickMap = {\n    \"BeforeUnloadEvent\": \"Event\",\n    \"DataTransfer\": \"Clipboard\",\n    \"GeoGeolocation\": \"Geolocation\",\n    \"Location\": \"!Location\",\n    \"WorkerMessageEvent\": \"MessageEvent\",\n    \"XMLDocument\": \"!Document\"};\n  function getTagFirefox(o) {\n    var tag = getTag(o);\n    return quickMap[tag] || tag;\n  }\n  hooks.getTag = getTagFirefox;\n}\nC.u=function(hooks) {\n  var userAgent = typeof navigator == \"object\" ? navigator.userAgent : \"\";\n  if (userAgent.indexOf(\"Trident/\") == -1) return hooks;\n  var getTag = hooks.getTag;\n  var quickMap = {\n    \"BeforeUnloadEvent\": \"Event\",\n    \"DataTransfer\": \"Clipboard\",\n    \"HTMLDDElement\": \"HTMLElement\",\n    \"HTMLDTElement\": \"HTMLElement\",\n    \"HTMLPhraseElement\": \"HTMLElement\",\n    \"Position\": \"Geoposition\"\n  };\n  function getTagIE(o) {\n    var tag = getTag(o);\n    var newTag = quickMap[tag];\n    if (newTag) return newTag;\n    if (tag == \"Object\") {\n      if (window.DataView && (o instanceof window.DataView)) return \"DataView\";\n    }\n    return tag;\n  }\n  function prototypeForTagIE(tag) {\n    var constructor = window[tag];\n    if (constructor == null) return null;\n    return constructor.prototype;\n  }\n  hooks.getTag = getTagIE;\n  hooks.prototypeForTag = prototypeForTagIE;\n}\nC.n=function(hooks) { return hooks; }\n\nC.z=H.i(u([\"*::class\",\"*::dir\",\"*::draggable\",\"*::hidden\",\"*::id\",\"*::inert\",\"*::itemprop\",\"*::itemref\",\"*::itemscope\",\"*::lang\",\"*::spellcheck\",\"*::title\",\"*::translate\",\"A::accesskey\",\"A::coords\",\"A::hreflang\",\"A::name\",\"A::shape\",\"A::tabindex\",\"A::target\",\"A::type\",\"AREA::accesskey\",\"AREA::alt\",\"AREA::coords\",\"AREA::nohref\",\"AREA::shape\",\"AREA::tabindex\",\"AREA::target\",\"AUDIO::controls\",\"AUDIO::loop\",\"AUDIO::mediagroup\",\"AUDIO::muted\",\"AUDIO::preload\",\"BDO::dir\",\"BODY::alink\",\"BODY::bgcolor\",\"BODY::link\",\"BODY::text\",\"BODY::vlink\",\"BR::clear\",\"BUTTON::accesskey\",\"BUTTON::disabled\",\"BUTTON::name\",\"BUTTON::tabindex\",\"BUTTON::type\",\"BUTTON::value\",\"CANVAS::height\",\"CANVAS::width\",\"CAPTION::align\",\"COL::align\",\"COL::char\",\"COL::charoff\",\"COL::span\",\"COL::valign\",\"COL::width\",\"COLGROUP::align\",\"COLGROUP::char\",\"COLGROUP::charoff\",\"COLGROUP::span\",\"COLGROUP::valign\",\"COLGROUP::width\",\"COMMAND::checked\",\"COMMAND::command\",\"COMMAND::disabled\",\"COMMAND::label\",\"COMMAND::radiogroup\",\"COMMAND::type\",\"DATA::value\",\"DEL::datetime\",\"DETAILS::open\",\"DIR::compact\",\"DIV::align\",\"DL::compact\",\"FIELDSET::disabled\",\"FONT::color\",\"FONT::face\",\"FONT::size\",\"FORM::accept\",\"FORM::autocomplete\",\"FORM::enctype\",\"FORM::method\",\"FORM::name\",\"FORM::novalidate\",\"FORM::target\",\"FRAME::name\",\"H1::align\",\"H2::align\",\"H3::align\",\"H4::align\",\"H5::align\",\"H6::align\",\"HR::align\",\"HR::noshade\",\"HR::size\",\"HR::width\",\"HTML::version\",\"IFRAME::align\",\"IFRAME::frameborder\",\"IFRAME::height\",\"IFRAME::marginheight\",\"IFRAME::marginwidth\",\"IFRAME::width\",\"IMG::align\",\"IMG::alt\",\"IMG::border\",\"IMG::height\",\"IMG::hspace\",\"IMG::ismap\",\"IMG::name\",\"IMG::usemap\",\"IMG::vspace\",\"IMG::width\",\"INPUT::accept\",\"INPUT::accesskey\",\"INPUT::align\",\"INPUT::alt\",\"INPUT::autocomplete\",\"INPUT::autofocus\",\"INPUT::checked\",\"INPUT::disabled\",\"INPUT::inputmode\",\"INPUT::ismap\",\"INPUT::list\",\"INPUT::max\",\"INPUT::maxlength\",\"INPUT::min\",\"INPUT::multiple\",\"INPUT::name\",\"INPUT::placeholder\",\"INPUT::readonly\",\"INPUT::required\",\"INPUT::size\",\"INPUT::step\",\"INPUT::tabindex\",\"INPUT::type\",\"INPUT::usemap\",\"INPUT::value\",\"INS::datetime\",\"KEYGEN::disabled\",\"KEYGEN::keytype\",\"KEYGEN::name\",\"LABEL::accesskey\",\"LABEL::for\",\"LEGEND::accesskey\",\"LEGEND::align\",\"LI::type\",\"LI::value\",\"LINK::sizes\",\"MAP::name\",\"MENU::compact\",\"MENU::label\",\"MENU::type\",\"METER::high\",\"METER::low\",\"METER::max\",\"METER::min\",\"METER::value\",\"OBJECT::typemustmatch\",\"OL::compact\",\"OL::reversed\",\"OL::start\",\"OL::type\",\"OPTGROUP::disabled\",\"OPTGROUP::label\",\"OPTION::disabled\",\"OPTION::label\",\"OPTION::selected\",\"OPTION::value\",\"OUTPUT::for\",\"OUTPUT::name\",\"P::align\",\"PRE::width\",\"PROGRESS::max\",\"PROGRESS::min\",\"PROGRESS::value\",\"SELECT::autocomplete\",\"SELECT::disabled\",\"SELECT::multiple\",\"SELECT::name\",\"SELECT::required\",\"SELECT::size\",\"SELECT::tabindex\",\"SOURCE::type\",\"TABLE::align\",\"TABLE::bgcolor\",\"TABLE::border\",\"TABLE::cellpadding\",\"TABLE::cellspacing\",\"TABLE::frame\",\"TABLE::rules\",\"TABLE::summary\",\"TABLE::width\",\"TBODY::align\",\"TBODY::char\",\"TBODY::charoff\",\"TBODY::valign\",\"TD::abbr\",\"TD::align\",\"TD::axis\",\"TD::bgcolor\",\"TD::char\",\"TD::charoff\",\"TD::colspan\",\"TD::headers\",\"TD::height\",\"TD::nowrap\",\"TD::rowspan\",\"TD::scope\",\"TD::valign\",\"TD::width\",\"TEXTAREA::accesskey\",\"TEXTAREA::autocomplete\",\"TEXTAREA::cols\",\"TEXTAREA::disabled\",\"TEXTAREA::inputmode\",\"TEXTAREA::name\",\"TEXTAREA::placeholder\",\"TEXTAREA::readonly\",\"TEXTAREA::required\",\"TEXTAREA::rows\",\"TEXTAREA::tabindex\",\"TEXTAREA::wrap\",\"TFOOT::align\",\"TFOOT::char\",\"TFOOT::charoff\",\"TFOOT::valign\",\"TH::abbr\",\"TH::align\",\"TH::axis\",\"TH::bgcolor\",\"TH::char\",\"TH::charoff\",\"TH::colspan\",\"TH::headers\",\"TH::height\",\"TH::nowrap\",\"TH::rowspan\",\"TH::scope\",\"TH::valign\",\"TH::width\",\"THEAD::align\",\"THEAD::char\",\"THEAD::charoff\",\"THEAD::valign\",\"TR::align\",\"TR::bgcolor\",\"TR::char\",\"TR::charoff\",\"TR::valign\",\"TRACK::default\",\"TRACK::kind\",\"TRACK::label\",\"TRACK::srclang\",\"UL::compact\",\"UL::type\",\"VIDEO::controls\",\"VIDEO::height\",\"VIDEO::loop\",\"VIDEO::mediagroup\",\"VIDEO::muted\",\"VIDEO::preload\",\"VIDEO::width\"]),[P.h])\nC.A=H.i(u([\"HEAD\",\"AREA\",\"BASE\",\"BASEFONT\",\"BR\",\"COL\",\"COLGROUP\",\"EMBED\",\"FRAME\",\"FRAMESET\",\"HR\",\"IMAGE\",\"IMG\",\"INPUT\",\"ISINDEX\",\"LINK\",\"META\",\"PARAM\",\"SOURCE\",\"STYLE\",\"TITLE\",\"WBR\"]),[P.h])\nC.B=H.i(u([]),[P.h])\nC.i=H.i(u([\"bind\",\"if\",\"ref\",\"repeat\",\"syntax\"]),[P.h])\nC.j=H.i(u([\"A::href\",\"AREA::href\",\"BLOCKQUOTE::cite\",\"BODY::background\",\"COMMAND::icon\",\"DEL::cite\",\"FORM::action\",\"IMG::src\",\"INPUT::src\",\"INS::cite\",\"Q::cite\",\"VIDEO::poster\"]),[P.h])\nC.e=new M.S(\"Operation.delete\")\nC.f=new M.S(\"Operation.insert\")\nC.d=new M.S(\"Operation.equal\")})();(function staticFields(){$.o=0\n$.L=null\n$.c9=null\n$.cw=null\n$.cr=null\n$.cz=null\n$.bG=null\n$.bN=null\n$.c4=null\n$.u=null\n$.bV=null\n$.cd=null\n$.cc=null})();(function lazyInitializers(){var u=hunkHelpers.lazy\nu($,\"dJ\",\"cB\",function(){return H.cu(\"_$dart_dartClosure\")})\nu($,\"dK\",\"c6\",function(){return H.cu(\"_$dart_js\")})\nu($,\"dL\",\"cC\",function(){return H.r(H.bn({\ntoString:function(){return\"$receiver$\"}}))})\nu($,\"dM\",\"cD\",function(){return H.r(H.bn({$method$:null,\ntoString:function(){return\"$receiver$\"}}))})\nu($,\"dN\",\"cE\",function(){return H.r(H.bn(null))})\nu($,\"dO\",\"cF\",function(){return H.r(function(){var $argumentsExpr$='$arguments$'\ntry{null.$method$($argumentsExpr$)}catch(t){return t.message}}())})\nu($,\"dR\",\"cI\",function(){return H.r(H.bn(void 0))})\nu($,\"dS\",\"cJ\",function(){return H.r(function(){var $argumentsExpr$='$arguments$'\ntry{(void 0).$method$($argumentsExpr$)}catch(t){return t.message}}())})\nu($,\"dQ\",\"cH\",function(){return H.r(H.cm(null))})\nu($,\"dP\",\"cG\",function(){return H.r(function(){try{null.$method$}catch(t){return t.message}}())})\nu($,\"dU\",\"cL\",function(){return H.r(H.cm(void 0))})\nu($,\"dT\",\"cK\",function(){return H.r(function(){try{(void 0).$method$}catch(t){return t.message}}())})\nu($,\"dX\",\"a3\",function(){return[]})\nu($,\"dV\",\"cM\",function(){return P.ch([\"A\",\"ABBR\",\"ACRONYM\",\"ADDRESS\",\"AREA\",\"ARTICLE\",\"ASIDE\",\"AUDIO\",\"B\",\"BDI\",\"BDO\",\"BIG\",\"BLOCKQUOTE\",\"BR\",\"BUTTON\",\"CANVAS\",\"CAPTION\",\"CENTER\",\"CITE\",\"CODE\",\"COL\",\"COLGROUP\",\"COMMAND\",\"DATA\",\"DATALIST\",\"DD\",\"DEL\",\"DETAILS\",\"DFN\",\"DIR\",\"DIV\",\"DL\",\"DT\",\"EM\",\"FIELDSET\",\"FIGCAPTION\",\"FIGURE\",\"FONT\",\"FOOTER\",\"FORM\",\"H1\",\"H2\",\"H3\",\"H4\",\"H5\",\"H6\",\"HEADER\",\"HGROUP\",\"HR\",\"I\",\"IFRAME\",\"IMG\",\"INPUT\",\"INS\",\"KBD\",\"LABEL\",\"LEGEND\",\"LI\",\"MAP\",\"MARK\",\"MENU\",\"METER\",\"NAV\",\"NOBR\",\"OL\",\"OPTGROUP\",\"OPTION\",\"OUTPUT\",\"P\",\"PRE\",\"PROGRESS\",\"Q\",\"S\",\"SAMP\",\"SECTION\",\"SELECT\",\"SMALL\",\"SOURCE\",\"SPAN\",\"STRIKE\",\"STRONG\",\"SUB\",\"SUMMARY\",\"SUP\",\"TABLE\",\"TBODY\",\"TD\",\"TEXTAREA\",\"TFOOT\",\"TH\",\"THEAD\",\"TIME\",\"TR\",\"TRACK\",\"TT\",\"U\",\"UL\",\"VAR\",\"VIDEO\",\"WBR\"],P.h)})\nu($,\"dW\",\"c7\",function(){return P.d8(P.h,P.aP)})})()\nvar v={mangledGlobalNames:{a0:\"int\",bH:\"double\",a1:\"num\",h:\"String\",J:\"bool\",ag:\"Null\",b2:\"List\"},mangledNames:{},getTypeFromName:getGlobalFromName,metadata:[],types:[{func:1,ret:P.J,args:[W.p,P.h,P.h,W.Z]},{func:1,ret:-1,args:[W.a]}],interceptorsByTag:null,leafTags:null};(function nativeSupport(){!function(){var u=function(a){var o={}\no[a]=1\nreturn Object.keys(hunkHelpers.convertToFastObject(o))[0]}\nv.getIsolateTag=function(a){return u(\"___dart_\"+a+v.isolateTag)}\nvar t=\"___dart_isolate_tags_\"\nvar s=Object[t]||(Object[t]=Object.create(null))\nvar r=\"_ZxYxX\"\nfor(var q=0;;q++){var p=u(r+\"_\"+q+\"_\")\nif(!(p in s)){s[p]=1\nv.isolateTag=p\nbreak}}v.dispatchPropertyName=v.getIsolateTag(\"dispatch_record\")}()\nhunkHelpers.setOrUpdateInterceptorsByTag({DOMError:J.k,DOMImplementation:J.k,MediaError:J.k,NavigatorUserMediaError:J.k,OverconstrainedError:J.k,PositionError:J.k,Range:J.k,SQLError:J.k,HTMLAudioElement:W.c,HTMLBRElement:W.c,HTMLBaseElement:W.c,HTMLButtonElement:W.c,HTMLCanvasElement:W.c,HTMLContentElement:W.c,HTMLDListElement:W.c,HTMLDataElement:W.c,HTMLDataListElement:W.c,HTMLDetailsElement:W.c,HTMLDialogElement:W.c,HTMLDivElement:W.c,HTMLEmbedElement:W.c,HTMLFieldSetElement:W.c,HTMLHRElement:W.c,HTMLHeadElement:W.c,HTMLHeadingElement:W.c,HTMLHtmlElement:W.c,HTMLIFrameElement:W.c,HTMLImageElement:W.c,HTMLInputElement:W.c,HTMLLIElement:W.c,HTMLLabelElement:W.c,HTMLLegendElement:W.c,HTMLLinkElement:W.c,HTMLMapElement:W.c,HTMLMediaElement:W.c,HTMLMenuElement:W.c,HTMLMetaElement:W.c,HTMLMeterElement:W.c,HTMLModElement:W.c,HTMLOListElement:W.c,HTMLObjectElement:W.c,HTMLOptGroupElement:W.c,HTMLOptionElement:W.c,HTMLOutputElement:W.c,HTMLParagraphElement:W.c,HTMLParamElement:W.c,HTMLPictureElement:W.c,HTMLPreElement:W.c,HTMLProgressElement:W.c,HTMLQuoteElement:W.c,HTMLScriptElement:W.c,HTMLShadowElement:W.c,HTMLSlotElement:W.c,HTMLSourceElement:W.c,HTMLSpanElement:W.c,HTMLStyleElement:W.c,HTMLTableCaptionElement:W.c,HTMLTableCellElement:W.c,HTMLTableDataCellElement:W.c,HTMLTableHeaderCellElement:W.c,HTMLTableColElement:W.c,HTMLTextAreaElement:W.c,HTMLTimeElement:W.c,HTMLTitleElement:W.c,HTMLTrackElement:W.c,HTMLUListElement:W.c,HTMLUnknownElement:W.c,HTMLVideoElement:W.c,HTMLDirectoryElement:W.c,HTMLFontElement:W.c,HTMLFrameElement:W.c,HTMLFrameSetElement:W.c,HTMLMarqueeElement:W.c,HTMLElement:W.c,HTMLAnchorElement:W.aB,HTMLAreaElement:W.aC,HTMLBodyElement:W.A,CDATASection:W.w,CharacterData:W.w,Comment:W.w,ProcessingInstruction:W.w,Text:W.w,DOMException:W.aI,Element:W.p,AbortPaymentEvent:W.a,AnimationEvent:W.a,AnimationPlaybackEvent:W.a,ApplicationCacheErrorEvent:W.a,BackgroundFetchClickEvent:W.a,BackgroundFetchEvent:W.a,BackgroundFetchFailEvent:W.a,BackgroundFetchedEvent:W.a,BeforeInstallPromptEvent:W.a,BeforeUnloadEvent:W.a,BlobEvent:W.a,CanMakePaymentEvent:W.a,ClipboardEvent:W.a,CloseEvent:W.a,CompositionEvent:W.a,CustomEvent:W.a,DeviceMotionEvent:W.a,DeviceOrientationEvent:W.a,ErrorEvent:W.a,Event:W.a,InputEvent:W.a,ExtendableEvent:W.a,ExtendableMessageEvent:W.a,FetchEvent:W.a,FocusEvent:W.a,FontFaceSetLoadEvent:W.a,ForeignFetchEvent:W.a,GamepadEvent:W.a,HashChangeEvent:W.a,InstallEvent:W.a,KeyboardEvent:W.a,MediaEncryptedEvent:W.a,MediaKeyMessageEvent:W.a,MediaQueryListEvent:W.a,MediaStreamEvent:W.a,MediaStreamTrackEvent:W.a,MessageEvent:W.a,MIDIConnectionEvent:W.a,MIDIMessageEvent:W.a,MouseEvent:W.a,DragEvent:W.a,MutationEvent:W.a,NotificationEvent:W.a,PageTransitionEvent:W.a,PaymentRequestEvent:W.a,PaymentRequestUpdateEvent:W.a,PointerEvent:W.a,PopStateEvent:W.a,PresentationConnectionAvailableEvent:W.a,PresentationConnectionCloseEvent:W.a,ProgressEvent:W.a,PromiseRejectionEvent:W.a,PushEvent:W.a,RTCDataChannelEvent:W.a,RTCDTMFToneChangeEvent:W.a,RTCPeerConnectionIceEvent:W.a,RTCTrackEvent:W.a,SecurityPolicyViolationEvent:W.a,SensorErrorEvent:W.a,SpeechRecognitionError:W.a,SpeechRecognitionEvent:W.a,SpeechSynthesisEvent:W.a,StorageEvent:W.a,SyncEvent:W.a,TextEvent:W.a,TouchEvent:W.a,TrackEvent:W.a,TransitionEvent:W.a,WebKitTransitionEvent:W.a,UIEvent:W.a,VRDeviceEvent:W.a,VRDisplayEvent:W.a,VRSessionEvent:W.a,WheelEvent:W.a,MojoInterfaceRequestEvent:W.a,ResourceProgressEvent:W.a,USBConnectionEvent:W.a,IDBVersionChangeEvent:W.a,AudioProcessingEvent:W.a,OfflineAudioCompletionEvent:W.a,WebGLContextEvent:W.a,Window:W.B,DOMWindow:W.B,EventTarget:W.B,HTMLFormElement:W.aN,Location:W.b4,Document:W.j,DocumentFragment:W.j,HTMLDocument:W.j,ShadowRoot:W.j,XMLDocument:W.j,Attr:W.j,DocumentType:W.j,Node:W.j,NodeList:W.ae,RadioNodeList:W.ae,HTMLSelectElement:W.bg,HTMLTableElement:W.al,HTMLTableRowElement:W.bi,HTMLTableSectionElement:W.bj,HTMLTemplateElement:W.W,NamedNodeMap:W.ao,MozNamedAttrMap:W.ao,SVGScriptElement:P.U,SVGAElement:P.b,SVGAnimateElement:P.b,SVGAnimateMotionElement:P.b,SVGAnimateTransformElement:P.b,SVGAnimationElement:P.b,SVGCircleElement:P.b,SVGClipPathElement:P.b,SVGDefsElement:P.b,SVGDescElement:P.b,SVGDiscardElement:P.b,SVGEllipseElement:P.b,SVGFEBlendElement:P.b,SVGFEColorMatrixElement:P.b,SVGFEComponentTransferElement:P.b,SVGFECompositeElement:P.b,SVGFEConvolveMatrixElement:P.b,SVGFEDiffuseLightingElement:P.b,SVGFEDisplacementMapElement:P.b,SVGFEDistantLightElement:P.b,SVGFEFloodElement:P.b,SVGFEFuncAElement:P.b,SVGFEFuncBElement:P.b,SVGFEFuncGElement:P.b,SVGFEFuncRElement:P.b,SVGFEGaussianBlurElement:P.b,SVGFEImageElement:P.b,SVGFEMergeElement:P.b,SVGFEMergeNodeElement:P.b,SVGFEMorphologyElement:P.b,SVGFEOffsetElement:P.b,SVGFEPointLightElement:P.b,SVGFESpecularLightingElement:P.b,SVGFESpotLightElement:P.b,SVGFETileElement:P.b,SVGFETurbulenceElement:P.b,SVGFilterElement:P.b,SVGForeignObjectElement:P.b,SVGGElement:P.b,SVGGeometryElement:P.b,SVGGraphicsElement:P.b,SVGImageElement:P.b,SVGLineElement:P.b,SVGLinearGradientElement:P.b,SVGMarkerElement:P.b,SVGMaskElement:P.b,SVGMetadataElement:P.b,SVGPathElement:P.b,SVGPatternElement:P.b,SVGPolygonElement:P.b,SVGPolylineElement:P.b,SVGRadialGradientElement:P.b,SVGRectElement:P.b,SVGSetElement:P.b,SVGStopElement:P.b,SVGStyleElement:P.b,SVGSVGElement:P.b,SVGSwitchElement:P.b,SVGSymbolElement:P.b,SVGTSpanElement:P.b,SVGTextContentElement:P.b,SVGTextElement:P.b,SVGTextPathElement:P.b,SVGTextPositioningElement:P.b,SVGTitleElement:P.b,SVGUseElement:P.b,SVGViewElement:P.b,SVGGradientElement:P.b,SVGComponentTransferFunctionElement:P.b,SVGFEDropShadowElement:P.b,SVGMPathElement:P.b,SVGElement:P.b})\nhunkHelpers.setOrUpdateLeafTags({DOMError:true,DOMImplementation:true,MediaError:true,NavigatorUserMediaError:true,OverconstrainedError:true,PositionError:true,Range:true,SQLError:true,HTMLAudioElement:true,HTMLBRElement:true,HTMLBaseElement:true,HTMLButtonElement:true,HTMLCanvasElement:true,HTMLContentElement:true,HTMLDListElement:true,HTMLDataElement:true,HTMLDataListElement:true,HTMLDetailsElement:true,HTMLDialogElement:true,HTMLDivElement:true,HTMLEmbedElement:true,HTMLFieldSetElement:true,HTMLHRElement:true,HTMLHeadElement:true,HTMLHeadingElement:true,HTMLHtmlElement:true,HTMLIFrameElement:true,HTMLImageElement:true,HTMLInputElement:true,HTMLLIElement:true,HTMLLabelElement:true,HTMLLegendElement:true,HTMLLinkElement:true,HTMLMapElement:true,HTMLMediaElement:true,HTMLMenuElement:true,HTMLMetaElement:true,HTMLMeterElement:true,HTMLModElement:true,HTMLOListElement:true,HTMLObjectElement:true,HTMLOptGroupElement:true,HTMLOptionElement:true,HTMLOutputElement:true,HTMLParagraphElement:true,HTMLParamElement:true,HTMLPictureElement:true,HTMLPreElement:true,HTMLProgressElement:true,HTMLQuoteElement:true,HTMLScriptElement:true,HTMLShadowElement:true,HTMLSlotElement:true,HTMLSourceElement:true,HTMLSpanElement:true,HTMLStyleElement:true,HTMLTableCaptionElement:true,HTMLTableCellElement:true,HTMLTableDataCellElement:true,HTMLTableHeaderCellElement:true,HTMLTableColElement:true,HTMLTextAreaElement:true,HTMLTimeElement:true,HTMLTitleElement:true,HTMLTrackElement:true,HTMLUListElement:true,HTMLUnknownElement:true,HTMLVideoElement:true,HTMLDirectoryElement:true,HTMLFontElement:true,HTMLFrameElement:true,HTMLFrameSetElement:true,HTMLMarqueeElement:true,HTMLElement:false,HTMLAnchorElement:true,HTMLAreaElement:true,HTMLBodyElement:true,CDATASection:true,CharacterData:true,Comment:true,ProcessingInstruction:true,Text:true,DOMException:true,Element:false,AbortPaymentEvent:true,AnimationEvent:true,AnimationPlaybackEvent:true,ApplicationCacheErrorEvent:true,BackgroundFetchClickEvent:true,BackgroundFetchEvent:true,BackgroundFetchFailEvent:true,BackgroundFetchedEvent:true,BeforeInstallPromptEvent:true,BeforeUnloadEvent:true,BlobEvent:true,CanMakePaymentEvent:true,ClipboardEvent:true,CloseEvent:true,CompositionEvent:true,CustomEvent:true,DeviceMotionEvent:true,DeviceOrientationEvent:true,ErrorEvent:true,Event:true,InputEvent:true,ExtendableEvent:true,ExtendableMessageEvent:true,FetchEvent:true,FocusEvent:true,FontFaceSetLoadEvent:true,ForeignFetchEvent:true,GamepadEvent:true,HashChangeEvent:true,InstallEvent:true,KeyboardEvent:true,MediaEncryptedEvent:true,MediaKeyMessageEvent:true,MediaQueryListEvent:true,MediaStreamEvent:true,MediaStreamTrackEvent:true,MessageEvent:true,MIDIConnectionEvent:true,MIDIMessageEvent:true,MouseEvent:true,DragEvent:true,MutationEvent:true,NotificationEvent:true,PageTransitionEvent:true,PaymentRequestEvent:true,PaymentRequestUpdateEvent:true,PointerEvent:true,PopStateEvent:true,PresentationConnectionAvailableEvent:true,PresentationConnectionCloseEvent:true,ProgressEvent:true,PromiseRejectionEvent:true,PushEvent:true,RTCDataChannelEvent:true,RTCDTMFToneChangeEvent:true,RTCPeerConnectionIceEvent:true,RTCTrackEvent:true,SecurityPolicyViolationEvent:true,SensorErrorEvent:true,SpeechRecognitionError:true,SpeechRecognitionEvent:true,SpeechSynthesisEvent:true,StorageEvent:true,SyncEvent:true,TextEvent:true,TouchEvent:true,TrackEvent:true,TransitionEvent:true,WebKitTransitionEvent:true,UIEvent:true,VRDeviceEvent:true,VRDisplayEvent:true,VRSessionEvent:true,WheelEvent:true,MojoInterfaceRequestEvent:true,ResourceProgressEvent:true,USBConnectionEvent:true,IDBVersionChangeEvent:true,AudioProcessingEvent:true,OfflineAudioCompletionEvent:true,WebGLContextEvent:true,Window:true,DOMWindow:true,EventTarget:false,HTMLFormElement:true,Location:true,Document:true,DocumentFragment:true,HTMLDocument:true,ShadowRoot:true,XMLDocument:true,Attr:true,DocumentType:true,Node:false,NodeList:true,RadioNodeList:true,HTMLSelectElement:true,HTMLTableElement:true,HTMLTableRowElement:true,HTMLTableSectionElement:true,HTMLTemplateElement:true,NamedNodeMap:true,MozNamedAttrMap:true,SVGScriptElement:true,SVGAElement:true,SVGAnimateElement:true,SVGAnimateMotionElement:true,SVGAnimateTransformElement:true,SVGAnimationElement:true,SVGCircleElement:true,SVGClipPathElement:true,SVGDefsElement:true,SVGDescElement:true,SVGDiscardElement:true,SVGEllipseElement:true,SVGFEBlendElement:true,SVGFEColorMatrixElement:true,SVGFEComponentTransferElement:true,SVGFECompositeElement:true,SVGFEConvolveMatrixElement:true,SVGFEDiffuseLightingElement:true,SVGFEDisplacementMapElement:true,SVGFEDistantLightElement:true,SVGFEFloodElement:true,SVGFEFuncAElement:true,SVGFEFuncBElement:true,SVGFEFuncGElement:true,SVGFEFuncRElement:true,SVGFEGaussianBlurElement:true,SVGFEImageElement:true,SVGFEMergeElement:true,SVGFEMergeNodeElement:true,SVGFEMorphologyElement:true,SVGFEOffsetElement:true,SVGFEPointLightElement:true,SVGFESpecularLightingElement:true,SVGFESpotLightElement:true,SVGFETileElement:true,SVGFETurbulenceElement:true,SVGFilterElement:true,SVGForeignObjectElement:true,SVGGElement:true,SVGGeometryElement:true,SVGGraphicsElement:true,SVGImageElement:true,SVGLineElement:true,SVGLinearGradientElement:true,SVGMarkerElement:true,SVGMaskElement:true,SVGMetadataElement:true,SVGPathElement:true,SVGPatternElement:true,SVGPolygonElement:true,SVGPolylineElement:true,SVGRadialGradientElement:true,SVGRectElement:true,SVGSetElement:true,SVGStopElement:true,SVGStyleElement:true,SVGSVGElement:true,SVGSwitchElement:true,SVGSymbolElement:true,SVGTSpanElement:true,SVGTextContentElement:true,SVGTextElement:true,SVGTextPathElement:true,SVGTextPositioningElement:true,SVGTitleElement:true,SVGUseElement:true,SVGViewElement:true,SVGGradientElement:true,SVGComponentTransferFunctionElement:true,SVGFEDropShadowElement:true,SVGMPathElement:true,SVGElement:false})})()\nconvertAllToFastObject(w)\nconvertToFastObject($);(function(a){if(typeof document===\"undefined\"){a(null)\nreturn}if(typeof document.currentScript!='undefined'){a(document.currentScript)\nreturn}var u=document.scripts\nfunction onLoad(b){for(var s=0;s<u.length;++s)u[s].removeEventListener(\"load\",onLoad,false)\na(b.target)}for(var t=0;t<u.length;++t)u[t].addEventListener(\"load\",onLoad,false)})(function(a){v.currentScript=a\nif(typeof dartMainRunner===\"function\")dartMainRunner(O.cx,[])\nelse O.cx([])})})()\n//# sourceMappingURL=Speedtest.dart.js.map\n"
  },
  {
    "path": "dart/tests/Speedtest.html",
    "content": "<html>\n  <head>\n    <title>Diff Speed Test</title>\n  </head>\n  <body>\n    <h1>Diff Speed Test</h1>\n\n<FORM action=\"#\" onsubmit=\"return false\">\n<TABLE WIDTH=\"100%\"><TR>\n  <TD WIDTH=\"50%\">\n<H3>Text Version 1:</H3>\n<TEXTAREA ID=\"text1\" STYLE=\"width: 100%\" ROWS=10>This is a '''list of newspapers published by [[Journal Register Company]]'''.\n\nThe company owns daily and weekly newspapers, other print media properties and newspaper-affiliated local Websites in the [[U.S.]] states of [[Connecticut]], [[Michigan]], [[New York]], [[Ohio]] and [[Pennsylvania]], organized in six geographic \"clusters\":&lt;ref&gt;[http://www.journalregister.com/newspapers.html Journal Register Company: Our Newspapers], accessed February 10, 2008.&lt;/ref&gt;\n\n== Capital-Saratoga ==\nThree dailies, associated weeklies and [[pennysaver]]s in greater [[Albany, New York]]; also [http://www.capitalcentral.com capitalcentral.com] and [http://www.jobsinnewyork.com JobsInNewYork.com].\n\n* ''The Oneida Daily Dispatch'' {{WS|oneidadispatch.com}} of [[Oneida, New York]]\n* ''[[The Record (Troy)|The Record]]'' {{WS|troyrecord.com}} of [[Troy, New York]]\n* ''[[The Saratogian]]'' {{WS|saratogian.com}} of [[Saratoga Springs, New York]]\n* Weeklies:\n** ''Community News'' {{WS|cnweekly.com}} weekly of [[Clifton Park, New York]]\n** ''Rome Observer'' of [[Rome, New York]]\n** ''Life &amp; Times of Utica'' of [[Utica, New York]]\n\n== Connecticut ==\nFive dailies, associated weeklies and [[pennysaver]]s in the state of [[Connecticut]]; also [http://www.ctcentral.com CTcentral.com], [http://www.ctcarsandtrucks.com CTCarsAndTrucks.com] and [http://www.jobsinct.com JobsInCT.com].\n\n* ''The Middletown Press'' {{WS|middletownpress.com}} of [[Middletown, Connecticut|Middletown]]\n* ''[[New Haven Register]]'' {{WS|newhavenregister.com}} of [[New Haven, Connecticut|New Haven]]\n* ''The Register Citizen'' {{WS|registercitizen.com}} of [[Torrington, Connecticut|Torrington]]\n\n* [[New Haven Register#Competitors|Elm City Newspapers]] {{WS|ctcentral.com}}\n** ''The Advertiser'' of [[East Haven, Connecticut|East Haven]]\n** ''Hamden Chronicle'' of [[Hamden, Connecticut|Hamden]]\n** ''Milford Weekly'' of [[Milford, Connecticut|Milford]]\n** ''The Orange Bulletin'' of [[Orange, Connecticut|Orange]]\n** ''The Post'' of [[North Haven, Connecticut|North Haven]]\n** ''Shelton Weekly'' of [[Shelton, Connecticut|Shelton]]\n** ''The Stratford Bard'' of [[Stratford, Connecticut|Stratford]]\n** ''Wallingford Voice'' of [[Wallingford, Connecticut|Wallingford]]\n** ''West Haven News'' of [[West Haven, Connecticut|West Haven]]\n* Housatonic Publications\n** ''The New Milford Times'' {{WS|newmilfordtimes.com}} of [[New Milford, Connecticut|New Milford]]\n** ''The Brookfield Journal'' of [[Brookfield, Connecticut|Brookfield]]\n** ''The Kent Good Times Dispatch'' of [[Kent, Connecticut|Kent]]\n** ''The Bethel Beacon'' of [[Bethel, Connecticut|Bethel]]\n** ''The Litchfield Enquirer'' of [[Litchfield, Connecticut|Litchfield]]\n** ''Litchfield County Times'' of [[Litchfield, Connecticut|Litchfield]]\n* Imprint Newspapers {{WS|imprintnewspapers.com}}\n** ''West Hartford News'' of [[West Hartford, Connecticut|West Hartford]]\n** ''Windsor Journal'' of [[Windsor, Connecticut|Windsor]]\n** ''Windsor Locks Journal'' of [[Windsor Locks, Connecticut|Windsor Locks]]\n** ''Avon Post'' of [[Avon, Connecticut|Avon]]\n** ''Farmington Post'' of [[Farmington, Connecticut|Farmington]]\n** ''Simsbury Post'' of [[Simsbury, Connecticut|Simsbury]]\n** ''Tri-Town Post'' of [[Burlington, Connecticut|Burlington]], [[Canton, Connecticut|Canton]] and [[Harwinton, Connecticut|Harwinton]]\n* Minuteman Publications\n** ''[[Fairfield Minuteman]]'' of [[Fairfield, Connecticut|Fairfield]]\n** ''The Westport Minuteman'' {{WS|westportminuteman.com}} of [[Westport, Connecticut|Westport]]\n* Shoreline Newspapers weeklies:\n** ''Branford Review'' of [[Branford, Connecticut|Branford]]\n** ''Clinton Recorder'' of [[Clinton, Connecticut|Clinton]]\n** ''The Dolphin'' of [[Naval Submarine Base New London]] in [[New London, Connecticut|New London]]\n** ''Main Street News'' {{WS|ctmainstreetnews.com}} of [[Essex, Connecticut|Essex]]\n** ''Pictorial Gazette'' of [[Old Saybrook, Connecticut|Old Saybrook]]\n** ''Regional Express'' of [[Colchester, Connecticut|Colchester]]\n** ''Regional Standard'' of [[Colchester, Connecticut|Colchester]]\n** ''Shoreline Times'' {{WS|shorelinetimes.com}} of [[Guilford, Connecticut|Guilford]]\n** ''Shore View East'' of [[Madison, Connecticut|Madison]]\n** ''Shore View West'' of [[Guilford, Connecticut|Guilford]]\n* Other weeklies:\n** ''Registro'' {{WS|registroct.com}} of [[New Haven, Connecticut|New Haven]]\n** ''Thomaston Express'' {{WS|thomastownexpress.com}} of [[Thomaston, Connecticut|Thomaston]]\n** ''Foothills Traders'' {{WS|foothillstrader.com}} of Torrington, Bristol, Canton\n\n== Michigan ==\nFour dailies, associated weeklies and [[pennysaver]]s in the state of [[Michigan]]; also [http://www.micentralhomes.com MIcentralhomes.com] and [http://www.micentralautos.com MIcentralautos.com]\n* ''[[Oakland Press]]'' {{WS|theoaklandpress.com}} of [[Oakland, Michigan|Oakland]]\n* ''Daily Tribune'' {{WS|dailytribune.com}} of [[Royal Oak, Michigan|Royal Oak]]\n* ''Macomb Daily'' {{WS|macombdaily.com}} of [[Mt. Clemens, Michigan|Mt. Clemens]]\n* ''[[Morning Sun]]'' {{WS|themorningsun.com}} of  [[Mount Pleasant, Michigan|Mount Pleasant]]\n* Heritage Newspapers {{WS|heritage.com}}\n** ''Belleville View''\n** ''Ile Camera''\n** ''Monroe Guardian''\n** ''Ypsilanti Courier''\n** ''News-Herald''\n** ''Press &amp; Guide''\n** ''Chelsea Standard &amp; Dexter Leader''\n** ''Manchester Enterprise''\n** ''Milan News-Leader''\n** ''Saline Reporter''\n* Independent Newspapers {{WS|sourcenewspapers.com}}\n** ''Advisor''\n** ''Source''\n* Morning Star {{WS|morningstarpublishing.com}}\n** ''Alma Reminder''\n** ''Alpena Star''\n** ''Antrim County News''\n** ''Carson City Reminder''\n** ''The Leader &amp; Kalkaskian''\n** ''Ogemaw/Oscoda County Star''\n** ''Petoskey/Charlevoix Star''\n** ''Presque Isle Star''\n** ''Preview Community Weekly''\n** ''Roscommon County Star''\n** ''St. Johns Reminder''\n** ''Straits Area Star''\n** ''The (Edmore) Advertiser''\n* Voice Newspapers {{WS|voicenews.com}}\n** ''Armada Times''\n** ''Bay Voice''\n** ''Blue Water Voice''\n** ''Downriver Voice''\n** ''Macomb Township Voice''\n** ''North Macomb Voice''\n** ''Weekend Voice''\n** ''Suburban Lifestyles'' {{WS|suburbanlifestyles.com}}\n\n== Mid-Hudson ==\nOne daily, associated magazines in the [[Hudson River Valley]] of [[New York]]; also [http://www.midhudsoncentral.com MidHudsonCentral.com] and [http://www.jobsinnewyork.com JobsInNewYork.com].\n\n* ''[[Daily Freeman]]'' {{WS|dailyfreeman.com}} of [[Kingston, New York]]\n\n== Ohio ==\nTwo dailies, associated magazines and three shared Websites, all in the state of [[Ohio]]: [http://www.allaroundcleveland.com AllAroundCleveland.com], [http://www.allaroundclevelandcars.com AllAroundClevelandCars.com] and [http://www.allaroundclevelandjobs.com AllAroundClevelandJobs.com].\n\n* ''[[The News-Herald (Ohio)|The News-Herald]]'' {{WS|news-herald.com}} of [[Willoughby, Ohio|Willoughby]]\n* ''[[The Morning Journal]]'' {{WS|morningjournal.com}} of [[Lorain, Ohio|Lorain]]\n\n== Philadelphia area ==\nSeven dailies and associated weeklies and magazines in [[Pennsylvania]] and [[New Jersey]], and associated Websites: [http://www.allaroundphilly.com AllAroundPhilly.com], [http://www.jobsinnj.com JobsInNJ.com], [http://www.jobsinpa.com JobsInPA.com], and [http://www.phillycarsearch.com PhillyCarSearch.com].\n\n* ''The Daily Local'' {{WS|dailylocal.com}} of [[West Chester, Pennsylvania|West Chester]]\n* ''[[Delaware County Daily and Sunday Times]] {{WS|delcotimes.com}} of Primos\n* ''[[The Mercury (Pennsylvania)|The Mercury]]'' {{WS|pottstownmercury.com}} of [[Pottstown, Pennsylvania|Pottstown]]\n* ''The Phoenix'' {{WS|phoenixvillenews.com}} of [[Phoenixville, Pennsylvania|Phoenixville]]\n* ''[[The Reporter (Lansdale)|The Reporter]]'' {{WS|thereporteronline.com}} of [[Lansdale, Pennsylvania|Lansdale]]\n* ''The Times Herald'' {{WS|timesherald.com}} of [[Norristown, Pennsylvania|Norristown]]\n* ''[[The Trentonian]]'' {{WS|trentonian.com}} of [[Trenton, New Jersey]]\n\n* Weeklies\n** ''El Latino Expreso'' of [[Trenton, New Jersey]]\n** ''La Voz'' of [[Norristown, Pennsylvania]]\n** ''The Village News'' of [[Downingtown, Pennsylvania]]\n** ''The Times Record'' of [[Kennett Square, Pennsylvania]]\n** ''The Tri-County Record'' {{WS|tricountyrecord.com}} of [[Morgantown, Pennsylvania]]\n** ''News of Delaware County'' {{WS|newsofdelawarecounty.com}}of [[Havertown, Pennsylvania]]\n** ''Main Line Times'' {{WS|mainlinetimes.com}}of [[Ardmore, Pennsylvania]]\n** ''Penny Pincher'' of [[Pottstown, Pennsylvania]]\n** ''Town Talk'' {{WS|towntalknews.com}} of [[Ridley, Pennsylvania]]\n* Chesapeake Publishing {{WS|pa8newsgroup.com}}\n** ''Solanco Sun Ledger'' of [[Quarryville, Pennsylvania]]\n** ''Columbia Ledger'' of [[Columbia, Pennsylvania]]\n** ''Coatesville Ledger'' of [[Downingtown, Pennsylvania]]\n** ''Parkesburg Post Ledger'' of [[Quarryville, Pennsylvania]]\n** ''Downingtown Ledger'' of [[Downingtown, Pennsylvania]]\n** ''The Kennett Paper'' of [[Kennett Square, Pennsylvania]]\n** ''Avon Grove Sun'' of [[West Grove, Pennsylvania]]\n** ''Oxford Tribune'' of [[Oxford, Pennsylvania]]\n** ''Elizabethtown Chronicle'' of [[Elizabethtown, Pennsylvania]]\n** ''Donegal Ledger'' of [[Donegal, Pennsylvania]]\n** ''Chadds Ford Post'' of [[Chadds Ford, Pennsylvania]]\n** ''The Central Record'' of [[Medford, New Jersey]]\n** ''Maple Shade Progress'' of [[Maple Shade, New Jersey]]\n* Intercounty Newspapers {{WS|buckslocalnews.com}}\n** ''The Review'' of Roxborough, Pennsylvania\n** ''The Recorder'' of [[Conshohocken, Pennsylvania]]\n** ''The Leader'' of [[Mount Airy, Pennsylvania|Mount Airy]] and West Oak Lake, Pennsylvania\n** ''The Pennington Post'' of [[Pennington, New Jersey]]\n** ''The Bristol Pilot'' of [[Bristol, Pennsylvania]]\n** ''Yardley News'' of [[Yardley, Pennsylvania]]\n** ''New Hope Gazette'' of [[New Hope, Pennsylvania]]\n** ''Doylestown Patriot'' of [[Doylestown, Pennsylvania]]\n** ''Newtown Advance'' of [[Newtown, Pennsylvania]]\n** ''The Plain Dealer'' of [[Williamstown, New Jersey]]\n** ''News Report'' of [[Sewell, New Jersey]]\n** ''Record Breeze'' of [[Berlin, New Jersey]]\n** ''Newsweekly'' of [[Moorestown, New Jersey]]\n** ''Haddon Herald'' of [[Haddonfield, New Jersey]]\n** ''New Egypt Press'' of [[New Egypt, New Jersey]]\n** ''Community News'' of [[Pemberton, New Jersey]]\n** ''Plymouth Meeting Journal'' of [[Plymouth Meeting, Pennsylvania]]\n** ''Lafayette Hill Journal'' of [[Lafayette Hill, Pennsylvania]]\n* Montgomery Newspapers {{WS|montgomerynews.com}}\n** ''Ambler Gazette'' of [[Ambler, Pennsylvania]]\n** ''Central Bucks Life'' of [[Bucks County, Pennsylvania]]\n** ''The Colonial'' of [[Plymouth Meeting, Pennsylvania]]\n** ''Glenside News'' of [[Glenside, Pennsylvania]]\n** ''The Globe'' of [[Lower Moreland Township, Pennsylvania]]\n** ''Main Line Life'' of [[Ardmore, Pennsylvania]]\n** ''Montgomery Life'' of [[Fort Washington, Pennsylvania]]\n** ''North Penn Life'' of [[Lansdale, Pennsylvania]]\n** ''Perkasie News Herald'' of [[Perkasie, Pennsylvania]]\n** ''Public Spirit'' of [[Hatboro, Pennsylvania]]\n** ''Souderton Independent'' of [[Souderton, Pennsylvania]]\n** ''Springfield Sun'' of [[Springfield, Pennsylvania]]\n** ''Spring-Ford Reporter'' of [[Royersford, Pennsylvania]]\n** ''Times Chronicle'' of [[Jenkintown, Pennsylvania]]\n** ''Valley Item'' of [[Perkiomenville, Pennsylvania]]\n** ''Willow Grove Guide'' of [[Willow Grove, Pennsylvania]]\n* News Gleaner Publications (closed December 2008) {{WS|newsgleaner.com}}\n** ''Life Newspapers'' of [[Philadelphia, Pennsylvania]]\n* Suburban Publications\n** ''The Suburban &amp; Wayne Times'' {{WS|waynesuburban.com}} of [[Wayne, Pennsylvania]]\n** ''The Suburban Advertiser'' of [[Exton, Pennsylvania]]\n** ''The King of Prussia Courier'' of [[King of Prussia, Pennsylvania]]\n* Press Newspapers {{WS|countypressonline.com}}\n** ''County Press'' of [[Newtown Square, Pennsylvania]]\n** ''Garnet Valley Press'' of [[Glen Mills, Pennsylvania]]\n** ''Haverford Press'' of [[Newtown Square, Pennsylvania]] (closed January 2009)\n** ''Hometown Press'' of [[Glen Mills, Pennsylvania]] (closed January 2009)\n** ''Media Press'' of [[Newtown Square, Pennsylvania]] (closed January 2009)\n** ''Springfield Press'' of [[Springfield, Pennsylvania]]\n* Berks-Mont Newspapers {{WS|berksmontnews.com}}\n** ''The Boyertown Area Times'' of [[Boyertown, Pennsylvania]]\n** ''The Kutztown Area Patriot'' of [[Kutztown, Pennsylvania]]\n** ''The Hamburg Area Item'' of [[Hamburg, Pennsylvania]]\n** ''The Southern Berks News'' of [[Exeter Township, Berks County, Pennsylvania]]\n** ''The Free Press'' of [[Quakertown, Pennsylvania]]\n** ''The Saucon News'' of [[Quakertown, Pennsylvania]]\n** ''Westside Weekly'' of [[Reading, Pennsylvania]]\n\n* Magazines\n** ''Bucks Co. Town &amp; Country Living''\n** ''Chester Co. Town &amp; Country Living''\n** ''Montomgery Co. Town &amp; Country Living''\n** ''Garden State Town &amp; Country Living''\n** ''Montgomery Homes''\n** ''Philadelphia Golfer''\n** ''Parents Express''\n** ''Art Matters''\n\n{{JRC}}\n\n==References==\n&lt;references /&gt;\n\n[[Category:Journal Register publications|*]]\n</TEXTAREA></TD>\n  <TD WIDTH=\"50%\">\n<H3>Text Version 2:</H3>\n\n<TEXTAREA ID=\"text2\" STYLE=\"width: 100%\" ROWS=10>This is a '''list of newspapers published by [[Journal Register Company]]'''.\n\nThe company owns daily and weekly newspapers, other print media properties and newspaper-affiliated local Websites in the [[U.S.]] states of [[Connecticut]], [[Michigan]], [[New York]], [[Ohio]], [[Pennsylvania]] and [[New Jersey]], organized in six geographic \"clusters\":&lt;ref&gt;[http://www.journalregister.com/publications.html Journal Register Company: Our Publications], accessed April 21, 2010.&lt;/ref&gt;\n\n== Capital-Saratoga ==\nThree dailies, associated weeklies and [[pennysaver]]s in greater [[Albany, New York]]; also [http://www.capitalcentral.com capitalcentral.com] and [http://www.jobsinnewyork.com JobsInNewYork.com].\n\n* ''The Oneida Daily Dispatch'' {{WS|oneidadispatch.com}} of [[Oneida, New York]]\n* ''[[The Record (Troy)|The Record]]'' {{WS|troyrecord.com}} of [[Troy, New York]]\n* ''[[The Saratogian]]'' {{WS|saratogian.com}} of [[Saratoga Springs, New York]]\n* Weeklies:\n** ''Community News'' {{WS|cnweekly.com}} weekly of [[Clifton Park, New York]]\n** ''Rome Observer'' {{WS|romeobserver.com}} of [[Rome, New York]]\n** ''WG Life '' {{WS|saratogian.com/wglife/}} of [[Wilton, New York]]\n** ''Ballston Spa Life '' {{WS|saratogian.com/bspalife}} of [[Ballston Spa, New York]]\n** ''Greenbush Life'' {{WS|troyrecord.com/greenbush}} of [[Troy, New York]]\n** ''Latham Life'' {{WS|troyrecord.com/latham}} of [[Latham, New York]]\n** ''River Life'' {{WS|troyrecord.com/river}} of [[Troy, New York]]\n\n== Connecticut ==\nThree dailies, associated weeklies and [[pennysaver]]s in the state of [[Connecticut]]; also [http://www.ctcentral.com CTcentral.com], [http://www.ctcarsandtrucks.com CTCarsAndTrucks.com] and [http://www.jobsinct.com JobsInCT.com].\n\n* ''The Middletown Press'' {{WS|middletownpress.com}} of [[Middletown, Connecticut|Middletown]]\n* ''[[New Haven Register]]'' {{WS|newhavenregister.com}} of [[New Haven, Connecticut|New Haven]]\n* ''The Register Citizen'' {{WS|registercitizen.com}} of [[Torrington, Connecticut|Torrington]]\n\n* Housatonic Publications\n** ''The Housatonic Times'' {{WS|housatonictimes.com}} of [[New Milford, Connecticut|New Milford]]\n** ''Litchfield County Times'' {{WS|countytimes.com}} of [[Litchfield, Connecticut|Litchfield]]\n\n* Minuteman Publications\n** ''[[Fairfield Minuteman]]'' {{WS|fairfieldminuteman.com}}of [[Fairfield, Connecticut|Fairfield]]\n** ''The Westport Minuteman'' {{WS|westportminuteman.com}} of [[Westport, Connecticut|Westport]]\n\n* Shoreline Newspapers\n** ''The Dolphin'' {{WS|dolphin-news.com}} of [[Naval Submarine Base New London]] in [[New London, Connecticut|New London]]\n** ''Shoreline Times'' {{WS|shorelinetimes.com}} of [[Guilford, Connecticut|Guilford]]\n\n* Foothills Media Group {{WS|foothillsmediagroup.com}}\n** ''Thomaston Express'' {{WS|thomastonexpress.com}} of [[Thomaston, Connecticut|Thomaston]]\n** ''Good News About Torrington'' {{WS|goodnewsabouttorrington.com}} of [[Torrington, Connecticut|Torrington]]\n** ''Granby News'' {{WS|foothillsmediagroup.com/granby}} of [[Granby, Connecticut|Granby]]\n** ''Canton News'' {{WS|foothillsmediagroup.com/canton}} of [[Canton, Connecticut|Canton]]\n** ''Avon News'' {{WS|foothillsmediagroup.com/avon}} of [[Avon, Connecticut|Avon]]\n** ''Simsbury News'' {{WS|foothillsmediagroup.com/simsbury}} of [[Simsbury, Connecticut|Simsbury]]\n** ''Litchfield News'' {{WS|foothillsmediagroup.com/litchfield}} of [[Litchfield, Connecticut|Litchfield]]\n** ''Foothills Trader'' {{WS|foothillstrader.com}} of Torrington, Bristol, Canton\n\n* Other weeklies\n** ''The Milford-Orange Bulletin'' {{WS|ctbulletin.com}} of [[Orange, Connecticut|Orange]]\n** ''The Post-Chronicle'' {{WS|ctpostchronicle.com}} of [[North Haven, Connecticut|North Haven]]\n** ''West Hartford News'' {{WS|westhartfordnews.com}} of [[West Hartford, Connecticut|West Hartford]]\n\n* Magazines\n** ''The Connecticut Bride'' {{WS|connecticutmag.com}}\n** ''Connecticut Magazine'' {{WS|theconnecticutbride.com}}\n** ''Passport Magazine'' {{WS|passport-mag.com}}\n\n== Michigan ==\nFour dailies, associated weeklies and [[pennysaver]]s in the state of [[Michigan]]; also [http://www.micentralhomes.com MIcentralhomes.com] and [http://www.micentralautos.com MIcentralautos.com]\n* ''[[Oakland Press]]'' {{WS|theoaklandpress.com}} of [[Oakland, Michigan|Oakland]]\n* ''Daily Tribune'' {{WS|dailytribune.com}} of [[Royal Oak, Michigan|Royal Oak]]\n* ''Macomb Daily'' {{WS|macombdaily.com}} of [[Mt. Clemens, Michigan|Mt. Clemens]]\n* ''[[Morning Sun]]'' {{WS|themorningsun.com}} of  [[Mount Pleasant, Michigan|Mount Pleasant]]\n\n* Heritage Newspapers {{WS|heritage.com}}\n** ''Belleville View'' {{WS|bellevilleview.com}}\n** ''Ile Camera'' {{WS|thenewsherald.com/ile_camera}}\n** ''Monroe Guardian''  {{WS|monreguardian.com}}\n** ''Ypsilanti Courier'' {{WS|ypsilanticourier.com}}\n** ''News-Herald'' {{WS|thenewsherald.com}}\n** ''Press &amp; Guide'' {{WS|pressandguide.com}}\n** ''Chelsea Standard &amp; Dexter Leader'' {{WS|chelseastandard.com}}\n** ''Manchester Enterprise'' {{WS|manchesterguardian.com}}\n** ''Milan News-Leader'' {{WS|milannews.com}}\n** ''Saline Reporter'' {{WS|salinereporter.com}}\n* Independent Newspapers\n** ''Advisor'' {{WS|sourcenewspapers.com}}\n** ''Source'' {{WS|sourcenewspapers.com}}\n* Morning Star {{WS|morningstarpublishing.com}}\n** ''The Leader &amp; Kalkaskian'' {{WS|leaderandkalkaskian.com}}\n** ''Grand Traverse Insider'' {{WS|grandtraverseinsider.com}}\n** ''Alma Reminder''\n** ''Alpena Star''\n** ''Ogemaw/Oscoda County Star''\n** ''Presque Isle Star''\n** ''St. Johns Reminder''\n\n* Voice Newspapers {{WS|voicenews.com}}\n** ''Armada Times''\n** ''Bay Voice''\n** ''Blue Water Voice''\n** ''Downriver Voice''\n** ''Macomb Township Voice''\n** ''North Macomb Voice''\n** ''Weekend Voice''\n\n== Mid-Hudson ==\nOne daily, associated magazines in the [[Hudson River Valley]] of [[New York]]; also [http://www.midhudsoncentral.com MidHudsonCentral.com] and [http://www.jobsinnewyork.com JobsInNewYork.com].\n\n* ''[[Daily Freeman]]'' {{WS|dailyfreeman.com}} of [[Kingston, New York]]\n* ''Las Noticias'' {{WS|lasnoticiasny.com}} of [[Kingston, New York]]\n\n== Ohio ==\nTwo dailies, associated magazines and three shared Websites, all in the state of [[Ohio]]: [http://www.allaroundcleveland.com AllAroundCleveland.com], [http://www.allaroundclevelandcars.com AllAroundClevelandCars.com] and [http://www.allaroundclevelandjobs.com AllAroundClevelandJobs.com].\n\n* ''[[The News-Herald (Ohio)|The News-Herald]]'' {{WS|news-herald.com}} of [[Willoughby, Ohio|Willoughby]]\n* ''[[The Morning Journal]]'' {{WS|morningjournal.com}} of [[Lorain, Ohio|Lorain]]\n* ''El Latino Expreso'' {{WS|lorainlatino.com}} of [[Lorain, Ohio|Lorain]]\n\n== Philadelphia area ==\nSeven dailies and associated weeklies and magazines in [[Pennsylvania]] and [[New Jersey]], and associated Websites: [http://www.allaroundphilly.com AllAroundPhilly.com], [http://www.jobsinnj.com JobsInNJ.com], [http://www.jobsinpa.com JobsInPA.com], and [http://www.phillycarsearch.com PhillyCarSearch.com].\n\n* ''[[The Daily Local News]]'' {{WS|dailylocal.com}} of [[West Chester, Pennsylvania|West Chester]]\n* ''[[Delaware County Daily and Sunday Times]] {{WS|delcotimes.com}} of Primos [[Upper Darby Township, Pennsylvania]]\n* ''[[The Mercury (Pennsylvania)|The Mercury]]'' {{WS|pottstownmercury.com}} of [[Pottstown, Pennsylvania|Pottstown]]\n* ''[[The Reporter (Lansdale)|The Reporter]]'' {{WS|thereporteronline.com}} of [[Lansdale, Pennsylvania|Lansdale]]\n* ''The Times Herald'' {{WS|timesherald.com}} of [[Norristown, Pennsylvania|Norristown]]\n* ''[[The Trentonian]]'' {{WS|trentonian.com}} of [[Trenton, New Jersey]]\n\n* Weeklies\n* ''The Phoenix'' {{WS|phoenixvillenews.com}} of [[Phoenixville, Pennsylvania]]\n** ''El Latino Expreso'' {{WS|njexpreso.com}} of [[Trenton, New Jersey]]\n** ''La Voz'' {{WS|lavozpa.com}} of [[Norristown, Pennsylvania]]\n** ''The Tri County Record'' {{WS|tricountyrecord.com}} of [[Morgantown, Pennsylvania]]\n** ''Penny Pincher'' {{WS|pennypincherpa.com}}of [[Pottstown, Pennsylvania]]\n\n* Chesapeake Publishing  {{WS|southernchestercountyweeklies.com}}\n** ''The Kennett Paper'' {{WS|kennettpaper.com}} of [[Kennett Square, Pennsylvania]]\n** ''Avon Grove Sun'' {{WS|avongrovesun.com}} of [[West Grove, Pennsylvania]]\n** ''The Central Record'' {{WS|medfordcentralrecord.com}} of [[Medford, New Jersey]]\n** ''Maple Shade Progress'' {{WS|mapleshadeprogress.com}} of [[Maple Shade, New Jersey]]\n\n* Intercounty Newspapers {{WS|buckslocalnews.com}} {{WS|southjerseylocalnews.com}}\n** ''The Pennington Post'' {{WS|penningtonpost.com}} of [[Pennington, New Jersey]]\n** ''The Bristol Pilot'' {{WS|bristolpilot.com}} of [[Bristol, Pennsylvania]]\n** ''Yardley News'' {{WS|yardleynews.com}} of [[Yardley, Pennsylvania]]\n** ''Advance of Bucks County'' {{WS|advanceofbucks.com}} of [[Newtown, Pennsylvania]]\n** ''Record Breeze'' {{WS|recordbreeze.com}} of [[Berlin, New Jersey]]\n** ''Community News'' {{WS|sjcommunitynews.com}} of [[Pemberton, New Jersey]]\n\n* Montgomery Newspapers {{WS|montgomerynews.com}}\n** ''Ambler Gazette'' {{WS|amblergazette.com}} of [[Ambler, Pennsylvania]]\n** ''The Colonial'' {{WS|colonialnews.com}} of [[Plymouth Meeting, Pennsylvania]]\n** ''Glenside News'' {{WS|glensidenews.com}} of [[Glenside, Pennsylvania]]\n** ''The Globe'' {{WS|globenewspaper.com}} of [[Lower Moreland Township, Pennsylvania]]\n** ''Montgomery Life'' {{WS|montgomerylife.com}} of [[Fort Washington, Pennsylvania]]\n** ''North Penn Life'' {{WS|northpennlife.com}} of [[Lansdale, Pennsylvania]]\n** ''Perkasie News Herald'' {{WS|perkasienewsherald.com}} of [[Perkasie, Pennsylvania]]\n** ''Public Spirit'' {{WS|thepublicspirit.com}} of [[Hatboro, Pennsylvania]]\n** ''Souderton Independent'' {{WS|soudertonindependent.com}} of [[Souderton, Pennsylvania]]\n** ''Springfield Sun'' {{WS|springfieldsun.com}} of [[Springfield, Pennsylvania]]\n** ''Spring-Ford Reporter'' {{WS|springfordreporter.com}} of [[Royersford, Pennsylvania]]\n** ''Times Chronicle'' {{WS|thetimeschronicle.com}} of [[Jenkintown, Pennsylvania]]\n** ''Valley Item'' {{WS|valleyitem.com}} of [[Perkiomenville, Pennsylvania]]\n** ''Willow Grove Guide'' {{WS|willowgroveguide.com}} of [[Willow Grove, Pennsylvania]]\n** ''The Review'' {{WS|roxreview.com}} of [[Roxborough, Philadelphia, Pennsylvania]]\n\n* Main Line Media News {{WS|mainlinemedianews.com}}\n** ''Main Line Times'' {{WS|mainlinetimes.com}} of [[Ardmore, Pennsylvania]]\n** ''Main Line Life'' {{WS|mainlinelife.com}} of [[Ardmore, Pennsylvania]]\n** ''The King of Prussia Courier'' {{WS|kingofprussiacourier.com}} of [[King of Prussia, Pennsylvania]]\n\n* Delaware County News Network {{WS|delconewsnetwork.com}}\n** ''News of Delaware County'' {{WS|newsofdelawarecounty.com}} of [[Havertown, Pennsylvania]]\n** ''County Press'' {{WS|countypressonline.com}} of [[Newtown Square, Pennsylvania]]\n** ''Garnet Valley Press'' {{WS|countypressonline.com}} of [[Glen Mills, Pennsylvania]]\n** ''Springfield Press'' {{WS|countypressonline.com}} of [[Springfield, Pennsylvania]]\n** ''Town Talk'' {{WS|towntalknews.com}} of [[Ridley, Pennsylvania]]\n\n* Berks-Mont Newspapers {{WS|berksmontnews.com}}\n** ''The Boyertown Area Times'' {{WS|berksmontnews.com/boyertown_area_times}} of [[Boyertown, Pennsylvania]]\n** ''The Kutztown Area Patriot'' {{WS|berksmontnews.com/kutztown_area_patriot}} of [[Kutztown, Pennsylvania]]\n** ''The Hamburg Area Item'' {{WS|berksmontnews.com/hamburg_area_item}} of [[Hamburg, Pennsylvania]]\n** ''The Southern Berks News'' {{WS|berksmontnews.com/southern_berks_news}} of [[Exeter Township, Berks County, Pennsylvania]]\n** ''Community Connection'' {{WS|berksmontnews.com/community_connection}} of [[Boyertown, Pennsylvania]]\n\n* Magazines\n** ''Bucks Co. Town &amp; Country Living'' {{WS|buckscountymagazine.com}}\n** ''Parents Express'' {{WS|parents-express.com}}\n** ''Real Men, Rednecks'' {{WS|realmenredneck.com}}\n\n{{JRC}}\n\n==References==\n&lt;references /&gt;\n\n[[Category:Journal Register publications|*]]\n</TEXTAREA></TD>\n</TR></TABLE>\n\n<P><INPUT TYPE=\"button\" ID=\"launch\" VALUE=\"Compute Diff\"></P>\n</FORM>\n\n<DIV ID=\"outputdiv\">Error: Dart is not running.</DIV>\n\n    <script type=\"text/javascript\" src=\"Speedtest.dart.js\"></script>\n  </body>\n</html>\n"
  },
  {
    "path": "dart/tests/SpeedtestVM.dart",
    "content": "import '../DiffMatchPatch.dart';\n\nvoid main() {\n  String text1 = \"This is a '''list of newspapers published by [[Journal Register Company]]'''.\\n\\nThe company owns daily and weekly newspapers, other print media properties and newspaper-affiliated local Websites in the [[U.S.]] states of [[Connecticut]], [[Michigan]], [[New York]], [[Ohio]] and [[Pennsylvania]], organized in six geographic \\\"clusters\\\":&lt;ref&gt;[http://www.journalregister.com/newspapers.html Journal Register Company: Our Newspapers], accessed February 10, 2008.&lt;/ref&gt;\\n\\n== Capital-Saratoga ==\\nThree dailies, associated weeklies and [[pennysaver]]s in greater [[Albany, New York]]; also [http://www.capitalcentral.com capitalcentral.com] and [http://www.jobsinnewyork.com JobsInNewYork.com].\\n\\n* ''The Oneida Daily Dispatch'' {{WS|oneidadispatch.com}} of [[Oneida, New York]]\\n* ''[[The Record (Troy)|The Record]]'' {{WS|troyrecord.com}} of [[Troy, New York]]\\n* ''[[The Saratogian]]'' {{WS|saratogian.com}} of [[Saratoga Springs, New York]]\\n* Weeklies:\\n** ''Community News'' {{WS|cnweekly.com}} weekly of [[Clifton Park, New York]]\\n** ''Rome Observer'' of [[Rome, New York]]\\n** ''Life &amp; Times of Utica'' of [[Utica, New York]]\\n\\n== Connecticut ==\\nFive dailies, associated weeklies and [[pennysaver]]s in the state of [[Connecticut]]; also [http://www.ctcentral.com CTcentral.com], [http://www.ctcarsandtrucks.com CTCarsAndTrucks.com] and [http://www.jobsinct.com JobsInCT.com].\\n\\n* ''The Middletown Press'' {{WS|middletownpress.com}} of [[Middletown, Connecticut|Middletown]]\\n* ''[[New Haven Register]]'' {{WS|newhavenregister.com}} of [[New Haven, Connecticut|New Haven]]\\n* ''The Register Citizen'' {{WS|registercitizen.com}} of [[Torrington, Connecticut|Torrington]]\\n\\n* [[New Haven Register#Competitors|Elm City Newspapers]] {{WS|ctcentral.com}}\\n** ''The Advertiser'' of [[East Haven, Connecticut|East Haven]]\\n** ''Hamden Chronicle'' of [[Hamden, Connecticut|Hamden]]\\n** ''Milford Weekly'' of [[Milford, Connecticut|Milford]]\\n** ''The Orange Bulletin'' of [[Orange, Connecticut|Orange]]\\n** ''The Post'' of [[North Haven, Connecticut|North Haven]]\\n** ''Shelton Weekly'' of [[Shelton, Connecticut|Shelton]]\\n** ''The Stratford Bard'' of [[Stratford, Connecticut|Stratford]]\\n** ''Wallingford Voice'' of [[Wallingford, Connecticut|Wallingford]]\\n** ''West Haven News'' of [[West Haven, Connecticut|West Haven]]\\n* Housatonic Publications \\n** ''The New Milford Times'' {{WS|newmilfordtimes.com}} of [[New Milford, Connecticut|New Milford]]\\n** ''The Brookfield Journal'' of [[Brookfield, Connecticut|Brookfield]]\\n** ''The Kent Good Times Dispatch'' of [[Kent, Connecticut|Kent]]\\n** ''The Bethel Beacon'' of [[Bethel, Connecticut|Bethel]]\\n** ''The Litchfield Enquirer'' of [[Litchfield, Connecticut|Litchfield]]\\n** ''Litchfield County Times'' of [[Litchfield, Connecticut|Litchfield]]\\n* Imprint Newspapers {{WS|imprintnewspapers.com}}\\n** ''West Hartford News'' of [[West Hartford, Connecticut|West Hartford]]\\n** ''Windsor Journal'' of [[Windsor, Connecticut|Windsor]]\\n** ''Windsor Locks Journal'' of [[Windsor Locks, Connecticut|Windsor Locks]]\\n** ''Avon Post'' of [[Avon, Connecticut|Avon]]\\n** ''Farmington Post'' of [[Farmington, Connecticut|Farmington]]\\n** ''Simsbury Post'' of [[Simsbury, Connecticut|Simsbury]]\\n** ''Tri-Town Post'' of [[Burlington, Connecticut|Burlington]], [[Canton, Connecticut|Canton]] and [[Harwinton, Connecticut|Harwinton]]\\n* Minuteman Publications\\n** ''[[Fairfield Minuteman]]'' of [[Fairfield, Connecticut|Fairfield]]\\n** ''The Westport Minuteman'' {{WS|westportminuteman.com}} of [[Westport, Connecticut|Westport]]\\n* Shoreline Newspapers weeklies:\\n** ''Branford Review'' of [[Branford, Connecticut|Branford]]\\n** ''Clinton Recorder'' of [[Clinton, Connecticut|Clinton]]\\n** ''The Dolphin'' of [[Naval Submarine Base New London]] in [[New London, Connecticut|New London]]\\n** ''Main Street News'' {{WS|ctmainstreetnews.com}} of [[Essex, Connecticut|Essex]]\\n** ''Pictorial Gazette'' of [[Old Saybrook, Connecticut|Old Saybrook]]\\n** ''Regional Express'' of [[Colchester, Connecticut|Colchester]]\\n** ''Regional Standard'' of [[Colchester, Connecticut|Colchester]]\\n** ''Shoreline Times'' {{WS|shorelinetimes.com}} of [[Guilford, Connecticut|Guilford]]\\n** ''Shore View East'' of [[Madison, Connecticut|Madison]]\\n** ''Shore View West'' of [[Guilford, Connecticut|Guilford]]\\n* Other weeklies:\\n** ''Registro'' {{WS|registroct.com}} of [[New Haven, Connecticut|New Haven]]\\n** ''Thomaston Express'' {{WS|thomastownexpress.com}} of [[Thomaston, Connecticut|Thomaston]]\\n** ''Foothills Traders'' {{WS|foothillstrader.com}} of Torrington, Bristol, Canton\\n\\n== Michigan ==\\nFour dailies, associated weeklies and [[pennysaver]]s in the state of [[Michigan]]; also [http://www.micentralhomes.com MIcentralhomes.com] and [http://www.micentralautos.com MIcentralautos.com]\\n* ''[[Oakland Press]]'' {{WS|theoaklandpress.com}} of [[Oakland, Michigan|Oakland]]\\n* ''Daily Tribune'' {{WS|dailytribune.com}} of [[Royal Oak, Michigan|Royal Oak]]\\n* ''Macomb Daily'' {{WS|macombdaily.com}} of [[Mt. Clemens, Michigan|Mt. Clemens]]\\n* ''[[Morning Sun]]'' {{WS|themorningsun.com}} of  [[Mount Pleasant, Michigan|Mount Pleasant]]\\n* Heritage Newspapers {{WS|heritage.com}}\\n** ''Belleville View''\\n** ''Ile Camera''\\n** ''Monroe Guardian''\\n** ''Ypsilanti Courier''\\n** ''News-Herald''\\n** ''Press &amp; Guide''\\n** ''Chelsea Standard &amp; Dexter Leader''\\n** ''Manchester Enterprise''\\n** ''Milan News-Leader''\\n** ''Saline Reporter''\\n* Independent Newspapers {{WS|sourcenewspapers.com}}\\n** ''Advisor''\\n** ''Source''\\n* Morning Star {{WS|morningstarpublishing.com}}\\n** ''Alma Reminder''\\n** ''Alpena Star''\\n** ''Antrim County News''\\n** ''Carson City Reminder''\\n** ''The Leader &amp; Kalkaskian''\\n** ''Ogemaw/Oscoda County Star''\\n** ''Petoskey/Charlevoix Star''\\n** ''Presque Isle Star''\\n** ''Preview Community Weekly''\\n** ''Roscommon County Star''\\n** ''St. Johns Reminder''\\n** ''Straits Area Star''\\n** ''The (Edmore) Advertiser'' \\n* Voice Newspapers {{WS|voicenews.com}}\\n** ''Armada Times''\\n** ''Bay Voice''\\n** ''Blue Water Voice''\\n** ''Downriver Voice''\\n** ''Macomb Township Voice''\\n** ''North Macomb Voice''\\n** ''Weekend Voice''\\n** ''Suburban Lifestyles'' {{WS|suburbanlifestyles.com}}\\n\\n== Mid-Hudson ==\\nOne daily, associated magazines in the [[Hudson River Valley]] of [[New York]]; also [http://www.midhudsoncentral.com MidHudsonCentral.com] and [http://www.jobsinnewyork.com JobsInNewYork.com].\\n\\n* ''[[Daily Freeman]]'' {{WS|dailyfreeman.com}} of [[Kingston, New York]]\\n\\n== Ohio ==\\nTwo dailies, associated magazines and three shared Websites, all in the state of [[Ohio]]: [http://www.allaroundcleveland.com AllAroundCleveland.com], [http://www.allaroundclevelandcars.com AllAroundClevelandCars.com] and [http://www.allaroundclevelandjobs.com AllAroundClevelandJobs.com].\\n\\n* ''[[The News-Herald (Ohio)|The News-Herald]]'' {{WS|news-herald.com}} of [[Willoughby, Ohio|Willoughby]]\\n* ''[[The Morning Journal]]'' {{WS|morningjournal.com}} of [[Lorain, Ohio|Lorain]]\\n\\n== Philadelphia area ==\\nSeven dailies and associated weeklies and magazines in [[Pennsylvania]] and [[New Jersey]], and associated Websites: [http://www.allaroundphilly.com AllAroundPhilly.com], [http://www.jobsinnj.com JobsInNJ.com], [http://www.jobsinpa.com JobsInPA.com], and [http://www.phillycarsearch.com PhillyCarSearch.com].\\n\\n* ''The Daily Local'' {{WS|dailylocal.com}} of [[West Chester, Pennsylvania|West Chester]]\\n* ''[[Delaware County Daily and Sunday Times]] {{WS|delcotimes.com}} of Primos\\n* ''[[The Mercury (Pennsylvania)|The Mercury]]'' {{WS|pottstownmercury.com}} of [[Pottstown, Pennsylvania|Pottstown]]\\n* ''The Phoenix'' {{WS|phoenixvillenews.com}} of [[Phoenixville, Pennsylvania|Phoenixville]]\\n* ''[[The Reporter (Lansdale)|The Reporter]]'' {{WS|thereporteronline.com}} of [[Lansdale, Pennsylvania|Lansdale]]\\n* ''The Times Herald'' {{WS|timesherald.com}} of [[Norristown, Pennsylvania|Norristown]]\\n* ''[[The Trentonian]]'' {{WS|trentonian.com}} of [[Trenton, New Jersey]]\\n\\n* Weeklies\\n** ''El Latino Expreso'' of [[Trenton, New Jersey]]\\n** ''La Voz'' of [[Norristown, Pennsylvania]]\\n** ''The Village News'' of [[Downingtown, Pennsylvania]]\\n** ''The Times Record'' of [[Kennett Square, Pennsylvania]]\\n** ''The Tri-County Record'' {{WS|tricountyrecord.com}} of [[Morgantown, Pennsylvania]]\\n** ''News of Delaware County'' {{WS|newsofdelawarecounty.com}}of [[Havertown, Pennsylvania]]\\n** ''Main Line Times'' {{WS|mainlinetimes.com}}of [[Ardmore, Pennsylvania]]\\n** ''Penny Pincher'' of [[Pottstown, Pennsylvania]]\\n** ''Town Talk'' {{WS|towntalknews.com}} of [[Ridley, Pennsylvania]]\\n* Chesapeake Publishing {{WS|pa8newsgroup.com}} \\n** ''Solanco Sun Ledger'' of [[Quarryville, Pennsylvania]]\\n** ''Columbia Ledger'' of [[Columbia, Pennsylvania]]\\n** ''Coatesville Ledger'' of [[Downingtown, Pennsylvania]]\\n** ''Parkesburg Post Ledger'' of [[Quarryville, Pennsylvania]]\\n** ''Downingtown Ledger'' of [[Downingtown, Pennsylvania]]\\n** ''The Kennett Paper'' of [[Kennett Square, Pennsylvania]]\\n** ''Avon Grove Sun'' of [[West Grove, Pennsylvania]]\\n** ''Oxford Tribune'' of [[Oxford, Pennsylvania]]\\n** ''Elizabethtown Chronicle'' of [[Elizabethtown, Pennsylvania]]\\n** ''Donegal Ledger'' of [[Donegal, Pennsylvania]]\\n** ''Chadds Ford Post'' of [[Chadds Ford, Pennsylvania]]\\n** ''The Central Record'' of [[Medford, New Jersey]]\\n** ''Maple Shade Progress'' of [[Maple Shade, New Jersey]]\\n* Intercounty Newspapers {{WS|buckslocalnews.com}} \\n** ''The Review'' of Roxborough, Pennsylvania\\n** ''The Recorder'' of [[Conshohocken, Pennsylvania]]\\n** ''The Leader'' of [[Mount Airy, Pennsylvania|Mount Airy]] and West Oak Lake, Pennsylvania\\n** ''The Pennington Post'' of [[Pennington, New Jersey]]\\n** ''The Bristol Pilot'' of [[Bristol, Pennsylvania]]\\n** ''Yardley News'' of [[Yardley, Pennsylvania]]\\n** ''New Hope Gazette'' of [[New Hope, Pennsylvania]]\\n** ''Doylestown Patriot'' of [[Doylestown, Pennsylvania]]\\n** ''Newtown Advance'' of [[Newtown, Pennsylvania]]\\n** ''The Plain Dealer'' of [[Williamstown, New Jersey]]\\n** ''News Report'' of [[Sewell, New Jersey]]\\n** ''Record Breeze'' of [[Berlin, New Jersey]]\\n** ''Newsweekly'' of [[Moorestown, New Jersey]]\\n** ''Haddon Herald'' of [[Haddonfield, New Jersey]]\\n** ''New Egypt Press'' of [[New Egypt, New Jersey]]\\n** ''Community News'' of [[Pemberton, New Jersey]]\\n** ''Plymouth Meeting Journal'' of [[Plymouth Meeting, Pennsylvania]]\\n** ''Lafayette Hill Journal'' of [[Lafayette Hill, Pennsylvania]]\\n* Montgomery Newspapers {{WS|montgomerynews.com}} \\n** ''Ambler Gazette'' of [[Ambler, Pennsylvania]]\\n** ''Central Bucks Life'' of [[Bucks County, Pennsylvania]]\\n** ''The Colonial'' of [[Plymouth Meeting, Pennsylvania]]\\n** ''Glenside News'' of [[Glenside, Pennsylvania]]\\n** ''The Globe'' of [[Lower Moreland Township, Pennsylvania]]\\n** ''Main Line Life'' of [[Ardmore, Pennsylvania]]\\n** ''Montgomery Life'' of [[Fort Washington, Pennsylvania]]\\n** ''North Penn Life'' of [[Lansdale, Pennsylvania]]\\n** ''Perkasie News Herald'' of [[Perkasie, Pennsylvania]]\\n** ''Public Spirit'' of [[Hatboro, Pennsylvania]]\\n** ''Souderton Independent'' of [[Souderton, Pennsylvania]]\\n** ''Springfield Sun'' of [[Springfield, Pennsylvania]]\\n** ''Spring-Ford Reporter'' of [[Royersford, Pennsylvania]]\\n** ''Times Chronicle'' of [[Jenkintown, Pennsylvania]]\\n** ''Valley Item'' of [[Perkiomenville, Pennsylvania]]\\n** ''Willow Grove Guide'' of [[Willow Grove, Pennsylvania]]\\n* News Gleaner Publications (closed December 2008) {{WS|newsgleaner.com}} \\n** ''Life Newspapers'' of [[Philadelphia, Pennsylvania]]\\n* Suburban Publications\\n** ''The Suburban &amp; Wayne Times'' {{WS|waynesuburban.com}} of [[Wayne, Pennsylvania]]\\n** ''The Suburban Advertiser'' of [[Exton, Pennsylvania]]\\n** ''The King of Prussia Courier'' of [[King of Prussia, Pennsylvania]]\\n* Press Newspapers {{WS|countypressonline.com}} \\n** ''County Press'' of [[Newtown Square, Pennsylvania]]\\n** ''Garnet Valley Press'' of [[Glen Mills, Pennsylvania]]\\n** ''Haverford Press'' of [[Newtown Square, Pennsylvania]] (closed January 2009)\\n** ''Hometown Press'' of [[Glen Mills, Pennsylvania]] (closed January 2009)\\n** ''Media Press'' of [[Newtown Square, Pennsylvania]] (closed January 2009)\\n** ''Springfield Press'' of [[Springfield, Pennsylvania]]\\n* Berks-Mont Newspapers {{WS|berksmontnews.com}} \\n** ''The Boyertown Area Times'' of [[Boyertown, Pennsylvania]]\\n** ''The Kutztown Area Patriot'' of [[Kutztown, Pennsylvania]]\\n** ''The Hamburg Area Item'' of [[Hamburg, Pennsylvania]]\\n** ''The Southern Berks News'' of [[Exeter Township, Berks County, Pennsylvania]]\\n** ''The Free Press'' of [[Quakertown, Pennsylvania]]\\n** ''The Saucon News'' of [[Quakertown, Pennsylvania]]\\n** ''Westside Weekly'' of [[Reading, Pennsylvania]]\\n\\n* Magazines\\n** ''Bucks Co. Town &amp; Country Living''\\n** ''Chester Co. Town &amp; Country Living''\\n** ''Montomgery Co. Town &amp; Country Living''\\n** ''Garden State Town &amp; Country Living''\\n** ''Montgomery Homes''\\n** ''Philadelphia Golfer''\\n** ''Parents Express''\\n** ''Art Matters''\\n\\n{{JRC}}\\n\\n==References==\\n&lt;references /&gt;\\n\\n[[Category:Journal Register publications|*]]\\n\";\n  String text2 = \"This is a '''list of newspapers published by [[Journal Register Company]]'''.\\n\\nThe company owns daily and weekly newspapers, other print media properties and newspaper-affiliated local Websites in the [[U.S.]] states of [[Connecticut]], [[Michigan]], [[New York]], [[Ohio]], [[Pennsylvania]] and [[New Jersey]], organized in six geographic \\\"clusters\\\":&lt;ref&gt;[http://www.journalregister.com/publications.html Journal Register Company: Our Publications], accessed April 21, 2010.&lt;/ref&gt;\\n\\n== Capital-Saratoga ==\\nThree dailies, associated weeklies and [[pennysaver]]s in greater [[Albany, New York]]; also [http://www.capitalcentral.com capitalcentral.com] and [http://www.jobsinnewyork.com JobsInNewYork.com].\\n\\n* ''The Oneida Daily Dispatch'' {{WS|oneidadispatch.com}} of [[Oneida, New York]]\\n* ''[[The Record (Troy)|The Record]]'' {{WS|troyrecord.com}} of [[Troy, New York]]\\n* ''[[The Saratogian]]'' {{WS|saratogian.com}} of [[Saratoga Springs, New York]]\\n* Weeklies:\\n** ''Community News'' {{WS|cnweekly.com}} weekly of [[Clifton Park, New York]]\\n** ''Rome Observer'' {{WS|romeobserver.com}} of [[Rome, New York]]\\n** ''WG Life '' {{WS|saratogian.com/wglife/}} of [[Wilton, New York]]\\n** ''Ballston Spa Life '' {{WS|saratogian.com/bspalife}} of [[Ballston Spa, New York]]\\n** ''Greenbush Life'' {{WS|troyrecord.com/greenbush}} of [[Troy, New York]]\\n** ''Latham Life'' {{WS|troyrecord.com/latham}} of [[Latham, New York]]\\n** ''River Life'' {{WS|troyrecord.com/river}} of [[Troy, New York]]\\n\\n== Connecticut ==\\nThree dailies, associated weeklies and [[pennysaver]]s in the state of [[Connecticut]]; also [http://www.ctcentral.com CTcentral.com], [http://www.ctcarsandtrucks.com CTCarsAndTrucks.com] and [http://www.jobsinct.com JobsInCT.com].\\n\\n* ''The Middletown Press'' {{WS|middletownpress.com}} of [[Middletown, Connecticut|Middletown]]\\n* ''[[New Haven Register]]'' {{WS|newhavenregister.com}} of [[New Haven, Connecticut|New Haven]]\\n* ''The Register Citizen'' {{WS|registercitizen.com}} of [[Torrington, Connecticut|Torrington]]\\n\\n* Housatonic Publications \\n** ''The Housatonic Times'' {{WS|housatonictimes.com}} of [[New Milford, Connecticut|New Milford]]\\n** ''Litchfield County Times'' {{WS|countytimes.com}} of [[Litchfield, Connecticut|Litchfield]]\\n\\n* Minuteman Publications\\n** ''[[Fairfield Minuteman]]'' {{WS|fairfieldminuteman.com}}of [[Fairfield, Connecticut|Fairfield]]\\n** ''The Westport Minuteman'' {{WS|westportminuteman.com}} of [[Westport, Connecticut|Westport]]\\n\\n* Shoreline Newspapers \\n** ''The Dolphin'' {{WS|dolphin-news.com}} of [[Naval Submarine Base New London]] in [[New London, Connecticut|New London]]\\n** ''Shoreline Times'' {{WS|shorelinetimes.com}} of [[Guilford, Connecticut|Guilford]]\\n\\n* Foothills Media Group {{WS|foothillsmediagroup.com}}\\n** ''Thomaston Express'' {{WS|thomastonexpress.com}} of [[Thomaston, Connecticut|Thomaston]]\\n** ''Good News About Torrington'' {{WS|goodnewsabouttorrington.com}} of [[Torrington, Connecticut|Torrington]]\\n** ''Granby News'' {{WS|foothillsmediagroup.com/granby}} of [[Granby, Connecticut|Granby]]\\n** ''Canton News'' {{WS|foothillsmediagroup.com/canton}} of [[Canton, Connecticut|Canton]]\\n** ''Avon News'' {{WS|foothillsmediagroup.com/avon}} of [[Avon, Connecticut|Avon]]\\n** ''Simsbury News'' {{WS|foothillsmediagroup.com/simsbury}} of [[Simsbury, Connecticut|Simsbury]]\\n** ''Litchfield News'' {{WS|foothillsmediagroup.com/litchfield}} of [[Litchfield, Connecticut|Litchfield]]\\n** ''Foothills Trader'' {{WS|foothillstrader.com}} of Torrington, Bristol, Canton\\n\\n* Other weeklies\\n** ''The Milford-Orange Bulletin'' {{WS|ctbulletin.com}} of [[Orange, Connecticut|Orange]]\\n** ''The Post-Chronicle'' {{WS|ctpostchronicle.com}} of [[North Haven, Connecticut|North Haven]]\\n** ''West Hartford News'' {{WS|westhartfordnews.com}} of [[West Hartford, Connecticut|West Hartford]]\\n\\n* Magazines\\n** ''The Connecticut Bride'' {{WS|connecticutmag.com}}\\n** ''Connecticut Magazine'' {{WS|theconnecticutbride.com}}\\n** ''Passport Magazine'' {{WS|passport-mag.com}}\\n\\n== Michigan ==\\nFour dailies, associated weeklies and [[pennysaver]]s in the state of [[Michigan]]; also [http://www.micentralhomes.com MIcentralhomes.com] and [http://www.micentralautos.com MIcentralautos.com]\\n* ''[[Oakland Press]]'' {{WS|theoaklandpress.com}} of [[Oakland, Michigan|Oakland]]\\n* ''Daily Tribune'' {{WS|dailytribune.com}} of [[Royal Oak, Michigan|Royal Oak]]\\n* ''Macomb Daily'' {{WS|macombdaily.com}} of [[Mt. Clemens, Michigan|Mt. Clemens]]\\n* ''[[Morning Sun]]'' {{WS|themorningsun.com}} of  [[Mount Pleasant, Michigan|Mount Pleasant]]\\n\\n* Heritage Newspapers {{WS|heritage.com}}\\n** ''Belleville View'' {{WS|bellevilleview.com}}\\n** ''Ile Camera'' {{WS|thenewsherald.com/ile_camera}}\\n** ''Monroe Guardian''  {{WS|monreguardian.com}}\\n** ''Ypsilanti Courier'' {{WS|ypsilanticourier.com}}\\n** ''News-Herald'' {{WS|thenewsherald.com}}\\n** ''Press &amp; Guide'' {{WS|pressandguide.com}}\\n** ''Chelsea Standard &amp; Dexter Leader'' {{WS|chelseastandard.com}}\\n** ''Manchester Enterprise'' {{WS|manchesterguardian.com}}\\n** ''Milan News-Leader'' {{WS|milannews.com}}\\n** ''Saline Reporter'' {{WS|salinereporter.com}}\\n* Independent Newspapers \\n** ''Advisor'' {{WS|sourcenewspapers.com}}\\n** ''Source'' {{WS|sourcenewspapers.com}}\\n* Morning Star {{WS|morningstarpublishing.com}}\\n** ''The Leader &amp; Kalkaskian'' {{WS|leaderandkalkaskian.com}}\\n** ''Grand Traverse Insider'' {{WS|grandtraverseinsider.com}}\\n** ''Alma Reminder''\\n** ''Alpena Star''\\n** ''Ogemaw/Oscoda County Star''\\n** ''Presque Isle Star''\\n** ''St. Johns Reminder''\\n\\n* Voice Newspapers {{WS|voicenews.com}}\\n** ''Armada Times''\\n** ''Bay Voice''\\n** ''Blue Water Voice''\\n** ''Downriver Voice''\\n** ''Macomb Township Voice''\\n** ''North Macomb Voice''\\n** ''Weekend Voice''\\n\\n== Mid-Hudson ==\\nOne daily, associated magazines in the [[Hudson River Valley]] of [[New York]]; also [http://www.midhudsoncentral.com MidHudsonCentral.com] and [http://www.jobsinnewyork.com JobsInNewYork.com].\\n\\n* ''[[Daily Freeman]]'' {{WS|dailyfreeman.com}} of [[Kingston, New York]]\\n* ''Las Noticias'' {{WS|lasnoticiasny.com}} of [[Kingston, New York]]\\n\\n== Ohio ==\\nTwo dailies, associated magazines and three shared Websites, all in the state of [[Ohio]]: [http://www.allaroundcleveland.com AllAroundCleveland.com], [http://www.allaroundclevelandcars.com AllAroundClevelandCars.com] and [http://www.allaroundclevelandjobs.com AllAroundClevelandJobs.com].\\n\\n* ''[[The News-Herald (Ohio)|The News-Herald]]'' {{WS|news-herald.com}} of [[Willoughby, Ohio|Willoughby]]\\n* ''[[The Morning Journal]]'' {{WS|morningjournal.com}} of [[Lorain, Ohio|Lorain]]\\n* ''El Latino Expreso'' {{WS|lorainlatino.com}} of [[Lorain, Ohio|Lorain]]\\n\\n== Philadelphia area ==\\nSeven dailies and associated weeklies and magazines in [[Pennsylvania]] and [[New Jersey]], and associated Websites: [http://www.allaroundphilly.com AllAroundPhilly.com], [http://www.jobsinnj.com JobsInNJ.com], [http://www.jobsinpa.com JobsInPA.com], and [http://www.phillycarsearch.com PhillyCarSearch.com].\\n\\n* ''[[The Daily Local News]]'' {{WS|dailylocal.com}} of [[West Chester, Pennsylvania|West Chester]]\\n* ''[[Delaware County Daily and Sunday Times]] {{WS|delcotimes.com}} of Primos [[Upper Darby Township, Pennsylvania]]\\n* ''[[The Mercury (Pennsylvania)|The Mercury]]'' {{WS|pottstownmercury.com}} of [[Pottstown, Pennsylvania|Pottstown]]\\n* ''[[The Reporter (Lansdale)|The Reporter]]'' {{WS|thereporteronline.com}} of [[Lansdale, Pennsylvania|Lansdale]]\\n* ''The Times Herald'' {{WS|timesherald.com}} of [[Norristown, Pennsylvania|Norristown]]\\n* ''[[The Trentonian]]'' {{WS|trentonian.com}} of [[Trenton, New Jersey]]\\n\\n* Weeklies\\n* ''The Phoenix'' {{WS|phoenixvillenews.com}} of [[Phoenixville, Pennsylvania]]\\n** ''El Latino Expreso'' {{WS|njexpreso.com}} of [[Trenton, New Jersey]]\\n** ''La Voz'' {{WS|lavozpa.com}} of [[Norristown, Pennsylvania]]\\n** ''The Tri County Record'' {{WS|tricountyrecord.com}} of [[Morgantown, Pennsylvania]]\\n** ''Penny Pincher'' {{WS|pennypincherpa.com}}of [[Pottstown, Pennsylvania]]\\n\\n* Chesapeake Publishing  {{WS|southernchestercountyweeklies.com}}\\n** ''The Kennett Paper'' {{WS|kennettpaper.com}} of [[Kennett Square, Pennsylvania]]\\n** ''Avon Grove Sun'' {{WS|avongrovesun.com}} of [[West Grove, Pennsylvania]]\\n** ''The Central Record'' {{WS|medfordcentralrecord.com}} of [[Medford, New Jersey]]\\n** ''Maple Shade Progress'' {{WS|mapleshadeprogress.com}} of [[Maple Shade, New Jersey]]\\n\\n* Intercounty Newspapers {{WS|buckslocalnews.com}} {{WS|southjerseylocalnews.com}} \\n** ''The Pennington Post'' {{WS|penningtonpost.com}} of [[Pennington, New Jersey]]\\n** ''The Bristol Pilot'' {{WS|bristolpilot.com}} of [[Bristol, Pennsylvania]]\\n** ''Yardley News'' {{WS|yardleynews.com}} of [[Yardley, Pennsylvania]]\\n** ''Advance of Bucks County'' {{WS|advanceofbucks.com}} of [[Newtown, Pennsylvania]]\\n** ''Record Breeze'' {{WS|recordbreeze.com}} of [[Berlin, New Jersey]]\\n** ''Community News'' {{WS|sjcommunitynews.com}} of [[Pemberton, New Jersey]]\\n\\n* Montgomery Newspapers {{WS|montgomerynews.com}} \\n** ''Ambler Gazette'' {{WS|amblergazette.com}} of [[Ambler, Pennsylvania]]\\n** ''The Colonial'' {{WS|colonialnews.com}} of [[Plymouth Meeting, Pennsylvania]]\\n** ''Glenside News'' {{WS|glensidenews.com}} of [[Glenside, Pennsylvania]]\\n** ''The Globe'' {{WS|globenewspaper.com}} of [[Lower Moreland Township, Pennsylvania]]\\n** ''Montgomery Life'' {{WS|montgomerylife.com}} of [[Fort Washington, Pennsylvania]]\\n** ''North Penn Life'' {{WS|northpennlife.com}} of [[Lansdale, Pennsylvania]]\\n** ''Perkasie News Herald'' {{WS|perkasienewsherald.com}} of [[Perkasie, Pennsylvania]]\\n** ''Public Spirit'' {{WS|thepublicspirit.com}} of [[Hatboro, Pennsylvania]]\\n** ''Souderton Independent'' {{WS|soudertonindependent.com}} of [[Souderton, Pennsylvania]]\\n** ''Springfield Sun'' {{WS|springfieldsun.com}} of [[Springfield, Pennsylvania]]\\n** ''Spring-Ford Reporter'' {{WS|springfordreporter.com}} of [[Royersford, Pennsylvania]]\\n** ''Times Chronicle'' {{WS|thetimeschronicle.com}} of [[Jenkintown, Pennsylvania]]\\n** ''Valley Item'' {{WS|valleyitem.com}} of [[Perkiomenville, Pennsylvania]]\\n** ''Willow Grove Guide'' {{WS|willowgroveguide.com}} of [[Willow Grove, Pennsylvania]]\\n** ''The Review'' {{WS|roxreview.com}} of [[Roxborough, Philadelphia, Pennsylvania]]\\n\\n* Main Line Media News {{WS|mainlinemedianews.com}}\\n** ''Main Line Times'' {{WS|mainlinetimes.com}} of [[Ardmore, Pennsylvania]]\\n** ''Main Line Life'' {{WS|mainlinelife.com}} of [[Ardmore, Pennsylvania]]\\n** ''The King of Prussia Courier'' {{WS|kingofprussiacourier.com}} of [[King of Prussia, Pennsylvania]]\\n\\n* Delaware County News Network {{WS|delconewsnetwork.com}} \\n** ''News of Delaware County'' {{WS|newsofdelawarecounty.com}} of [[Havertown, Pennsylvania]]\\n** ''County Press'' {{WS|countypressonline.com}} of [[Newtown Square, Pennsylvania]]\\n** ''Garnet Valley Press'' {{WS|countypressonline.com}} of [[Glen Mills, Pennsylvania]]\\n** ''Springfield Press'' {{WS|countypressonline.com}} of [[Springfield, Pennsylvania]]\\n** ''Town Talk'' {{WS|towntalknews.com}} of [[Ridley, Pennsylvania]]\\n\\n* Berks-Mont Newspapers {{WS|berksmontnews.com}} \\n** ''The Boyertown Area Times'' {{WS|berksmontnews.com/boyertown_area_times}} of [[Boyertown, Pennsylvania]]\\n** ''The Kutztown Area Patriot'' {{WS|berksmontnews.com/kutztown_area_patriot}} of [[Kutztown, Pennsylvania]]\\n** ''The Hamburg Area Item'' {{WS|berksmontnews.com/hamburg_area_item}} of [[Hamburg, Pennsylvania]]\\n** ''The Southern Berks News'' {{WS|berksmontnews.com/southern_berks_news}} of [[Exeter Township, Berks County, Pennsylvania]]\\n** ''Community Connection'' {{WS|berksmontnews.com/community_connection}} of [[Boyertown, Pennsylvania]]\\n\\n* Magazines\\n** ''Bucks Co. Town &amp; Country Living'' {{WS|buckscountymagazine.com}} \\n** ''Parents Express'' {{WS|parents-express.com}} \\n** ''Real Men, Rednecks'' {{WS|realmenredneck.com}} \\n\\n{{JRC}}\\n\\n==References==\\n&lt;references /&gt;\\n\\n[[Category:Journal Register publications|*]]\\n\";\n\n  DiffMatchPatch dmp = new DiffMatchPatch();\n  dmp.Diff_Timeout = 0.0;\n\n  // No warmup loop since it risks triggering an 'unresponsive script' dialog.\n  DateTime date_start = new DateTime.now();\n  List<Diff> d = dmp.diff_main(text1, text2, false);\n  DateTime date_end = new DateTime.now();\n\n  print (dmp.diff_prettyHtml(d));\n  print ('\\nTime: ${date_end.difference(date_start)} (h:mm:ss.mmm)');\n}\n"
  },
  {
    "path": "demos/diff.html",
    "content": "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01 Transitional//EN\" \"http://www.w3.org/TR/html4/loose.dtd\">\n<HTML>\n<HEAD>\n  <TITLE>Diff, Match and Patch: Demo of Diff</TITLE>\n  <SCRIPT SRC=\"../javascript/diff_match_patch.js\"></SCRIPT>\n</HEAD>\n\n<BODY>\n<H1>Diff, Match and Patch</H1>\n<H2>Demo of Diff</H2>\n\n<P>Diff takes two texts and finds the differences.  This implementation works on a character by character basis.\nThe result of any diff may contain 'chaff', irrelevant small commonalities which complicate the output.\nA post-diff cleanup algorithm factors out these trivial commonalities.</P>\n\n<SCRIPT>\nvar dmp = new diff_match_patch();\n\nfunction launch() {\n  var text1 = document.getElementById('text1').value;\n  var text2 = document.getElementById('text2').value;\n  dmp.Diff_Timeout = parseFloat(document.getElementById('timeout').value);\n  dmp.Diff_EditCost = parseFloat(document.getElementById('editcost').value);\n\n  var ms_start = (new Date()).getTime();\n  var d = dmp.diff_main(text1, text2);\n  var ms_end = (new Date()).getTime();\n\n  if (document.getElementById('semantic').checked) {\n    dmp.diff_cleanupSemantic(d);\n  }\n  if (document.getElementById('efficiency').checked) {\n    dmp.diff_cleanupEfficiency(d);\n  }\n  var ds = dmp.diff_prettyHtml(d);\n  document.getElementById('outputdiv').innerHTML = ds + '<BR>Time: ' + (ms_end - ms_start) / 1000 + 's';\n}\n</SCRIPT>\n\n<FORM action=\"#\" onsubmit=\"return false\">\n<TABLE WIDTH=\"100%\"><TR>\n  <TD WIDTH=\"50%\">\n<H3>Text Version 1:</H3>\n<TEXTAREA ID=\"text1\" STYLE=\"width: 100%\" ROWS=10>I am the very model of a modern Major-General,\nI've information vegetable, animal, and mineral,\nI know the kings of England, and I quote the fights historical,\nFrom Marathon to Waterloo, in order categorical.</TEXTAREA></TD>\n  <TD WIDTH=\"50%\">\n<H3>Text Version 2:</H3>\n<TEXTAREA ID=\"text2\" STYLE=\"width: 100%\" ROWS=10>I am the very model of a cartoon individual,\nMy animation's comical, unusual, and whimsical,\nI'm quite adept at funny gags, comedic theory I have read,\nFrom wicked puns and stupid jokes to anvils that drop on your head.</TEXTAREA></TD>\n</TR></TABLE>\n\n<H3>Diff timeout:</H3>\n<P><INPUT TYPE=\"text\" SIZE=3 MAXLENGTH=5 VALUE=\"1\" ID=\"timeout\"> seconds<BR>\nIf the mapping phase of the diff computation takes longer than this, then the computation\nis truncated and the best solution to date is returned.  While guaranteed to be correct,\nit may not be optimal.  A timeout of '0' allows for unlimited computation.</P>\n\n<H3>Post-diff cleanup:</H3>\n<DL>\n<DT><INPUT TYPE=\"radio\" NAME=\"cleanup\" ID=\"semantic\" CHECKED>\n<LABEL FOR=\"semantic\">Semantic Cleanup</LABEL></DT>\n<DD>Increase human readability by factoring out commonalities which are likely to be\ncoincidental.</DD>\n<DT><INPUT TYPE=\"radio\" NAME=\"cleanup\" ID=\"efficiency\">\n<LABEL FOR=\"efficiency\">Efficiency Cleanup</LABEL>,\nedit cost: <INPUT TYPE=\"text\" SIZE=3 MAXLENGTH=5 VALUE=\"4\" ID=\"editcost\">\n<DD>Increase computational efficiency by factoring out short commonalities which are\nnot worth the overhead.  The larger the edit cost, the more aggressive the cleanup.</DD>\n<DT><INPUT TYPE=\"radio\" NAME=\"cleanup\" ID=\"raw\">\n<LABEL FOR=\"raw\">No Cleanup</LABEL></DT>\n<DD>Raw output.</DD>\n</DL>\n\n<P><INPUT TYPE=\"button\" onClick=\"launch()\" VALUE=\"Compute Diff\"></P>\n</FORM>\n\n<DIV ID=\"outputdiv\"></DIV>\n\n<HR>\nBack to <A HREF=\"https://github.com/google/diff-match-patch\">Diff, Match and Patch</A>\n\n</BODY>\n</HTML>\n"
  },
  {
    "path": "demos/match.html",
    "content": "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01 Transitional//EN\" \"http://www.w3.org/TR/html4/loose.dtd\">\n<HTML>\n<HEAD>\n  <TITLE>Diff, Match and Patch: Demo of Match</TITLE>\n  <SCRIPT SRC=\"../javascript/diff_match_patch.js\"></SCRIPT>\n</HEAD>\n\n<BODY>\n<H1>Diff, Match and Patch</H1>\n<H2>Demo of Match</H2>\n\n<P>Match looks for a pattern within a larger text.\nThis implementation of match is fuzzy, meaning it can find a match even if the\npattern contains errors and doesn't exactly match what is found in the text.\nThis implementation also accepts an expected location, near which the match should be found.\nThe candidate matches are scored based on a) the number of spelling differences between the\npattern and the text and b) the distance between the candidate match and the expected location.\nThe match distance parameter sets the relative importance of these two metrics.</P>\n\n<FORM action=\"#\" onsubmit=\"return false\">\n<H3>Text:</H3>\n<TEXTAREA ID=\"text\" STYLE=\"width: 100%\" ROWS=10 onChange=\"textchange()\">'Twas brillig, and the slithy toves\nDid gyre and gimble in the wabe.\nAll mimsy were the borogroves,\nAnd the mome raths outgrabe.</TEXTAREA>\n\n<H3>Fuzzy pattern:</H3>\n<P><INPUT ID=\"pattern\" SIZE=32 VALUE=\"slimy tools\"> <SPAN ID=\"maxlengthspan\"></SPAN><BR>\nApproximate pattern to search for in the text.  Due to limitations of the Bitap algorithm, the pattern has a limited length.</P>\n\n<H3>Fuzzy location:</H3>\n<P><INPUT ID=\"loc\" SIZE=4 MAXLENGTH=10 VALUE=\"30\"> <SPAN ID=\"maxtextspan\"></SPAN><BR>\nApproximately where in the text is the pattern expected to be found?</P>\n\n<H3>Match distance:</H3>\n<P><INPUT TYPE=\"text\" SIZE=3 MAXLENGTH=8 VALUE=\"1000\" ID=\"distance\"><BR>\nDetermines how close the match must be to the fuzzy location (specified above).  An exact letter match which is 'distance' characters away from the fuzzy location would\nscore as a complete mismatch.  A distance of '0' requires the match be at the exact location specified, a threshold of '1000'\nwould require a perfect match to be within 800 characters of the fuzzy location to be found using a 0.8 threshold.</P>\n\n<H3>Match threshold:</H3>\n<P><INPUT TYPE=\"text\" SIZE=3 MAXLENGTH=5 VALUE=\"0.8\" ID=\"threshold\"><BR>\nAt what point does the match algorithm give up.  A threshold of '0.0' requires a perfect match (of both letters and location), a threshold of '1.0' would match anything.</P>\n\n<INPUT TYPE=\"button\" onClick=\"launch()\" VALUE=\"Compute Match\">\n</FORM>\n\n<DIV ID=\"outputdiv\"></DIV>\n\n<DIV ID=\"datediv\"></DIV>\n\n<SCRIPT>\nvar dmp = new diff_match_patch();\n\nfunction launch() {\n  var text = document.getElementById('text').value;\n  var pattern = document.getElementById('pattern').value;\n  var loc = parseInt(document.getElementById('loc').value, 10);\n\n  dmp.Match_Distance = parseFloat(document.getElementById('distance').value);\n  dmp.Match_Threshold = parseFloat(document.getElementById('threshold').value);\n\n  var ms_start = (new Date()).getTime();\n  var match = dmp.match_main(text, pattern, loc);\n  var ms_end = (new Date()).getTime();\n\n  document.getElementById('datediv').innerHTML = 'Time: ' + (ms_end - ms_start) / 1000  + 's';\n  if (match == -1) {\n    document.getElementById('outputdiv').innerHTML = 'No match found.';\n  } else {\n    var quote = text.substring(match, match + pattern.length);\n    quote = quote.replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;');\n    quote = quote.replace(/\\n/g, '&para;');\n    document.getElementById('outputdiv').innerHTML = 'Match found at character ' + match +\n        ': &nbsp; <CODE>' + quote + '</' + 'CODE>';\n  }\n}\n\nfunction textchange() {\n  document.getElementById('maxtextspan').innerHTML = '(text is currently ' + document.getElementById('text').value.length + ' characters long)';\n}\n\ntextchange();\ndocument.getElementById('pattern').maxLength = dmp.Match_MaxBits;\ndocument.getElementById('maxlengthspan').innerHTML = '(maxlength in this browser: ' + dmp.Match_MaxBits + ')';\n</SCRIPT>\n\n<HR>\nBack to <A HREF=\"https://github.com/google/diff-match-patch\">Diff, Match and Patch</A>\n\n</BODY>\n</HTML>\n"
  },
  {
    "path": "demos/patch.html",
    "content": "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01 Transitional//EN\" \"http://www.w3.org/TR/html4/loose.dtd\">\n<HTML>\n<HEAD>\n  <TITLE>Diff, Match and Patch: Demo of Patch</TITLE>\n  <SCRIPT SRC=\"../javascript/diff_match_patch.js\"></SCRIPT>\n</HEAD>\n\n<BODY>\n<H1>Diff, Match and Patch</H1>\n<H2>Demo of Patch</H2>\n\n<P>Two texts can be diffed against each other, generating a list of patches.\nThese patches can then be applied against a third text.  If the third text has edits of its own, this version of patch\nwill apply its changes on a best-effort basis, reporting which patches succeeded and which failed.</P>\n\n<P>In this scenario Shakespeare wrote Hamlet in Early Modern English, the source document.  Then two derivative\nworks were created.  One is Hamlet updated to Modern English.  The other is a Star Trek parody in Early Modern English.\nThis demonstrantion creates a list of patches between the source and the Modern English version.  Then it\napplies those patches onto the Star Trek parody, thus creating a Star Trek parody in\nModern English.</P>\n\n<SCRIPT>\nvar dmp = new diff_match_patch();\n\nvar patch_text = '';\n\nfunction diff_launch() {\n  var text1 = document.getElementById('text1a').value;\n  var text2 = document.getElementById('text2a').value;\n\n  var ms_start = (new Date).getTime();\n  var diff = dmp.diff_main(text1, text2, true);\n  var ms_end = (new Date).getTime();\n\n  if (diff.length > 2) {\n    dmp.diff_cleanupSemantic(diff);\n  }\n\n  var patch_list = dmp.patch_make(text1, text2, diff);\n  patch_text = dmp.patch_toText(patch_list);\n\n  document.getElementById('diffdatediv').innerHTML =\n      'Time: ' + (ms_end - ms_start) / 1000 + 's';\n  document.getElementById('diffoutputdiv').innerHTML =\n      '<FIELDSET><LEGEND>Patch:</' + 'LEGEND><PRE>' + patch_text +\n      '</' + 'PRE></' + 'FIELDSET>';\n  //document.getElementById('diffoutputdiv').innerHTML = dmp.diff_prettyHtml(diff);\n  document.getElementById('patchbutton').disabled = false;\n}\n\n\nfunction patch_launch() {\n  var text1 = document.getElementById('text1b').value;\n  var patches = dmp.patch_fromText(patch_text);\n\n  var ms_start = (new Date).getTime();\n  var results = dmp.patch_apply(patches, text1);\n  var ms_end = (new Date).getTime();\n\n  document.getElementById('patchdatediv').innerHTML =\n      'Time: ' + (ms_end - ms_start) / 1000 + 's';\n  document.getElementById('text2b').value = results[0];\n  results = results[1];\n  var html = '';\n  for (var x = 0; x < results.length; x++) {\n    if (results[x]) {\n      html += '<LI><FONT COLOR=\"#009900\">Ok</' + 'FONT>';\n    } else {\n      html += '<LI><FONT COLOR=\"#990000\">Fail</' + 'FONT>';\n    }\n  }\n  document.getElementById('passfaildiv').innerHTML = html;\n}\n</SCRIPT>\n\n<FORM action=\"#\" onsubmit=\"return false\">\n<H3>Shakespeare's copy:</H3>\n<TABLE WIDTH=\"100%\"><TR>\n  <TD WIDTH=\"50%\">Old Version:<BR><TEXTAREA ID=\"text1a\" STYLE=\"width: 100%\" ROWS=10>Hamlet: Do you see yonder cloud that's almost in shape of a camel?\nPolonius: By the mass, and 'tis like a camel, indeed.\nHamlet: Methinks it is like a weasel.\nPolonius: It is backed like a weasel.\nHamlet: Or like a whale?\nPolonius: Very like a whale.\n-- Shakespeare</TEXTAREA></TD>\n  <TD WIDTH=\"50%\">New Version:<BR><TEXTAREA ID=\"text2a\" STYLE=\"width: 100%\" ROWS=10>Hamlet: Do you see the cloud over there that's almost the shape of a camel?\nPolonius: By golly, it is like a camel, indeed.\nHamlet: I think it looks like a weasel.\nPolonius: It is shaped like a weasel.\nHamlet: Or like a whale?\nPolonius: It's totally like a whale.\n-- Shakespeare</TEXTAREA></TD>\n</TR></TABLE>\n<P><INPUT TYPE=\"button\" onClick=\"diff_launch()\" VALUE=\"Compute Patch\"></P>\n<BLOCKQUOTE><DIV ID=\"diffoutputdiv\"></DIV></BLOCKQUOTE>\n\n<DIV ID=\"diffdatediv\"></DIV>\n\n<H3>Trekkie's copy:</H3>\n<TABLE WIDTH=\"100%\"><TR>\n  <TD WIDTH=\"50%\">Old Version:<BR><TEXTAREA ID=\"text1b\" STYLE=\"width: 100%\" ROWS=10>Kirk: Do you see yonder cloud that's almost in shape of a Klingon?\nSpock: By the mass, and 'tis like a Klingon, indeed.\nKirk: Methinks it is like a Vulcan.\nSpock: It is backed like a Vulcan.\nKirk: Or like a Romulan?\nSpock: Very like a Romulan.\n-- Trekkie</TEXTAREA></TD>\n  <TD WIDTH=\"50%\">New Version:<BR><TEXTAREA READONLY ID=\"text2b\" STYLE=\"width: 100%\" ROWS=10></TEXTAREA></TD>\n</TR></TABLE>\n<INPUT TYPE=\"button\" ID=\"patchbutton\" onClick=\"patch_launch()\" VALUE=\"Apply Patch\" DISABLED>\n</FORM>\n\n<OL ID=\"passfaildiv\"></OL>\n<DIV ID=\"patchdatediv\"></DIV>\n\n<HR>\nBack to <A HREF=\"https://github.com/google/diff-match-patch\">Diff, Match and Patch</A>\n\n</BODY>\n</HTML>\n"
  },
  {
    "path": "java/src/name/fraser/neil/plaintext/diff_match_patch.java",
    "content": "/*\n * Diff Match and Patch\n * Copyright 2018 The diff-match-patch Authors.\n * https://github.com/google/diff-match-patch\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *   http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage name.fraser.neil.plaintext;\n\nimport java.io.UnsupportedEncodingException;\nimport java.net.URLDecoder;\nimport java.net.URLEncoder;\nimport java.util.*;\nimport java.util.regex.Matcher;\nimport java.util.regex.Pattern;\n\n/*\n * Functions for diff, match and patch.\n * Computes the difference between two texts to create a patch.\n * Applies the patch onto another text, allowing for errors.\n *\n * @author fraser@google.com (Neil Fraser)\n */\n\n/**\n * Class containing the diff, match and patch methods.\n * Also contains the behaviour settings.\n */\npublic class diff_match_patch {\n\n  // Defaults.\n  // Set these on your diff_match_patch instance to override the defaults.\n\n  /**\n   * Number of seconds to map a diff before giving up (0 for infinity).\n   */\n  public float Diff_Timeout = 1.0f;\n  /**\n   * Cost of an empty edit operation in terms of edit characters.\n   */\n  public short Diff_EditCost = 4;\n  /**\n   * At what point is no match declared (0.0 = perfection, 1.0 = very loose).\n   */\n  public float Match_Threshold = 0.5f;\n  /**\n   * How far to search for a match (0 = exact location, 1000+ = broad match).\n   * A match this many characters away from the expected location will add\n   * 1.0 to the score (0.0 is a perfect match).\n   */\n  public int Match_Distance = 1000;\n  /**\n   * When deleting a large block of text (over ~64 characters), how close do\n   * the contents have to be to match the expected contents. (0.0 = perfection,\n   * 1.0 = very loose).  Note that Match_Threshold controls how closely the\n   * end points of a delete need to match.\n   */\n  public float Patch_DeleteThreshold = 0.5f;\n  /**\n   * Chunk size for context length.\n   */\n  public short Patch_Margin = 4;\n\n  /**\n   * The number of bits in an int.\n   */\n  private short Match_MaxBits = 32;\n\n  /**\n   * Internal class for returning results from diff_linesToChars().\n   * Other less paranoid languages just use a three-element array.\n   */\n  protected static class LinesToCharsResult {\n    protected String chars1;\n    protected String chars2;\n    protected List<String> lineArray;\n\n    protected LinesToCharsResult(String chars1, String chars2,\n        List<String> lineArray) {\n      this.chars1 = chars1;\n      this.chars2 = chars2;\n      this.lineArray = lineArray;\n    }\n  }\n\n\n  //  DIFF FUNCTIONS\n\n\n  /**\n   * The data structure representing a diff is a Linked list of Diff objects:\n   * {Diff(Operation.DELETE, \"Hello\"), Diff(Operation.INSERT, \"Goodbye\"),\n   *  Diff(Operation.EQUAL, \" world.\")}\n   * which means: delete \"Hello\", add \"Goodbye\" and keep \" world.\"\n   */\n  public enum Operation {\n    DELETE, INSERT, EQUAL\n  }\n\n  /**\n   * Find the differences between two texts.\n   * Run a faster, slightly less optimal diff.\n   * This method allows the 'checklines' of diff_main() to be optional.\n   * Most of the time checklines is wanted, so default to true.\n   * @param text1 Old string to be diffed.\n   * @param text2 New string to be diffed.\n   * @return Linked List of Diff objects.\n   */\n  public LinkedList<Diff> diff_main(String text1, String text2) {\n    return diff_main(text1, text2, true);\n  }\n\n  /**\n   * Find the differences between two texts.\n   * @param text1 Old string to be diffed.\n   * @param text2 New string to be diffed.\n   * @param checklines Speedup flag.  If false, then don't run a\n   *     line-level diff first to identify the changed areas.\n   *     If true, then run a faster slightly less optimal diff.\n   * @return Linked List of Diff objects.\n   */\n  public LinkedList<Diff> diff_main(String text1, String text2,\n                                    boolean checklines) {\n    // Set a deadline by which time the diff must be complete.\n    long deadline;\n    if (Diff_Timeout <= 0) {\n      deadline = Long.MAX_VALUE;\n    } else {\n      deadline = System.currentTimeMillis() + (long) (Diff_Timeout * 1000);\n    }\n    return diff_main(text1, text2, checklines, deadline);\n  }\n\n  /**\n   * Find the differences between two texts.  Simplifies the problem by\n   * stripping any common prefix or suffix off the texts before diffing.\n   * @param text1 Old string to be diffed.\n   * @param text2 New string to be diffed.\n   * @param checklines Speedup flag.  If false, then don't run a\n   *     line-level diff first to identify the changed areas.\n   *     If true, then run a faster slightly less optimal diff.\n   * @param deadline Time when the diff should be complete by.  Used\n   *     internally for recursive calls.  Users should set DiffTimeout instead.\n   * @return Linked List of Diff objects.\n   */\n  private LinkedList<Diff> diff_main(String text1, String text2,\n                                     boolean checklines, long deadline) {\n    // Check for null inputs.\n    if (text1 == null || text2 == null) {\n      throw new IllegalArgumentException(\"Null inputs. (diff_main)\");\n    }\n\n    // Check for equality (speedup).\n    LinkedList<Diff> diffs;\n    if (text1.equals(text2)) {\n      diffs = new LinkedList<Diff>();\n      if (text1.length() != 0) {\n        diffs.add(new Diff(Operation.EQUAL, text1));\n      }\n      return diffs;\n    }\n\n    // Trim off common prefix (speedup).\n    int commonlength = diff_commonPrefix(text1, text2);\n    String commonprefix = text1.substring(0, commonlength);\n    text1 = text1.substring(commonlength);\n    text2 = text2.substring(commonlength);\n\n    // Trim off common suffix (speedup).\n    commonlength = diff_commonSuffix(text1, text2);\n    String commonsuffix = text1.substring(text1.length() - commonlength);\n    text1 = text1.substring(0, text1.length() - commonlength);\n    text2 = text2.substring(0, text2.length() - commonlength);\n\n    // Compute the diff on the middle block.\n    diffs = diff_compute(text1, text2, checklines, deadline);\n\n    // Restore the prefix and suffix.\n    if (commonprefix.length() != 0) {\n      diffs.addFirst(new Diff(Operation.EQUAL, commonprefix));\n    }\n    if (commonsuffix.length() != 0) {\n      diffs.addLast(new Diff(Operation.EQUAL, commonsuffix));\n    }\n\n    diff_cleanupMerge(diffs);\n    return diffs;\n  }\n\n  /**\n   * Find the differences between two texts.  Assumes that the texts do not\n   * have any common prefix or suffix.\n   * @param text1 Old string to be diffed.\n   * @param text2 New string to be diffed.\n   * @param checklines Speedup flag.  If false, then don't run a\n   *     line-level diff first to identify the changed areas.\n   *     If true, then run a faster slightly less optimal diff.\n   * @param deadline Time when the diff should be complete by.\n   * @return Linked List of Diff objects.\n   */\n  private LinkedList<Diff> diff_compute(String text1, String text2,\n                                        boolean checklines, long deadline) {\n    LinkedList<Diff> diffs = new LinkedList<Diff>();\n\n    if (text1.length() == 0) {\n      // Just add some text (speedup).\n      diffs.add(new Diff(Operation.INSERT, text2));\n      return diffs;\n    }\n\n    if (text2.length() == 0) {\n      // Just delete some text (speedup).\n      diffs.add(new Diff(Operation.DELETE, text1));\n      return diffs;\n    }\n\n    String longtext = text1.length() > text2.length() ? text1 : text2;\n    String shorttext = text1.length() > text2.length() ? text2 : text1;\n    int i = longtext.indexOf(shorttext);\n    if (i != -1) {\n      // Shorter text is inside the longer text (speedup).\n      Operation op = (text1.length() > text2.length()) ?\n                     Operation.DELETE : Operation.INSERT;\n      diffs.add(new Diff(op, longtext.substring(0, i)));\n      diffs.add(new Diff(Operation.EQUAL, shorttext));\n      diffs.add(new Diff(op, longtext.substring(i + shorttext.length())));\n      return diffs;\n    }\n\n    if (shorttext.length() == 1) {\n      // Single character string.\n      // After the previous speedup, the character can't be an equality.\n      diffs.add(new Diff(Operation.DELETE, text1));\n      diffs.add(new Diff(Operation.INSERT, text2));\n      return diffs;\n    }\n\n    // Check to see if the problem can be split in two.\n    String[] hm = diff_halfMatch(text1, text2);\n    if (hm != null) {\n      // A half-match was found, sort out the return data.\n      String text1_a = hm[0];\n      String text1_b = hm[1];\n      String text2_a = hm[2];\n      String text2_b = hm[3];\n      String mid_common = hm[4];\n      // Send both pairs off for separate processing.\n      LinkedList<Diff> diffs_a = diff_main(text1_a, text2_a,\n                                           checklines, deadline);\n      LinkedList<Diff> diffs_b = diff_main(text1_b, text2_b,\n                                           checklines, deadline);\n      // Merge the results.\n      diffs = diffs_a;\n      diffs.add(new Diff(Operation.EQUAL, mid_common));\n      diffs.addAll(diffs_b);\n      return diffs;\n    }\n\n    if (checklines && text1.length() > 100 && text2.length() > 100) {\n      return diff_lineMode(text1, text2, deadline);\n    }\n\n    return diff_bisect(text1, text2, deadline);\n  }\n\n  /**\n   * Do a quick line-level diff on both strings, then rediff the parts for\n   * greater accuracy.\n   * This speedup can produce non-minimal diffs.\n   * @param text1 Old string to be diffed.\n   * @param text2 New string to be diffed.\n   * @param deadline Time when the diff should be complete by.\n   * @return Linked List of Diff objects.\n   */\n  private LinkedList<Diff> diff_lineMode(String text1, String text2,\n                                         long deadline) {\n    // Scan the text on a line-by-line basis first.\n    LinesToCharsResult a = diff_linesToChars(text1, text2);\n    text1 = a.chars1;\n    text2 = a.chars2;\n    List<String> linearray = a.lineArray;\n\n    LinkedList<Diff> diffs = diff_main(text1, text2, false, deadline);\n\n    // Convert the diff back to original text.\n    diff_charsToLines(diffs, linearray);\n    // Eliminate freak matches (e.g. blank lines)\n    diff_cleanupSemantic(diffs);\n\n    // Rediff any replacement blocks, this time character-by-character.\n    // Add a dummy entry at the end.\n    diffs.add(new Diff(Operation.EQUAL, \"\"));\n    int count_delete = 0;\n    int count_insert = 0;\n    String text_delete = \"\";\n    String text_insert = \"\";\n    ListIterator<Diff> pointer = diffs.listIterator();\n    Diff thisDiff = pointer.next();\n    while (thisDiff != null) {\n      switch (thisDiff.operation) {\n      case INSERT:\n        count_insert++;\n        text_insert += thisDiff.text;\n        break;\n      case DELETE:\n        count_delete++;\n        text_delete += thisDiff.text;\n        break;\n      case EQUAL:\n        // Upon reaching an equality, check for prior redundancies.\n        if (count_delete >= 1 && count_insert >= 1) {\n          // Delete the offending records and add the merged ones.\n          pointer.previous();\n          for (int j = 0; j < count_delete + count_insert; j++) {\n            pointer.previous();\n            pointer.remove();\n          }\n          for (Diff subDiff : diff_main(text_delete, text_insert, false,\n              deadline)) {\n            pointer.add(subDiff);\n          }\n        }\n        count_insert = 0;\n        count_delete = 0;\n        text_delete = \"\";\n        text_insert = \"\";\n        break;\n      }\n      thisDiff = pointer.hasNext() ? pointer.next() : null;\n    }\n    diffs.removeLast();  // Remove the dummy entry at the end.\n\n    return diffs;\n  }\n\n  /**\n   * Find the 'middle snake' of a diff, split the problem in two\n   * and return the recursively constructed diff.\n   * See Myers 1986 paper: An O(ND) Difference Algorithm and Its Variations.\n   * @param text1 Old string to be diffed.\n   * @param text2 New string to be diffed.\n   * @param deadline Time at which to bail if not yet complete.\n   * @return LinkedList of Diff objects.\n   */\n  protected LinkedList<Diff> diff_bisect(String text1, String text2,\n      long deadline) {\n    // Cache the text lengths to prevent multiple calls.\n    int text1_length = text1.length();\n    int text2_length = text2.length();\n    int max_d = (text1_length + text2_length + 1) / 2;\n    int v_offset = max_d;\n    int v_length = 2 * max_d;\n    int[] v1 = new int[v_length];\n    int[] v2 = new int[v_length];\n    for (int x = 0; x < v_length; x++) {\n      v1[x] = -1;\n      v2[x] = -1;\n    }\n    v1[v_offset + 1] = 0;\n    v2[v_offset + 1] = 0;\n    int delta = text1_length - text2_length;\n    // If the total number of characters is odd, then the front path will\n    // collide with the reverse path.\n    boolean front = (delta % 2 != 0);\n    // Offsets for start and end of k loop.\n    // Prevents mapping of space beyond the grid.\n    int k1start = 0;\n    int k1end = 0;\n    int k2start = 0;\n    int k2end = 0;\n    for (int d = 0; d < max_d; d++) {\n      // Bail out if deadline is reached.\n      if (System.currentTimeMillis() > deadline) {\n        break;\n      }\n\n      // Walk the front path one step.\n      for (int k1 = -d + k1start; k1 <= d - k1end; k1 += 2) {\n        int k1_offset = v_offset + k1;\n        int x1;\n        if (k1 == -d || (k1 != d && v1[k1_offset - 1] < v1[k1_offset + 1])) {\n          x1 = v1[k1_offset + 1];\n        } else {\n          x1 = v1[k1_offset - 1] + 1;\n        }\n        int y1 = x1 - k1;\n        while (x1 < text1_length && y1 < text2_length\n               && text1.charAt(x1) == text2.charAt(y1)) {\n          x1++;\n          y1++;\n        }\n        v1[k1_offset] = x1;\n        if (x1 > text1_length) {\n          // Ran off the right of the graph.\n          k1end += 2;\n        } else if (y1 > text2_length) {\n          // Ran off the bottom of the graph.\n          k1start += 2;\n        } else if (front) {\n          int k2_offset = v_offset + delta - k1;\n          if (k2_offset >= 0 && k2_offset < v_length && v2[k2_offset] != -1) {\n            // Mirror x2 onto top-left coordinate system.\n            int x2 = text1_length - v2[k2_offset];\n            if (x1 >= x2) {\n              // Overlap detected.\n              return diff_bisectSplit(text1, text2, x1, y1, deadline);\n            }\n          }\n        }\n      }\n\n      // Walk the reverse path one step.\n      for (int k2 = -d + k2start; k2 <= d - k2end; k2 += 2) {\n        int k2_offset = v_offset + k2;\n        int x2;\n        if (k2 == -d || (k2 != d && v2[k2_offset - 1] < v2[k2_offset + 1])) {\n          x2 = v2[k2_offset + 1];\n        } else {\n          x2 = v2[k2_offset - 1] + 1;\n        }\n        int y2 = x2 - k2;\n        while (x2 < text1_length && y2 < text2_length\n               && text1.charAt(text1_length - x2 - 1)\n               == text2.charAt(text2_length - y2 - 1)) {\n          x2++;\n          y2++;\n        }\n        v2[k2_offset] = x2;\n        if (x2 > text1_length) {\n          // Ran off the left of the graph.\n          k2end += 2;\n        } else if (y2 > text2_length) {\n          // Ran off the top of the graph.\n          k2start += 2;\n        } else if (!front) {\n          int k1_offset = v_offset + delta - k2;\n          if (k1_offset >= 0 && k1_offset < v_length && v1[k1_offset] != -1) {\n            int x1 = v1[k1_offset];\n            int y1 = v_offset + x1 - k1_offset;\n            // Mirror x2 onto top-left coordinate system.\n            x2 = text1_length - x2;\n            if (x1 >= x2) {\n              // Overlap detected.\n              return diff_bisectSplit(text1, text2, x1, y1, deadline);\n            }\n          }\n        }\n      }\n    }\n    // Diff took too long and hit the deadline or\n    // number of diffs equals number of characters, no commonality at all.\n    LinkedList<Diff> diffs = new LinkedList<Diff>();\n    diffs.add(new Diff(Operation.DELETE, text1));\n    diffs.add(new Diff(Operation.INSERT, text2));\n    return diffs;\n  }\n\n  /**\n   * Given the location of the 'middle snake', split the diff in two parts\n   * and recurse.\n   * @param text1 Old string to be diffed.\n   * @param text2 New string to be diffed.\n   * @param x Index of split point in text1.\n   * @param y Index of split point in text2.\n   * @param deadline Time at which to bail if not yet complete.\n   * @return LinkedList of Diff objects.\n   */\n  private LinkedList<Diff> diff_bisectSplit(String text1, String text2,\n                                            int x, int y, long deadline) {\n    String text1a = text1.substring(0, x);\n    String text2a = text2.substring(0, y);\n    String text1b = text1.substring(x);\n    String text2b = text2.substring(y);\n\n    // Compute both diffs serially.\n    LinkedList<Diff> diffs = diff_main(text1a, text2a, false, deadline);\n    LinkedList<Diff> diffsb = diff_main(text1b, text2b, false, deadline);\n\n    diffs.addAll(diffsb);\n    return diffs;\n  }\n\n  /**\n   * Split two texts into a list of strings.  Reduce the texts to a string of\n   * hashes where each Unicode character represents one line.\n   * @param text1 First string.\n   * @param text2 Second string.\n   * @return An object containing the encoded text1, the encoded text2 and\n   *     the List of unique strings.  The zeroth element of the List of\n   *     unique strings is intentionally blank.\n   */\n  protected LinesToCharsResult diff_linesToChars(String text1, String text2) {\n    List<String> lineArray = new ArrayList<String>();\n    Map<String, Integer> lineHash = new HashMap<String, Integer>();\n    // e.g. linearray[4] == \"Hello\\n\"\n    // e.g. linehash.get(\"Hello\\n\") == 4\n\n    // \"\\x00\" is a valid character, but various debuggers don't like it.\n    // So we'll insert a junk entry to avoid generating a null character.\n    lineArray.add(\"\");\n\n    // Allocate 2/3rds of the space for text1, the rest for text2.\n    String chars1 = diff_linesToCharsMunge(text1, lineArray, lineHash, 40000);\n    String chars2 = diff_linesToCharsMunge(text2, lineArray, lineHash, 65535);\n    return new LinesToCharsResult(chars1, chars2, lineArray);\n  }\n\n  /**\n   * Split a text into a list of strings.  Reduce the texts to a string of\n   * hashes where each Unicode character represents one line.\n   * @param text String to encode.\n   * @param lineArray List of unique strings.\n   * @param lineHash Map of strings to indices.\n   * @param maxLines Maximum length of lineArray.\n   * @return Encoded string.\n   */\n  private String diff_linesToCharsMunge(String text, List<String> lineArray,\n      Map<String, Integer> lineHash, int maxLines) {\n    int lineStart = 0;\n    int lineEnd = -1;\n    String line;\n    StringBuilder chars = new StringBuilder();\n    // Walk the text, pulling out a substring for each line.\n    // text.split('\\n') would would temporarily double our memory footprint.\n    // Modifying text would create many large strings to garbage collect.\n    while (lineEnd < text.length() - 1) {\n      lineEnd = text.indexOf('\\n', lineStart);\n      if (lineEnd == -1) {\n        lineEnd = text.length() - 1;\n      }\n      line = text.substring(lineStart, lineEnd + 1);\n\n      if (lineHash.containsKey(line)) {\n        chars.append(String.valueOf((char) (int) lineHash.get(line)));\n      } else {\n        if (lineArray.size() == maxLines) {\n          // Bail out at 65535 because\n          // String.valueOf((char) 65536).equals(String.valueOf(((char) 0)))\n          line = text.substring(lineStart);\n          lineEnd = text.length();\n        }\n        lineArray.add(line);\n        lineHash.put(line, lineArray.size() - 1);\n        chars.append(String.valueOf((char) (lineArray.size() - 1)));\n      }\n      lineStart = lineEnd + 1;\n    }\n    return chars.toString();\n  }\n\n  /**\n   * Rehydrate the text in a diff from a string of line hashes to real lines of\n   * text.\n   * @param diffs List of Diff objects.\n   * @param lineArray List of unique strings.\n   */\n  protected void diff_charsToLines(List<Diff> diffs,\n                                  List<String> lineArray) {\n    StringBuilder text;\n    for (Diff diff : diffs) {\n      text = new StringBuilder();\n      for (int j = 0; j < diff.text.length(); j++) {\n        text.append(lineArray.get(diff.text.charAt(j)));\n      }\n      diff.text = text.toString();\n    }\n  }\n\n  /**\n   * Determine the common prefix of two strings\n   * @param text1 First string.\n   * @param text2 Second string.\n   * @return The number of characters common to the start of each string.\n   */\n  public int diff_commonPrefix(String text1, String text2) {\n    // Performance analysis: https://neil.fraser.name/news/2007/10/09/\n    int n = Math.min(text1.length(), text2.length());\n    for (int i = 0; i < n; i++) {\n      if (text1.charAt(i) != text2.charAt(i)) {\n        return i;\n      }\n    }\n    return n;\n  }\n\n  /**\n   * Determine the common suffix of two strings\n   * @param text1 First string.\n   * @param text2 Second string.\n   * @return The number of characters common to the end of each string.\n   */\n  public int diff_commonSuffix(String text1, String text2) {\n    // Performance analysis: https://neil.fraser.name/news/2007/10/09/\n    int text1_length = text1.length();\n    int text2_length = text2.length();\n    int n = Math.min(text1_length, text2_length);\n    for (int i = 1; i <= n; i++) {\n      if (text1.charAt(text1_length - i) != text2.charAt(text2_length - i)) {\n        return i - 1;\n      }\n    }\n    return n;\n  }\n\n  /**\n   * Determine if the suffix of one string is the prefix of another.\n   * @param text1 First string.\n   * @param text2 Second string.\n   * @return The number of characters common to the end of the first\n   *     string and the start of the second string.\n   */\n  protected int diff_commonOverlap(String text1, String text2) {\n    // Cache the text lengths to prevent multiple calls.\n    int text1_length = text1.length();\n    int text2_length = text2.length();\n    // Eliminate the null case.\n    if (text1_length == 0 || text2_length == 0) {\n      return 0;\n    }\n    // Truncate the longer string.\n    if (text1_length > text2_length) {\n      text1 = text1.substring(text1_length - text2_length);\n    } else if (text1_length < text2_length) {\n      text2 = text2.substring(0, text1_length);\n    }\n    int text_length = Math.min(text1_length, text2_length);\n    // Quick check for the worst case.\n    if (text1.equals(text2)) {\n      return text_length;\n    }\n\n    // Start by looking for a single character match\n    // and increase length until no match is found.\n    // Performance analysis: https://neil.fraser.name/news/2010/11/04/\n    int best = 0;\n    int length = 1;\n    while (true) {\n      String pattern = text1.substring(text_length - length);\n      int found = text2.indexOf(pattern);\n      if (found == -1) {\n        return best;\n      }\n      length += found;\n      if (found == 0 || text1.substring(text_length - length).equals(\n          text2.substring(0, length))) {\n        best = length;\n        length++;\n      }\n    }\n  }\n\n  /**\n   * Do the two texts share a substring which is at least half the length of\n   * the longer text?\n   * This speedup can produce non-minimal diffs.\n   * @param text1 First string.\n   * @param text2 Second string.\n   * @return Five element String array, containing the prefix of text1, the\n   *     suffix of text1, the prefix of text2, the suffix of text2 and the\n   *     common middle.  Or null if there was no match.\n   */\n  protected String[] diff_halfMatch(String text1, String text2) {\n    if (Diff_Timeout <= 0) {\n      // Don't risk returning a non-optimal diff if we have unlimited time.\n      return null;\n    }\n    String longtext = text1.length() > text2.length() ? text1 : text2;\n    String shorttext = text1.length() > text2.length() ? text2 : text1;\n    if (longtext.length() < 4 || shorttext.length() * 2 < longtext.length()) {\n      return null;  // Pointless.\n    }\n\n    // First check if the second quarter is the seed for a half-match.\n    String[] hm1 = diff_halfMatchI(longtext, shorttext,\n                                   (longtext.length() + 3) / 4);\n    // Check again based on the third quarter.\n    String[] hm2 = diff_halfMatchI(longtext, shorttext,\n                                   (longtext.length() + 1) / 2);\n    String[] hm;\n    if (hm1 == null && hm2 == null) {\n      return null;\n    } else if (hm2 == null) {\n      hm = hm1;\n    } else if (hm1 == null) {\n      hm = hm2;\n    } else {\n      // Both matched.  Select the longest.\n      hm = hm1[4].length() > hm2[4].length() ? hm1 : hm2;\n    }\n\n    // A half-match was found, sort out the return data.\n    if (text1.length() > text2.length()) {\n      return hm;\n      //return new String[]{hm[0], hm[1], hm[2], hm[3], hm[4]};\n    } else {\n      return new String[]{hm[2], hm[3], hm[0], hm[1], hm[4]};\n    }\n  }\n\n  /**\n   * Does a substring of shorttext exist within longtext such that the\n   * substring is at least half the length of longtext?\n   * @param longtext Longer string.\n   * @param shorttext Shorter string.\n   * @param i Start index of quarter length substring within longtext.\n   * @return Five element String array, containing the prefix of longtext, the\n   *     suffix of longtext, the prefix of shorttext, the suffix of shorttext\n   *     and the common middle.  Or null if there was no match.\n   */\n  private String[] diff_halfMatchI(String longtext, String shorttext, int i) {\n    // Start with a 1/4 length substring at position i as a seed.\n    String seed = longtext.substring(i, i + longtext.length() / 4);\n    int j = -1;\n    String best_common = \"\";\n    String best_longtext_a = \"\", best_longtext_b = \"\";\n    String best_shorttext_a = \"\", best_shorttext_b = \"\";\n    while ((j = shorttext.indexOf(seed, j + 1)) != -1) {\n      int prefixLength = diff_commonPrefix(longtext.substring(i),\n                                           shorttext.substring(j));\n      int suffixLength = diff_commonSuffix(longtext.substring(0, i),\n                                           shorttext.substring(0, j));\n      if (best_common.length() < suffixLength + prefixLength) {\n        best_common = shorttext.substring(j - suffixLength, j)\n            + shorttext.substring(j, j + prefixLength);\n        best_longtext_a = longtext.substring(0, i - suffixLength);\n        best_longtext_b = longtext.substring(i + prefixLength);\n        best_shorttext_a = shorttext.substring(0, j - suffixLength);\n        best_shorttext_b = shorttext.substring(j + prefixLength);\n      }\n    }\n    if (best_common.length() * 2 >= longtext.length()) {\n      return new String[]{best_longtext_a, best_longtext_b,\n                          best_shorttext_a, best_shorttext_b, best_common};\n    } else {\n      return null;\n    }\n  }\n\n  /**\n   * Reduce the number of edits by eliminating semantically trivial equalities.\n   * @param diffs LinkedList of Diff objects.\n   */\n  public void diff_cleanupSemantic(LinkedList<Diff> diffs) {\n    if (diffs.isEmpty()) {\n      return;\n    }\n    boolean changes = false;\n    Deque<Diff> equalities = new ArrayDeque<Diff>();  // Double-ended queue of qualities.\n    String lastEquality = null; // Always equal to equalities.peek().text\n    ListIterator<Diff> pointer = diffs.listIterator();\n    // Number of characters that changed prior to the equality.\n    int length_insertions1 = 0;\n    int length_deletions1 = 0;\n    // Number of characters that changed after the equality.\n    int length_insertions2 = 0;\n    int length_deletions2 = 0;\n    Diff thisDiff = pointer.next();\n    while (thisDiff != null) {\n      if (thisDiff.operation == Operation.EQUAL) {\n        // Equality found.\n        equalities.push(thisDiff);\n        length_insertions1 = length_insertions2;\n        length_deletions1 = length_deletions2;\n        length_insertions2 = 0;\n        length_deletions2 = 0;\n        lastEquality = thisDiff.text;\n      } else {\n        // An insertion or deletion.\n        if (thisDiff.operation == Operation.INSERT) {\n          length_insertions2 += thisDiff.text.length();\n        } else {\n          length_deletions2 += thisDiff.text.length();\n        }\n        // Eliminate an equality that is smaller or equal to the edits on both\n        // sides of it.\n        if (lastEquality != null && (lastEquality.length()\n            <= Math.max(length_insertions1, length_deletions1))\n            && (lastEquality.length()\n                <= Math.max(length_insertions2, length_deletions2))) {\n          //System.out.println(\"Splitting: '\" + lastEquality + \"'\");\n          // Walk back to offending equality.\n          while (thisDiff != equalities.peek()) {\n            thisDiff = pointer.previous();\n          }\n          pointer.next();\n\n          // Replace equality with a delete.\n          pointer.set(new Diff(Operation.DELETE, lastEquality));\n          // Insert a corresponding an insert.\n          pointer.add(new Diff(Operation.INSERT, lastEquality));\n\n          equalities.pop();  // Throw away the equality we just deleted.\n          if (!equalities.isEmpty()) {\n            // Throw away the previous equality (it needs to be reevaluated).\n            equalities.pop();\n          }\n          if (equalities.isEmpty()) {\n            // There are no previous equalities, walk back to the start.\n            while (pointer.hasPrevious()) {\n              pointer.previous();\n            }\n          } else {\n            // There is a safe equality we can fall back to.\n            thisDiff = equalities.peek();\n            while (thisDiff != pointer.previous()) {\n              // Intentionally empty loop.\n            }\n          }\n\n          length_insertions1 = 0;  // Reset the counters.\n          length_insertions2 = 0;\n          length_deletions1 = 0;\n          length_deletions2 = 0;\n          lastEquality = null;\n          changes = true;\n        }\n      }\n      thisDiff = pointer.hasNext() ? pointer.next() : null;\n    }\n\n    // Normalize the diff.\n    if (changes) {\n      diff_cleanupMerge(diffs);\n    }\n    diff_cleanupSemanticLossless(diffs);\n\n    // Find any overlaps between deletions and insertions.\n    // e.g: <del>abcxxx</del><ins>xxxdef</ins>\n    //   -> <del>abc</del>xxx<ins>def</ins>\n    // e.g: <del>xxxabc</del><ins>defxxx</ins>\n    //   -> <ins>def</ins>xxx<del>abc</del>\n    // Only extract an overlap if it is as big as the edit ahead or behind it.\n    pointer = diffs.listIterator();\n    Diff prevDiff = null;\n    thisDiff = null;\n    if (pointer.hasNext()) {\n      prevDiff = pointer.next();\n      if (pointer.hasNext()) {\n        thisDiff = pointer.next();\n      }\n    }\n    while (thisDiff != null) {\n      if (prevDiff.operation == Operation.DELETE &&\n          thisDiff.operation == Operation.INSERT) {\n        String deletion = prevDiff.text;\n        String insertion = thisDiff.text;\n        int overlap_length1 = this.diff_commonOverlap(deletion, insertion);\n        int overlap_length2 = this.diff_commonOverlap(insertion, deletion);\n        if (overlap_length1 >= overlap_length2) {\n          if (overlap_length1 >= deletion.length() / 2.0 ||\n              overlap_length1 >= insertion.length() / 2.0) {\n            // Overlap found. Insert an equality and trim the surrounding edits.\n            pointer.previous();\n            pointer.add(new Diff(Operation.EQUAL,\n                                 insertion.substring(0, overlap_length1)));\n            prevDiff.text =\n                deletion.substring(0, deletion.length() - overlap_length1);\n            thisDiff.text = insertion.substring(overlap_length1);\n            // pointer.add inserts the element before the cursor, so there is\n            // no need to step past the new element.\n          }\n        } else {\n          if (overlap_length2 >= deletion.length() / 2.0 ||\n              overlap_length2 >= insertion.length() / 2.0) {\n            // Reverse overlap found.\n            // Insert an equality and swap and trim the surrounding edits.\n            pointer.previous();\n            pointer.add(new Diff(Operation.EQUAL,\n                                 deletion.substring(0, overlap_length2)));\n            prevDiff.operation = Operation.INSERT;\n            prevDiff.text =\n              insertion.substring(0, insertion.length() - overlap_length2);\n            thisDiff.operation = Operation.DELETE;\n            thisDiff.text = deletion.substring(overlap_length2);\n            // pointer.add inserts the element before the cursor, so there is\n            // no need to step past the new element.\n          }\n        }\n        thisDiff = pointer.hasNext() ? pointer.next() : null;\n      }\n      prevDiff = thisDiff;\n      thisDiff = pointer.hasNext() ? pointer.next() : null;\n    }\n  }\n\n  /**\n   * Look for single edits surrounded on both sides by equalities\n   * which can be shifted sideways to align the edit to a word boundary.\n   * e.g: The c<ins>at c</ins>ame. -> The <ins>cat </ins>came.\n   * @param diffs LinkedList of Diff objects.\n   */\n  public void diff_cleanupSemanticLossless(LinkedList<Diff> diffs) {\n    String equality1, edit, equality2;\n    String commonString;\n    int commonOffset;\n    int score, bestScore;\n    String bestEquality1, bestEdit, bestEquality2;\n    // Create a new iterator at the start.\n    ListIterator<Diff> pointer = diffs.listIterator();\n    Diff prevDiff = pointer.hasNext() ? pointer.next() : null;\n    Diff thisDiff = pointer.hasNext() ? pointer.next() : null;\n    Diff nextDiff = pointer.hasNext() ? pointer.next() : null;\n    // Intentionally ignore the first and last element (don't need checking).\n    while (nextDiff != null) {\n      if (prevDiff.operation == Operation.EQUAL &&\n          nextDiff.operation == Operation.EQUAL) {\n        // This is a single edit surrounded by equalities.\n        equality1 = prevDiff.text;\n        edit = thisDiff.text;\n        equality2 = nextDiff.text;\n\n        // First, shift the edit as far left as possible.\n        commonOffset = diff_commonSuffix(equality1, edit);\n        if (commonOffset != 0) {\n          commonString = edit.substring(edit.length() - commonOffset);\n          equality1 = equality1.substring(0, equality1.length() - commonOffset);\n          edit = commonString + edit.substring(0, edit.length() - commonOffset);\n          equality2 = commonString + equality2;\n        }\n\n        // Second, step character by character right, looking for the best fit.\n        bestEquality1 = equality1;\n        bestEdit = edit;\n        bestEquality2 = equality2;\n        bestScore = diff_cleanupSemanticScore(equality1, edit)\n            + diff_cleanupSemanticScore(edit, equality2);\n        while (edit.length() != 0 && equality2.length() != 0\n            && edit.charAt(0) == equality2.charAt(0)) {\n          equality1 += edit.charAt(0);\n          edit = edit.substring(1) + equality2.charAt(0);\n          equality2 = equality2.substring(1);\n          score = diff_cleanupSemanticScore(equality1, edit)\n              + diff_cleanupSemanticScore(edit, equality2);\n          // The >= encourages trailing rather than leading whitespace on edits.\n          if (score >= bestScore) {\n            bestScore = score;\n            bestEquality1 = equality1;\n            bestEdit = edit;\n            bestEquality2 = equality2;\n          }\n        }\n\n        if (!prevDiff.text.equals(bestEquality1)) {\n          // We have an improvement, save it back to the diff.\n          if (bestEquality1.length() != 0) {\n            prevDiff.text = bestEquality1;\n          } else {\n            pointer.previous(); // Walk past nextDiff.\n            pointer.previous(); // Walk past thisDiff.\n            pointer.previous(); // Walk past prevDiff.\n            pointer.remove(); // Delete prevDiff.\n            pointer.next(); // Walk past thisDiff.\n            pointer.next(); // Walk past nextDiff.\n          }\n          thisDiff.text = bestEdit;\n          if (bestEquality2.length() != 0) {\n            nextDiff.text = bestEquality2;\n          } else {\n            pointer.remove(); // Delete nextDiff.\n            nextDiff = thisDiff;\n            thisDiff = prevDiff;\n          }\n        }\n      }\n      prevDiff = thisDiff;\n      thisDiff = nextDiff;\n      nextDiff = pointer.hasNext() ? pointer.next() : null;\n    }\n  }\n\n  /**\n   * Given two strings, compute a score representing whether the internal\n   * boundary falls on logical boundaries.\n   * Scores range from 6 (best) to 0 (worst).\n   * @param one First string.\n   * @param two Second string.\n   * @return The score.\n   */\n  private int diff_cleanupSemanticScore(String one, String two) {\n    if (one.length() == 0 || two.length() == 0) {\n      // Edges are the best.\n      return 6;\n    }\n\n    // Each port of this function behaves slightly differently due to\n    // subtle differences in each language's definition of things like\n    // 'whitespace'.  Since this function's purpose is largely cosmetic,\n    // the choice has been made to use each language's native features\n    // rather than force total conformity.\n    char char1 = one.charAt(one.length() - 1);\n    char char2 = two.charAt(0);\n    boolean nonAlphaNumeric1 = !Character.isLetterOrDigit(char1);\n    boolean nonAlphaNumeric2 = !Character.isLetterOrDigit(char2);\n    boolean whitespace1 = nonAlphaNumeric1 && Character.isWhitespace(char1);\n    boolean whitespace2 = nonAlphaNumeric2 && Character.isWhitespace(char2);\n    boolean lineBreak1 = whitespace1\n        && Character.getType(char1) == Character.CONTROL;\n    boolean lineBreak2 = whitespace2\n        && Character.getType(char2) == Character.CONTROL;\n    boolean blankLine1 = lineBreak1 && BLANKLINEEND.matcher(one).find();\n    boolean blankLine2 = lineBreak2 && BLANKLINESTART.matcher(two).find();\n\n    if (blankLine1 || blankLine2) {\n      // Five points for blank lines.\n      return 5;\n    } else if (lineBreak1 || lineBreak2) {\n      // Four points for line breaks.\n      return 4;\n    } else if (nonAlphaNumeric1 && !whitespace1 && whitespace2) {\n      // Three points for end of sentences.\n      return 3;\n    } else if (whitespace1 || whitespace2) {\n      // Two points for whitespace.\n      return 2;\n    } else if (nonAlphaNumeric1 || nonAlphaNumeric2) {\n      // One point for non-alphanumeric.\n      return 1;\n    }\n    return 0;\n  }\n\n  // Define some regex patterns for matching boundaries.\n  private Pattern BLANKLINEEND\n      = Pattern.compile(\"\\\\n\\\\r?\\\\n\\\\Z\", Pattern.DOTALL);\n  private Pattern BLANKLINESTART\n      = Pattern.compile(\"\\\\A\\\\r?\\\\n\\\\r?\\\\n\", Pattern.DOTALL);\n\n  /**\n   * Reduce the number of edits by eliminating operationally trivial equalities.\n   * @param diffs LinkedList of Diff objects.\n   */\n  public void diff_cleanupEfficiency(LinkedList<Diff> diffs) {\n    if (diffs.isEmpty()) {\n      return;\n    }\n    boolean changes = false;\n    Deque<Diff> equalities = new ArrayDeque<Diff>();  // Double-ended queue of equalities.\n    String lastEquality = null; // Always equal to equalities.peek().text\n    ListIterator<Diff> pointer = diffs.listIterator();\n    // Is there an insertion operation before the last equality.\n    boolean pre_ins = false;\n    // Is there a deletion operation before the last equality.\n    boolean pre_del = false;\n    // Is there an insertion operation after the last equality.\n    boolean post_ins = false;\n    // Is there a deletion operation after the last equality.\n    boolean post_del = false;\n    Diff thisDiff = pointer.next();\n    Diff safeDiff = thisDiff;  // The last Diff that is known to be unsplittable.\n    while (thisDiff != null) {\n      if (thisDiff.operation == Operation.EQUAL) {\n        // Equality found.\n        if (thisDiff.text.length() < Diff_EditCost && (post_ins || post_del)) {\n          // Candidate found.\n          equalities.push(thisDiff);\n          pre_ins = post_ins;\n          pre_del = post_del;\n          lastEquality = thisDiff.text;\n        } else {\n          // Not a candidate, and can never become one.\n          equalities.clear();\n          lastEquality = null;\n          safeDiff = thisDiff;\n        }\n        post_ins = post_del = false;\n      } else {\n        // An insertion or deletion.\n        if (thisDiff.operation == Operation.DELETE) {\n          post_del = true;\n        } else {\n          post_ins = true;\n        }\n        /*\n         * Five types to be split:\n         * <ins>A</ins><del>B</del>XY<ins>C</ins><del>D</del>\n         * <ins>A</ins>X<ins>C</ins><del>D</del>\n         * <ins>A</ins><del>B</del>X<ins>C</ins>\n         * <ins>A</del>X<ins>C</ins><del>D</del>\n         * <ins>A</ins><del>B</del>X<del>C</del>\n         */\n        if (lastEquality != null\n            && ((pre_ins && pre_del && post_ins && post_del)\n                || ((lastEquality.length() < Diff_EditCost / 2)\n                    && ((pre_ins ? 1 : 0) + (pre_del ? 1 : 0)\n                        + (post_ins ? 1 : 0) + (post_del ? 1 : 0)) == 3))) {\n          //System.out.println(\"Splitting: '\" + lastEquality + \"'\");\n          // Walk back to offending equality.\n          while (thisDiff != equalities.peek()) {\n            thisDiff = pointer.previous();\n          }\n          pointer.next();\n\n          // Replace equality with a delete.\n          pointer.set(new Diff(Operation.DELETE, lastEquality));\n          // Insert a corresponding an insert.\n          pointer.add(thisDiff = new Diff(Operation.INSERT, lastEquality));\n\n          equalities.pop();  // Throw away the equality we just deleted.\n          lastEquality = null;\n          if (pre_ins && pre_del) {\n            // No changes made which could affect previous entry, keep going.\n            post_ins = post_del = true;\n            equalities.clear();\n            safeDiff = thisDiff;\n          } else {\n            if (!equalities.isEmpty()) {\n              // Throw away the previous equality (it needs to be reevaluated).\n              equalities.pop();\n            }\n            if (equalities.isEmpty()) {\n              // There are no previous questionable equalities,\n              // walk back to the last known safe diff.\n              thisDiff = safeDiff;\n            } else {\n              // There is an equality we can fall back to.\n              thisDiff = equalities.peek();\n            }\n            while (thisDiff != pointer.previous()) {\n              // Intentionally empty loop.\n            }\n            post_ins = post_del = false;\n          }\n\n          changes = true;\n        }\n      }\n      thisDiff = pointer.hasNext() ? pointer.next() : null;\n    }\n\n    if (changes) {\n      diff_cleanupMerge(diffs);\n    }\n  }\n\n  /**\n   * Reorder and merge like edit sections.  Merge equalities.\n   * Any edit section can move as long as it doesn't cross an equality.\n   * @param diffs LinkedList of Diff objects.\n   */\n  public void diff_cleanupMerge(LinkedList<Diff> diffs) {\n    diffs.add(new Diff(Operation.EQUAL, \"\"));  // Add a dummy entry at the end.\n    ListIterator<Diff> pointer = diffs.listIterator();\n    int count_delete = 0;\n    int count_insert = 0;\n    String text_delete = \"\";\n    String text_insert = \"\";\n    Diff thisDiff = pointer.next();\n    Diff prevEqual = null;\n    int commonlength;\n    while (thisDiff != null) {\n      switch (thisDiff.operation) {\n      case INSERT:\n        count_insert++;\n        text_insert += thisDiff.text;\n        prevEqual = null;\n        break;\n      case DELETE:\n        count_delete++;\n        text_delete += thisDiff.text;\n        prevEqual = null;\n        break;\n      case EQUAL:\n        if (count_delete + count_insert > 1) {\n          boolean both_types = count_delete != 0 && count_insert != 0;\n          // Delete the offending records.\n          pointer.previous();  // Reverse direction.\n          while (count_delete-- > 0) {\n            pointer.previous();\n            pointer.remove();\n          }\n          while (count_insert-- > 0) {\n            pointer.previous();\n            pointer.remove();\n          }\n          if (both_types) {\n            // Factor out any common prefixies.\n            commonlength = diff_commonPrefix(text_insert, text_delete);\n            if (commonlength != 0) {\n              if (pointer.hasPrevious()) {\n                thisDiff = pointer.previous();\n                assert thisDiff.operation == Operation.EQUAL\n                       : \"Previous diff should have been an equality.\";\n                thisDiff.text += text_insert.substring(0, commonlength);\n                pointer.next();\n              } else {\n                pointer.add(new Diff(Operation.EQUAL,\n                    text_insert.substring(0, commonlength)));\n              }\n              text_insert = text_insert.substring(commonlength);\n              text_delete = text_delete.substring(commonlength);\n            }\n            // Factor out any common suffixies.\n            commonlength = diff_commonSuffix(text_insert, text_delete);\n            if (commonlength != 0) {\n              thisDiff = pointer.next();\n              thisDiff.text = text_insert.substring(text_insert.length()\n                  - commonlength) + thisDiff.text;\n              text_insert = text_insert.substring(0, text_insert.length()\n                  - commonlength);\n              text_delete = text_delete.substring(0, text_delete.length()\n                  - commonlength);\n              pointer.previous();\n            }\n          }\n          // Insert the merged records.\n          if (text_delete.length() != 0) {\n            pointer.add(new Diff(Operation.DELETE, text_delete));\n          }\n          if (text_insert.length() != 0) {\n            pointer.add(new Diff(Operation.INSERT, text_insert));\n          }\n          // Step forward to the equality.\n          thisDiff = pointer.hasNext() ? pointer.next() : null;\n        } else if (prevEqual != null) {\n          // Merge this equality with the previous one.\n          prevEqual.text += thisDiff.text;\n          pointer.remove();\n          thisDiff = pointer.previous();\n          pointer.next();  // Forward direction\n        }\n        count_insert = 0;\n        count_delete = 0;\n        text_delete = \"\";\n        text_insert = \"\";\n        prevEqual = thisDiff;\n        break;\n      }\n      thisDiff = pointer.hasNext() ? pointer.next() : null;\n    }\n    if (diffs.getLast().text.length() == 0) {\n      diffs.removeLast();  // Remove the dummy entry at the end.\n    }\n\n    /*\n     * Second pass: look for single edits surrounded on both sides by equalities\n     * which can be shifted sideways to eliminate an equality.\n     * e.g: A<ins>BA</ins>C -> <ins>AB</ins>AC\n     */\n    boolean changes = false;\n    // Create a new iterator at the start.\n    // (As opposed to walking the current one back.)\n    pointer = diffs.listIterator();\n    Diff prevDiff = pointer.hasNext() ? pointer.next() : null;\n    thisDiff = pointer.hasNext() ? pointer.next() : null;\n    Diff nextDiff = pointer.hasNext() ? pointer.next() : null;\n    // Intentionally ignore the first and last element (don't need checking).\n    while (nextDiff != null) {\n      if (prevDiff.operation == Operation.EQUAL &&\n          nextDiff.operation == Operation.EQUAL) {\n        // This is a single edit surrounded by equalities.\n        if (thisDiff.text.endsWith(prevDiff.text)) {\n          // Shift the edit over the previous equality.\n          thisDiff.text = prevDiff.text\n              + thisDiff.text.substring(0, thisDiff.text.length()\n                                           - prevDiff.text.length());\n          nextDiff.text = prevDiff.text + nextDiff.text;\n          pointer.previous(); // Walk past nextDiff.\n          pointer.previous(); // Walk past thisDiff.\n          pointer.previous(); // Walk past prevDiff.\n          pointer.remove(); // Delete prevDiff.\n          pointer.next(); // Walk past thisDiff.\n          thisDiff = pointer.next(); // Walk past nextDiff.\n          nextDiff = pointer.hasNext() ? pointer.next() : null;\n          changes = true;\n        } else if (thisDiff.text.startsWith(nextDiff.text)) {\n          // Shift the edit over the next equality.\n          prevDiff.text += nextDiff.text;\n          thisDiff.text = thisDiff.text.substring(nextDiff.text.length())\n              + nextDiff.text;\n          pointer.remove(); // Delete nextDiff.\n          nextDiff = pointer.hasNext() ? pointer.next() : null;\n          changes = true;\n        }\n      }\n      prevDiff = thisDiff;\n      thisDiff = nextDiff;\n      nextDiff = pointer.hasNext() ? pointer.next() : null;\n    }\n    // If shifts were made, the diff needs reordering and another shift sweep.\n    if (changes) {\n      diff_cleanupMerge(diffs);\n    }\n  }\n\n  /**\n   * loc is a location in text1, compute and return the equivalent location in\n   * text2.\n   * e.g. \"The cat\" vs \"The big cat\", 1->1, 5->8\n   * @param diffs List of Diff objects.\n   * @param loc Location within text1.\n   * @return Location within text2.\n   */\n  public int diff_xIndex(List<Diff> diffs, int loc) {\n    int chars1 = 0;\n    int chars2 = 0;\n    int last_chars1 = 0;\n    int last_chars2 = 0;\n    Diff lastDiff = null;\n    for (Diff aDiff : diffs) {\n      if (aDiff.operation != Operation.INSERT) {\n        // Equality or deletion.\n        chars1 += aDiff.text.length();\n      }\n      if (aDiff.operation != Operation.DELETE) {\n        // Equality or insertion.\n        chars2 += aDiff.text.length();\n      }\n      if (chars1 > loc) {\n        // Overshot the location.\n        lastDiff = aDiff;\n        break;\n      }\n      last_chars1 = chars1;\n      last_chars2 = chars2;\n    }\n    if (lastDiff != null && lastDiff.operation == Operation.DELETE) {\n      // The location was deleted.\n      return last_chars2;\n    }\n    // Add the remaining character length.\n    return last_chars2 + (loc - last_chars1);\n  }\n\n  /**\n   * Convert a Diff list into a pretty HTML report.\n   * @param diffs List of Diff objects.\n   * @return HTML representation.\n   */\n  public String diff_prettyHtml(List<Diff> diffs) {\n    StringBuilder html = new StringBuilder();\n    for (Diff aDiff : diffs) {\n      String text = aDiff.text.replace(\"&\", \"&amp;\").replace(\"<\", \"&lt;\")\n          .replace(\">\", \"&gt;\").replace(\"\\n\", \"&para;<br>\");\n      switch (aDiff.operation) {\n      case INSERT:\n        html.append(\"<ins style=\\\"background:#e6ffe6;\\\">\").append(text)\n            .append(\"</ins>\");\n        break;\n      case DELETE:\n        html.append(\"<del style=\\\"background:#ffe6e6;\\\">\").append(text)\n            .append(\"</del>\");\n        break;\n      case EQUAL:\n        html.append(\"<span>\").append(text).append(\"</span>\");\n        break;\n      }\n    }\n    return html.toString();\n  }\n\n  /**\n   * Compute and return the source text (all equalities and deletions).\n   * @param diffs List of Diff objects.\n   * @return Source text.\n   */\n  public String diff_text1(List<Diff> diffs) {\n    StringBuilder text = new StringBuilder();\n    for (Diff aDiff : diffs) {\n      if (aDiff.operation != Operation.INSERT) {\n        text.append(aDiff.text);\n      }\n    }\n    return text.toString();\n  }\n\n  /**\n   * Compute and return the destination text (all equalities and insertions).\n   * @param diffs List of Diff objects.\n   * @return Destination text.\n   */\n  public String diff_text2(List<Diff> diffs) {\n    StringBuilder text = new StringBuilder();\n    for (Diff aDiff : diffs) {\n      if (aDiff.operation != Operation.DELETE) {\n        text.append(aDiff.text);\n      }\n    }\n    return text.toString();\n  }\n\n  /**\n   * Compute the Levenshtein distance; the number of inserted, deleted or\n   * substituted characters.\n   * @param diffs List of Diff objects.\n   * @return Number of changes.\n   */\n  public int diff_levenshtein(List<Diff> diffs) {\n    int levenshtein = 0;\n    int insertions = 0;\n    int deletions = 0;\n    for (Diff aDiff : diffs) {\n      switch (aDiff.operation) {\n      case INSERT:\n        insertions += aDiff.text.length();\n        break;\n      case DELETE:\n        deletions += aDiff.text.length();\n        break;\n      case EQUAL:\n        // A deletion and an insertion is one substitution.\n        levenshtein += Math.max(insertions, deletions);\n        insertions = 0;\n        deletions = 0;\n        break;\n      }\n    }\n    levenshtein += Math.max(insertions, deletions);\n    return levenshtein;\n  }\n\n  /**\n   * Crush the diff into an encoded string which describes the operations\n   * required to transform text1 into text2.\n   * E.g. =3\\t-2\\t+ing  -> Keep 3 chars, delete 2 chars, insert 'ing'.\n   * Operations are tab-separated.  Inserted text is escaped using %xx notation.\n   * @param diffs List of Diff objects.\n   * @return Delta text.\n   */\n  public String diff_toDelta(List<Diff> diffs) {\n    StringBuilder text = new StringBuilder();\n    for (Diff aDiff : diffs) {\n      switch (aDiff.operation) {\n      case INSERT:\n        try {\n          text.append(\"+\").append(URLEncoder.encode(aDiff.text, \"UTF-8\")\n                                            .replace('+', ' ')).append(\"\\t\");\n        } catch (UnsupportedEncodingException e) {\n          // Not likely on modern system.\n          throw new Error(\"This system does not support UTF-8.\", e);\n        }\n        break;\n      case DELETE:\n        text.append(\"-\").append(aDiff.text.length()).append(\"\\t\");\n        break;\n      case EQUAL:\n        text.append(\"=\").append(aDiff.text.length()).append(\"\\t\");\n        break;\n      }\n    }\n    String delta = text.toString();\n    if (delta.length() != 0) {\n      // Strip off trailing tab character.\n      delta = delta.substring(0, delta.length() - 1);\n      delta = unescapeForEncodeUriCompatability(delta);\n    }\n    return delta;\n  }\n\n  /**\n   * Given the original text1, and an encoded string which describes the\n   * operations required to transform text1 into text2, compute the full diff.\n   * @param text1 Source string for the diff.\n   * @param delta Delta text.\n   * @return Array of Diff objects or null if invalid.\n   * @throws IllegalArgumentException If invalid input.\n   */\n  public LinkedList<Diff> diff_fromDelta(String text1, String delta)\n      throws IllegalArgumentException {\n    LinkedList<Diff> diffs = new LinkedList<Diff>();\n    int pointer = 0;  // Cursor in text1\n    String[] tokens = delta.split(\"\\t\");\n    for (String token : tokens) {\n      if (token.length() == 0) {\n        // Blank tokens are ok (from a trailing \\t).\n        continue;\n      }\n      // Each token begins with a one character parameter which specifies the\n      // operation of this token (delete, insert, equality).\n      String param = token.substring(1);\n      switch (token.charAt(0)) {\n      case '+':\n        // decode would change all \"+\" to \" \"\n        param = param.replace(\"+\", \"%2B\");\n        try {\n          param = URLDecoder.decode(param, \"UTF-8\");\n        } catch (UnsupportedEncodingException e) {\n          // Not likely on modern system.\n          throw new Error(\"This system does not support UTF-8.\", e);\n        } catch (IllegalArgumentException e) {\n          // Malformed URI sequence.\n          throw new IllegalArgumentException(\n              \"Illegal escape in diff_fromDelta: \" + param, e);\n        }\n        diffs.add(new Diff(Operation.INSERT, param));\n        break;\n      case '-':\n        // Fall through.\n      case '=':\n        int n;\n        try {\n          n = Integer.parseInt(param);\n        } catch (NumberFormatException e) {\n          throw new IllegalArgumentException(\n              \"Invalid number in diff_fromDelta: \" + param, e);\n        }\n        if (n < 0) {\n          throw new IllegalArgumentException(\n              \"Negative number in diff_fromDelta: \" + param);\n        }\n        String text;\n        try {\n          text = text1.substring(pointer, pointer += n);\n        } catch (StringIndexOutOfBoundsException e) {\n          throw new IllegalArgumentException(\"Delta length (\" + pointer\n              + \") larger than source text length (\" + text1.length()\n              + \").\", e);\n        }\n        if (token.charAt(0) == '=') {\n          diffs.add(new Diff(Operation.EQUAL, text));\n        } else {\n          diffs.add(new Diff(Operation.DELETE, text));\n        }\n        break;\n      default:\n        // Anything else is an error.\n        throw new IllegalArgumentException(\n            \"Invalid diff operation in diff_fromDelta: \" + token.charAt(0));\n      }\n    }\n    if (pointer != text1.length()) {\n      throw new IllegalArgumentException(\"Delta length (\" + pointer\n          + \") smaller than source text length (\" + text1.length() + \").\");\n    }\n    return diffs;\n  }\n\n\n  //  MATCH FUNCTIONS\n\n\n  /**\n   * Locate the best instance of 'pattern' in 'text' near 'loc'.\n   * Returns -1 if no match found.\n   * @param text The text to search.\n   * @param pattern The pattern to search for.\n   * @param loc The location to search around.\n   * @return Best match index or -1.\n   */\n  public int match_main(String text, String pattern, int loc) {\n    // Check for null inputs.\n    if (text == null || pattern == null) {\n      throw new IllegalArgumentException(\"Null inputs. (match_main)\");\n    }\n\n    loc = Math.max(0, Math.min(loc, text.length()));\n    if (text.equals(pattern)) {\n      // Shortcut (potentially not guaranteed by the algorithm)\n      return 0;\n    } else if (text.length() == 0) {\n      // Nothing to match.\n      return -1;\n    } else if (loc + pattern.length() <= text.length()\n        && text.substring(loc, loc + pattern.length()).equals(pattern)) {\n      // Perfect match at the perfect spot!  (Includes case of null pattern)\n      return loc;\n    } else {\n      // Do a fuzzy compare.\n      return match_bitap(text, pattern, loc);\n    }\n  }\n\n  /**\n   * Locate the best instance of 'pattern' in 'text' near 'loc' using the\n   * Bitap algorithm.  Returns -1 if no match found.\n   * @param text The text to search.\n   * @param pattern The pattern to search for.\n   * @param loc The location to search around.\n   * @return Best match index or -1.\n   */\n  protected int match_bitap(String text, String pattern, int loc) {\n    assert (Match_MaxBits == 0 || pattern.length() <= Match_MaxBits)\n        : \"Pattern too long for this application.\";\n\n    // Initialise the alphabet.\n    Map<Character, Integer> s = match_alphabet(pattern);\n\n    // Highest score beyond which we give up.\n    double score_threshold = Match_Threshold;\n    // Is there a nearby exact match? (speedup)\n    int best_loc = text.indexOf(pattern, loc);\n    if (best_loc != -1) {\n      score_threshold = Math.min(match_bitapScore(0, best_loc, loc, pattern),\n          score_threshold);\n      // What about in the other direction? (speedup)\n      best_loc = text.lastIndexOf(pattern, loc + pattern.length());\n      if (best_loc != -1) {\n        score_threshold = Math.min(match_bitapScore(0, best_loc, loc, pattern),\n            score_threshold);\n      }\n    }\n\n    // Initialise the bit arrays.\n    int matchmask = 1 << (pattern.length() - 1);\n    best_loc = -1;\n\n    int bin_min, bin_mid;\n    int bin_max = pattern.length() + text.length();\n    // Empty initialization added to appease Java compiler.\n    int[] last_rd = new int[0];\n    for (int d = 0; d < pattern.length(); d++) {\n      // Scan for the best match; each iteration allows for one more error.\n      // Run a binary search to determine how far from 'loc' we can stray at\n      // this error level.\n      bin_min = 0;\n      bin_mid = bin_max;\n      while (bin_min < bin_mid) {\n        if (match_bitapScore(d, loc + bin_mid, loc, pattern)\n            <= score_threshold) {\n          bin_min = bin_mid;\n        } else {\n          bin_max = bin_mid;\n        }\n        bin_mid = (bin_max - bin_min) / 2 + bin_min;\n      }\n      // Use the result from this iteration as the maximum for the next.\n      bin_max = bin_mid;\n      int start = Math.max(1, loc - bin_mid + 1);\n      int finish = Math.min(loc + bin_mid, text.length()) + pattern.length();\n\n      int[] rd = new int[finish + 2];\n      rd[finish + 1] = (1 << d) - 1;\n      for (int j = finish; j >= start; j--) {\n        int charMatch;\n        if (text.length() <= j - 1 || !s.containsKey(text.charAt(j - 1))) {\n          // Out of range.\n          charMatch = 0;\n        } else {\n          charMatch = s.get(text.charAt(j - 1));\n        }\n        if (d == 0) {\n          // First pass: exact match.\n          rd[j] = ((rd[j + 1] << 1) | 1) & charMatch;\n        } else {\n          // Subsequent passes: fuzzy match.\n          rd[j] = (((rd[j + 1] << 1) | 1) & charMatch)\n              | (((last_rd[j + 1] | last_rd[j]) << 1) | 1) | last_rd[j + 1];\n        }\n        if ((rd[j] & matchmask) != 0) {\n          double score = match_bitapScore(d, j - 1, loc, pattern);\n          // This match will almost certainly be better than any existing\n          // match.  But check anyway.\n          if (score <= score_threshold) {\n            // Told you so.\n            score_threshold = score;\n            best_loc = j - 1;\n            if (best_loc > loc) {\n              // When passing loc, don't exceed our current distance from loc.\n              start = Math.max(1, 2 * loc - best_loc);\n            } else {\n              // Already passed loc, downhill from here on in.\n              break;\n            }\n          }\n        }\n      }\n      if (match_bitapScore(d + 1, loc, loc, pattern) > score_threshold) {\n        // No hope for a (better) match at greater error levels.\n        break;\n      }\n      last_rd = rd;\n    }\n    return best_loc;\n  }\n\n  /**\n   * Compute and return the score for a match with e errors and x location.\n   * @param e Number of errors in match.\n   * @param x Location of match.\n   * @param loc Expected location of match.\n   * @param pattern Pattern being sought.\n   * @return Overall score for match (0.0 = good, 1.0 = bad).\n   */\n  private double match_bitapScore(int e, int x, int loc, String pattern) {\n    float accuracy = (float) e / pattern.length();\n    int proximity = Math.abs(loc - x);\n    if (Match_Distance == 0) {\n      // Dodge divide by zero error.\n      return proximity == 0 ? accuracy : 1.0;\n    }\n    return accuracy + (proximity / (float) Match_Distance);\n  }\n\n  /**\n   * Initialise the alphabet for the Bitap algorithm.\n   * @param pattern The text to encode.\n   * @return Hash of character locations.\n   */\n  protected Map<Character, Integer> match_alphabet(String pattern) {\n    Map<Character, Integer> s = new HashMap<Character, Integer>();\n    char[] char_pattern = pattern.toCharArray();\n    for (char c : char_pattern) {\n      s.put(c, 0);\n    }\n    int i = 0;\n    for (char c : char_pattern) {\n      s.put(c, s.get(c) | (1 << (pattern.length() - i - 1)));\n      i++;\n    }\n    return s;\n  }\n\n\n  //  PATCH FUNCTIONS\n\n\n  /**\n   * Increase the context until it is unique,\n   * but don't let the pattern expand beyond Match_MaxBits.\n   * @param patch The patch to grow.\n   * @param text Source text.\n   */\n  protected void patch_addContext(Patch patch, String text) {\n    if (text.length() == 0) {\n      return;\n    }\n    String pattern = text.substring(patch.start2, patch.start2 + patch.length1);\n    int padding = 0;\n\n    // Look for the first and last matches of pattern in text.  If two different\n    // matches are found, increase the pattern length.\n    while (text.indexOf(pattern) != text.lastIndexOf(pattern)\n        && pattern.length() < Match_MaxBits - Patch_Margin - Patch_Margin) {\n      padding += Patch_Margin;\n      pattern = text.substring(Math.max(0, patch.start2 - padding),\n          Math.min(text.length(), patch.start2 + patch.length1 + padding));\n    }\n    // Add one chunk for good luck.\n    padding += Patch_Margin;\n\n    // Add the prefix.\n    String prefix = text.substring(Math.max(0, patch.start2 - padding),\n        patch.start2);\n    if (prefix.length() != 0) {\n      patch.diffs.addFirst(new Diff(Operation.EQUAL, prefix));\n    }\n    // Add the suffix.\n    String suffix = text.substring(patch.start2 + patch.length1,\n        Math.min(text.length(), patch.start2 + patch.length1 + padding));\n    if (suffix.length() != 0) {\n      patch.diffs.addLast(new Diff(Operation.EQUAL, suffix));\n    }\n\n    // Roll back the start points.\n    patch.start1 -= prefix.length();\n    patch.start2 -= prefix.length();\n    // Extend the lengths.\n    patch.length1 += prefix.length() + suffix.length();\n    patch.length2 += prefix.length() + suffix.length();\n  }\n\n  /**\n   * Compute a list of patches to turn text1 into text2.\n   * A set of diffs will be computed.\n   * @param text1 Old text.\n   * @param text2 New text.\n   * @return LinkedList of Patch objects.\n   */\n  public LinkedList<Patch> patch_make(String text1, String text2) {\n    if (text1 == null || text2 == null) {\n      throw new IllegalArgumentException(\"Null inputs. (patch_make)\");\n    }\n    // No diffs provided, compute our own.\n    LinkedList<Diff> diffs = diff_main(text1, text2, true);\n    if (diffs.size() > 2) {\n      diff_cleanupSemantic(diffs);\n      diff_cleanupEfficiency(diffs);\n    }\n    return patch_make(text1, diffs);\n  }\n\n  /**\n   * Compute a list of patches to turn text1 into text2.\n   * text1 will be derived from the provided diffs.\n   * @param diffs Array of Diff objects for text1 to text2.\n   * @return LinkedList of Patch objects.\n   */\n  public LinkedList<Patch> patch_make(LinkedList<Diff> diffs) {\n    if (diffs == null) {\n      throw new IllegalArgumentException(\"Null inputs. (patch_make)\");\n    }\n    // No origin string provided, compute our own.\n    String text1 = diff_text1(diffs);\n    return patch_make(text1, diffs);\n  }\n\n  /**\n   * Compute a list of patches to turn text1 into text2.\n   * text2 is ignored, diffs are the delta between text1 and text2.\n   * @param text1 Old text\n   * @param text2 Ignored.\n   * @param diffs Array of Diff objects for text1 to text2.\n   * @return LinkedList of Patch objects.\n   * @deprecated Prefer patch_make(String text1, LinkedList<Diff> diffs).\n   */\n  @Deprecated public LinkedList<Patch> patch_make(String text1, String text2,\n      LinkedList<Diff> diffs) {\n    return patch_make(text1, diffs);\n  }\n\n  /**\n   * Compute a list of patches to turn text1 into text2.\n   * text2 is not provided, diffs are the delta between text1 and text2.\n   * @param text1 Old text.\n   * @param diffs Array of Diff objects for text1 to text2.\n   * @return LinkedList of Patch objects.\n   */\n  public LinkedList<Patch> patch_make(String text1, LinkedList<Diff> diffs) {\n    if (text1 == null || diffs == null) {\n      throw new IllegalArgumentException(\"Null inputs. (patch_make)\");\n    }\n\n    LinkedList<Patch> patches = new LinkedList<Patch>();\n    if (diffs.isEmpty()) {\n      return patches;  // Get rid of the null case.\n    }\n    Patch patch = new Patch();\n    int char_count1 = 0;  // Number of characters into the text1 string.\n    int char_count2 = 0;  // Number of characters into the text2 string.\n    // Start with text1 (prepatch_text) and apply the diffs until we arrive at\n    // text2 (postpatch_text). We recreate the patches one by one to determine\n    // context info.\n    String prepatch_text = text1;\n    String postpatch_text = text1;\n    for (Diff aDiff : diffs) {\n      if (patch.diffs.isEmpty() && aDiff.operation != Operation.EQUAL) {\n        // A new patch starts here.\n        patch.start1 = char_count1;\n        patch.start2 = char_count2;\n      }\n\n      switch (aDiff.operation) {\n      case INSERT:\n        patch.diffs.add(aDiff);\n        patch.length2 += aDiff.text.length();\n        postpatch_text = postpatch_text.substring(0, char_count2)\n            + aDiff.text + postpatch_text.substring(char_count2);\n        break;\n      case DELETE:\n        patch.length1 += aDiff.text.length();\n        patch.diffs.add(aDiff);\n        postpatch_text = postpatch_text.substring(0, char_count2)\n            + postpatch_text.substring(char_count2 + aDiff.text.length());\n        break;\n      case EQUAL:\n        if (aDiff.text.length() <= 2 * Patch_Margin\n            && !patch.diffs.isEmpty() && aDiff != diffs.getLast()) {\n          // Small equality inside a patch.\n          patch.diffs.add(aDiff);\n          patch.length1 += aDiff.text.length();\n          patch.length2 += aDiff.text.length();\n        }\n\n        if (aDiff.text.length() >= 2 * Patch_Margin && !patch.diffs.isEmpty()) {\n          // Time for a new patch.\n          if (!patch.diffs.isEmpty()) {\n            patch_addContext(patch, prepatch_text);\n            patches.add(patch);\n            patch = new Patch();\n            // Unlike Unidiff, our patch lists have a rolling context.\n            // https://github.com/google/diff-match-patch/wiki/Unidiff\n            // Update prepatch text & pos to reflect the application of the\n            // just completed patch.\n            prepatch_text = postpatch_text;\n            char_count1 = char_count2;\n          }\n        }\n        break;\n      }\n\n      // Update the current character count.\n      if (aDiff.operation != Operation.INSERT) {\n        char_count1 += aDiff.text.length();\n      }\n      if (aDiff.operation != Operation.DELETE) {\n        char_count2 += aDiff.text.length();\n      }\n    }\n    // Pick up the leftover patch if not empty.\n    if (!patch.diffs.isEmpty()) {\n      patch_addContext(patch, prepatch_text);\n      patches.add(patch);\n    }\n\n    return patches;\n  }\n\n  /**\n   * Given an array of patches, return another array that is identical.\n   * @param patches Array of Patch objects.\n   * @return Array of Patch objects.\n   */\n  public LinkedList<Patch> patch_deepCopy(LinkedList<Patch> patches) {\n    LinkedList<Patch> patchesCopy = new LinkedList<Patch>();\n    for (Patch aPatch : patches) {\n      Patch patchCopy = new Patch();\n      for (Diff aDiff : aPatch.diffs) {\n        Diff diffCopy = new Diff(aDiff.operation, aDiff.text);\n        patchCopy.diffs.add(diffCopy);\n      }\n      patchCopy.start1 = aPatch.start1;\n      patchCopy.start2 = aPatch.start2;\n      patchCopy.length1 = aPatch.length1;\n      patchCopy.length2 = aPatch.length2;\n      patchesCopy.add(patchCopy);\n    }\n    return patchesCopy;\n  }\n\n  /**\n   * Merge a set of patches onto the text.  Return a patched text, as well\n   * as an array of true/false values indicating which patches were applied.\n   * @param patches Array of Patch objects\n   * @param text Old text.\n   * @return Two element Object array, containing the new text and an array of\n   *      boolean values.\n   */\n  public Object[] patch_apply(LinkedList<Patch> patches, String text) {\n    if (patches.isEmpty()) {\n      return new Object[]{text, new boolean[0]};\n    }\n\n    // Deep copy the patches so that no changes are made to originals.\n    patches = patch_deepCopy(patches);\n\n    String nullPadding = patch_addPadding(patches);\n    text = nullPadding + text + nullPadding;\n    patch_splitMax(patches);\n\n    int x = 0;\n    // delta keeps track of the offset between the expected and actual location\n    // of the previous patch.  If there are patches expected at positions 10 and\n    // 20, but the first patch was found at 12, delta is 2 and the second patch\n    // has an effective expected position of 22.\n    int delta = 0;\n    boolean[] results = new boolean[patches.size()];\n    for (Patch aPatch : patches) {\n      int expected_loc = aPatch.start2 + delta;\n      String text1 = diff_text1(aPatch.diffs);\n      int start_loc;\n      int end_loc = -1;\n      if (text1.length() > this.Match_MaxBits) {\n        // patch_splitMax will only provide an oversized pattern in the case of\n        // a monster delete.\n        start_loc = match_main(text,\n            text1.substring(0, this.Match_MaxBits), expected_loc);\n        if (start_loc != -1) {\n          end_loc = match_main(text,\n              text1.substring(text1.length() - this.Match_MaxBits),\n              expected_loc + text1.length() - this.Match_MaxBits);\n          if (end_loc == -1 || start_loc >= end_loc) {\n            // Can't find valid trailing context.  Drop this patch.\n            start_loc = -1;\n          }\n        }\n      } else {\n        start_loc = match_main(text, text1, expected_loc);\n      }\n      if (start_loc == -1) {\n        // No match found.  :(\n        results[x] = false;\n        // Subtract the delta for this failed patch from subsequent patches.\n        delta -= aPatch.length2 - aPatch.length1;\n      } else {\n        // Found a match.  :)\n        results[x] = true;\n        delta = start_loc - expected_loc;\n        String text2;\n        if (end_loc == -1) {\n          text2 = text.substring(start_loc,\n              Math.min(start_loc + text1.length(), text.length()));\n        } else {\n          text2 = text.substring(start_loc,\n              Math.min(end_loc + this.Match_MaxBits, text.length()));\n        }\n        if (text1.equals(text2)) {\n          // Perfect match, just shove the replacement text in.\n          text = text.substring(0, start_loc) + diff_text2(aPatch.diffs)\n              + text.substring(start_loc + text1.length());\n        } else {\n          // Imperfect match.  Run a diff to get a framework of equivalent\n          // indices.\n          LinkedList<Diff> diffs = diff_main(text1, text2, false);\n          if (text1.length() > this.Match_MaxBits\n              && diff_levenshtein(diffs) / (float) text1.length()\n              > this.Patch_DeleteThreshold) {\n            // The end points match, but the content is unacceptably bad.\n            results[x] = false;\n          } else {\n            diff_cleanupSemanticLossless(diffs);\n            int index1 = 0;\n            for (Diff aDiff : aPatch.diffs) {\n              if (aDiff.operation != Operation.EQUAL) {\n                int index2 = diff_xIndex(diffs, index1);\n                if (aDiff.operation == Operation.INSERT) {\n                  // Insertion\n                  text = text.substring(0, start_loc + index2) + aDiff.text\n                      + text.substring(start_loc + index2);\n                } else if (aDiff.operation == Operation.DELETE) {\n                  // Deletion\n                  text = text.substring(0, start_loc + index2)\n                      + text.substring(start_loc + diff_xIndex(diffs,\n                      index1 + aDiff.text.length()));\n                }\n              }\n              if (aDiff.operation != Operation.DELETE) {\n                index1 += aDiff.text.length();\n              }\n            }\n          }\n        }\n      }\n      x++;\n    }\n    // Strip the padding off.\n    text = text.substring(nullPadding.length(), text.length()\n        - nullPadding.length());\n    return new Object[]{text, results};\n  }\n\n  /**\n   * Add some padding on text start and end so that edges can match something.\n   * Intended to be called only from within patch_apply.\n   * @param patches Array of Patch objects.\n   * @return The padding string added to each side.\n   */\n  public String patch_addPadding(LinkedList<Patch> patches) {\n    short paddingLength = this.Patch_Margin;\n    String nullPadding = \"\";\n    for (short x = 1; x <= paddingLength; x++) {\n      nullPadding += String.valueOf((char) x);\n    }\n\n    // Bump all the patches forward.\n    for (Patch aPatch : patches) {\n      aPatch.start1 += paddingLength;\n      aPatch.start2 += paddingLength;\n    }\n\n    // Add some padding on start of first diff.\n    Patch patch = patches.getFirst();\n    LinkedList<Diff> diffs = patch.diffs;\n    if (diffs.isEmpty() || diffs.getFirst().operation != Operation.EQUAL) {\n      // Add nullPadding equality.\n      diffs.addFirst(new Diff(Operation.EQUAL, nullPadding));\n      patch.start1 -= paddingLength;  // Should be 0.\n      patch.start2 -= paddingLength;  // Should be 0.\n      patch.length1 += paddingLength;\n      patch.length2 += paddingLength;\n    } else if (paddingLength > diffs.getFirst().text.length()) {\n      // Grow first equality.\n      Diff firstDiff = diffs.getFirst();\n      int extraLength = paddingLength - firstDiff.text.length();\n      firstDiff.text = nullPadding.substring(firstDiff.text.length())\n          + firstDiff.text;\n      patch.start1 -= extraLength;\n      patch.start2 -= extraLength;\n      patch.length1 += extraLength;\n      patch.length2 += extraLength;\n    }\n\n    // Add some padding on end of last diff.\n    patch = patches.getLast();\n    diffs = patch.diffs;\n    if (diffs.isEmpty() || diffs.getLast().operation != Operation.EQUAL) {\n      // Add nullPadding equality.\n      diffs.addLast(new Diff(Operation.EQUAL, nullPadding));\n      patch.length1 += paddingLength;\n      patch.length2 += paddingLength;\n    } else if (paddingLength > diffs.getLast().text.length()) {\n      // Grow last equality.\n      Diff lastDiff = diffs.getLast();\n      int extraLength = paddingLength - lastDiff.text.length();\n      lastDiff.text += nullPadding.substring(0, extraLength);\n      patch.length1 += extraLength;\n      patch.length2 += extraLength;\n    }\n\n    return nullPadding;\n  }\n\n  /**\n   * Look through the patches and break up any which are longer than the\n   * maximum limit of the match algorithm.\n   * Intended to be called only from within patch_apply.\n   * @param patches LinkedList of Patch objects.\n   */\n  public void patch_splitMax(LinkedList<Patch> patches) {\n    short patch_size = Match_MaxBits;\n    String precontext, postcontext;\n    Patch patch;\n    int start1, start2;\n    boolean empty;\n    Operation diff_type;\n    String diff_text;\n    ListIterator<Patch> pointer = patches.listIterator();\n    Patch bigpatch = pointer.hasNext() ? pointer.next() : null;\n    while (bigpatch != null) {\n      if (bigpatch.length1 <= Match_MaxBits) {\n        bigpatch = pointer.hasNext() ? pointer.next() : null;\n        continue;\n      }\n      // Remove the big old patch.\n      pointer.remove();\n      start1 = bigpatch.start1;\n      start2 = bigpatch.start2;\n      precontext = \"\";\n      while (!bigpatch.diffs.isEmpty()) {\n        // Create one of several smaller patches.\n        patch = new Patch();\n        empty = true;\n        patch.start1 = start1 - precontext.length();\n        patch.start2 = start2 - precontext.length();\n        if (precontext.length() != 0) {\n          patch.length1 = patch.length2 = precontext.length();\n          patch.diffs.add(new Diff(Operation.EQUAL, precontext));\n        }\n        while (!bigpatch.diffs.isEmpty()\n            && patch.length1 < patch_size - Patch_Margin) {\n          diff_type = bigpatch.diffs.getFirst().operation;\n          diff_text = bigpatch.diffs.getFirst().text;\n          if (diff_type == Operation.INSERT) {\n            // Insertions are harmless.\n            patch.length2 += diff_text.length();\n            start2 += diff_text.length();\n            patch.diffs.addLast(bigpatch.diffs.removeFirst());\n            empty = false;\n          } else if (diff_type == Operation.DELETE && patch.diffs.size() == 1\n              && patch.diffs.getFirst().operation == Operation.EQUAL\n              && diff_text.length() > 2 * patch_size) {\n            // This is a large deletion.  Let it pass in one chunk.\n            patch.length1 += diff_text.length();\n            start1 += diff_text.length();\n            empty = false;\n            patch.diffs.add(new Diff(diff_type, diff_text));\n            bigpatch.diffs.removeFirst();\n          } else {\n            // Deletion or equality.  Only take as much as we can stomach.\n            diff_text = diff_text.substring(0, Math.min(diff_text.length(),\n                patch_size - patch.length1 - Patch_Margin));\n            patch.length1 += diff_text.length();\n            start1 += diff_text.length();\n            if (diff_type == Operation.EQUAL) {\n              patch.length2 += diff_text.length();\n              start2 += diff_text.length();\n            } else {\n              empty = false;\n            }\n            patch.diffs.add(new Diff(diff_type, diff_text));\n            if (diff_text.equals(bigpatch.diffs.getFirst().text)) {\n              bigpatch.diffs.removeFirst();\n            } else {\n              bigpatch.diffs.getFirst().text = bigpatch.diffs.getFirst().text\n                  .substring(diff_text.length());\n            }\n          }\n        }\n        // Compute the head context for the next patch.\n        precontext = diff_text2(patch.diffs);\n        precontext = precontext.substring(Math.max(0, precontext.length()\n            - Patch_Margin));\n        // Append the end context for this patch.\n        if (diff_text1(bigpatch.diffs).length() > Patch_Margin) {\n          postcontext = diff_text1(bigpatch.diffs).substring(0, Patch_Margin);\n        } else {\n          postcontext = diff_text1(bigpatch.diffs);\n        }\n        if (postcontext.length() != 0) {\n          patch.length1 += postcontext.length();\n          patch.length2 += postcontext.length();\n          if (!patch.diffs.isEmpty()\n              && patch.diffs.getLast().operation == Operation.EQUAL) {\n            patch.diffs.getLast().text += postcontext;\n          } else {\n            patch.diffs.add(new Diff(Operation.EQUAL, postcontext));\n          }\n        }\n        if (!empty) {\n          pointer.add(patch);\n        }\n      }\n      bigpatch = pointer.hasNext() ? pointer.next() : null;\n    }\n  }\n\n  /**\n   * Take a list of patches and return a textual representation.\n   * @param patches List of Patch objects.\n   * @return Text representation of patches.\n   */\n  public String patch_toText(List<Patch> patches) {\n    StringBuilder text = new StringBuilder();\n    for (Patch aPatch : patches) {\n      text.append(aPatch);\n    }\n    return text.toString();\n  }\n\n  /**\n   * Parse a textual representation of patches and return a List of Patch\n   * objects.\n   * @param textline Text representation of patches.\n   * @return List of Patch objects.\n   * @throws IllegalArgumentException If invalid input.\n   */\n  public List<Patch> patch_fromText(String textline)\n      throws IllegalArgumentException {\n    List<Patch> patches = new LinkedList<Patch>();\n    if (textline.length() == 0) {\n      return patches;\n    }\n    List<String> textList = Arrays.asList(textline.split(\"\\n\"));\n    LinkedList<String> text = new LinkedList<String>(textList);\n    Patch patch;\n    Pattern patchHeader\n        = Pattern.compile(\"^@@ -(\\\\d+),?(\\\\d*) \\\\+(\\\\d+),?(\\\\d*) @@$\");\n    Matcher m;\n    char sign;\n    String line;\n    while (!text.isEmpty()) {\n      m = patchHeader.matcher(text.getFirst());\n      if (!m.matches()) {\n        throw new IllegalArgumentException(\n            \"Invalid patch string: \" + text.getFirst());\n      }\n      patch = new Patch();\n      patches.add(patch);\n      patch.start1 = Integer.parseInt(m.group(1));\n      if (m.group(2).length() == 0) {\n        patch.start1--;\n        patch.length1 = 1;\n      } else if (m.group(2).equals(\"0\")) {\n        patch.length1 = 0;\n      } else {\n        patch.start1--;\n        patch.length1 = Integer.parseInt(m.group(2));\n      }\n\n      patch.start2 = Integer.parseInt(m.group(3));\n      if (m.group(4).length() == 0) {\n        patch.start2--;\n        patch.length2 = 1;\n      } else if (m.group(4).equals(\"0\")) {\n        patch.length2 = 0;\n      } else {\n        patch.start2--;\n        patch.length2 = Integer.parseInt(m.group(4));\n      }\n      text.removeFirst();\n\n      while (!text.isEmpty()) {\n        try {\n          sign = text.getFirst().charAt(0);\n        } catch (IndexOutOfBoundsException e) {\n          // Blank line?  Whatever.\n          text.removeFirst();\n          continue;\n        }\n        line = text.getFirst().substring(1);\n        line = line.replace(\"+\", \"%2B\");  // decode would change all \"+\" to \" \"\n        try {\n          line = URLDecoder.decode(line, \"UTF-8\");\n        } catch (UnsupportedEncodingException e) {\n          // Not likely on modern system.\n          throw new Error(\"This system does not support UTF-8.\", e);\n        } catch (IllegalArgumentException e) {\n          // Malformed URI sequence.\n          throw new IllegalArgumentException(\n              \"Illegal escape in patch_fromText: \" + line, e);\n        }\n        if (sign == '-') {\n          // Deletion.\n          patch.diffs.add(new Diff(Operation.DELETE, line));\n        } else if (sign == '+') {\n          // Insertion.\n          patch.diffs.add(new Diff(Operation.INSERT, line));\n        } else if (sign == ' ') {\n          // Minor equality.\n          patch.diffs.add(new Diff(Operation.EQUAL, line));\n        } else if (sign == '@') {\n          // Start of next patch.\n          break;\n        } else {\n          // WTF?\n          throw new IllegalArgumentException(\n              \"Invalid patch mode '\" + sign + \"' in: \" + line);\n        }\n        text.removeFirst();\n      }\n    }\n    return patches;\n  }\n\n\n  /**\n   * Class representing one diff operation.\n   */\n  public static class Diff {\n    /**\n     * One of: INSERT, DELETE or EQUAL.\n     */\n    public Operation operation;\n    /**\n     * The text associated with this diff operation.\n     */\n    public String text;\n\n    /**\n     * Constructor.  Initializes the diff with the provided values.\n     * @param operation One of INSERT, DELETE or EQUAL.\n     * @param text The text being applied.\n     */\n    public Diff(Operation operation, String text) {\n      // Construct a diff with the specified operation and text.\n      this.operation = operation;\n      this.text = text;\n    }\n\n    /**\n     * Display a human-readable version of this Diff.\n     * @return text version.\n     */\n    public String toString() {\n      String prettyText = this.text.replace('\\n', '\\u00b6');\n      return \"Diff(\" + this.operation + \",\\\"\" + prettyText + \"\\\")\";\n    }\n\n    /**\n     * Create a numeric hash value for a Diff.\n     * This function is not used by DMP.\n     * @return Hash value.\n     */\n    @Override\n    public int hashCode() {\n      final int prime = 31;\n      int result = (operation == null) ? 0 : operation.hashCode();\n      result += prime * ((text == null) ? 0 : text.hashCode());\n      return result;\n    }\n\n    /**\n     * Is this Diff equivalent to another Diff?\n     * @param obj Another Diff to compare against.\n     * @return true or false.\n     */\n    @Override\n    public boolean equals(Object obj) {\n      if (this == obj) {\n        return true;\n      }\n      if (obj == null) {\n        return false;\n      }\n      if (getClass() != obj.getClass()) {\n        return false;\n      }\n      Diff other = (Diff) obj;\n      if (operation != other.operation) {\n        return false;\n      }\n      if (text == null) {\n        if (other.text != null) {\n          return false;\n        }\n      } else if (!text.equals(other.text)) {\n        return false;\n      }\n      return true;\n    }\n  }\n\n\n  /**\n   * Class representing one patch operation.\n   */\n  public static class Patch {\n    public LinkedList<Diff> diffs;\n    public int start1;\n    public int start2;\n    public int length1;\n    public int length2;\n\n    /**\n     * Constructor.  Initializes with an empty list of diffs.\n     */\n    public Patch() {\n      this.diffs = new LinkedList<Diff>();\n    }\n\n    /**\n     * Emulate GNU diff's format.\n     * Header: @@ -382,8 +481,9 @@\n     * Indices are printed as 1-based, not 0-based.\n     * @return The GNU diff string.\n     */\n    public String toString() {\n      String coords1, coords2;\n      if (this.length1 == 0) {\n        coords1 = this.start1 + \",0\";\n      } else if (this.length1 == 1) {\n        coords1 = Integer.toString(this.start1 + 1);\n      } else {\n        coords1 = (this.start1 + 1) + \",\" + this.length1;\n      }\n      if (this.length2 == 0) {\n        coords2 = this.start2 + \",0\";\n      } else if (this.length2 == 1) {\n        coords2 = Integer.toString(this.start2 + 1);\n      } else {\n        coords2 = (this.start2 + 1) + \",\" + this.length2;\n      }\n      StringBuilder text = new StringBuilder();\n      text.append(\"@@ -\").append(coords1).append(\" +\").append(coords2)\n          .append(\" @@\\n\");\n      // Escape the body of the patch with %xx notation.\n      for (Diff aDiff : this.diffs) {\n        switch (aDiff.operation) {\n        case INSERT:\n          text.append('+');\n          break;\n        case DELETE:\n          text.append('-');\n          break;\n        case EQUAL:\n          text.append(' ');\n          break;\n        }\n        try {\n          text.append(URLEncoder.encode(aDiff.text, \"UTF-8\").replace('+', ' '))\n              .append(\"\\n\");\n        } catch (UnsupportedEncodingException e) {\n          // Not likely on modern system.\n          throw new Error(\"This system does not support UTF-8.\", e);\n        }\n      }\n      return unescapeForEncodeUriCompatability(text.toString());\n    }\n  }\n\n  /**\n   * Unescape selected chars for compatability with JavaScript's encodeURI.\n   * In speed critical applications this could be dropped since the\n   * receiving application will certainly decode these fine.\n   * Note that this function is case-sensitive.  Thus \"%3f\" would not be\n   * unescaped.  But this is ok because it is only called with the output of\n   * URLEncoder.encode which returns uppercase hex.\n   *\n   * Example: \"%3F\" -> \"?\", \"%24\" -> \"$\", etc.\n   *\n   * @param str The string to escape.\n   * @return The escaped string.\n   */\n  private static String unescapeForEncodeUriCompatability(String str) {\n    return str.replace(\"%21\", \"!\").replace(\"%7E\", \"~\")\n        .replace(\"%27\", \"'\").replace(\"%28\", \"(\").replace(\"%29\", \")\")\n        .replace(\"%3B\", \";\").replace(\"%2F\", \"/\").replace(\"%3F\", \"?\")\n        .replace(\"%3A\", \":\").replace(\"%40\", \"@\").replace(\"%26\", \"&\")\n        .replace(\"%3D\", \"=\").replace(\"%2B\", \"+\").replace(\"%24\", \"$\")\n        .replace(\"%2C\", \",\").replace(\"%23\", \"#\");\n  }\n}\n"
  },
  {
    "path": "java/tests/name/fraser/neil/plaintext/Speedtest.java",
    "content": "// Copyright 2010 Google Inc. All Rights Reserved.\n\n/**\n * Diff Speed Test\n *\n * Compile from diff-match-patch/java with:\n * javac -d classes src/name/fraser/neil/plaintext/diff_match_patch.java tests/name/fraser/neil/plaintext/Speedtest.java\n * Execute with:\n * java -classpath classes name/fraser/neil/plaintext/Speedtest\n *\n * @author fraser@google.com (Neil Fraser)\n */\n\npackage name.fraser.neil.plaintext;\n\nimport java.io.BufferedReader;\nimport java.io.FileReader;\nimport java.io.IOException;\n\npublic class Speedtest {\n\n  public static void main(String args[]) throws IOException {\n    String text1 = readFile(\"tests/name/fraser/neil/plaintext/Speedtest1.txt\");\n    String text2 = readFile(\"tests/name/fraser/neil/plaintext/Speedtest2.txt\");\n\n    diff_match_patch dmp = new diff_match_patch();\n    dmp.Diff_Timeout = 0;\n\n    // Execute one reverse diff as a warmup.\n    dmp.diff_main(text2, text1, false);\n\n    long start_time = System.nanoTime();\n    dmp.diff_main(text1, text2, false);\n    long end_time = System.nanoTime();\n    System.out.printf(\"Elapsed time: %f\\n\", ((end_time - start_time) / 1000000000.0));\n  }\n\n  private static String readFile(String filename) throws IOException {\n    // Read a file from disk and return the text contents.\n    StringBuilder sb = new StringBuilder();\n    FileReader input = new FileReader(filename);\n    BufferedReader bufRead = new BufferedReader(input);\n    try {\n      String line = bufRead.readLine();\n      while (line != null) {\n        sb.append(line).append('\\n');\n        line = bufRead.readLine();\n      }\n    } finally {\n      bufRead.close();\n      input.close();\n    }\n    return sb.toString();\n  }\n}\n"
  },
  {
    "path": "java/tests/name/fraser/neil/plaintext/Speedtest1.txt",
    "content": "This is a '''list of newspapers published by [[Journal Register Company]]'''.\n\nThe company owns daily and weekly newspapers, other print media properties and newspaper-affiliated local Websites in the [[U.S.]] states of [[Connecticut]], [[Michigan]], [[New York]], [[Ohio]] and [[Pennsylvania]], organized in six geographic \"clusters\":<ref>[http://www.journalregister.com/newspapers.html Journal Register Company: Our Newspapers], accessed February 10, 2008.</ref>\n\n== Capital-Saratoga ==\nThree dailies, associated weeklies and [[pennysaver]]s in greater [[Albany, New York]]; also [http://www.capitalcentral.com capitalcentral.com] and [http://www.jobsinnewyork.com JobsInNewYork.com].\n\n* ''The Oneida Daily Dispatch'' {{WS|oneidadispatch.com}} of [[Oneida, New York]]\n* ''[[The Record (Troy)|The Record]]'' {{WS|troyrecord.com}} of [[Troy, New York]]\n* ''[[The Saratogian]]'' {{WS|saratogian.com}} of [[Saratoga Springs, New York]]\n* Weeklies:\n** ''Community News'' {{WS|cnweekly.com}} weekly of [[Clifton Park, New York]]\n** ''Rome Observer'' of [[Rome, New York]]\n** ''Life & Times of Utica'' of [[Utica, New York]]\n\n== Connecticut ==\nFive dailies, associated weeklies and [[pennysaver]]s in the state of [[Connecticut]]; also [http://www.ctcentral.com CTcentral.com], [http://www.ctcarsandtrucks.com CTCarsAndTrucks.com] and [http://www.jobsinct.com JobsInCT.com].\n\n* ''The Middletown Press'' {{WS|middletownpress.com}} of [[Middletown, Connecticut|Middletown]]\n* ''[[New Haven Register]]'' {{WS|newhavenregister.com}} of [[New Haven, Connecticut|New Haven]]\n* ''The Register Citizen'' {{WS|registercitizen.com}} of [[Torrington, Connecticut|Torrington]]\n\n* [[New Haven Register#Competitors|Elm City Newspapers]] {{WS|ctcentral.com}}\n** ''The Advertiser'' of [[East Haven, Connecticut|East Haven]]\n** ''Hamden Chronicle'' of [[Hamden, Connecticut|Hamden]]\n** ''Milford Weekly'' of [[Milford, Connecticut|Milford]]\n** ''The Orange Bulletin'' of [[Orange, Connecticut|Orange]]\n** ''The Post'' of [[North Haven, Connecticut|North Haven]]\n** ''Shelton Weekly'' of [[Shelton, Connecticut|Shelton]]\n** ''The Stratford Bard'' of [[Stratford, Connecticut|Stratford]]\n** ''Wallingford Voice'' of [[Wallingford, Connecticut|Wallingford]]\n** ''West Haven News'' of [[West Haven, Connecticut|West Haven]]\n* Housatonic Publications \n** ''The New Milford Times'' {{WS|newmilfordtimes.com}} of [[New Milford, Connecticut|New Milford]]\n** ''The Brookfield Journal'' of [[Brookfield, Connecticut|Brookfield]]\n** ''The Kent Good Times Dispatch'' of [[Kent, Connecticut|Kent]]\n** ''The Bethel Beacon'' of [[Bethel, Connecticut|Bethel]]\n** ''The Litchfield Enquirer'' of [[Litchfield, Connecticut|Litchfield]]\n** ''Litchfield County Times'' of [[Litchfield, Connecticut|Litchfield]]\n* Imprint Newspapers {{WS|imprintnewspapers.com}}\n** ''West Hartford News'' of [[West Hartford, Connecticut|West Hartford]]\n** ''Windsor Journal'' of [[Windsor, Connecticut|Windsor]]\n** ''Windsor Locks Journal'' of [[Windsor Locks, Connecticut|Windsor Locks]]\n** ''Avon Post'' of [[Avon, Connecticut|Avon]]\n** ''Farmington Post'' of [[Farmington, Connecticut|Farmington]]\n** ''Simsbury Post'' of [[Simsbury, Connecticut|Simsbury]]\n** ''Tri-Town Post'' of [[Burlington, Connecticut|Burlington]], [[Canton, Connecticut|Canton]] and [[Harwinton, Connecticut|Harwinton]]\n* Minuteman Publications\n** ''[[Fairfield Minuteman]]'' of [[Fairfield, Connecticut|Fairfield]]\n** ''The Westport Minuteman'' {{WS|westportminuteman.com}} of [[Westport, Connecticut|Westport]]\n* Shoreline Newspapers weeklies:\n** ''Branford Review'' of [[Branford, Connecticut|Branford]]\n** ''Clinton Recorder'' of [[Clinton, Connecticut|Clinton]]\n** ''The Dolphin'' of [[Naval Submarine Base New London]] in [[New London, Connecticut|New London]]\n** ''Main Street News'' {{WS|ctmainstreetnews.com}} of [[Essex, Connecticut|Essex]]\n** ''Pictorial Gazette'' of [[Old Saybrook, Connecticut|Old Saybrook]]\n** ''Regional Express'' of [[Colchester, Connecticut|Colchester]]\n** ''Regional Standard'' of [[Colchester, Connecticut|Colchester]]\n** ''Shoreline Times'' {{WS|shorelinetimes.com}} of [[Guilford, Connecticut|Guilford]]\n** ''Shore View East'' of [[Madison, Connecticut|Madison]]\n** ''Shore View West'' of [[Guilford, Connecticut|Guilford]]\n* Other weeklies:\n** ''Registro'' {{WS|registroct.com}} of [[New Haven, Connecticut|New Haven]]\n** ''Thomaston Express'' {{WS|thomastownexpress.com}} of [[Thomaston, Connecticut|Thomaston]]\n** ''Foothills Traders'' {{WS|foothillstrader.com}} of Torrington, Bristol, Canton\n\n== Michigan ==\nFour dailies, associated weeklies and [[pennysaver]]s in the state of [[Michigan]]; also [http://www.micentralhomes.com MIcentralhomes.com] and [http://www.micentralautos.com MIcentralautos.com]\n* ''[[Oakland Press]]'' {{WS|theoaklandpress.com}} of [[Oakland, Michigan|Oakland]]\n* ''Daily Tribune'' {{WS|dailytribune.com}} of [[Royal Oak, Michigan|Royal Oak]]\n* ''Macomb Daily'' {{WS|macombdaily.com}} of [[Mt. Clemens, Michigan|Mt. Clemens]]\n* ''[[Morning Sun]]'' {{WS|themorningsun.com}} of  [[Mount Pleasant, Michigan|Mount Pleasant]]\n* Heritage Newspapers {{WS|heritage.com}}\n** ''Belleville View''\n** ''Ile Camera''\n** ''Monroe Guardian''\n** ''Ypsilanti Courier''\n** ''News-Herald''\n** ''Press & Guide''\n** ''Chelsea Standard & Dexter Leader''\n** ''Manchester Enterprise''\n** ''Milan News-Leader''\n** ''Saline Reporter''\n* Independent Newspapers {{WS|sourcenewspapers.com}}\n** ''Advisor''\n** ''Source''\n* Morning Star {{WS|morningstarpublishing.com}}\n** ''Alma Reminder''\n** ''Alpena Star''\n** ''Antrim County News''\n** ''Carson City Reminder''\n** ''The Leader & Kalkaskian''\n** ''Ogemaw/Oscoda County Star''\n** ''Petoskey/Charlevoix Star''\n** ''Presque Isle Star''\n** ''Preview Community Weekly''\n** ''Roscommon County Star''\n** ''St. Johns Reminder''\n** ''Straits Area Star''\n** ''The (Edmore) Advertiser'' \n* Voice Newspapers {{WS|voicenews.com}}\n** ''Armada Times''\n** ''Bay Voice''\n** ''Blue Water Voice''\n** ''Downriver Voice''\n** ''Macomb Township Voice''\n** ''North Macomb Voice''\n** ''Weekend Voice''\n** ''Suburban Lifestyles'' {{WS|suburbanlifestyles.com}}\n\n== Mid-Hudson ==\nOne daily, associated magazines in the [[Hudson River Valley]] of [[New York]]; also [http://www.midhudsoncentral.com MidHudsonCentral.com] and [http://www.jobsinnewyork.com JobsInNewYork.com].\n\n* ''[[Daily Freeman]]'' {{WS|dailyfreeman.com}} of [[Kingston, New York]]\n\n== Ohio ==\nTwo dailies, associated magazines and three shared Websites, all in the state of [[Ohio]]: [http://www.allaroundcleveland.com AllAroundCleveland.com], [http://www.allaroundclevelandcars.com AllAroundClevelandCars.com] and [http://www.allaroundclevelandjobs.com AllAroundClevelandJobs.com].\n\n* ''[[The News-Herald (Ohio)|The News-Herald]]'' {{WS|news-herald.com}} of [[Willoughby, Ohio|Willoughby]]\n* ''[[The Morning Journal]]'' {{WS|morningjournal.com}} of [[Lorain, Ohio|Lorain]]\n\n== Philadelphia area ==\nSeven dailies and associated weeklies and magazines in [[Pennsylvania]] and [[New Jersey]], and associated Websites: [http://www.allaroundphilly.com AllAroundPhilly.com], [http://www.jobsinnj.com JobsInNJ.com], [http://www.jobsinpa.com JobsInPA.com], and [http://www.phillycarsearch.com PhillyCarSearch.com].\n\n* ''The Daily Local'' {{WS|dailylocal.com}} of [[West Chester, Pennsylvania|West Chester]]\n* ''[[Delaware County Daily and Sunday Times]] {{WS|delcotimes.com}} of Primos\n* ''[[The Mercury (Pennsylvania)|The Mercury]]'' {{WS|pottstownmercury.com}} of [[Pottstown, Pennsylvania|Pottstown]]\n* ''The Phoenix'' {{WS|phoenixvillenews.com}} of [[Phoenixville, Pennsylvania|Phoenixville]]\n* ''[[The Reporter (Lansdale)|The Reporter]]'' {{WS|thereporteronline.com}} of [[Lansdale, Pennsylvania|Lansdale]]\n* ''The Times Herald'' {{WS|timesherald.com}} of [[Norristown, Pennsylvania|Norristown]]\n* ''[[The Trentonian]]'' {{WS|trentonian.com}} of [[Trenton, New Jersey]]\n\n* Weeklies\n** ''El Latino Expreso'' of [[Trenton, New Jersey]]\n** ''La Voz'' of [[Norristown, Pennsylvania]]\n** ''The Village News'' of [[Downingtown, Pennsylvania]]\n** ''The Times Record'' of [[Kennett Square, Pennsylvania]]\n** ''The Tri-County Record'' {{WS|tricountyrecord.com}} of [[Morgantown, Pennsylvania]]\n** ''News of Delaware County'' {{WS|newsofdelawarecounty.com}}of [[Havertown, Pennsylvania]]\n** ''Main Line Times'' {{WS|mainlinetimes.com}}of [[Ardmore, Pennsylvania]]\n** ''Penny Pincher'' of [[Pottstown, Pennsylvania]]\n** ''Town Talk'' {{WS|towntalknews.com}} of [[Ridley, Pennsylvania]]\n* Chesapeake Publishing {{WS|pa8newsgroup.com}} \n** ''Solanco Sun Ledger'' of [[Quarryville, Pennsylvania]]\n** ''Columbia Ledger'' of [[Columbia, Pennsylvania]]\n** ''Coatesville Ledger'' of [[Downingtown, Pennsylvania]]\n** ''Parkesburg Post Ledger'' of [[Quarryville, Pennsylvania]]\n** ''Downingtown Ledger'' of [[Downingtown, Pennsylvania]]\n** ''The Kennett Paper'' of [[Kennett Square, Pennsylvania]]\n** ''Avon Grove Sun'' of [[West Grove, Pennsylvania]]\n** ''Oxford Tribune'' of [[Oxford, Pennsylvania]]\n** ''Elizabethtown Chronicle'' of [[Elizabethtown, Pennsylvania]]\n** ''Donegal Ledger'' of [[Donegal, Pennsylvania]]\n** ''Chadds Ford Post'' of [[Chadds Ford, Pennsylvania]]\n** ''The Central Record'' of [[Medford, New Jersey]]\n** ''Maple Shade Progress'' of [[Maple Shade, New Jersey]]\n* Intercounty Newspapers {{WS|buckslocalnews.com}} \n** ''The Review'' of Roxborough, Pennsylvania\n** ''The Recorder'' of [[Conshohocken, Pennsylvania]]\n** ''The Leader'' of [[Mount Airy, Pennsylvania|Mount Airy]] and West Oak Lake, Pennsylvania\n** ''The Pennington Post'' of [[Pennington, New Jersey]]\n** ''The Bristol Pilot'' of [[Bristol, Pennsylvania]]\n** ''Yardley News'' of [[Yardley, Pennsylvania]]\n** ''New Hope Gazette'' of [[New Hope, Pennsylvania]]\n** ''Doylestown Patriot'' of [[Doylestown, Pennsylvania]]\n** ''Newtown Advance'' of [[Newtown, Pennsylvania]]\n** ''The Plain Dealer'' of [[Williamstown, New Jersey]]\n** ''News Report'' of [[Sewell, New Jersey]]\n** ''Record Breeze'' of [[Berlin, New Jersey]]\n** ''Newsweekly'' of [[Moorestown, New Jersey]]\n** ''Haddon Herald'' of [[Haddonfield, New Jersey]]\n** ''New Egypt Press'' of [[New Egypt, New Jersey]]\n** ''Community News'' of [[Pemberton, New Jersey]]\n** ''Plymouth Meeting Journal'' of [[Plymouth Meeting, Pennsylvania]]\n** ''Lafayette Hill Journal'' of [[Lafayette Hill, Pennsylvania]]\n* Montgomery Newspapers {{WS|montgomerynews.com}} \n** ''Ambler Gazette'' of [[Ambler, Pennsylvania]]\n** ''Central Bucks Life'' of [[Bucks County, Pennsylvania]]\n** ''The Colonial'' of [[Plymouth Meeting, Pennsylvania]]\n** ''Glenside News'' of [[Glenside, Pennsylvania]]\n** ''The Globe'' of [[Lower Moreland Township, Pennsylvania]]\n** ''Main Line Life'' of [[Ardmore, Pennsylvania]]\n** ''Montgomery Life'' of [[Fort Washington, Pennsylvania]]\n** ''North Penn Life'' of [[Lansdale, Pennsylvania]]\n** ''Perkasie News Herald'' of [[Perkasie, Pennsylvania]]\n** ''Public Spirit'' of [[Hatboro, Pennsylvania]]\n** ''Souderton Independent'' of [[Souderton, Pennsylvania]]\n** ''Springfield Sun'' of [[Springfield, Pennsylvania]]\n** ''Spring-Ford Reporter'' of [[Royersford, Pennsylvania]]\n** ''Times Chronicle'' of [[Jenkintown, Pennsylvania]]\n** ''Valley Item'' of [[Perkiomenville, Pennsylvania]]\n** ''Willow Grove Guide'' of [[Willow Grove, Pennsylvania]]\n* News Gleaner Publications (closed December 2008) {{WS|newsgleaner.com}} \n** ''Life Newspapers'' of [[Philadelphia, Pennsylvania]]\n* Suburban Publications\n** ''The Suburban & Wayne Times'' {{WS|waynesuburban.com}} of [[Wayne, Pennsylvania]]\n** ''The Suburban Advertiser'' of [[Exton, Pennsylvania]]\n** ''The King of Prussia Courier'' of [[King of Prussia, Pennsylvania]]\n* Press Newspapers {{WS|countypressonline.com}} \n** ''County Press'' of [[Newtown Square, Pennsylvania]]\n** ''Garnet Valley Press'' of [[Glen Mills, Pennsylvania]]\n** ''Haverford Press'' of [[Newtown Square, Pennsylvania]] (closed January 2009)\n** ''Hometown Press'' of [[Glen Mills, Pennsylvania]] (closed January 2009)\n** ''Media Press'' of [[Newtown Square, Pennsylvania]] (closed January 2009)\n** ''Springfield Press'' of [[Springfield, Pennsylvania]]\n* Berks-Mont Newspapers {{WS|berksmontnews.com}} \n** ''The Boyertown Area Times'' of [[Boyertown, Pennsylvania]]\n** ''The Kutztown Area Patriot'' of [[Kutztown, Pennsylvania]]\n** ''The Hamburg Area Item'' of [[Hamburg, Pennsylvania]]\n** ''The Southern Berks News'' of [[Exeter Township, Berks County, Pennsylvania]]\n** ''The Free Press'' of [[Quakertown, Pennsylvania]]\n** ''The Saucon News'' of [[Quakertown, Pennsylvania]]\n** ''Westside Weekly'' of [[Reading, Pennsylvania]]\n\n* Magazines\n** ''Bucks Co. Town & Country Living''\n** ''Chester Co. Town & Country Living''\n** ''Montomgery Co. Town & Country Living''\n** ''Garden State Town & Country Living''\n** ''Montgomery Homes''\n** ''Philadelphia Golfer''\n** ''Parents Express''\n** ''Art Matters''\n\n{{JRC}}\n\n==References==\n<references />\n\n[[Category:Journal Register publications|*]]\n"
  },
  {
    "path": "java/tests/name/fraser/neil/plaintext/Speedtest2.txt",
    "content": "This is a '''list of newspapers published by [[Journal Register Company]]'''.\n\nThe company owns daily and weekly newspapers, other print media properties and newspaper-affiliated local Websites in the [[U.S.]] states of [[Connecticut]], [[Michigan]], [[New York]], [[Ohio]], [[Pennsylvania]] and [[New Jersey]], organized in six geographic \"clusters\":<ref>[http://www.journalregister.com/publications.html Journal Register Company: Our Publications], accessed April 21, 2010.</ref>\n\n== Capital-Saratoga ==\nThree dailies, associated weeklies and [[pennysaver]]s in greater [[Albany, New York]]; also [http://www.capitalcentral.com capitalcentral.com] and [http://www.jobsinnewyork.com JobsInNewYork.com].\n\n* ''The Oneida Daily Dispatch'' {{WS|oneidadispatch.com}} of [[Oneida, New York]]\n* ''[[The Record (Troy)|The Record]]'' {{WS|troyrecord.com}} of [[Troy, New York]]\n* ''[[The Saratogian]]'' {{WS|saratogian.com}} of [[Saratoga Springs, New York]]\n* Weeklies:\n** ''Community News'' {{WS|cnweekly.com}} weekly of [[Clifton Park, New York]]\n** ''Rome Observer'' {{WS|romeobserver.com}} of [[Rome, New York]]\n** ''WG Life '' {{WS|saratogian.com/wglife/}} of [[Wilton, New York]]\n** ''Ballston Spa Life '' {{WS|saratogian.com/bspalife}} of [[Ballston Spa, New York]]\n** ''Greenbush Life'' {{WS|troyrecord.com/greenbush}} of [[Troy, New York]]\n** ''Latham Life'' {{WS|troyrecord.com/latham}} of [[Latham, New York]]\n** ''River Life'' {{WS|troyrecord.com/river}} of [[Troy, New York]]\n\n== Connecticut ==\nThree dailies, associated weeklies and [[pennysaver]]s in the state of [[Connecticut]]; also [http://www.ctcentral.com CTcentral.com], [http://www.ctcarsandtrucks.com CTCarsAndTrucks.com] and [http://www.jobsinct.com JobsInCT.com].\n\n* ''The Middletown Press'' {{WS|middletownpress.com}} of [[Middletown, Connecticut|Middletown]]\n* ''[[New Haven Register]]'' {{WS|newhavenregister.com}} of [[New Haven, Connecticut|New Haven]]\n* ''The Register Citizen'' {{WS|registercitizen.com}} of [[Torrington, Connecticut|Torrington]]\n\n* Housatonic Publications \n** ''The Housatonic Times'' {{WS|housatonictimes.com}} of [[New Milford, Connecticut|New Milford]]\n** ''Litchfield County Times'' {{WS|countytimes.com}} of [[Litchfield, Connecticut|Litchfield]]\n\n* Minuteman Publications\n** ''[[Fairfield Minuteman]]'' {{WS|fairfieldminuteman.com}}of [[Fairfield, Connecticut|Fairfield]]\n** ''The Westport Minuteman'' {{WS|westportminuteman.com}} of [[Westport, Connecticut|Westport]]\n\n* Shoreline Newspapers \n** ''The Dolphin'' {{WS|dolphin-news.com}} of [[Naval Submarine Base New London]] in [[New London, Connecticut|New London]]\n** ''Shoreline Times'' {{WS|shorelinetimes.com}} of [[Guilford, Connecticut|Guilford]]\n\n* Foothills Media Group {{WS|foothillsmediagroup.com}}\n** ''Thomaston Express'' {{WS|thomastonexpress.com}} of [[Thomaston, Connecticut|Thomaston]]\n** ''Good News About Torrington'' {{WS|goodnewsabouttorrington.com}} of [[Torrington, Connecticut|Torrington]]\n** ''Granby News'' {{WS|foothillsmediagroup.com/granby}} of [[Granby, Connecticut|Granby]]\n** ''Canton News'' {{WS|foothillsmediagroup.com/canton}} of [[Canton, Connecticut|Canton]]\n** ''Avon News'' {{WS|foothillsmediagroup.com/avon}} of [[Avon, Connecticut|Avon]]\n** ''Simsbury News'' {{WS|foothillsmediagroup.com/simsbury}} of [[Simsbury, Connecticut|Simsbury]]\n** ''Litchfield News'' {{WS|foothillsmediagroup.com/litchfield}} of [[Litchfield, Connecticut|Litchfield]]\n** ''Foothills Trader'' {{WS|foothillstrader.com}} of Torrington, Bristol, Canton\n\n* Other weeklies\n** ''The Milford-Orange Bulletin'' {{WS|ctbulletin.com}} of [[Orange, Connecticut|Orange]]\n** ''The Post-Chronicle'' {{WS|ctpostchronicle.com}} of [[North Haven, Connecticut|North Haven]]\n** ''West Hartford News'' {{WS|westhartfordnews.com}} of [[West Hartford, Connecticut|West Hartford]]\n\n* Magazines\n** ''The Connecticut Bride'' {{WS|connecticutmag.com}}\n** ''Connecticut Magazine'' {{WS|theconnecticutbride.com}}\n** ''Passport Magazine'' {{WS|passport-mag.com}}\n\n== Michigan ==\nFour dailies, associated weeklies and [[pennysaver]]s in the state of [[Michigan]]; also [http://www.micentralhomes.com MIcentralhomes.com] and [http://www.micentralautos.com MIcentralautos.com]\n* ''[[Oakland Press]]'' {{WS|theoaklandpress.com}} of [[Oakland, Michigan|Oakland]]\n* ''Daily Tribune'' {{WS|dailytribune.com}} of [[Royal Oak, Michigan|Royal Oak]]\n* ''Macomb Daily'' {{WS|macombdaily.com}} of [[Mt. Clemens, Michigan|Mt. Clemens]]\n* ''[[Morning Sun]]'' {{WS|themorningsun.com}} of  [[Mount Pleasant, Michigan|Mount Pleasant]]\n\n* Heritage Newspapers {{WS|heritage.com}}\n** ''Belleville View'' {{WS|bellevilleview.com}}\n** ''Ile Camera'' {{WS|thenewsherald.com/ile_camera}}\n** ''Monroe Guardian''  {{WS|monreguardian.com}}\n** ''Ypsilanti Courier'' {{WS|ypsilanticourier.com}}\n** ''News-Herald'' {{WS|thenewsherald.com}}\n** ''Press & Guide'' {{WS|pressandguide.com}}\n** ''Chelsea Standard & Dexter Leader'' {{WS|chelseastandard.com}}\n** ''Manchester Enterprise'' {{WS|manchesterguardian.com}}\n** ''Milan News-Leader'' {{WS|milannews.com}}\n** ''Saline Reporter'' {{WS|salinereporter.com}}\n* Independent Newspapers \n** ''Advisor'' {{WS|sourcenewspapers.com}}\n** ''Source'' {{WS|sourcenewspapers.com}}\n* Morning Star {{WS|morningstarpublishing.com}}\n** ''The Leader & Kalkaskian'' {{WS|leaderandkalkaskian.com}}\n** ''Grand Traverse Insider'' {{WS|grandtraverseinsider.com}}\n** ''Alma Reminder''\n** ''Alpena Star''\n** ''Ogemaw/Oscoda County Star''\n** ''Presque Isle Star''\n** ''St. Johns Reminder''\n\n* Voice Newspapers {{WS|voicenews.com}}\n** ''Armada Times''\n** ''Bay Voice''\n** ''Blue Water Voice''\n** ''Downriver Voice''\n** ''Macomb Township Voice''\n** ''North Macomb Voice''\n** ''Weekend Voice''\n\n== Mid-Hudson ==\nOne daily, associated magazines in the [[Hudson River Valley]] of [[New York]]; also [http://www.midhudsoncentral.com MidHudsonCentral.com] and [http://www.jobsinnewyork.com JobsInNewYork.com].\n\n* ''[[Daily Freeman]]'' {{WS|dailyfreeman.com}} of [[Kingston, New York]]\n* ''Las Noticias'' {{WS|lasnoticiasny.com}} of [[Kingston, New York]]\n\n== Ohio ==\nTwo dailies, associated magazines and three shared Websites, all in the state of [[Ohio]]: [http://www.allaroundcleveland.com AllAroundCleveland.com], [http://www.allaroundclevelandcars.com AllAroundClevelandCars.com] and [http://www.allaroundclevelandjobs.com AllAroundClevelandJobs.com].\n\n* ''[[The News-Herald (Ohio)|The News-Herald]]'' {{WS|news-herald.com}} of [[Willoughby, Ohio|Willoughby]]\n* ''[[The Morning Journal]]'' {{WS|morningjournal.com}} of [[Lorain, Ohio|Lorain]]\n* ''El Latino Expreso'' {{WS|lorainlatino.com}} of [[Lorain, Ohio|Lorain]]\n\n== Philadelphia area ==\nSeven dailies and associated weeklies and magazines in [[Pennsylvania]] and [[New Jersey]], and associated Websites: [http://www.allaroundphilly.com AllAroundPhilly.com], [http://www.jobsinnj.com JobsInNJ.com], [http://www.jobsinpa.com JobsInPA.com], and [http://www.phillycarsearch.com PhillyCarSearch.com].\n\n* ''[[The Daily Local News]]'' {{WS|dailylocal.com}} of [[West Chester, Pennsylvania|West Chester]]\n* ''[[Delaware County Daily and Sunday Times]] {{WS|delcotimes.com}} of Primos [[Upper Darby Township, Pennsylvania]]\n* ''[[The Mercury (Pennsylvania)|The Mercury]]'' {{WS|pottstownmercury.com}} of [[Pottstown, Pennsylvania|Pottstown]]\n* ''[[The Reporter (Lansdale)|The Reporter]]'' {{WS|thereporteronline.com}} of [[Lansdale, Pennsylvania|Lansdale]]\n* ''The Times Herald'' {{WS|timesherald.com}} of [[Norristown, Pennsylvania|Norristown]]\n* ''[[The Trentonian]]'' {{WS|trentonian.com}} of [[Trenton, New Jersey]]\n\n* Weeklies\n* ''The Phoenix'' {{WS|phoenixvillenews.com}} of [[Phoenixville, Pennsylvania]]\n** ''El Latino Expreso'' {{WS|njexpreso.com}} of [[Trenton, New Jersey]]\n** ''La Voz'' {{WS|lavozpa.com}} of [[Norristown, Pennsylvania]]\n** ''The Tri County Record'' {{WS|tricountyrecord.com}} of [[Morgantown, Pennsylvania]]\n** ''Penny Pincher'' {{WS|pennypincherpa.com}}of [[Pottstown, Pennsylvania]]\n\n* Chesapeake Publishing  {{WS|southernchestercountyweeklies.com}}\n** ''The Kennett Paper'' {{WS|kennettpaper.com}} of [[Kennett Square, Pennsylvania]]\n** ''Avon Grove Sun'' {{WS|avongrovesun.com}} of [[West Grove, Pennsylvania]]\n** ''The Central Record'' {{WS|medfordcentralrecord.com}} of [[Medford, New Jersey]]\n** ''Maple Shade Progress'' {{WS|mapleshadeprogress.com}} of [[Maple Shade, New Jersey]]\n\n* Intercounty Newspapers {{WS|buckslocalnews.com}} {{WS|southjerseylocalnews.com}} \n** ''The Pennington Post'' {{WS|penningtonpost.com}} of [[Pennington, New Jersey]]\n** ''The Bristol Pilot'' {{WS|bristolpilot.com}} of [[Bristol, Pennsylvania]]\n** ''Yardley News'' {{WS|yardleynews.com}} of [[Yardley, Pennsylvania]]\n** ''Advance of Bucks County'' {{WS|advanceofbucks.com}} of [[Newtown, Pennsylvania]]\n** ''Record Breeze'' {{WS|recordbreeze.com}} of [[Berlin, New Jersey]]\n** ''Community News'' {{WS|sjcommunitynews.com}} of [[Pemberton, New Jersey]]\n\n* Montgomery Newspapers {{WS|montgomerynews.com}} \n** ''Ambler Gazette'' {{WS|amblergazette.com}} of [[Ambler, Pennsylvania]]\n** ''The Colonial'' {{WS|colonialnews.com}} of [[Plymouth Meeting, Pennsylvania]]\n** ''Glenside News'' {{WS|glensidenews.com}} of [[Glenside, Pennsylvania]]\n** ''The Globe'' {{WS|globenewspaper.com}} of [[Lower Moreland Township, Pennsylvania]]\n** ''Montgomery Life'' {{WS|montgomerylife.com}} of [[Fort Washington, Pennsylvania]]\n** ''North Penn Life'' {{WS|northpennlife.com}} of [[Lansdale, Pennsylvania]]\n** ''Perkasie News Herald'' {{WS|perkasienewsherald.com}} of [[Perkasie, Pennsylvania]]\n** ''Public Spirit'' {{WS|thepublicspirit.com}} of [[Hatboro, Pennsylvania]]\n** ''Souderton Independent'' {{WS|soudertonindependent.com}} of [[Souderton, Pennsylvania]]\n** ''Springfield Sun'' {{WS|springfieldsun.com}} of [[Springfield, Pennsylvania]]\n** ''Spring-Ford Reporter'' {{WS|springfordreporter.com}} of [[Royersford, Pennsylvania]]\n** ''Times Chronicle'' {{WS|thetimeschronicle.com}} of [[Jenkintown, Pennsylvania]]\n** ''Valley Item'' {{WS|valleyitem.com}} of [[Perkiomenville, Pennsylvania]]\n** ''Willow Grove Guide'' {{WS|willowgroveguide.com}} of [[Willow Grove, Pennsylvania]]\n** ''The Review'' {{WS|roxreview.com}} of [[Roxborough, Philadelphia, Pennsylvania]]\n\n* Main Line Media News {{WS|mainlinemedianews.com}}\n** ''Main Line Times'' {{WS|mainlinetimes.com}} of [[Ardmore, Pennsylvania]]\n** ''Main Line Life'' {{WS|mainlinelife.com}} of [[Ardmore, Pennsylvania]]\n** ''The King of Prussia Courier'' {{WS|kingofprussiacourier.com}} of [[King of Prussia, Pennsylvania]]\n\n* Delaware County News Network {{WS|delconewsnetwork.com}} \n** ''News of Delaware County'' {{WS|newsofdelawarecounty.com}} of [[Havertown, Pennsylvania]]\n** ''County Press'' {{WS|countypressonline.com}} of [[Newtown Square, Pennsylvania]]\n** ''Garnet Valley Press'' {{WS|countypressonline.com}} of [[Glen Mills, Pennsylvania]]\n** ''Springfield Press'' {{WS|countypressonline.com}} of [[Springfield, Pennsylvania]]\n** ''Town Talk'' {{WS|towntalknews.com}} of [[Ridley, Pennsylvania]]\n\n* Berks-Mont Newspapers {{WS|berksmontnews.com}} \n** ''The Boyertown Area Times'' {{WS|berksmontnews.com/boyertown_area_times}} of [[Boyertown, Pennsylvania]]\n** ''The Kutztown Area Patriot'' {{WS|berksmontnews.com/kutztown_area_patriot}} of [[Kutztown, Pennsylvania]]\n** ''The Hamburg Area Item'' {{WS|berksmontnews.com/hamburg_area_item}} of [[Hamburg, Pennsylvania]]\n** ''The Southern Berks News'' {{WS|berksmontnews.com/southern_berks_news}} of [[Exeter Township, Berks County, Pennsylvania]]\n** ''Community Connection'' {{WS|berksmontnews.com/community_connection}} of [[Boyertown, Pennsylvania]]\n\n* Magazines\n** ''Bucks Co. Town & Country Living'' {{WS|buckscountymagazine.com}} \n** ''Parents Express'' {{WS|parents-express.com}} \n** ''Real Men, Rednecks'' {{WS|realmenredneck.com}} \n\n{{JRC}}\n\n==References==\n<references />\n\n[[Category:Journal Register publications|*]]\n"
  },
  {
    "path": "java/tests/name/fraser/neil/plaintext/diff_match_patch_test.java",
    "content": "/*\n * Diff Match and Patch -- Test harness\n * Copyright 2018 The diff-match-patch Authors.\n * https://github.com/google/diff-match-patch\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *   http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n/**\n * Compile from diff-match-patch/java with:\n * javac -d classes src/name/fraser/neil/plaintext/diff_match_patch.java tests/name/fraser/neil/plaintext/diff_match_patch_test.java\n * Execute with:\n * java -classpath classes name/fraser/neil/plaintext/diff_match_patch_test\n */\n\npackage name.fraser.neil.plaintext;\n\nimport java.util.ArrayList;\nimport java.util.Arrays;\nimport java.util.HashMap;\nimport java.util.LinkedList;\nimport java.util.List;\nimport java.util.Map;\n\nimport name.fraser.neil.plaintext.diff_match_patch;\nimport name.fraser.neil.plaintext.diff_match_patch.Diff;\nimport name.fraser.neil.plaintext.diff_match_patch.LinesToCharsResult;\nimport name.fraser.neil.plaintext.diff_match_patch.Patch;\n\npublic class diff_match_patch_test {\n\n  private static diff_match_patch dmp;\n  private static diff_match_patch.Operation DELETE = diff_match_patch.Operation.DELETE;\n  private static diff_match_patch.Operation EQUAL = diff_match_patch.Operation.EQUAL;\n  private static diff_match_patch.Operation INSERT = diff_match_patch.Operation.INSERT;\n\n\n  //  DIFF TEST FUNCTIONS\n\n\n  public static void testDiffCommonPrefix() {\n    // Detect any common prefix.\n    assertEquals(\"diff_commonPrefix: Null case.\", 0, dmp.diff_commonPrefix(\"abc\", \"xyz\"));\n\n    assertEquals(\"diff_commonPrefix: Non-null case.\", 4, dmp.diff_commonPrefix(\"1234abcdef\", \"1234xyz\"));\n\n    assertEquals(\"diff_commonPrefix: Whole case.\", 4, dmp.diff_commonPrefix(\"1234\", \"1234xyz\"));\n  }\n\n  public static void testDiffCommonSuffix() {\n    // Detect any common suffix.\n    assertEquals(\"diff_commonSuffix: Null case.\", 0, dmp.diff_commonSuffix(\"abc\", \"xyz\"));\n\n    assertEquals(\"diff_commonSuffix: Non-null case.\", 4, dmp.diff_commonSuffix(\"abcdef1234\", \"xyz1234\"));\n\n    assertEquals(\"diff_commonSuffix: Whole case.\", 4, dmp.diff_commonSuffix(\"1234\", \"xyz1234\"));\n  }\n\n  public static void testDiffCommonOverlap() {\n    // Detect any suffix/prefix overlap.\n    assertEquals(\"diff_commonOverlap: Null case.\", 0, dmp.diff_commonOverlap(\"\", \"abcd\"));\n\n    assertEquals(\"diff_commonOverlap: Whole case.\", 3, dmp.diff_commonOverlap(\"abc\", \"abcd\"));\n\n    assertEquals(\"diff_commonOverlap: No overlap.\", 0, dmp.diff_commonOverlap(\"123456\", \"abcd\"));\n\n    assertEquals(\"diff_commonOverlap: Overlap.\", 3, dmp.diff_commonOverlap(\"123456xxx\", \"xxxabcd\"));\n\n    // Some overly clever languages (C#) may treat ligatures as equal to their\n    // component letters.  E.g. U+FB01 == 'fi'\n    assertEquals(\"diff_commonOverlap: Unicode.\", 0, dmp.diff_commonOverlap(\"fi\", \"\\ufb01i\"));\n  }\n\n  public static void testDiffHalfmatch() {\n    // Detect a halfmatch.\n    dmp.Diff_Timeout = 1;\n    assertNull(\"diff_halfMatch: No match #1.\", dmp.diff_halfMatch(\"1234567890\", \"abcdef\"));\n\n    assertNull(\"diff_halfMatch: No match #2.\", dmp.diff_halfMatch(\"12345\", \"23\"));\n\n    assertArrayEquals(\"diff_halfMatch: Single Match #1.\", new String[]{\"12\", \"90\", \"a\", \"z\", \"345678\"}, dmp.diff_halfMatch(\"1234567890\", \"a345678z\"));\n\n    assertArrayEquals(\"diff_halfMatch: Single Match #2.\", new String[]{\"a\", \"z\", \"12\", \"90\", \"345678\"}, dmp.diff_halfMatch(\"a345678z\", \"1234567890\"));\n\n    assertArrayEquals(\"diff_halfMatch: Single Match #3.\", new String[]{\"abc\", \"z\", \"1234\", \"0\", \"56789\"}, dmp.diff_halfMatch(\"abc56789z\", \"1234567890\"));\n\n    assertArrayEquals(\"diff_halfMatch: Single Match #4.\", new String[]{\"a\", \"xyz\", \"1\", \"7890\", \"23456\"}, dmp.diff_halfMatch(\"a23456xyz\", \"1234567890\"));\n\n    assertArrayEquals(\"diff_halfMatch: Multiple Matches #1.\", new String[]{\"12123\", \"123121\", \"a\", \"z\", \"1234123451234\"}, dmp.diff_halfMatch(\"121231234123451234123121\", \"a1234123451234z\"));\n\n    assertArrayEquals(\"diff_halfMatch: Multiple Matches #2.\", new String[]{\"\", \"-=-=-=-=-=\", \"x\", \"\", \"x-=-=-=-=-=-=-=\"}, dmp.diff_halfMatch(\"x-=-=-=-=-=-=-=-=-=-=-=-=\", \"xx-=-=-=-=-=-=-=\"));\n\n    assertArrayEquals(\"diff_halfMatch: Multiple Matches #3.\", new String[]{\"-=-=-=-=-=\", \"\", \"\", \"y\", \"-=-=-=-=-=-=-=y\"}, dmp.diff_halfMatch(\"-=-=-=-=-=-=-=-=-=-=-=-=y\", \"-=-=-=-=-=-=-=yy\"));\n\n    // Optimal diff would be -q+x=H-i+e=lloHe+Hu=llo-Hew+y not -qHillo+x=HelloHe-w+Hulloy\n    assertArrayEquals(\"diff_halfMatch: Non-optimal halfmatch.\", new String[]{\"qHillo\", \"w\", \"x\", \"Hulloy\", \"HelloHe\"}, dmp.diff_halfMatch(\"qHilloHelloHew\", \"xHelloHeHulloy\"));\n\n    dmp.Diff_Timeout = 0;\n    assertNull(\"diff_halfMatch: Optimal no halfmatch.\", dmp.diff_halfMatch(\"qHilloHelloHew\", \"xHelloHeHulloy\"));\n  }\n\n  public static void testDiffLinesToChars() {\n    // Convert lines down to characters.\n    ArrayList<String> tmpVector = new ArrayList<String>();\n    tmpVector.add(\"\");\n    tmpVector.add(\"alpha\\n\");\n    tmpVector.add(\"beta\\n\");\n    assertLinesToCharsResultEquals(\"diff_linesToChars: Shared lines.\", new LinesToCharsResult(\"\\u0001\\u0002\\u0001\", \"\\u0002\\u0001\\u0002\", tmpVector), dmp.diff_linesToChars(\"alpha\\nbeta\\nalpha\\n\", \"beta\\nalpha\\nbeta\\n\"));\n\n    tmpVector.clear();\n    tmpVector.add(\"\");\n    tmpVector.add(\"alpha\\r\\n\");\n    tmpVector.add(\"beta\\r\\n\");\n    tmpVector.add(\"\\r\\n\");\n    assertLinesToCharsResultEquals(\"diff_linesToChars: Empty string and blank lines.\", new LinesToCharsResult(\"\", \"\\u0001\\u0002\\u0003\\u0003\", tmpVector), dmp.diff_linesToChars(\"\", \"alpha\\r\\nbeta\\r\\n\\r\\n\\r\\n\"));\n\n    tmpVector.clear();\n    tmpVector.add(\"\");\n    tmpVector.add(\"a\");\n    tmpVector.add(\"b\");\n    assertLinesToCharsResultEquals(\"diff_linesToChars: No linebreaks.\", new LinesToCharsResult(\"\\u0001\", \"\\u0002\", tmpVector), dmp.diff_linesToChars(\"a\", \"b\"));\n\n    // More than 256 to reveal any 8-bit limitations.\n    int n = 300;\n    tmpVector.clear();\n    StringBuilder lineList = new StringBuilder();\n    StringBuilder charList = new StringBuilder();\n    for (int i = 1; i < n + 1; i++) {\n      tmpVector.add(i + \"\\n\");\n      lineList.append(i + \"\\n\");\n      charList.append(String.valueOf((char) i));\n    }\n    assertEquals(\"Test initialization fail #1.\", n, tmpVector.size());\n    String lines = lineList.toString();\n    String chars = charList.toString();\n    assertEquals(\"Test initialization fail #2.\", n, chars.length());\n    tmpVector.add(0, \"\");\n    assertLinesToCharsResultEquals(\"diff_linesToChars: More than 256.\", new LinesToCharsResult(chars, \"\", tmpVector), dmp.diff_linesToChars(lines, \"\"));\n  }\n\n  public static void testDiffCharsToLines() {\n    // First check that Diff equality works.\n    assertTrue(\"diff_charsToLines: Equality #1.\", new Diff(EQUAL, \"a\").equals(new Diff(EQUAL, \"a\")));\n\n    assertEquals(\"diff_charsToLines: Equality #2.\", new Diff(EQUAL, \"a\"), new Diff(EQUAL, \"a\"));\n\n    // Convert chars up to lines.\n    LinkedList<Diff> diffs = diffList(new Diff(EQUAL, \"\\u0001\\u0002\\u0001\"), new Diff(INSERT, \"\\u0002\\u0001\\u0002\"));\n    ArrayList<String> tmpVector = new ArrayList<String>();\n    tmpVector.add(\"\");\n    tmpVector.add(\"alpha\\n\");\n    tmpVector.add(\"beta\\n\");\n    dmp.diff_charsToLines(diffs, tmpVector);\n    assertEquals(\"diff_charsToLines: Shared lines.\", diffList(new Diff(EQUAL, \"alpha\\nbeta\\nalpha\\n\"), new Diff(INSERT, \"beta\\nalpha\\nbeta\\n\")), diffs);\n\n    // More than 256 to reveal any 8-bit limitations.\n    int n = 300;\n    tmpVector.clear();\n    StringBuilder lineList = new StringBuilder();\n    StringBuilder charList = new StringBuilder();\n    for (int i = 1; i < n + 1; i++) {\n      tmpVector.add(i + \"\\n\");\n      lineList.append(i + \"\\n\");\n      charList.append(String.valueOf((char) i));\n    }\n    assertEquals(\"Test initialization fail #3.\", n, tmpVector.size());\n    String lines = lineList.toString();\n    String chars = charList.toString();\n    assertEquals(\"Test initialization fail #4.\", n, chars.length());\n    tmpVector.add(0, \"\");\n    diffs = diffList(new Diff(DELETE, chars));\n    dmp.diff_charsToLines(diffs, tmpVector);\n    assertEquals(\"diff_charsToLines: More than 256.\", diffList(new Diff(DELETE, lines)), diffs);\n\n    // More than 65536 to verify any 16-bit limitation.\n    lineList = new StringBuilder();\n    for (int i = 0; i < 66000; i++) {\n      lineList.append(i + \"\\n\");\n    }\n    chars = lineList.toString();\n    LinesToCharsResult results = dmp.diff_linesToChars(chars, \"\");\n    diffs = diffList(new Diff(INSERT, results.chars1));\n    dmp.diff_charsToLines(diffs, results.lineArray);\n    assertEquals(\"diff_charsToLines: More than 65536.\", chars, diffs.getFirst().text);\n  }\n\n  public static void testDiffCleanupMerge() {\n    // Cleanup a messy diff.\n    LinkedList<Diff> diffs = diffList();\n    dmp.diff_cleanupMerge(diffs);\n    assertEquals(\"diff_cleanupMerge: Null case.\", diffList(), diffs);\n\n    diffs = diffList(new Diff(EQUAL, \"a\"), new Diff(DELETE, \"b\"), new Diff(INSERT, \"c\"));\n    dmp.diff_cleanupMerge(diffs);\n    assertEquals(\"diff_cleanupMerge: No change case.\", diffList(new Diff(EQUAL, \"a\"), new Diff(DELETE, \"b\"), new Diff(INSERT, \"c\")), diffs);\n\n    diffs = diffList(new Diff(EQUAL, \"a\"), new Diff(EQUAL, \"b\"), new Diff(EQUAL, \"c\"));\n    dmp.diff_cleanupMerge(diffs);\n    assertEquals(\"diff_cleanupMerge: Merge equalities.\", diffList(new Diff(EQUAL, \"abc\")), diffs);\n\n    diffs = diffList(new Diff(DELETE, \"a\"), new Diff(DELETE, \"b\"), new Diff(DELETE, \"c\"));\n    dmp.diff_cleanupMerge(diffs);\n    assertEquals(\"diff_cleanupMerge: Merge deletions.\", diffList(new Diff(DELETE, \"abc\")), diffs);\n\n    diffs = diffList(new Diff(INSERT, \"a\"), new Diff(INSERT, \"b\"), new Diff(INSERT, \"c\"));\n    dmp.diff_cleanupMerge(diffs);\n    assertEquals(\"diff_cleanupMerge: Merge insertions.\", diffList(new Diff(INSERT, \"abc\")), diffs);\n\n    diffs = diffList(new Diff(DELETE, \"a\"), new Diff(INSERT, \"b\"), new Diff(DELETE, \"c\"), new Diff(INSERT, \"d\"), new Diff(EQUAL, \"e\"), new Diff(EQUAL, \"f\"));\n    dmp.diff_cleanupMerge(diffs);\n    assertEquals(\"diff_cleanupMerge: Merge interweave.\", diffList(new Diff(DELETE, \"ac\"), new Diff(INSERT, \"bd\"), new Diff(EQUAL, \"ef\")), diffs);\n\n    diffs = diffList(new Diff(DELETE, \"a\"), new Diff(INSERT, \"abc\"), new Diff(DELETE, \"dc\"));\n    dmp.diff_cleanupMerge(diffs);\n    assertEquals(\"diff_cleanupMerge: Prefix and suffix detection.\", diffList(new Diff(EQUAL, \"a\"), new Diff(DELETE, \"d\"), new Diff(INSERT, \"b\"), new Diff(EQUAL, \"c\")), diffs);\n\n    diffs = diffList(new Diff(EQUAL, \"x\"), new Diff(DELETE, \"a\"), new Diff(INSERT, \"abc\"), new Diff(DELETE, \"dc\"), new Diff(EQUAL, \"y\"));\n    dmp.diff_cleanupMerge(diffs);\n    assertEquals(\"diff_cleanupMerge: Prefix and suffix detection with equalities.\", diffList(new Diff(EQUAL, \"xa\"), new Diff(DELETE, \"d\"), new Diff(INSERT, \"b\"), new Diff(EQUAL, \"cy\")), diffs);\n\n    diffs = diffList(new Diff(EQUAL, \"a\"), new Diff(INSERT, \"ba\"), new Diff(EQUAL, \"c\"));\n    dmp.diff_cleanupMerge(diffs);\n    assertEquals(\"diff_cleanupMerge: Slide edit left.\", diffList(new Diff(INSERT, \"ab\"), new Diff(EQUAL, \"ac\")), diffs);\n\n    diffs = diffList(new Diff(EQUAL, \"c\"), new Diff(INSERT, \"ab\"), new Diff(EQUAL, \"a\"));\n    dmp.diff_cleanupMerge(diffs);\n    assertEquals(\"diff_cleanupMerge: Slide edit right.\", diffList(new Diff(EQUAL, \"ca\"), new Diff(INSERT, \"ba\")), diffs);\n\n    diffs = diffList(new Diff(EQUAL, \"a\"), new Diff(DELETE, \"b\"), new Diff(EQUAL, \"c\"), new Diff(DELETE, \"ac\"), new Diff(EQUAL, \"x\"));\n    dmp.diff_cleanupMerge(diffs);\n    assertEquals(\"diff_cleanupMerge: Slide edit left recursive.\", diffList(new Diff(DELETE, \"abc\"), new Diff(EQUAL, \"acx\")), diffs);\n\n    diffs = diffList(new Diff(EQUAL, \"x\"), new Diff(DELETE, \"ca\"), new Diff(EQUAL, \"c\"), new Diff(DELETE, \"b\"), new Diff(EQUAL, \"a\"));\n    dmp.diff_cleanupMerge(diffs);\n    assertEquals(\"diff_cleanupMerge: Slide edit right recursive.\", diffList(new Diff(EQUAL, \"xca\"), new Diff(DELETE, \"cba\")), diffs);\n\n    diffs = diffList(new Diff(DELETE, \"b\"), new Diff(INSERT, \"ab\"), new Diff(EQUAL, \"c\"));\n    dmp.diff_cleanupMerge(diffs);\n    assertEquals(\"diff_cleanupMerge: Empty merge.\", diffList(new Diff(INSERT, \"a\"), new Diff(EQUAL, \"bc\")), diffs);\n\n    diffs = diffList(new Diff(EQUAL, \"\"), new Diff(INSERT, \"a\"), new Diff(EQUAL, \"b\"));\n    dmp.diff_cleanupMerge(diffs);\n    assertEquals(\"diff_cleanupMerge: Empty equality.\", diffList(new Diff(INSERT, \"a\"), new Diff(EQUAL, \"b\")), diffs);\n  }\n\n  public static void testDiffCleanupSemanticLossless() {\n    // Slide diffs to match logical boundaries.\n    LinkedList<Diff> diffs = diffList();\n    dmp.diff_cleanupSemanticLossless(diffs);\n    assertEquals(\"diff_cleanupSemanticLossless: Null case.\", diffList(), diffs);\n\n    diffs = diffList(new Diff(EQUAL, \"AAA\\r\\n\\r\\nBBB\"), new Diff(INSERT, \"\\r\\nDDD\\r\\n\\r\\nBBB\"), new Diff(EQUAL, \"\\r\\nEEE\"));\n    dmp.diff_cleanupSemanticLossless(diffs);\n    assertEquals(\"diff_cleanupSemanticLossless: Blank lines.\", diffList(new Diff(EQUAL, \"AAA\\r\\n\\r\\n\"), new Diff(INSERT, \"BBB\\r\\nDDD\\r\\n\\r\\n\"), new Diff(EQUAL, \"BBB\\r\\nEEE\")), diffs);\n\n    diffs = diffList(new Diff(EQUAL, \"AAA\\r\\nBBB\"), new Diff(INSERT, \" DDD\\r\\nBBB\"), new Diff(EQUAL, \" EEE\"));\n    dmp.diff_cleanupSemanticLossless(diffs);\n    assertEquals(\"diff_cleanupSemanticLossless: Line boundaries.\", diffList(new Diff(EQUAL, \"AAA\\r\\n\"), new Diff(INSERT, \"BBB DDD\\r\\n\"), new Diff(EQUAL, \"BBB EEE\")), diffs);\n\n    diffs = diffList(new Diff(EQUAL, \"The c\"), new Diff(INSERT, \"ow and the c\"), new Diff(EQUAL, \"at.\"));\n    dmp.diff_cleanupSemanticLossless(diffs);\n    assertEquals(\"diff_cleanupSemanticLossless: Word boundaries.\", diffList(new Diff(EQUAL, \"The \"), new Diff(INSERT, \"cow and the \"), new Diff(EQUAL, \"cat.\")), diffs);\n\n    diffs = diffList(new Diff(EQUAL, \"The-c\"), new Diff(INSERT, \"ow-and-the-c\"), new Diff(EQUAL, \"at.\"));\n    dmp.diff_cleanupSemanticLossless(diffs);\n    assertEquals(\"diff_cleanupSemanticLossless: Alphanumeric boundaries.\", diffList(new Diff(EQUAL, \"The-\"), new Diff(INSERT, \"cow-and-the-\"), new Diff(EQUAL, \"cat.\")), diffs);\n\n    diffs = diffList(new Diff(EQUAL, \"a\"), new Diff(DELETE, \"a\"), new Diff(EQUAL, \"ax\"));\n    dmp.diff_cleanupSemanticLossless(diffs);\n    assertEquals(\"diff_cleanupSemanticLossless: Hitting the start.\", diffList(new Diff(DELETE, \"a\"), new Diff(EQUAL, \"aax\")), diffs);\n\n    diffs = diffList(new Diff(EQUAL, \"xa\"), new Diff(DELETE, \"a\"), new Diff(EQUAL, \"a\"));\n    dmp.diff_cleanupSemanticLossless(diffs);\n    assertEquals(\"diff_cleanupSemanticLossless: Hitting the end.\", diffList(new Diff(EQUAL, \"xaa\"), new Diff(DELETE, \"a\")), diffs);\n\n    diffs = diffList(new Diff(EQUAL, \"The xxx. The \"), new Diff(INSERT, \"zzz. The \"), new Diff(EQUAL, \"yyy.\"));\n    dmp.diff_cleanupSemanticLossless(diffs);\n    assertEquals(\"diff_cleanupSemanticLossless: Sentence boundaries.\", diffList(new Diff(EQUAL, \"The xxx.\"), new Diff(INSERT, \" The zzz.\"), new Diff(EQUAL, \" The yyy.\")), diffs);\n  }\n\n  public static void testDiffCleanupSemantic() {\n    // Cleanup semantically trivial equalities.\n    LinkedList<Diff> diffs = diffList();\n    dmp.diff_cleanupSemantic(diffs);\n    assertEquals(\"diff_cleanupSemantic: Null case.\", diffList(), diffs);\n\n    diffs = diffList(new Diff(DELETE, \"ab\"), new Diff(INSERT, \"cd\"), new Diff(EQUAL, \"12\"), new Diff(DELETE, \"e\"));\n    dmp.diff_cleanupSemantic(diffs);\n    assertEquals(\"diff_cleanupSemantic: No elimination #1.\", diffList(new Diff(DELETE, \"ab\"), new Diff(INSERT, \"cd\"), new Diff(EQUAL, \"12\"), new Diff(DELETE, \"e\")), diffs);\n\n    diffs = diffList(new Diff(DELETE, \"abc\"), new Diff(INSERT, \"ABC\"), new Diff(EQUAL, \"1234\"), new Diff(DELETE, \"wxyz\"));\n    dmp.diff_cleanupSemantic(diffs);\n    assertEquals(\"diff_cleanupSemantic: No elimination #2.\", diffList(new Diff(DELETE, \"abc\"), new Diff(INSERT, \"ABC\"), new Diff(EQUAL, \"1234\"), new Diff(DELETE, \"wxyz\")), diffs);\n\n    diffs = diffList(new Diff(DELETE, \"a\"), new Diff(EQUAL, \"b\"), new Diff(DELETE, \"c\"));\n    dmp.diff_cleanupSemantic(diffs);\n    assertEquals(\"diff_cleanupSemantic: Simple elimination.\", diffList(new Diff(DELETE, \"abc\"), new Diff(INSERT, \"b\")), diffs);\n\n    diffs = diffList(new Diff(DELETE, \"ab\"), new Diff(EQUAL, \"cd\"), new Diff(DELETE, \"e\"), new Diff(EQUAL, \"f\"), new Diff(INSERT, \"g\"));\n    dmp.diff_cleanupSemantic(diffs);\n    assertEquals(\"diff_cleanupSemantic: Backpass elimination.\", diffList(new Diff(DELETE, \"abcdef\"), new Diff(INSERT, \"cdfg\")), diffs);\n\n    diffs = diffList(new Diff(INSERT, \"1\"), new Diff(EQUAL, \"A\"), new Diff(DELETE, \"B\"), new Diff(INSERT, \"2\"), new Diff(EQUAL, \"_\"), new Diff(INSERT, \"1\"), new Diff(EQUAL, \"A\"), new Diff(DELETE, \"B\"), new Diff(INSERT, \"2\"));\n    dmp.diff_cleanupSemantic(diffs);\n    assertEquals(\"diff_cleanupSemantic: Multiple elimination.\", diffList(new Diff(DELETE, \"AB_AB\"), new Diff(INSERT, \"1A2_1A2\")), diffs);\n\n    diffs = diffList(new Diff(EQUAL, \"The c\"), new Diff(DELETE, \"ow and the c\"), new Diff(EQUAL, \"at.\"));\n    dmp.diff_cleanupSemantic(diffs);\n    assertEquals(\"diff_cleanupSemantic: Word boundaries.\", diffList(new Diff(EQUAL, \"The \"), new Diff(DELETE, \"cow and the \"), new Diff(EQUAL, \"cat.\")), diffs);\n\n    diffs = diffList(new Diff(DELETE, \"abcxx\"), new Diff(INSERT, \"xxdef\"));\n    dmp.diff_cleanupSemantic(diffs);\n    assertEquals(\"diff_cleanupSemantic: No overlap elimination.\", diffList(new Diff(DELETE, \"abcxx\"), new Diff(INSERT, \"xxdef\")), diffs);\n\n    diffs = diffList(new Diff(DELETE, \"abcxxx\"), new Diff(INSERT, \"xxxdef\"));\n    dmp.diff_cleanupSemantic(diffs);\n    assertEquals(\"diff_cleanupSemantic: Overlap elimination.\", diffList(new Diff(DELETE, \"abc\"), new Diff(EQUAL, \"xxx\"), new Diff(INSERT, \"def\")), diffs);\n\n    diffs = diffList(new Diff(DELETE, \"xxxabc\"), new Diff(INSERT, \"defxxx\"));\n    dmp.diff_cleanupSemantic(diffs);\n    assertEquals(\"diff_cleanupSemantic: Reverse overlap elimination.\", diffList(new Diff(INSERT, \"def\"), new Diff(EQUAL, \"xxx\"), new Diff(DELETE, \"abc\")), diffs);\n\n    diffs = diffList(new Diff(DELETE, \"abcd1212\"), new Diff(INSERT, \"1212efghi\"), new Diff(EQUAL, \"----\"), new Diff(DELETE, \"A3\"), new Diff(INSERT, \"3BC\"));\n    dmp.diff_cleanupSemantic(diffs);\n    assertEquals(\"diff_cleanupSemantic: Two overlap eliminations.\", diffList(new Diff(DELETE, \"abcd\"), new Diff(EQUAL, \"1212\"), new Diff(INSERT, \"efghi\"), new Diff(EQUAL, \"----\"), new Diff(DELETE, \"A\"), new Diff(EQUAL, \"3\"), new Diff(INSERT, \"BC\")), diffs);\n  }\n\n  public static void testDiffCleanupEfficiency() {\n    // Cleanup operationally trivial equalities.\n    dmp.Diff_EditCost = 4;\n    LinkedList<Diff> diffs = diffList();\n    dmp.diff_cleanupEfficiency(diffs);\n    assertEquals(\"diff_cleanupEfficiency: Null case.\", diffList(), diffs);\n\n    diffs = diffList(new Diff(DELETE, \"ab\"), new Diff(INSERT, \"12\"), new Diff(EQUAL, \"wxyz\"), new Diff(DELETE, \"cd\"), new Diff(INSERT, \"34\"));\n    dmp.diff_cleanupEfficiency(diffs);\n    assertEquals(\"diff_cleanupEfficiency: No elimination.\", diffList(new Diff(DELETE, \"ab\"), new Diff(INSERT, \"12\"), new Diff(EQUAL, \"wxyz\"), new Diff(DELETE, \"cd\"), new Diff(INSERT, \"34\")), diffs);\n\n    diffs = diffList(new Diff(DELETE, \"ab\"), new Diff(INSERT, \"12\"), new Diff(EQUAL, \"xyz\"), new Diff(DELETE, \"cd\"), new Diff(INSERT, \"34\"));\n    dmp.diff_cleanupEfficiency(diffs);\n    assertEquals(\"diff_cleanupEfficiency: Four-edit elimination.\", diffList(new Diff(DELETE, \"abxyzcd\"), new Diff(INSERT, \"12xyz34\")), diffs);\n\n    diffs = diffList(new Diff(INSERT, \"12\"), new Diff(EQUAL, \"x\"), new Diff(DELETE, \"cd\"), new Diff(INSERT, \"34\"));\n    dmp.diff_cleanupEfficiency(diffs);\n    assertEquals(\"diff_cleanupEfficiency: Three-edit elimination.\", diffList(new Diff(DELETE, \"xcd\"), new Diff(INSERT, \"12x34\")), diffs);\n\n    diffs = diffList(new Diff(DELETE, \"ab\"), new Diff(INSERT, \"12\"), new Diff(EQUAL, \"xy\"), new Diff(INSERT, \"34\"), new Diff(EQUAL, \"z\"), new Diff(DELETE, \"cd\"), new Diff(INSERT, \"56\"));\n    dmp.diff_cleanupEfficiency(diffs);\n    assertEquals(\"diff_cleanupEfficiency: Backpass elimination.\", diffList(new Diff(DELETE, \"abxyzcd\"), new Diff(INSERT, \"12xy34z56\")), diffs);\n\n    dmp.Diff_EditCost = 5;\n    diffs = diffList(new Diff(DELETE, \"ab\"), new Diff(INSERT, \"12\"), new Diff(EQUAL, \"wxyz\"), new Diff(DELETE, \"cd\"), new Diff(INSERT, \"34\"));\n    dmp.diff_cleanupEfficiency(diffs);\n    assertEquals(\"diff_cleanupEfficiency: High cost elimination.\", diffList(new Diff(DELETE, \"abwxyzcd\"), new Diff(INSERT, \"12wxyz34\")), diffs);\n    dmp.Diff_EditCost = 4;\n  }\n\n  public static void testDiffPrettyHtml() {\n    // Pretty print.\n    LinkedList<Diff> diffs = diffList(new Diff(EQUAL, \"a\\n\"), new Diff(DELETE, \"<B>b</B>\"), new Diff(INSERT, \"c&d\"));\n    assertEquals(\"diff_prettyHtml:\", \"<span>a&para;<br></span><del style=\\\"background:#ffe6e6;\\\">&lt;B&gt;b&lt;/B&gt;</del><ins style=\\\"background:#e6ffe6;\\\">c&amp;d</ins>\", dmp.diff_prettyHtml(diffs));\n  }\n\n  public static void testDiffText() {\n    // Compute the source and destination texts.\n    LinkedList<Diff> diffs = diffList(new Diff(EQUAL, \"jump\"), new Diff(DELETE, \"s\"), new Diff(INSERT, \"ed\"), new Diff(EQUAL, \" over \"), new Diff(DELETE, \"the\"), new Diff(INSERT, \"a\"), new Diff(EQUAL, \" lazy\"));\n    assertEquals(\"diff_text1:\", \"jumps over the lazy\", dmp.diff_text1(diffs));\n    assertEquals(\"diff_text2:\", \"jumped over a lazy\", dmp.diff_text2(diffs));\n  }\n\n  public static void testDiffDelta() {\n    // Convert a diff into delta string.\n    LinkedList<Diff> diffs = diffList(new Diff(EQUAL, \"jump\"), new Diff(DELETE, \"s\"), new Diff(INSERT, \"ed\"), new Diff(EQUAL, \" over \"), new Diff(DELETE, \"the\"), new Diff(INSERT, \"a\"), new Diff(EQUAL, \" lazy\"), new Diff(INSERT, \"old dog\"));\n    String text1 = dmp.diff_text1(diffs);\n    assertEquals(\"diff_text1: Base text.\", \"jumps over the lazy\", text1);\n\n    String delta = dmp.diff_toDelta(diffs);\n    assertEquals(\"diff_toDelta:\", \"=4\\t-1\\t+ed\\t=6\\t-3\\t+a\\t=5\\t+old dog\", delta);\n\n    // Convert delta string into a diff.\n    assertEquals(\"diff_fromDelta: Normal.\", diffs, dmp.diff_fromDelta(text1, delta));\n\n    // Generates error (19 < 20).\n    try {\n      dmp.diff_fromDelta(text1 + \"x\", delta);\n      fail(\"diff_fromDelta: Too long.\");\n    } catch (IllegalArgumentException ex) {\n      // Exception expected.\n    }\n\n    // Generates error (19 > 18).\n    try {\n      dmp.diff_fromDelta(text1.substring(1), delta);\n      fail(\"diff_fromDelta: Too short.\");\n    } catch (IllegalArgumentException ex) {\n      // Exception expected.\n    }\n\n    // Generates error (%c3%xy invalid Unicode).\n    try {\n      dmp.diff_fromDelta(\"\", \"+%c3%xy\");\n      fail(\"diff_fromDelta: Invalid character.\");\n    } catch (IllegalArgumentException ex) {\n      // Exception expected.\n    }\n\n    // Test deltas with special characters.\n    diffs = diffList(new Diff(EQUAL, \"\\u0680 \\000 \\t %\"), new Diff(DELETE, \"\\u0681 \\001 \\n ^\"), new Diff(INSERT, \"\\u0682 \\002 \\\\ |\"));\n    text1 = dmp.diff_text1(diffs);\n    assertEquals(\"diff_text1: Unicode text.\", \"\\u0680 \\000 \\t %\\u0681 \\001 \\n ^\", text1);\n\n    delta = dmp.diff_toDelta(diffs);\n    assertEquals(\"diff_toDelta: Unicode.\", \"=7\\t-7\\t+%DA%82 %02 %5C %7C\", delta);\n\n    assertEquals(\"diff_fromDelta: Unicode.\", diffs, dmp.diff_fromDelta(text1, delta));\n\n    // Verify pool of unchanged characters.\n    diffs = diffList(new Diff(INSERT, \"A-Z a-z 0-9 - _ . ! ~ * ' ( ) ; / ? : @ & = + $ , # \"));\n    String text2 = dmp.diff_text2(diffs);\n    assertEquals(\"diff_text2: Unchanged characters.\", \"A-Z a-z 0-9 - _ . ! ~ * \\' ( ) ; / ? : @ & = + $ , # \", text2);\n\n    delta = dmp.diff_toDelta(diffs);\n    assertEquals(\"diff_toDelta: Unchanged characters.\", \"+A-Z a-z 0-9 - _ . ! ~ * \\' ( ) ; / ? : @ & = + $ , # \", delta);\n\n    // Convert delta string into a diff.\n    assertEquals(\"diff_fromDelta: Unchanged characters.\", diffs, dmp.diff_fromDelta(\"\", delta));\n\n    // 160 kb string.\n    String a = \"abcdefghij\";\n    for (int i = 0; i < 14; i++) {\n      a += a;\n    }\n    diffs = diffList(new Diff(INSERT, a));\n    delta = dmp.diff_toDelta(diffs);\n    assertEquals(\"diff_toDelta: 160kb string.\", \"+\" + a, delta);\n\n    // Convert delta string into a diff.\n    assertEquals(\"diff_fromDelta: 160kb string.\", diffs, dmp.diff_fromDelta(\"\", delta));\n  }\n\n  public static void testDiffXIndex() {\n    // Translate a location in text1 to text2.\n    LinkedList<Diff> diffs = diffList(new Diff(DELETE, \"a\"), new Diff(INSERT, \"1234\"), new Diff(EQUAL, \"xyz\"));\n    assertEquals(\"diff_xIndex: Translation on equality.\", 5, dmp.diff_xIndex(diffs, 2));\n\n    diffs = diffList(new Diff(EQUAL, \"a\"), new Diff(DELETE, \"1234\"), new Diff(EQUAL, \"xyz\"));\n    assertEquals(\"diff_xIndex: Translation on deletion.\", 1, dmp.diff_xIndex(diffs, 3));\n  }\n\n  public static void testDiffLevenshtein() {\n    LinkedList<Diff> diffs = diffList(new Diff(DELETE, \"abc\"), new Diff(INSERT, \"1234\"), new Diff(EQUAL, \"xyz\"));\n    assertEquals(\"diff_levenshtein: Levenshtein with trailing equality.\", 4, dmp.diff_levenshtein(diffs));\n\n    diffs = diffList(new Diff(EQUAL, \"xyz\"), new Diff(DELETE, \"abc\"), new Diff(INSERT, \"1234\"));\n    assertEquals(\"diff_levenshtein: Levenshtein with leading equality.\", 4, dmp.diff_levenshtein(diffs));\n\n    diffs = diffList(new Diff(DELETE, \"abc\"), new Diff(EQUAL, \"xyz\"), new Diff(INSERT, \"1234\"));\n    assertEquals(\"diff_levenshtein: Levenshtein with middle equality.\", 7, dmp.diff_levenshtein(diffs));\n  }\n\n  public static void testDiffBisect() {\n    // Normal.\n    String a = \"cat\";\n    String b = \"map\";\n    // Since the resulting diff hasn't been normalized, it would be ok if\n    // the insertion and deletion pairs are swapped.\n    // If the order changes, tweak this test as required.\n    LinkedList<Diff> diffs = diffList(new Diff(DELETE, \"c\"), new Diff(INSERT, \"m\"), new Diff(EQUAL, \"a\"), new Diff(DELETE, \"t\"), new Diff(INSERT, \"p\"));\n    assertEquals(\"diff_bisect: Normal.\", diffs, dmp.diff_bisect(a, b, Long.MAX_VALUE));\n\n    // Timeout.\n    diffs = diffList(new Diff(DELETE, \"cat\"), new Diff(INSERT, \"map\"));\n    assertEquals(\"diff_bisect: Timeout.\", diffs, dmp.diff_bisect(a, b, 0));\n  }\n\n  public static void testDiffMain() {\n    // Perform a trivial diff.\n    LinkedList<Diff> diffs = diffList();\n    assertEquals(\"diff_main: Null case.\", diffs, dmp.diff_main(\"\", \"\", false));\n\n    diffs = diffList(new Diff(EQUAL, \"abc\"));\n    assertEquals(\"diff_main: Equality.\", diffs, dmp.diff_main(\"abc\", \"abc\", false));\n\n    diffs = diffList(new Diff(EQUAL, \"ab\"), new Diff(INSERT, \"123\"), new Diff(EQUAL, \"c\"));\n    assertEquals(\"diff_main: Simple insertion.\", diffs, dmp.diff_main(\"abc\", \"ab123c\", false));\n\n    diffs = diffList(new Diff(EQUAL, \"a\"), new Diff(DELETE, \"123\"), new Diff(EQUAL, \"bc\"));\n    assertEquals(\"diff_main: Simple deletion.\", diffs, dmp.diff_main(\"a123bc\", \"abc\", false));\n\n    diffs = diffList(new Diff(EQUAL, \"a\"), new Diff(INSERT, \"123\"), new Diff(EQUAL, \"b\"), new Diff(INSERT, \"456\"), new Diff(EQUAL, \"c\"));\n    assertEquals(\"diff_main: Two insertions.\", diffs, dmp.diff_main(\"abc\", \"a123b456c\", false));\n\n    diffs = diffList(new Diff(EQUAL, \"a\"), new Diff(DELETE, \"123\"), new Diff(EQUAL, \"b\"), new Diff(DELETE, \"456\"), new Diff(EQUAL, \"c\"));\n    assertEquals(\"diff_main: Two deletions.\", diffs, dmp.diff_main(\"a123b456c\", \"abc\", false));\n\n    // Perform a real diff.\n    // Switch off the timeout.\n    dmp.Diff_Timeout = 0;\n    diffs = diffList(new Diff(DELETE, \"a\"), new Diff(INSERT, \"b\"));\n    assertEquals(\"diff_main: Simple case #1.\", diffs, dmp.diff_main(\"a\", \"b\", false));\n\n    diffs = diffList(new Diff(DELETE, \"Apple\"), new Diff(INSERT, \"Banana\"), new Diff(EQUAL, \"s are a\"), new Diff(INSERT, \"lso\"), new Diff(EQUAL, \" fruit.\"));\n    assertEquals(\"diff_main: Simple case #2.\", diffs, dmp.diff_main(\"Apples are a fruit.\", \"Bananas are also fruit.\", false));\n\n    diffs = diffList(new Diff(DELETE, \"a\"), new Diff(INSERT, \"\\u0680\"), new Diff(EQUAL, \"x\"), new Diff(DELETE, \"\\t\"), new Diff(INSERT, \"\\000\"));\n    assertEquals(\"diff_main: Simple case #3.\", diffs, dmp.diff_main(\"ax\\t\", \"\\u0680x\\000\", false));\n\n    diffs = diffList(new Diff(DELETE, \"1\"), new Diff(EQUAL, \"a\"), new Diff(DELETE, \"y\"), new Diff(EQUAL, \"b\"), new Diff(DELETE, \"2\"), new Diff(INSERT, \"xab\"));\n    assertEquals(\"diff_main: Overlap #1.\", diffs, dmp.diff_main(\"1ayb2\", \"abxab\", false));\n\n    diffs = diffList(new Diff(INSERT, \"xaxcx\"), new Diff(EQUAL, \"abc\"), new Diff(DELETE, \"y\"));\n    assertEquals(\"diff_main: Overlap #2.\", diffs, dmp.diff_main(\"abcy\", \"xaxcxabc\", false));\n\n    diffs = diffList(new Diff(DELETE, \"ABCD\"), new Diff(EQUAL, \"a\"), new Diff(DELETE, \"=\"), new Diff(INSERT, \"-\"), new Diff(EQUAL, \"bcd\"), new Diff(DELETE, \"=\"), new Diff(INSERT, \"-\"), new Diff(EQUAL, \"efghijklmnopqrs\"), new Diff(DELETE, \"EFGHIJKLMNOefg\"));\n    assertEquals(\"diff_main: Overlap #3.\", diffs, dmp.diff_main(\"ABCDa=bcd=efghijklmnopqrsEFGHIJKLMNOefg\", \"a-bcd-efghijklmnopqrs\", false));\n\n    diffs = diffList(new Diff(INSERT, \" \"), new Diff(EQUAL, \"a\"), new Diff(INSERT, \"nd\"), new Diff(EQUAL, \" [[Pennsylvania]]\"), new Diff(DELETE, \" and [[New\"));\n    assertEquals(\"diff_main: Large equality.\", diffs, dmp.diff_main(\"a [[Pennsylvania]] and [[New\", \" and [[Pennsylvania]]\", false));\n\n    dmp.Diff_Timeout = 0.1f;  // 100ms\n    String a = \"`Twas brillig, and the slithy toves\\nDid gyre and gimble in the wabe:\\nAll mimsy were the borogoves,\\nAnd the mome raths outgrabe.\\n\";\n    String b = \"I am the very model of a modern major general,\\nI've information vegetable, animal, and mineral,\\nI know the kings of England, and I quote the fights historical,\\nFrom Marathon to Waterloo, in order categorical.\\n\";\n    // Increase the text lengths by 1024 times to ensure a timeout.\n    for (int i = 0; i < 10; i++) {\n      a += a;\n      b += b;\n    }\n    long startTime = System.currentTimeMillis();\n    dmp.diff_main(a, b);\n    long endTime = System.currentTimeMillis();\n    // Test that we took at least the timeout period.\n    assertTrue(\"diff_main: Timeout min.\", dmp.Diff_Timeout * 1000 <= endTime - startTime);\n    // Test that we didn't take forever (be forgiving).\n    // Theoretically this test could fail very occasionally if the\n    // OS task swaps or locks up for a second at the wrong moment.\n    assertTrue(\"diff_main: Timeout max.\", dmp.Diff_Timeout * 1000 * 2 > endTime - startTime);\n    dmp.Diff_Timeout = 0;\n\n    // Test the linemode speedup.\n    // Must be long to pass the 100 char cutoff.\n    a = \"1234567890\\n1234567890\\n1234567890\\n1234567890\\n1234567890\\n1234567890\\n1234567890\\n1234567890\\n1234567890\\n1234567890\\n1234567890\\n1234567890\\n1234567890\\n\";\n    b = \"abcdefghij\\nabcdefghij\\nabcdefghij\\nabcdefghij\\nabcdefghij\\nabcdefghij\\nabcdefghij\\nabcdefghij\\nabcdefghij\\nabcdefghij\\nabcdefghij\\nabcdefghij\\nabcdefghij\\n\";\n    assertEquals(\"diff_main: Simple line-mode.\", dmp.diff_main(a, b, true), dmp.diff_main(a, b, false));\n\n    a = \"1234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890\";\n    b = \"abcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghij\";\n    assertEquals(\"diff_main: Single line-mode.\", dmp.diff_main(a, b, true), dmp.diff_main(a, b, false));\n\n    a = \"1234567890\\n1234567890\\n1234567890\\n1234567890\\n1234567890\\n1234567890\\n1234567890\\n1234567890\\n1234567890\\n1234567890\\n1234567890\\n1234567890\\n1234567890\\n\";\n    b = \"abcdefghij\\n1234567890\\n1234567890\\n1234567890\\nabcdefghij\\n1234567890\\n1234567890\\n1234567890\\nabcdefghij\\n1234567890\\n1234567890\\n1234567890\\nabcdefghij\\n\";\n    String[] texts_linemode = diff_rebuildtexts(dmp.diff_main(a, b, true));\n    String[] texts_textmode = diff_rebuildtexts(dmp.diff_main(a, b, false));\n    assertArrayEquals(\"diff_main: Overlap line-mode.\", texts_textmode, texts_linemode);\n\n    // Test null inputs.\n    try {\n      dmp.diff_main(null, null);\n      fail(\"diff_main: Null inputs.\");\n    } catch (IllegalArgumentException ex) {\n      // Error expected.\n    }\n  }\n\n\n  //  MATCH TEST FUNCTIONS\n\n\n  public static void testMatchAlphabet() {\n    // Initialise the bitmasks for Bitap.\n    Map<Character, Integer> bitmask;\n    bitmask = new HashMap<Character, Integer>();\n    bitmask.put('a', 4); bitmask.put('b', 2); bitmask.put('c', 1);\n    assertEquals(\"match_alphabet: Unique.\", bitmask, dmp.match_alphabet(\"abc\"));\n\n    bitmask = new HashMap<Character, Integer>();\n    bitmask.put('a', 37); bitmask.put('b', 18); bitmask.put('c', 8);\n    assertEquals(\"match_alphabet: Duplicates.\", bitmask, dmp.match_alphabet(\"abcaba\"));\n  }\n\n  public static void testMatchBitap() {\n    // Bitap algorithm.\n    dmp.Match_Distance = 100;\n    dmp.Match_Threshold = 0.5f;\n    assertEquals(\"match_bitap: Exact match #1.\", 5, dmp.match_bitap(\"abcdefghijk\", \"fgh\", 5));\n\n    assertEquals(\"match_bitap: Exact match #2.\", 5, dmp.match_bitap(\"abcdefghijk\", \"fgh\", 0));\n\n    assertEquals(\"match_bitap: Fuzzy match #1.\", 4, dmp.match_bitap(\"abcdefghijk\", \"efxhi\", 0));\n\n    assertEquals(\"match_bitap: Fuzzy match #2.\", 2, dmp.match_bitap(\"abcdefghijk\", \"cdefxyhijk\", 5));\n\n    assertEquals(\"match_bitap: Fuzzy match #3.\", -1, dmp.match_bitap(\"abcdefghijk\", \"bxy\", 1));\n\n    assertEquals(\"match_bitap: Overflow.\", 2, dmp.match_bitap(\"123456789xx0\", \"3456789x0\", 2));\n\n    assertEquals(\"match_bitap: Before start match.\", 0, dmp.match_bitap(\"abcdef\", \"xxabc\", 4));\n\n    assertEquals(\"match_bitap: Beyond end match.\", 3, dmp.match_bitap(\"abcdef\", \"defyy\", 4));\n\n    assertEquals(\"match_bitap: Oversized pattern.\", 0, dmp.match_bitap(\"abcdef\", \"xabcdefy\", 0));\n\n    dmp.Match_Threshold = 0.4f;\n    assertEquals(\"match_bitap: Threshold #1.\", 4, dmp.match_bitap(\"abcdefghijk\", \"efxyhi\", 1));\n\n    dmp.Match_Threshold = 0.3f;\n    assertEquals(\"match_bitap: Threshold #2.\", -1, dmp.match_bitap(\"abcdefghijk\", \"efxyhi\", 1));\n\n    dmp.Match_Threshold = 0.0f;\n    assertEquals(\"match_bitap: Threshold #3.\", 1, dmp.match_bitap(\"abcdefghijk\", \"bcdef\", 1));\n\n    dmp.Match_Threshold = 0.5f;\n    assertEquals(\"match_bitap: Multiple select #1.\", 0, dmp.match_bitap(\"abcdexyzabcde\", \"abccde\", 3));\n\n    assertEquals(\"match_bitap: Multiple select #2.\", 8, dmp.match_bitap(\"abcdexyzabcde\", \"abccde\", 5));\n\n    dmp.Match_Distance = 10;  // Strict location.\n    assertEquals(\"match_bitap: Distance test #1.\", -1, dmp.match_bitap(\"abcdefghijklmnopqrstuvwxyz\", \"abcdefg\", 24));\n\n    assertEquals(\"match_bitap: Distance test #2.\", 0, dmp.match_bitap(\"abcdefghijklmnopqrstuvwxyz\", \"abcdxxefg\", 1));\n\n    dmp.Match_Distance = 1000;  // Loose location.\n    assertEquals(\"match_bitap: Distance test #3.\", 0, dmp.match_bitap(\"abcdefghijklmnopqrstuvwxyz\", \"abcdefg\", 24));\n  }\n\n  public static void testMatchMain() {\n    // Full match.\n    assertEquals(\"match_main: Equality.\", 0, dmp.match_main(\"abcdef\", \"abcdef\", 1000));\n\n    assertEquals(\"match_main: Null text.\", -1, dmp.match_main(\"\", \"abcdef\", 1));\n\n    assertEquals(\"match_main: Null pattern.\", 3, dmp.match_main(\"abcdef\", \"\", 3));\n\n    assertEquals(\"match_main: Exact match.\", 3, dmp.match_main(\"abcdef\", \"de\", 3));\n\n    assertEquals(\"match_main: Beyond end match.\", 3, dmp.match_main(\"abcdef\", \"defy\", 4));\n\n    assertEquals(\"match_main: Oversized pattern.\", 0, dmp.match_main(\"abcdef\", \"abcdefy\", 0));\n\n    dmp.Match_Threshold = 0.7f;\n    assertEquals(\"match_main: Complex match.\", 4, dmp.match_main(\"I am the very model of a modern major general.\", \" that berry \", 5));\n    dmp.Match_Threshold = 0.5f;\n\n    // Test null inputs.\n    try {\n      dmp.match_main(null, null, 0);\n      fail(\"match_main: Null inputs.\");\n    } catch (IllegalArgumentException ex) {\n      // Error expected.\n    }\n  }\n\n\n  //  PATCH TEST FUNCTIONS\n\n\n  public static void testPatchObj() {\n    // Patch Object.\n    Patch p = new Patch();\n    p.start1 = 20;\n    p.start2 = 21;\n    p.length1 = 18;\n    p.length2 = 17;\n    p.diffs = diffList(new Diff(EQUAL, \"jump\"), new Diff(DELETE, \"s\"), new Diff(INSERT, \"ed\"), new Diff(EQUAL, \" over \"), new Diff(DELETE, \"the\"), new Diff(INSERT, \"a\"), new Diff(EQUAL, \"\\nlaz\"));\n    String strp = \"@@ -21,18 +22,17 @@\\n jump\\n-s\\n+ed\\n  over \\n-the\\n+a\\n %0Alaz\\n\";\n    assertEquals(\"Patch: toString.\", strp, p.toString());\n  }\n\n  public static void testPatchFromText() {\n    assertTrue(\"patch_fromText: #0.\", dmp.patch_fromText(\"\").isEmpty());\n\n    String strp = \"@@ -21,18 +22,17 @@\\n jump\\n-s\\n+ed\\n  over \\n-the\\n+a\\n %0Alaz\\n\";\n    assertEquals(\"patch_fromText: #1.\", strp, dmp.patch_fromText(strp).get(0).toString());\n\n    assertEquals(\"patch_fromText: #2.\", \"@@ -1 +1 @@\\n-a\\n+b\\n\", dmp.patch_fromText(\"@@ -1 +1 @@\\n-a\\n+b\\n\").get(0).toString());\n\n    assertEquals(\"patch_fromText: #3.\", \"@@ -1,3 +0,0 @@\\n-abc\\n\", dmp.patch_fromText(\"@@ -1,3 +0,0 @@\\n-abc\\n\").get(0).toString());\n\n    assertEquals(\"patch_fromText: #4.\", \"@@ -0,0 +1,3 @@\\n+abc\\n\", dmp.patch_fromText(\"@@ -0,0 +1,3 @@\\n+abc\\n\").get(0).toString());\n\n    // Generates error.\n    try {\n      dmp.patch_fromText(\"Bad\\nPatch\\n\");\n      fail(\"patch_fromText: #5.\");\n    } catch (IllegalArgumentException ex) {\n      // Exception expected.\n    }\n  }\n\n  public static void testPatchToText() {\n    String strp = \"@@ -21,18 +22,17 @@\\n jump\\n-s\\n+ed\\n  over \\n-the\\n+a\\n  laz\\n\";\n    List<Patch> patches;\n    patches = dmp.patch_fromText(strp);\n    assertEquals(\"patch_toText: Single.\", strp, dmp.patch_toText(patches));\n\n    strp = \"@@ -1,9 +1,9 @@\\n-f\\n+F\\n oo+fooba\\n@@ -7,9 +7,9 @@\\n obar\\n-,\\n+.\\n  tes\\n\";\n    patches = dmp.patch_fromText(strp);\n    assertEquals(\"patch_toText: Dual.\", strp, dmp.patch_toText(patches));\n  }\n\n  public static void testPatchAddContext() {\n    dmp.Patch_Margin = 4;\n    Patch p;\n    p = dmp.patch_fromText(\"@@ -21,4 +21,10 @@\\n-jump\\n+somersault\\n\").get(0);\n    dmp.patch_addContext(p, \"The quick brown fox jumps over the lazy dog.\");\n    assertEquals(\"patch_addContext: Simple case.\", \"@@ -17,12 +17,18 @@\\n fox \\n-jump\\n+somersault\\n s ov\\n\", p.toString());\n\n    p = dmp.patch_fromText(\"@@ -21,4 +21,10 @@\\n-jump\\n+somersault\\n\").get(0);\n    dmp.patch_addContext(p, \"The quick brown fox jumps.\");\n    assertEquals(\"patch_addContext: Not enough trailing context.\", \"@@ -17,10 +17,16 @@\\n fox \\n-jump\\n+somersault\\n s.\\n\", p.toString());\n\n    p = dmp.patch_fromText(\"@@ -3 +3,2 @@\\n-e\\n+at\\n\").get(0);\n    dmp.patch_addContext(p, \"The quick brown fox jumps.\");\n    assertEquals(\"patch_addContext: Not enough leading context.\", \"@@ -1,7 +1,8 @@\\n Th\\n-e\\n+at\\n  qui\\n\", p.toString());\n\n    p = dmp.patch_fromText(\"@@ -3 +3,2 @@\\n-e\\n+at\\n\").get(0);\n    dmp.patch_addContext(p, \"The quick brown fox jumps.  The quick brown fox crashes.\");\n    assertEquals(\"patch_addContext: Ambiguity.\", \"@@ -1,27 +1,28 @@\\n Th\\n-e\\n+at\\n  quick brown fox jumps. \\n\", p.toString());\n  }\n\n  @SuppressWarnings(\"deprecation\")\n  public static void testPatchMake() {\n    LinkedList<Patch> patches;\n    patches = dmp.patch_make(\"\", \"\");\n    assertEquals(\"patch_make: Null case.\", \"\", dmp.patch_toText(patches));\n\n    String text1 = \"The quick brown fox jumps over the lazy dog.\";\n    String text2 = \"That quick brown fox jumped over a lazy dog.\";\n    String expectedPatch = \"@@ -1,8 +1,7 @@\\n Th\\n-at\\n+e\\n  qui\\n@@ -21,17 +21,18 @@\\n jump\\n-ed\\n+s\\n  over \\n-a\\n+the\\n  laz\\n\";\n    // The second patch must be \"-21,17 +21,18\", not \"-22,17 +21,18\" due to rolling context.\n    patches = dmp.patch_make(text2, text1);\n    assertEquals(\"patch_make: Text2+Text1 inputs.\", expectedPatch, dmp.patch_toText(patches));\n\n    expectedPatch = \"@@ -1,11 +1,12 @@\\n Th\\n-e\\n+at\\n  quick b\\n@@ -22,18 +22,17 @@\\n jump\\n-s\\n+ed\\n  over \\n-the\\n+a\\n  laz\\n\";\n    patches = dmp.patch_make(text1, text2);\n    assertEquals(\"patch_make: Text1+Text2 inputs.\", expectedPatch, dmp.patch_toText(patches));\n\n    LinkedList<Diff> diffs = dmp.diff_main(text1, text2, false);\n    patches = dmp.patch_make(diffs);\n    assertEquals(\"patch_make: Diff input.\", expectedPatch, dmp.patch_toText(patches));\n\n    patches = dmp.patch_make(text1, diffs);\n    assertEquals(\"patch_make: Text1+Diff inputs.\", expectedPatch, dmp.patch_toText(patches));\n\n    patches = dmp.patch_make(text1, text2, diffs);\n    assertEquals(\"patch_make: Text1+Text2+Diff inputs (deprecated).\", expectedPatch, dmp.patch_toText(patches));\n\n    patches = dmp.patch_make(\"`1234567890-=[]\\\\;',./\", \"~!@#$%^&*()_+{}|:\\\"<>?\");\n    assertEquals(\"patch_toText: Character encoding.\", \"@@ -1,21 +1,21 @@\\n-%601234567890-=%5B%5D%5C;',./\\n+~!@#$%25%5E&*()_+%7B%7D%7C:%22%3C%3E?\\n\", dmp.patch_toText(patches));\n\n    diffs = diffList(new Diff(DELETE, \"`1234567890-=[]\\\\;',./\"), new Diff(INSERT, \"~!@#$%^&*()_+{}|:\\\"<>?\"));\n    assertEquals(\"patch_fromText: Character decoding.\", diffs, dmp.patch_fromText(\"@@ -1,21 +1,21 @@\\n-%601234567890-=%5B%5D%5C;',./\\n+~!@#$%25%5E&*()_+%7B%7D%7C:%22%3C%3E?\\n\").get(0).diffs);\n\n    text1 = \"\";\n    for (int x = 0; x < 100; x++) {\n      text1 += \"abcdef\";\n    }\n    text2 = text1 + \"123\";\n    expectedPatch = \"@@ -573,28 +573,31 @@\\n cdefabcdefabcdefabcdefabcdef\\n+123\\n\";\n    patches = dmp.patch_make(text1, text2);\n    assertEquals(\"patch_make: Long string with repeats.\", expectedPatch, dmp.patch_toText(patches));\n\n    // Test null inputs.\n    try {\n      dmp.patch_make(null);\n      fail(\"patch_make: Null inputs.\");\n    } catch (IllegalArgumentException ex) {\n      // Error expected.\n    }\n  }\n\n  public static void testPatchSplitMax() {\n    // Assumes that Match_MaxBits is 32.\n    LinkedList<Patch> patches;\n    patches = dmp.patch_make(\"abcdefghijklmnopqrstuvwxyz01234567890\", \"XabXcdXefXghXijXklXmnXopXqrXstXuvXwxXyzX01X23X45X67X89X0\");\n    dmp.patch_splitMax(patches);\n    assertEquals(\"patch_splitMax: #1.\", \"@@ -1,32 +1,46 @@\\n+X\\n ab\\n+X\\n cd\\n+X\\n ef\\n+X\\n gh\\n+X\\n ij\\n+X\\n kl\\n+X\\n mn\\n+X\\n op\\n+X\\n qr\\n+X\\n st\\n+X\\n uv\\n+X\\n wx\\n+X\\n yz\\n+X\\n 012345\\n@@ -25,13 +39,18 @@\\n zX01\\n+X\\n 23\\n+X\\n 45\\n+X\\n 67\\n+X\\n 89\\n+X\\n 0\\n\", dmp.patch_toText(patches));\n\n    patches = dmp.patch_make(\"abcdef1234567890123456789012345678901234567890123456789012345678901234567890uvwxyz\", \"abcdefuvwxyz\");\n    String oldToText = dmp.patch_toText(patches);\n    dmp.patch_splitMax(patches);\n    assertEquals(\"patch_splitMax: #2.\", oldToText, dmp.patch_toText(patches));\n\n    patches = dmp.patch_make(\"1234567890123456789012345678901234567890123456789012345678901234567890\", \"abc\");\n    dmp.patch_splitMax(patches);\n    assertEquals(\"patch_splitMax: #3.\", \"@@ -1,32 +1,4 @@\\n-1234567890123456789012345678\\n 9012\\n@@ -29,32 +1,4 @@\\n-9012345678901234567890123456\\n 7890\\n@@ -57,14 +1,3 @@\\n-78901234567890\\n+abc\\n\", dmp.patch_toText(patches));\n\n    patches = dmp.patch_make(\"abcdefghij , h : 0 , t : 1 abcdefghij , h : 0 , t : 1 abcdefghij , h : 0 , t : 1\", \"abcdefghij , h : 1 , t : 1 abcdefghij , h : 1 , t : 1 abcdefghij , h : 0 , t : 1\");\n    dmp.patch_splitMax(patches);\n    assertEquals(\"patch_splitMax: #4.\", \"@@ -2,32 +2,32 @@\\n bcdefghij , h : \\n-0\\n+1\\n  , t : 1 abcdef\\n@@ -29,32 +29,32 @@\\n bcdefghij , h : \\n-0\\n+1\\n  , t : 1 abcdef\\n\", dmp.patch_toText(patches));\n  }\n\n  public static void testPatchAddPadding() {\n    LinkedList<Patch> patches;\n    patches = dmp.patch_make(\"\", \"test\");\n    assertEquals(\"patch_addPadding: Both edges full.\", \"@@ -0,0 +1,4 @@\\n+test\\n\", dmp.patch_toText(patches));\n    dmp.patch_addPadding(patches);\n    assertEquals(\"patch_addPadding: Both edges full.\", \"@@ -1,8 +1,12 @@\\n %01%02%03%04\\n+test\\n %01%02%03%04\\n\", dmp.patch_toText(patches));\n\n    patches = dmp.patch_make(\"XY\", \"XtestY\");\n    assertEquals(\"patch_addPadding: Both edges partial.\", \"@@ -1,2 +1,6 @@\\n X\\n+test\\n Y\\n\", dmp.patch_toText(patches));\n    dmp.patch_addPadding(patches);\n    assertEquals(\"patch_addPadding: Both edges partial.\", \"@@ -2,8 +2,12 @@\\n %02%03%04X\\n+test\\n Y%01%02%03\\n\", dmp.patch_toText(patches));\n\n    patches = dmp.patch_make(\"XXXXYYYY\", \"XXXXtestYYYY\");\n    assertEquals(\"patch_addPadding: Both edges none.\", \"@@ -1,8 +1,12 @@\\n XXXX\\n+test\\n YYYY\\n\", dmp.patch_toText(patches));\n    dmp.patch_addPadding(patches);\n    assertEquals(\"patch_addPadding: Both edges none.\", \"@@ -5,8 +5,12 @@\\n XXXX\\n+test\\n YYYY\\n\", dmp.patch_toText(patches));\n  }\n\n  public static void testPatchApply() {\n    dmp.Match_Distance = 1000;\n    dmp.Match_Threshold = 0.5f;\n    dmp.Patch_DeleteThreshold = 0.5f;\n    LinkedList<Patch> patches;\n    patches = dmp.patch_make(\"\", \"\");\n    Object[] results = dmp.patch_apply(patches, \"Hello world.\");\n    boolean[] boolArray = (boolean[]) results[1];\n    String resultStr = results[0] + \"\\t\" + boolArray.length;\n    assertEquals(\"patch_apply: Null case.\", \"Hello world.\\t0\", resultStr);\n\n    patches = dmp.patch_make(\"The quick brown fox jumps over the lazy dog.\", \"That quick brown fox jumped over a lazy dog.\");\n    results = dmp.patch_apply(patches, \"The quick brown fox jumps over the lazy dog.\");\n    boolArray = (boolean[]) results[1];\n    resultStr = results[0] + \"\\t\" + boolArray[0] + \"\\t\" + boolArray[1];\n    assertEquals(\"patch_apply: Exact match.\", \"That quick brown fox jumped over a lazy dog.\\ttrue\\ttrue\", resultStr);\n\n    results = dmp.patch_apply(patches, \"The quick red rabbit jumps over the tired tiger.\");\n    boolArray = (boolean[]) results[1];\n    resultStr = results[0] + \"\\t\" + boolArray[0] + \"\\t\" + boolArray[1];\n    assertEquals(\"patch_apply: Partial match.\", \"That quick red rabbit jumped over a tired tiger.\\ttrue\\ttrue\", resultStr);\n\n    results = dmp.patch_apply(patches, \"I am the very model of a modern major general.\");\n    boolArray = (boolean[]) results[1];\n    resultStr = results[0] + \"\\t\" + boolArray[0] + \"\\t\" + boolArray[1];\n    assertEquals(\"patch_apply: Failed match.\", \"I am the very model of a modern major general.\\tfalse\\tfalse\", resultStr);\n\n    patches = dmp.patch_make(\"x1234567890123456789012345678901234567890123456789012345678901234567890y\", \"xabcy\");\n    results = dmp.patch_apply(patches, \"x123456789012345678901234567890-----++++++++++-----123456789012345678901234567890y\");\n    boolArray = (boolean[]) results[1];\n    resultStr = results[0] + \"\\t\" + boolArray[0] + \"\\t\" + boolArray[1];\n    assertEquals(\"patch_apply: Big delete, small change.\", \"xabcy\\ttrue\\ttrue\", resultStr);\n\n    patches = dmp.patch_make(\"x1234567890123456789012345678901234567890123456789012345678901234567890y\", \"xabcy\");\n    results = dmp.patch_apply(patches, \"x12345678901234567890---------------++++++++++---------------12345678901234567890y\");\n    boolArray = (boolean[]) results[1];\n    resultStr = results[0] + \"\\t\" + boolArray[0] + \"\\t\" + boolArray[1];\n    assertEquals(\"patch_apply: Big delete, big change 1.\", \"xabc12345678901234567890---------------++++++++++---------------12345678901234567890y\\tfalse\\ttrue\", resultStr);\n\n    dmp.Patch_DeleteThreshold = 0.6f;\n    patches = dmp.patch_make(\"x1234567890123456789012345678901234567890123456789012345678901234567890y\", \"xabcy\");\n    results = dmp.patch_apply(patches, \"x12345678901234567890---------------++++++++++---------------12345678901234567890y\");\n    boolArray = (boolean[]) results[1];\n    resultStr = results[0] + \"\\t\" + boolArray[0] + \"\\t\" + boolArray[1];\n    assertEquals(\"patch_apply: Big delete, big change 2.\", \"xabcy\\ttrue\\ttrue\", resultStr);\n    dmp.Patch_DeleteThreshold = 0.5f;\n\n    // Compensate for failed patch.\n    dmp.Match_Threshold = 0.0f;\n    dmp.Match_Distance = 0;\n    patches = dmp.patch_make(\"abcdefghijklmnopqrstuvwxyz--------------------1234567890\", \"abcXXXXXXXXXXdefghijklmnopqrstuvwxyz--------------------1234567YYYYYYYYYY890\");\n    results = dmp.patch_apply(patches, \"ABCDEFGHIJKLMNOPQRSTUVWXYZ--------------------1234567890\");\n    boolArray = (boolean[]) results[1];\n    resultStr = results[0] + \"\\t\" + boolArray[0] + \"\\t\" + boolArray[1];\n    assertEquals(\"patch_apply: Compensate for failed patch.\", \"ABCDEFGHIJKLMNOPQRSTUVWXYZ--------------------1234567YYYYYYYYYY890\\tfalse\\ttrue\", resultStr);\n    dmp.Match_Threshold = 0.5f;\n    dmp.Match_Distance = 1000;\n\n    patches = dmp.patch_make(\"\", \"test\");\n    String patchStr = dmp.patch_toText(patches);\n    dmp.patch_apply(patches, \"\");\n    assertEquals(\"patch_apply: No side effects.\", patchStr, dmp.patch_toText(patches));\n\n    patches = dmp.patch_make(\"The quick brown fox jumps over the lazy dog.\", \"Woof\");\n    patchStr = dmp.patch_toText(patches);\n    dmp.patch_apply(patches, \"The quick brown fox jumps over the lazy dog.\");\n    assertEquals(\"patch_apply: No side effects with major delete.\", patchStr, dmp.patch_toText(patches));\n\n    patches = dmp.patch_make(\"\", \"test\");\n    results = dmp.patch_apply(patches, \"\");\n    boolArray = (boolean[]) results[1];\n    resultStr = results[0] + \"\\t\" + boolArray[0];\n    assertEquals(\"patch_apply: Edge exact match.\", \"test\\ttrue\", resultStr);\n\n    patches = dmp.patch_make(\"XY\", \"XtestY\");\n    results = dmp.patch_apply(patches, \"XY\");\n    boolArray = (boolean[]) results[1];\n    resultStr = results[0] + \"\\t\" + boolArray[0];\n    assertEquals(\"patch_apply: Near edge exact match.\", \"XtestY\\ttrue\", resultStr);\n\n    patches = dmp.patch_make(\"y\", \"y123\");\n    results = dmp.patch_apply(patches, \"x\");\n    boolArray = (boolean[]) results[1];\n    resultStr = results[0] + \"\\t\" + boolArray[0];\n    assertEquals(\"patch_apply: Edge partial match.\", \"x123\\ttrue\", resultStr);\n  }\n\n  private static void assertEquals(String error_msg, Object a, Object b) {\n    if (!a.toString().equals(b.toString())) {\n      throw new Error(\"assertEquals fail:\\n Expected: \" + a + \"\\n Actual: \" + b\n                      + \"\\n\" + error_msg);\n    }\n  }\n\n  private static void assertTrue(String error_msg, boolean a) {\n    if (!a) {\n      throw new Error(\"assertTrue fail: \" + error_msg);\n    }\n  }\n\n  private static void assertNull(String error_msg, Object n) {\n    if (n != null) {\n      throw new Error(\"assertNull fail: \" + error_msg);\n    }\n  }\n\n  private static void fail(String error_msg) {\n    throw new Error(\"Fail: \" + error_msg);\n  }\n\n  private static void assertArrayEquals(String error_msg, Object[] a, Object[] b) {\n    List<Object> list_a = Arrays.asList(a);\n    List<Object> list_b = Arrays.asList(b);\n    assertEquals(error_msg, list_a, list_b);\n  }\n\n  private static void assertLinesToCharsResultEquals(String error_msg,\n      LinesToCharsResult a, LinesToCharsResult b) {\n    assertEquals(error_msg, a.chars1, b.chars1);\n    assertEquals(error_msg, a.chars2, b.chars2);\n    assertEquals(error_msg, a.lineArray, b.lineArray);\n  }\n\n  // Construct the two texts which made up the diff originally.\n  private static String[] diff_rebuildtexts(LinkedList<Diff> diffs) {\n    String[] text = {\"\", \"\"};\n    for (Diff myDiff : diffs) {\n      if (myDiff.operation != diff_match_patch.Operation.INSERT) {\n        text[0] += myDiff.text;\n      }\n      if (myDiff.operation != diff_match_patch.Operation.DELETE) {\n        text[1] += myDiff.text;\n      }\n    }\n    return text;\n  }\n\n  // Private function for quickly building lists of diffs.\n  private static LinkedList<Diff> diffList(Diff... diffs) {\n      return new LinkedList<Diff>(Arrays.asList(diffs));\n  }\n\n  public static void main(String args[]) {\n    dmp = new diff_match_patch();\n\n    testDiffCommonPrefix();\n    testDiffCommonSuffix();\n    testDiffCommonOverlap();\n    testDiffHalfmatch();\n    testDiffLinesToChars();\n    testDiffCharsToLines();\n    testDiffCleanupMerge();\n    testDiffCleanupSemanticLossless();\n    testDiffCleanupSemantic();\n    testDiffCleanupEfficiency();\n    testDiffPrettyHtml();\n    testDiffText();\n    testDiffDelta();\n    testDiffXIndex();\n    testDiffLevenshtein();\n    testDiffBisect();\n    testDiffMain();\n\n    testMatchAlphabet();\n    testMatchBitap();\n    testMatchMain();\n\n    testPatchObj();\n    testPatchFromText();\n    testPatchToText();\n    testPatchAddContext();\n    testPatchMake();\n    testPatchSplitMax();\n    testPatchAddPadding();\n    testPatchApply();\n\n    System.out.println(\"All tests passed.\");\n  }\n}\n"
  },
  {
    "path": "javascript/diff_match_patch.js",
    "content": "var diff_match_patch=function(){this.Diff_Timeout=1;this.Diff_EditCost=4;this.Match_Threshold=.5;this.Match_Distance=1E3;this.Patch_DeleteThreshold=.5;this.Patch_Margin=4;this.Match_MaxBits=32},DIFF_DELETE=-1,DIFF_INSERT=1,DIFF_EQUAL=0;diff_match_patch.Diff=function(a,b){this[0]=a;this[1]=b};diff_match_patch.Diff.prototype.length=2;diff_match_patch.Diff.prototype.toString=function(){return this[0]+\",\"+this[1]};\ndiff_match_patch.prototype.diff_main=function(a,b,c,d){\"undefined\"==typeof d&&(d=0>=this.Diff_Timeout?Number.MAX_VALUE:(new Date).getTime()+1E3*this.Diff_Timeout);if(null==a||null==b)throw Error(\"Null input. (diff_main)\");if(a==b)return a?[new diff_match_patch.Diff(DIFF_EQUAL,a)]:[];\"undefined\"==typeof c&&(c=!0);var e=c,f=this.diff_commonPrefix(a,b);c=a.substring(0,f);a=a.substring(f);b=b.substring(f);f=this.diff_commonSuffix(a,b);var g=a.substring(a.length-f);a=a.substring(0,a.length-f);b=b.substring(0,\nb.length-f);a=this.diff_compute_(a,b,e,d);c&&a.unshift(new diff_match_patch.Diff(DIFF_EQUAL,c));g&&a.push(new diff_match_patch.Diff(DIFF_EQUAL,g));this.diff_cleanupMerge(a);return a};\ndiff_match_patch.prototype.diff_compute_=function(a,b,c,d){if(!a)return[new diff_match_patch.Diff(DIFF_INSERT,b)];if(!b)return[new diff_match_patch.Diff(DIFF_DELETE,a)];var e=a.length>b.length?a:b,f=a.length>b.length?b:a,g=e.indexOf(f);return-1!=g?(c=[new diff_match_patch.Diff(DIFF_INSERT,e.substring(0,g)),new diff_match_patch.Diff(DIFF_EQUAL,f),new diff_match_patch.Diff(DIFF_INSERT,e.substring(g+f.length))],a.length>b.length&&(c[0][0]=c[2][0]=DIFF_DELETE),c):1==f.length?[new diff_match_patch.Diff(DIFF_DELETE,\na),new diff_match_patch.Diff(DIFF_INSERT,b)]:(e=this.diff_halfMatch_(a,b))?(b=e[1],f=e[3],a=e[4],e=this.diff_main(e[0],e[2],c,d),c=this.diff_main(b,f,c,d),e.concat([new diff_match_patch.Diff(DIFF_EQUAL,a)],c)):c&&100<a.length&&100<b.length?this.diff_lineMode_(a,b,d):this.diff_bisect_(a,b,d)};\ndiff_match_patch.prototype.diff_lineMode_=function(a,b,c){var d=this.diff_linesToChars_(a,b);a=d.chars1;b=d.chars2;d=d.lineArray;a=this.diff_main(a,b,!1,c);this.diff_charsToLines_(a,d);this.diff_cleanupSemantic(a);a.push(new diff_match_patch.Diff(DIFF_EQUAL,\"\"));for(var e=d=b=0,f=\"\",g=\"\";b<a.length;){switch(a[b][0]){case DIFF_INSERT:e++;g+=a[b][1];break;case DIFF_DELETE:d++;f+=a[b][1];break;case DIFF_EQUAL:if(1<=d&&1<=e){a.splice(b-d-e,d+e);b=b-d-e;d=this.diff_main(f,g,!1,c);for(e=d.length-1;0<=e;e--)a.splice(b,\n0,d[e]);b+=d.length}d=e=0;g=f=\"\"}b++}a.pop();return a};\ndiff_match_patch.prototype.diff_bisect_=function(a,b,c){for(var d=a.length,e=b.length,f=Math.ceil((d+e)/2),g=2*f,h=Array(g),l=Array(g),k=0;k<g;k++)h[k]=-1,l[k]=-1;h[f+1]=0;l[f+1]=0;k=d-e;for(var m=0!=k%2,p=0,x=0,w=0,q=0,t=0;t<f&&!((new Date).getTime()>c);t++){for(var v=-t+p;v<=t-x;v+=2){var n=f+v;var r=v==-t||v!=t&&h[n-1]<h[n+1]?h[n+1]:h[n-1]+1;for(var y=r-v;r<d&&y<e&&a.charAt(r)==b.charAt(y);)r++,y++;h[n]=r;if(r>d)x+=2;else if(y>e)p+=2;else if(m&&(n=f+k-v,0<=n&&n<g&&-1!=l[n])){var u=d-l[n];if(r>=\nu)return this.diff_bisectSplit_(a,b,r,y,c)}}for(v=-t+w;v<=t-q;v+=2){n=f+v;u=v==-t||v!=t&&l[n-1]<l[n+1]?l[n+1]:l[n-1]+1;for(r=u-v;u<d&&r<e&&a.charAt(d-u-1)==b.charAt(e-r-1);)u++,r++;l[n]=u;if(u>d)q+=2;else if(r>e)w+=2;else if(!m&&(n=f+k-v,0<=n&&n<g&&-1!=h[n]&&(r=h[n],y=f+r-n,u=d-u,r>=u)))return this.diff_bisectSplit_(a,b,r,y,c)}}return[new diff_match_patch.Diff(DIFF_DELETE,a),new diff_match_patch.Diff(DIFF_INSERT,b)]};\ndiff_match_patch.prototype.diff_bisectSplit_=function(a,b,c,d,e){var f=a.substring(0,c),g=b.substring(0,d);a=a.substring(c);b=b.substring(d);f=this.diff_main(f,g,!1,e);e=this.diff_main(a,b,!1,e);return f.concat(e)};\ndiff_match_patch.prototype.diff_linesToChars_=function(a,b){function c(a){for(var b=\"\",c=0,g=-1,h=d.length;g<a.length-1;){g=a.indexOf(\"\\n\",c);-1==g&&(g=a.length-1);var l=a.substring(c,g+1);(e.hasOwnProperty?e.hasOwnProperty(l):void 0!==e[l])?b+=String.fromCharCode(e[l]):(h==f&&(l=a.substring(c),g=a.length),b+=String.fromCharCode(h),e[l]=h,d[h++]=l);c=g+1}return b}var d=[],e={};d[0]=\"\";var f=4E4,g=c(a);f=65535;var h=c(b);return{chars1:g,chars2:h,lineArray:d}};\ndiff_match_patch.prototype.diff_charsToLines_=function(a,b){for(var c=0;c<a.length;c++){for(var d=a[c][1],e=[],f=0;f<d.length;f++)e[f]=b[d.charCodeAt(f)];a[c][1]=e.join(\"\")}};diff_match_patch.prototype.diff_commonPrefix=function(a,b){if(!a||!b||a.charAt(0)!=b.charAt(0))return 0;for(var c=0,d=Math.min(a.length,b.length),e=d,f=0;c<e;)a.substring(f,e)==b.substring(f,e)?f=c=e:d=e,e=Math.floor((d-c)/2+c);return e};\ndiff_match_patch.prototype.diff_commonSuffix=function(a,b){if(!a||!b||a.charAt(a.length-1)!=b.charAt(b.length-1))return 0;for(var c=0,d=Math.min(a.length,b.length),e=d,f=0;c<e;)a.substring(a.length-e,a.length-f)==b.substring(b.length-e,b.length-f)?f=c=e:d=e,e=Math.floor((d-c)/2+c);return e};\ndiff_match_patch.prototype.diff_commonOverlap_=function(a,b){var c=a.length,d=b.length;if(0==c||0==d)return 0;c>d?a=a.substring(c-d):c<d&&(b=b.substring(0,c));c=Math.min(c,d);if(a==b)return c;d=0;for(var e=1;;){var f=a.substring(c-e);f=b.indexOf(f);if(-1==f)return d;e+=f;if(0==f||a.substring(c-e)==b.substring(0,e))d=e,e++}};\ndiff_match_patch.prototype.diff_halfMatch_=function(a,b){function c(a,b,c){for(var d=a.substring(c,c+Math.floor(a.length/4)),e=-1,g=\"\",h,k,l,m;-1!=(e=b.indexOf(d,e+1));){var p=f.diff_commonPrefix(a.substring(c),b.substring(e)),u=f.diff_commonSuffix(a.substring(0,c),b.substring(0,e));g.length<u+p&&(g=b.substring(e-u,e)+b.substring(e,e+p),h=a.substring(0,c-u),k=a.substring(c+p),l=b.substring(0,e-u),m=b.substring(e+p))}return 2*g.length>=a.length?[h,k,l,m,g]:null}if(0>=this.Diff_Timeout)return null;\nvar d=a.length>b.length?a:b,e=a.length>b.length?b:a;if(4>d.length||2*e.length<d.length)return null;var f=this,g=c(d,e,Math.ceil(d.length/4));d=c(d,e,Math.ceil(d.length/2));if(g||d)g=d?g?g[4].length>d[4].length?g:d:d:g;else return null;if(a.length>b.length){d=g[0];e=g[1];var h=g[2];var l=g[3]}else h=g[0],l=g[1],d=g[2],e=g[3];return[d,e,h,l,g[4]]};\ndiff_match_patch.prototype.diff_cleanupSemantic=function(a){for(var b=!1,c=[],d=0,e=null,f=0,g=0,h=0,l=0,k=0;f<a.length;)a[f][0]==DIFF_EQUAL?(c[d++]=f,g=l,h=k,k=l=0,e=a[f][1]):(a[f][0]==DIFF_INSERT?l+=a[f][1].length:k+=a[f][1].length,e&&e.length<=Math.max(g,h)&&e.length<=Math.max(l,k)&&(a.splice(c[d-1],0,new diff_match_patch.Diff(DIFF_DELETE,e)),a[c[d-1]+1][0]=DIFF_INSERT,d--,d--,f=0<d?c[d-1]:-1,k=l=h=g=0,e=null,b=!0)),f++;b&&this.diff_cleanupMerge(a);this.diff_cleanupSemanticLossless(a);for(f=1;f<\na.length;){if(a[f-1][0]==DIFF_DELETE&&a[f][0]==DIFF_INSERT){b=a[f-1][1];c=a[f][1];d=this.diff_commonOverlap_(b,c);e=this.diff_commonOverlap_(c,b);if(d>=e){if(d>=b.length/2||d>=c.length/2)a.splice(f,0,new diff_match_patch.Diff(DIFF_EQUAL,c.substring(0,d))),a[f-1][1]=b.substring(0,b.length-d),a[f+1][1]=c.substring(d),f++}else if(e>=b.length/2||e>=c.length/2)a.splice(f,0,new diff_match_patch.Diff(DIFF_EQUAL,b.substring(0,e))),a[f-1][0]=DIFF_INSERT,a[f-1][1]=c.substring(0,c.length-e),a[f+1][0]=DIFF_DELETE,\na[f+1][1]=b.substring(e),f++;f++}f++}};\ndiff_match_patch.prototype.diff_cleanupSemanticLossless=function(a){function b(a,b){if(!a||!b)return 6;var c=a.charAt(a.length-1),d=b.charAt(0),e=c.match(diff_match_patch.nonAlphaNumericRegex_),f=d.match(diff_match_patch.nonAlphaNumericRegex_),g=e&&c.match(diff_match_patch.whitespaceRegex_),h=f&&d.match(diff_match_patch.whitespaceRegex_);c=g&&c.match(diff_match_patch.linebreakRegex_);d=h&&d.match(diff_match_patch.linebreakRegex_);var k=c&&a.match(diff_match_patch.blanklineEndRegex_),l=d&&b.match(diff_match_patch.blanklineStartRegex_);\nreturn k||l?5:c||d?4:e&&!g&&h?3:g||h?2:e||f?1:0}for(var c=1;c<a.length-1;){if(a[c-1][0]==DIFF_EQUAL&&a[c+1][0]==DIFF_EQUAL){var d=a[c-1][1],e=a[c][1],f=a[c+1][1],g=this.diff_commonSuffix(d,e);if(g){var h=e.substring(e.length-g);d=d.substring(0,d.length-g);e=h+e.substring(0,e.length-g);f=h+f}g=d;h=e;for(var l=f,k=b(d,e)+b(e,f);e.charAt(0)===f.charAt(0);){d+=e.charAt(0);e=e.substring(1)+f.charAt(0);f=f.substring(1);var m=b(d,e)+b(e,f);m>=k&&(k=m,g=d,h=e,l=f)}a[c-1][1]!=g&&(g?a[c-1][1]=g:(a.splice(c-\n1,1),c--),a[c][1]=h,l?a[c+1][1]=l:(a.splice(c+1,1),c--))}c++}};diff_match_patch.nonAlphaNumericRegex_=/[^a-zA-Z0-9]/;diff_match_patch.whitespaceRegex_=/\\s/;diff_match_patch.linebreakRegex_=/[\\r\\n]/;diff_match_patch.blanklineEndRegex_=/\\n\\r?\\n$/;diff_match_patch.blanklineStartRegex_=/^\\r?\\n\\r?\\n/;\ndiff_match_patch.prototype.diff_cleanupEfficiency=function(a){for(var b=!1,c=[],d=0,e=null,f=0,g=!1,h=!1,l=!1,k=!1;f<a.length;)a[f][0]==DIFF_EQUAL?(a[f][1].length<this.Diff_EditCost&&(l||k)?(c[d++]=f,g=l,h=k,e=a[f][1]):(d=0,e=null),l=k=!1):(a[f][0]==DIFF_DELETE?k=!0:l=!0,e&&(g&&h&&l&&k||e.length<this.Diff_EditCost/2&&3==g+h+l+k)&&(a.splice(c[d-1],0,new diff_match_patch.Diff(DIFF_DELETE,e)),a[c[d-1]+1][0]=DIFF_INSERT,d--,e=null,g&&h?(l=k=!0,d=0):(d--,f=0<d?c[d-1]:-1,l=k=!1),b=!0)),f++;b&&this.diff_cleanupMerge(a)};\ndiff_match_patch.prototype.diff_cleanupMerge=function(a){a.push(new diff_match_patch.Diff(DIFF_EQUAL,\"\"));for(var b=0,c=0,d=0,e=\"\",f=\"\",g;b<a.length;)switch(a[b][0]){case DIFF_INSERT:d++;f+=a[b][1];b++;break;case DIFF_DELETE:c++;e+=a[b][1];b++;break;case DIFF_EQUAL:1<c+d?(0!==c&&0!==d&&(g=this.diff_commonPrefix(f,e),0!==g&&(0<b-c-d&&a[b-c-d-1][0]==DIFF_EQUAL?a[b-c-d-1][1]+=f.substring(0,g):(a.splice(0,0,new diff_match_patch.Diff(DIFF_EQUAL,f.substring(0,g))),b++),f=f.substring(g),e=e.substring(g)),\ng=this.diff_commonSuffix(f,e),0!==g&&(a[b][1]=f.substring(f.length-g)+a[b][1],f=f.substring(0,f.length-g),e=e.substring(0,e.length-g))),b-=c+d,a.splice(b,c+d),e.length&&(a.splice(b,0,new diff_match_patch.Diff(DIFF_DELETE,e)),b++),f.length&&(a.splice(b,0,new diff_match_patch.Diff(DIFF_INSERT,f)),b++),b++):0!==b&&a[b-1][0]==DIFF_EQUAL?(a[b-1][1]+=a[b][1],a.splice(b,1)):b++,c=d=0,f=e=\"\"}\"\"===a[a.length-1][1]&&a.pop();c=!1;for(b=1;b<a.length-1;)a[b-1][0]==DIFF_EQUAL&&a[b+1][0]==DIFF_EQUAL&&(a[b][1].substring(a[b][1].length-\na[b-1][1].length)==a[b-1][1]?(a[b][1]=a[b-1][1]+a[b][1].substring(0,a[b][1].length-a[b-1][1].length),a[b+1][1]=a[b-1][1]+a[b+1][1],a.splice(b-1,1),c=!0):a[b][1].substring(0,a[b+1][1].length)==a[b+1][1]&&(a[b-1][1]+=a[b+1][1],a[b][1]=a[b][1].substring(a[b+1][1].length)+a[b+1][1],a.splice(b+1,1),c=!0)),b++;c&&this.diff_cleanupMerge(a)};\ndiff_match_patch.prototype.diff_xIndex=function(a,b){var c=0,d=0,e=0,f=0,g;for(g=0;g<a.length;g++){a[g][0]!==DIFF_INSERT&&(c+=a[g][1].length);a[g][0]!==DIFF_DELETE&&(d+=a[g][1].length);if(c>b)break;e=c;f=d}return a.length!=g&&a[g][0]===DIFF_DELETE?f:f+(b-e)};\ndiff_match_patch.prototype.diff_prettyHtml=function(a){for(var b=[],c=/&/g,d=/</g,e=/>/g,f=/\\n/g,g=0;g<a.length;g++){var h=a[g][0],l=a[g][1].replace(c,\"&amp;\").replace(d,\"&lt;\").replace(e,\"&gt;\").replace(f,\"&para;<br>\");switch(h){case DIFF_INSERT:b[g]='<ins style=\"background:#e6ffe6;\">'+l+\"</ins>\";break;case DIFF_DELETE:b[g]='<del style=\"background:#ffe6e6;\">'+l+\"</del>\";break;case DIFF_EQUAL:b[g]=\"<span>\"+l+\"</span>\"}}return b.join(\"\")};\ndiff_match_patch.prototype.diff_text1=function(a){for(var b=[],c=0;c<a.length;c++)a[c][0]!==DIFF_INSERT&&(b[c]=a[c][1]);return b.join(\"\")};diff_match_patch.prototype.diff_text2=function(a){for(var b=[],c=0;c<a.length;c++)a[c][0]!==DIFF_DELETE&&(b[c]=a[c][1]);return b.join(\"\")};\ndiff_match_patch.prototype.diff_levenshtein=function(a){for(var b=0,c=0,d=0,e=0;e<a.length;e++){var f=a[e][1];switch(a[e][0]){case DIFF_INSERT:c+=f.length;break;case DIFF_DELETE:d+=f.length;break;case DIFF_EQUAL:b+=Math.max(c,d),d=c=0}}return b+=Math.max(c,d)};\ndiff_match_patch.prototype.diff_toDelta=function(a){for(var b=[],c=0;c<a.length;c++)switch(a[c][0]){case DIFF_INSERT:b[c]=\"+\"+encodeURI(a[c][1]);break;case DIFF_DELETE:b[c]=\"-\"+a[c][1].length;break;case DIFF_EQUAL:b[c]=\"=\"+a[c][1].length}return b.join(\"\\t\").replace(/%20/g,\" \")};\ndiff_match_patch.prototype.diff_fromDelta=function(a,b){for(var c=[],d=0,e=0,f=b.split(/\\t/g),g=0;g<f.length;g++){var h=f[g].substring(1);switch(f[g].charAt(0)){case \"+\":try{c[d++]=new diff_match_patch.Diff(DIFF_INSERT,decodeURI(h))}catch(k){throw Error(\"Illegal escape in diff_fromDelta: \"+h);}break;case \"-\":case \"=\":var l=parseInt(h,10);if(isNaN(l)||0>l)throw Error(\"Invalid number in diff_fromDelta: \"+h);h=a.substring(e,e+=l);\"=\"==f[g].charAt(0)?c[d++]=new diff_match_patch.Diff(DIFF_EQUAL,h):c[d++]=\nnew diff_match_patch.Diff(DIFF_DELETE,h);break;default:if(f[g])throw Error(\"Invalid diff operation in diff_fromDelta: \"+f[g]);}}if(e!=a.length)throw Error(\"Delta length (\"+e+\") does not equal source text length (\"+a.length+\").\");return c};diff_match_patch.prototype.match_main=function(a,b,c){if(null==a||null==b||null==c)throw Error(\"Null input. (match_main)\");c=Math.max(0,Math.min(c,a.length));return a==b?0:a.length?a.substring(c,c+b.length)==b?c:this.match_bitap_(a,b,c):-1};\ndiff_match_patch.prototype.match_bitap_=function(a,b,c){function d(a,d){var e=a/b.length,g=Math.abs(c-d);return f.Match_Distance?e+g/f.Match_Distance:g?1:e}if(b.length>this.Match_MaxBits)throw Error(\"Pattern too long for this browser.\");var e=this.match_alphabet_(b),f=this,g=this.Match_Threshold,h=a.indexOf(b,c);-1!=h&&(g=Math.min(d(0,h),g),h=a.lastIndexOf(b,c+b.length),-1!=h&&(g=Math.min(d(0,h),g)));var l=1<<b.length-1;h=-1;for(var k,m,p=b.length+a.length,x,w=0;w<b.length;w++){k=0;for(m=p;k<m;)d(w,\nc+m)<=g?k=m:p=m,m=Math.floor((p-k)/2+k);p=m;k=Math.max(1,c-m+1);var q=Math.min(c+m,a.length)+b.length;m=Array(q+2);for(m[q+1]=(1<<w)-1;q>=k;q--){var t=e[a.charAt(q-1)];m[q]=0===w?(m[q+1]<<1|1)&t:(m[q+1]<<1|1)&t|(x[q+1]|x[q])<<1|1|x[q+1];if(m[q]&l&&(t=d(w,q-1),t<=g))if(g=t,h=q-1,h>c)k=Math.max(1,2*c-h);else break}if(d(w+1,c)>g)break;x=m}return h};\ndiff_match_patch.prototype.match_alphabet_=function(a){for(var b={},c=0;c<a.length;c++)b[a.charAt(c)]=0;for(c=0;c<a.length;c++)b[a.charAt(c)]|=1<<a.length-c-1;return b};\ndiff_match_patch.prototype.patch_addContext_=function(a,b){if(0!=b.length){if(null===a.start2)throw Error(\"patch not initialized\");for(var c=b.substring(a.start2,a.start2+a.length1),d=0;b.indexOf(c)!=b.lastIndexOf(c)&&c.length<this.Match_MaxBits-this.Patch_Margin-this.Patch_Margin;)d+=this.Patch_Margin,c=b.substring(a.start2-d,a.start2+a.length1+d);d+=this.Patch_Margin;(c=b.substring(a.start2-d,a.start2))&&a.diffs.unshift(new diff_match_patch.Diff(DIFF_EQUAL,c));(d=b.substring(a.start2+a.length1,\na.start2+a.length1+d))&&a.diffs.push(new diff_match_patch.Diff(DIFF_EQUAL,d));a.start1-=c.length;a.start2-=c.length;a.length1+=c.length+d.length;a.length2+=c.length+d.length}};\ndiff_match_patch.prototype.patch_make=function(a,b,c){if(\"string\"==typeof a&&\"string\"==typeof b&&\"undefined\"==typeof c){var d=a;b=this.diff_main(d,b,!0);2<b.length&&(this.diff_cleanupSemantic(b),this.diff_cleanupEfficiency(b))}else if(a&&\"object\"==typeof a&&\"undefined\"==typeof b&&\"undefined\"==typeof c)b=a,d=this.diff_text1(b);else if(\"string\"==typeof a&&b&&\"object\"==typeof b&&\"undefined\"==typeof c)d=a;else if(\"string\"==typeof a&&\"string\"==typeof b&&c&&\"object\"==typeof c)d=a,b=c;else throw Error(\"Unknown call format to patch_make.\");\nif(0===b.length)return[];c=[];a=new diff_match_patch.patch_obj;for(var e=0,f=0,g=0,h=d,l=0;l<b.length;l++){var k=b[l][0],m=b[l][1];e||k===DIFF_EQUAL||(a.start1=f,a.start2=g);switch(k){case DIFF_INSERT:a.diffs[e++]=b[l];a.length2+=m.length;d=d.substring(0,g)+m+d.substring(g);break;case DIFF_DELETE:a.length1+=m.length;a.diffs[e++]=b[l];d=d.substring(0,g)+d.substring(g+m.length);break;case DIFF_EQUAL:m.length<=2*this.Patch_Margin&&e&&b.length!=l+1?(a.diffs[e++]=b[l],a.length1+=m.length,a.length2+=m.length):\nm.length>=2*this.Patch_Margin&&e&&(this.patch_addContext_(a,h),c.push(a),a=new diff_match_patch.patch_obj,e=0,h=d,f=g)}k!==DIFF_INSERT&&(f+=m.length);k!==DIFF_DELETE&&(g+=m.length)}e&&(this.patch_addContext_(a,h),c.push(a));return c};\ndiff_match_patch.prototype.patch_deepCopy=function(a){for(var b=[],c=0;c<a.length;c++){var d=a[c],e=new diff_match_patch.patch_obj;e.diffs=[];for(var f=0;f<d.diffs.length;f++)e.diffs[f]=new diff_match_patch.Diff(d.diffs[f][0],d.diffs[f][1]);e.start1=d.start1;e.start2=d.start2;e.length1=d.length1;e.length2=d.length2;b[c]=e}return b};\ndiff_match_patch.prototype.patch_apply=function(a,b){if(0==a.length)return[b,[]];a=this.patch_deepCopy(a);var c=this.patch_addPadding(a);b=c+b+c;this.patch_splitMax(a);for(var d=0,e=[],f=0;f<a.length;f++){var g=a[f].start2+d,h=this.diff_text1(a[f].diffs),l=-1;if(h.length>this.Match_MaxBits){var k=this.match_main(b,h.substring(0,this.Match_MaxBits),g);-1!=k&&(l=this.match_main(b,h.substring(h.length-this.Match_MaxBits),g+h.length-this.Match_MaxBits),-1==l||k>=l)&&(k=-1)}else k=this.match_main(b,h,\ng);if(-1==k)e[f]=!1,d-=a[f].length2-a[f].length1;else if(e[f]=!0,d=k-g,g=-1==l?b.substring(k,k+h.length):b.substring(k,l+this.Match_MaxBits),h==g)b=b.substring(0,k)+this.diff_text2(a[f].diffs)+b.substring(k+h.length);else if(g=this.diff_main(h,g,!1),h.length>this.Match_MaxBits&&this.diff_levenshtein(g)/h.length>this.Patch_DeleteThreshold)e[f]=!1;else{this.diff_cleanupSemanticLossless(g);h=0;var m;for(l=0;l<a[f].diffs.length;l++){var p=a[f].diffs[l];p[0]!==DIFF_EQUAL&&(m=this.diff_xIndex(g,h));p[0]===\nDIFF_INSERT?b=b.substring(0,k+m)+p[1]+b.substring(k+m):p[0]===DIFF_DELETE&&(b=b.substring(0,k+m)+b.substring(k+this.diff_xIndex(g,h+p[1].length)));p[0]!==DIFF_DELETE&&(h+=p[1].length)}}}b=b.substring(c.length,b.length-c.length);return[b,e]};\ndiff_match_patch.prototype.patch_addPadding=function(a){for(var b=this.Patch_Margin,c=\"\",d=1;d<=b;d++)c+=String.fromCharCode(d);for(d=0;d<a.length;d++)a[d].start1+=b,a[d].start2+=b;d=a[0];var e=d.diffs;if(0==e.length||e[0][0]!=DIFF_EQUAL)e.unshift(new diff_match_patch.Diff(DIFF_EQUAL,c)),d.start1-=b,d.start2-=b,d.length1+=b,d.length2+=b;else if(b>e[0][1].length){var f=b-e[0][1].length;e[0][1]=c.substring(e[0][1].length)+e[0][1];d.start1-=f;d.start2-=f;d.length1+=f;d.length2+=f}d=a[a.length-1];e=d.diffs;\n0==e.length||e[e.length-1][0]!=DIFF_EQUAL?(e.push(new diff_match_patch.Diff(DIFF_EQUAL,c)),d.length1+=b,d.length2+=b):b>e[e.length-1][1].length&&(f=b-e[e.length-1][1].length,e[e.length-1][1]+=c.substring(0,f),d.length1+=f,d.length2+=f);return c};\ndiff_match_patch.prototype.patch_splitMax=function(a){for(var b=this.Match_MaxBits,c=0;c<a.length;c++)if(!(a[c].length1<=b)){var d=a[c];a.splice(c--,1);for(var e=d.start1,f=d.start2,g=\"\";0!==d.diffs.length;){var h=new diff_match_patch.patch_obj,l=!0;h.start1=e-g.length;h.start2=f-g.length;\"\"!==g&&(h.length1=h.length2=g.length,h.diffs.push(new diff_match_patch.Diff(DIFF_EQUAL,g)));for(;0!==d.diffs.length&&h.length1<b-this.Patch_Margin;){g=d.diffs[0][0];var k=d.diffs[0][1];g===DIFF_INSERT?(h.length2+=\nk.length,f+=k.length,h.diffs.push(d.diffs.shift()),l=!1):g===DIFF_DELETE&&1==h.diffs.length&&h.diffs[0][0]==DIFF_EQUAL&&k.length>2*b?(h.length1+=k.length,e+=k.length,l=!1,h.diffs.push(new diff_match_patch.Diff(g,k)),d.diffs.shift()):(k=k.substring(0,b-h.length1-this.Patch_Margin),h.length1+=k.length,e+=k.length,g===DIFF_EQUAL?(h.length2+=k.length,f+=k.length):l=!1,h.diffs.push(new diff_match_patch.Diff(g,k)),k==d.diffs[0][1]?d.diffs.shift():d.diffs[0][1]=d.diffs[0][1].substring(k.length))}g=this.diff_text2(h.diffs);\ng=g.substring(g.length-this.Patch_Margin);k=this.diff_text1(d.diffs).substring(0,this.Patch_Margin);\"\"!==k&&(h.length1+=k.length,h.length2+=k.length,0!==h.diffs.length&&h.diffs[h.diffs.length-1][0]===DIFF_EQUAL?h.diffs[h.diffs.length-1][1]+=k:h.diffs.push(new diff_match_patch.Diff(DIFF_EQUAL,k)));l||a.splice(++c,0,h)}}};diff_match_patch.prototype.patch_toText=function(a){for(var b=[],c=0;c<a.length;c++)b[c]=a[c];return b.join(\"\")};\ndiff_match_patch.prototype.patch_fromText=function(a){var b=[];if(!a)return b;a=a.split(\"\\n\");for(var c=0,d=/^@@ -(\\d+),?(\\d*) \\+(\\d+),?(\\d*) @@$/;c<a.length;){var e=a[c].match(d);if(!e)throw Error(\"Invalid patch string: \"+a[c]);var f=new diff_match_patch.patch_obj;b.push(f);f.start1=parseInt(e[1],10);\"\"===e[2]?(f.start1--,f.length1=1):\"0\"==e[2]?f.length1=0:(f.start1--,f.length1=parseInt(e[2],10));f.start2=parseInt(e[3],10);\"\"===e[4]?(f.start2--,f.length2=1):\"0\"==e[4]?f.length2=0:(f.start2--,f.length2=\nparseInt(e[4],10));for(c++;c<a.length;){e=a[c].charAt(0);try{var g=decodeURI(a[c].substring(1))}catch(h){throw Error(\"Illegal escape in patch_fromText: \"+g);}if(\"-\"==e)f.diffs.push(new diff_match_patch.Diff(DIFF_DELETE,g));else if(\"+\"==e)f.diffs.push(new diff_match_patch.Diff(DIFF_INSERT,g));else if(\" \"==e)f.diffs.push(new diff_match_patch.Diff(DIFF_EQUAL,g));else if(\"@\"==e)break;else if(\"\"!==e)throw Error('Invalid patch mode \"'+e+'\" in: '+g);c++}}return b};\ndiff_match_patch.patch_obj=function(){this.diffs=[];this.start2=this.start1=null;this.length2=this.length1=0};\ndiff_match_patch.patch_obj.prototype.toString=function(){for(var a=[\"@@ -\"+(0===this.length1?this.start1+\",0\":1==this.length1?this.start1+1:this.start1+1+\",\"+this.length1)+\" +\"+(0===this.length2?this.start2+\",0\":1==this.length2?this.start2+1:this.start2+1+\",\"+this.length2)+\" @@\\n\"],b,c=0;c<this.diffs.length;c++){switch(this.diffs[c][0]){case DIFF_INSERT:b=\"+\";break;case DIFF_DELETE:b=\"-\";break;case DIFF_EQUAL:b=\" \"}a[c+1]=b+encodeURI(this.diffs[c][1])+\"\\n\"}return a.join(\"\").replace(/%20/g,\" \")};\nthis.diff_match_patch=diff_match_patch;this.DIFF_DELETE=DIFF_DELETE;this.DIFF_INSERT=DIFF_INSERT;this.DIFF_EQUAL=DIFF_EQUAL;\n"
  },
  {
    "path": "javascript/diff_match_patch_uncompressed.js",
    "content": "/**\n * Diff Match and Patch\n * Copyright 2018 The diff-match-patch Authors.\n * https://github.com/google/diff-match-patch\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *   http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n/**\n * @fileoverview Computes the difference between two texts to create a patch.\n * Applies the patch onto another text, allowing for errors.\n * @author fraser@google.com (Neil Fraser)\n */\n\n/**\n * Class containing the diff, match and patch methods.\n * @constructor\n */\nvar diff_match_patch = function() {\n\n  // Defaults.\n  // Redefine these in your program to override the defaults.\n\n  // Number of seconds to map a diff before giving up (0 for infinity).\n  this.Diff_Timeout = 1.0;\n  // Cost of an empty edit operation in terms of edit characters.\n  this.Diff_EditCost = 4;\n  // At what point is no match declared (0.0 = perfection, 1.0 = very loose).\n  this.Match_Threshold = 0.5;\n  // How far to search for a match (0 = exact location, 1000+ = broad match).\n  // A match this many characters away from the expected location will add\n  // 1.0 to the score (0.0 is a perfect match).\n  this.Match_Distance = 1000;\n  // When deleting a large block of text (over ~64 characters), how close do\n  // the contents have to be to match the expected contents. (0.0 = perfection,\n  // 1.0 = very loose).  Note that Match_Threshold controls how closely the\n  // end points of a delete need to match.\n  this.Patch_DeleteThreshold = 0.5;\n  // Chunk size for context length.\n  this.Patch_Margin = 4;\n\n  // The number of bits in an int.\n  this.Match_MaxBits = 32;\n};\n\n\n//  DIFF FUNCTIONS\n\n\n/**\n * The data structure representing a diff is an array of tuples:\n * [[DIFF_DELETE, 'Hello'], [DIFF_INSERT, 'Goodbye'], [DIFF_EQUAL, ' world.']]\n * which means: delete 'Hello', add 'Goodbye' and keep ' world.'\n */\nvar DIFF_DELETE = -1;\nvar DIFF_INSERT = 1;\nvar DIFF_EQUAL = 0;\n\n/**\n * Class representing one diff tuple.\n * Attempts to look like a two-element array (which is what this used to be).\n * @param {number} op Operation, one of: DIFF_DELETE, DIFF_INSERT, DIFF_EQUAL.\n * @param {string} text Text to be deleted, inserted, or retained.\n * @constructor\n */\ndiff_match_patch.Diff = function(op, text) {\n  this[0] = op;\n  this[1] = text;\n};\n\ndiff_match_patch.Diff.prototype.length = 2;\n\n/**\n * Emulate the output of a two-element array.\n * @return {string} Diff operation as a string.\n */\ndiff_match_patch.Diff.prototype.toString = function() {\n  return this[0] + ',' + this[1];\n};\n\n\n/**\n * Find the differences between two texts.  Simplifies the problem by stripping\n * any common prefix or suffix off the texts before diffing.\n * @param {string} text1 Old string to be diffed.\n * @param {string} text2 New string to be diffed.\n * @param {boolean=} opt_checklines Optional speedup flag. If present and false,\n *     then don't run a line-level diff first to identify the changed areas.\n *     Defaults to true, which does a faster, slightly less optimal diff.\n * @param {number=} opt_deadline Optional time when the diff should be complete\n *     by.  Used internally for recursive calls.  Users should set DiffTimeout\n *     instead.\n * @return {!Array.<!diff_match_patch.Diff>} Array of diff tuples.\n */\ndiff_match_patch.prototype.diff_main = function(text1, text2, opt_checklines,\n    opt_deadline) {\n  // Set a deadline by which time the diff must be complete.\n  if (typeof opt_deadline == 'undefined') {\n    if (this.Diff_Timeout <= 0) {\n      opt_deadline = Number.MAX_VALUE;\n    } else {\n      opt_deadline = (new Date).getTime() + this.Diff_Timeout * 1000;\n    }\n  }\n  var deadline = opt_deadline;\n\n  // Check for null inputs.\n  if (text1 == null || text2 == null) {\n    throw new Error('Null input. (diff_main)');\n  }\n\n  // Check for equality (speedup).\n  if (text1 == text2) {\n    if (text1) {\n      return [new diff_match_patch.Diff(DIFF_EQUAL, text1)];\n    }\n    return [];\n  }\n\n  if (typeof opt_checklines == 'undefined') {\n    opt_checklines = true;\n  }\n  var checklines = opt_checklines;\n\n  // Trim off common prefix (speedup).\n  var commonlength = this.diff_commonPrefix(text1, text2);\n  var commonprefix = text1.substring(0, commonlength);\n  text1 = text1.substring(commonlength);\n  text2 = text2.substring(commonlength);\n\n  // Trim off common suffix (speedup).\n  commonlength = this.diff_commonSuffix(text1, text2);\n  var commonsuffix = text1.substring(text1.length - commonlength);\n  text1 = text1.substring(0, text1.length - commonlength);\n  text2 = text2.substring(0, text2.length - commonlength);\n\n  // Compute the diff on the middle block.\n  var diffs = this.diff_compute_(text1, text2, checklines, deadline);\n\n  // Restore the prefix and suffix.\n  if (commonprefix) {\n    diffs.unshift(new diff_match_patch.Diff(DIFF_EQUAL, commonprefix));\n  }\n  if (commonsuffix) {\n    diffs.push(new diff_match_patch.Diff(DIFF_EQUAL, commonsuffix));\n  }\n  this.diff_cleanupMerge(diffs);\n  return diffs;\n};\n\n\n/**\n * Find the differences between two texts.  Assumes that the texts do not\n * have any common prefix or suffix.\n * @param {string} text1 Old string to be diffed.\n * @param {string} text2 New string to be diffed.\n * @param {boolean} checklines Speedup flag.  If false, then don't run a\n *     line-level diff first to identify the changed areas.\n *     If true, then run a faster, slightly less optimal diff.\n * @param {number} deadline Time when the diff should be complete by.\n * @return {!Array.<!diff_match_patch.Diff>} Array of diff tuples.\n * @private\n */\ndiff_match_patch.prototype.diff_compute_ = function(text1, text2, checklines,\n    deadline) {\n  var diffs;\n\n  if (!text1) {\n    // Just add some text (speedup).\n    return [new diff_match_patch.Diff(DIFF_INSERT, text2)];\n  }\n\n  if (!text2) {\n    // Just delete some text (speedup).\n    return [new diff_match_patch.Diff(DIFF_DELETE, text1)];\n  }\n\n  var longtext = text1.length > text2.length ? text1 : text2;\n  var shorttext = text1.length > text2.length ? text2 : text1;\n  var i = longtext.indexOf(shorttext);\n  if (i != -1) {\n    // Shorter text is inside the longer text (speedup).\n    diffs = [new diff_match_patch.Diff(DIFF_INSERT, longtext.substring(0, i)),\n             new diff_match_patch.Diff(DIFF_EQUAL, shorttext),\n             new diff_match_patch.Diff(DIFF_INSERT,\n                 longtext.substring(i + shorttext.length))];\n    // Swap insertions for deletions if diff is reversed.\n    if (text1.length > text2.length) {\n      diffs[0][0] = diffs[2][0] = DIFF_DELETE;\n    }\n    return diffs;\n  }\n\n  if (shorttext.length == 1) {\n    // Single character string.\n    // After the previous speedup, the character can't be an equality.\n    return [new diff_match_patch.Diff(DIFF_DELETE, text1),\n            new diff_match_patch.Diff(DIFF_INSERT, text2)];\n  }\n\n  // Check to see if the problem can be split in two.\n  var hm = this.diff_halfMatch_(text1, text2);\n  if (hm) {\n    // A half-match was found, sort out the return data.\n    var text1_a = hm[0];\n    var text1_b = hm[1];\n    var text2_a = hm[2];\n    var text2_b = hm[3];\n    var mid_common = hm[4];\n    // Send both pairs off for separate processing.\n    var diffs_a = this.diff_main(text1_a, text2_a, checklines, deadline);\n    var diffs_b = this.diff_main(text1_b, text2_b, checklines, deadline);\n    // Merge the results.\n    return diffs_a.concat([new diff_match_patch.Diff(DIFF_EQUAL, mid_common)],\n                          diffs_b);\n  }\n\n  if (checklines && text1.length > 100 && text2.length > 100) {\n    return this.diff_lineMode_(text1, text2, deadline);\n  }\n\n  return this.diff_bisect_(text1, text2, deadline);\n};\n\n\n/**\n * Do a quick line-level diff on both strings, then rediff the parts for\n * greater accuracy.\n * This speedup can produce non-minimal diffs.\n * @param {string} text1 Old string to be diffed.\n * @param {string} text2 New string to be diffed.\n * @param {number} deadline Time when the diff should be complete by.\n * @return {!Array.<!diff_match_patch.Diff>} Array of diff tuples.\n * @private\n */\ndiff_match_patch.prototype.diff_lineMode_ = function(text1, text2, deadline) {\n  // Scan the text on a line-by-line basis first.\n  var a = this.diff_linesToChars_(text1, text2);\n  text1 = a.chars1;\n  text2 = a.chars2;\n  var linearray = a.lineArray;\n\n  var diffs = this.diff_main(text1, text2, false, deadline);\n\n  // Convert the diff back to original text.\n  this.diff_charsToLines_(diffs, linearray);\n  // Eliminate freak matches (e.g. blank lines)\n  this.diff_cleanupSemantic(diffs);\n\n  // Rediff any replacement blocks, this time character-by-character.\n  // Add a dummy entry at the end.\n  diffs.push(new diff_match_patch.Diff(DIFF_EQUAL, ''));\n  var pointer = 0;\n  var count_delete = 0;\n  var count_insert = 0;\n  var text_delete = '';\n  var text_insert = '';\n  while (pointer < diffs.length) {\n    switch (diffs[pointer][0]) {\n      case DIFF_INSERT:\n        count_insert++;\n        text_insert += diffs[pointer][1];\n        break;\n      case DIFF_DELETE:\n        count_delete++;\n        text_delete += diffs[pointer][1];\n        break;\n      case DIFF_EQUAL:\n        // Upon reaching an equality, check for prior redundancies.\n        if (count_delete >= 1 && count_insert >= 1) {\n          // Delete the offending records and add the merged ones.\n          diffs.splice(pointer - count_delete - count_insert,\n                       count_delete + count_insert);\n          pointer = pointer - count_delete - count_insert;\n          var subDiff =\n              this.diff_main(text_delete, text_insert, false, deadline);\n          for (var j = subDiff.length - 1; j >= 0; j--) {\n            diffs.splice(pointer, 0, subDiff[j]);\n          }\n          pointer = pointer + subDiff.length;\n        }\n        count_insert = 0;\n        count_delete = 0;\n        text_delete = '';\n        text_insert = '';\n        break;\n    }\n    pointer++;\n  }\n  diffs.pop();  // Remove the dummy entry at the end.\n\n  return diffs;\n};\n\n\n/**\n * Find the 'middle snake' of a diff, split the problem in two\n * and return the recursively constructed diff.\n * See Myers 1986 paper: An O(ND) Difference Algorithm and Its Variations.\n * @param {string} text1 Old string to be diffed.\n * @param {string} text2 New string to be diffed.\n * @param {number} deadline Time at which to bail if not yet complete.\n * @return {!Array.<!diff_match_patch.Diff>} Array of diff tuples.\n * @private\n */\ndiff_match_patch.prototype.diff_bisect_ = function(text1, text2, deadline) {\n  // Cache the text lengths to prevent multiple calls.\n  var text1_length = text1.length;\n  var text2_length = text2.length;\n  var max_d = Math.ceil((text1_length + text2_length) / 2);\n  var v_offset = max_d;\n  var v_length = 2 * max_d;\n  var v1 = new Array(v_length);\n  var v2 = new Array(v_length);\n  // Setting all elements to -1 is faster in Chrome & Firefox than mixing\n  // integers and undefined.\n  for (var x = 0; x < v_length; x++) {\n    v1[x] = -1;\n    v2[x] = -1;\n  }\n  v1[v_offset + 1] = 0;\n  v2[v_offset + 1] = 0;\n  var delta = text1_length - text2_length;\n  // If the total number of characters is odd, then the front path will collide\n  // with the reverse path.\n  var front = (delta % 2 != 0);\n  // Offsets for start and end of k loop.\n  // Prevents mapping of space beyond the grid.\n  var k1start = 0;\n  var k1end = 0;\n  var k2start = 0;\n  var k2end = 0;\n  for (var d = 0; d < max_d; d++) {\n    // Bail out if deadline is reached.\n    if ((new Date()).getTime() > deadline) {\n      break;\n    }\n\n    // Walk the front path one step.\n    for (var k1 = -d + k1start; k1 <= d - k1end; k1 += 2) {\n      var k1_offset = v_offset + k1;\n      var x1;\n      if (k1 == -d || (k1 != d && v1[k1_offset - 1] < v1[k1_offset + 1])) {\n        x1 = v1[k1_offset + 1];\n      } else {\n        x1 = v1[k1_offset - 1] + 1;\n      }\n      var y1 = x1 - k1;\n      while (x1 < text1_length && y1 < text2_length &&\n             text1.charAt(x1) == text2.charAt(y1)) {\n        x1++;\n        y1++;\n      }\n      v1[k1_offset] = x1;\n      if (x1 > text1_length) {\n        // Ran off the right of the graph.\n        k1end += 2;\n      } else if (y1 > text2_length) {\n        // Ran off the bottom of the graph.\n        k1start += 2;\n      } else if (front) {\n        var k2_offset = v_offset + delta - k1;\n        if (k2_offset >= 0 && k2_offset < v_length && v2[k2_offset] != -1) {\n          // Mirror x2 onto top-left coordinate system.\n          var x2 = text1_length - v2[k2_offset];\n          if (x1 >= x2) {\n            // Overlap detected.\n            return this.diff_bisectSplit_(text1, text2, x1, y1, deadline);\n          }\n        }\n      }\n    }\n\n    // Walk the reverse path one step.\n    for (var k2 = -d + k2start; k2 <= d - k2end; k2 += 2) {\n      var k2_offset = v_offset + k2;\n      var x2;\n      if (k2 == -d || (k2 != d && v2[k2_offset - 1] < v2[k2_offset + 1])) {\n        x2 = v2[k2_offset + 1];\n      } else {\n        x2 = v2[k2_offset - 1] + 1;\n      }\n      var y2 = x2 - k2;\n      while (x2 < text1_length && y2 < text2_length &&\n             text1.charAt(text1_length - x2 - 1) ==\n             text2.charAt(text2_length - y2 - 1)) {\n        x2++;\n        y2++;\n      }\n      v2[k2_offset] = x2;\n      if (x2 > text1_length) {\n        // Ran off the left of the graph.\n        k2end += 2;\n      } else if (y2 > text2_length) {\n        // Ran off the top of the graph.\n        k2start += 2;\n      } else if (!front) {\n        var k1_offset = v_offset + delta - k2;\n        if (k1_offset >= 0 && k1_offset < v_length && v1[k1_offset] != -1) {\n          var x1 = v1[k1_offset];\n          var y1 = v_offset + x1 - k1_offset;\n          // Mirror x2 onto top-left coordinate system.\n          x2 = text1_length - x2;\n          if (x1 >= x2) {\n            // Overlap detected.\n            return this.diff_bisectSplit_(text1, text2, x1, y1, deadline);\n          }\n        }\n      }\n    }\n  }\n  // Diff took too long and hit the deadline or\n  // number of diffs equals number of characters, no commonality at all.\n  return [new diff_match_patch.Diff(DIFF_DELETE, text1),\n          new diff_match_patch.Diff(DIFF_INSERT, text2)];\n};\n\n\n/**\n * Given the location of the 'middle snake', split the diff in two parts\n * and recurse.\n * @param {string} text1 Old string to be diffed.\n * @param {string} text2 New string to be diffed.\n * @param {number} x Index of split point in text1.\n * @param {number} y Index of split point in text2.\n * @param {number} deadline Time at which to bail if not yet complete.\n * @return {!Array.<!diff_match_patch.Diff>} Array of diff tuples.\n * @private\n */\ndiff_match_patch.prototype.diff_bisectSplit_ = function(text1, text2, x, y,\n    deadline) {\n  var text1a = text1.substring(0, x);\n  var text2a = text2.substring(0, y);\n  var text1b = text1.substring(x);\n  var text2b = text2.substring(y);\n\n  // Compute both diffs serially.\n  var diffs = this.diff_main(text1a, text2a, false, deadline);\n  var diffsb = this.diff_main(text1b, text2b, false, deadline);\n\n  return diffs.concat(diffsb);\n};\n\n\n/**\n * Split two texts into an array of strings.  Reduce the texts to a string of\n * hashes where each Unicode character represents one line.\n * @param {string} text1 First string.\n * @param {string} text2 Second string.\n * @return {{chars1: string, chars2: string, lineArray: !Array.<string>}}\n *     An object containing the encoded text1, the encoded text2 and\n *     the array of unique strings.\n *     The zeroth element of the array of unique strings is intentionally blank.\n * @private\n */\ndiff_match_patch.prototype.diff_linesToChars_ = function(text1, text2) {\n  var lineArray = [];  // e.g. lineArray[4] == 'Hello\\n'\n  var lineHash = {};   // e.g. lineHash['Hello\\n'] == 4\n\n  // '\\x00' is a valid character, but various debuggers don't like it.\n  // So we'll insert a junk entry to avoid generating a null character.\n  lineArray[0] = '';\n\n  /**\n   * Split a text into an array of strings.  Reduce the texts to a string of\n   * hashes where each Unicode character represents one line.\n   * Modifies linearray and linehash through being a closure.\n   * @param {string} text String to encode.\n   * @return {string} Encoded string.\n   * @private\n   */\n  function diff_linesToCharsMunge_(text) {\n    var chars = '';\n    // Walk the text, pulling out a substring for each line.\n    // text.split('\\n') would would temporarily double our memory footprint.\n    // Modifying text would create many large strings to garbage collect.\n    var lineStart = 0;\n    var lineEnd = -1;\n    // Keeping our own length variable is faster than looking it up.\n    var lineArrayLength = lineArray.length;\n    while (lineEnd < text.length - 1) {\n      lineEnd = text.indexOf('\\n', lineStart);\n      if (lineEnd == -1) {\n        lineEnd = text.length - 1;\n      }\n      var line = text.substring(lineStart, lineEnd + 1);\n\n      if (lineHash.hasOwnProperty ? lineHash.hasOwnProperty(line) :\n          (lineHash[line] !== undefined)) {\n        chars += String.fromCharCode(lineHash[line]);\n      } else {\n        if (lineArrayLength == maxLines) {\n          // Bail out at 65535 because\n          // String.fromCharCode(65536) == String.fromCharCode(0)\n          line = text.substring(lineStart);\n          lineEnd = text.length;\n        }\n        chars += String.fromCharCode(lineArrayLength);\n        lineHash[line] = lineArrayLength;\n        lineArray[lineArrayLength++] = line;\n      }\n      lineStart = lineEnd + 1;\n    }\n    return chars;\n  }\n  // Allocate 2/3rds of the space for text1, the rest for text2.\n  var maxLines = 40000;\n  var chars1 = diff_linesToCharsMunge_(text1);\n  maxLines = 65535;\n  var chars2 = diff_linesToCharsMunge_(text2);\n  return {chars1: chars1, chars2: chars2, lineArray: lineArray};\n};\n\n\n/**\n * Rehydrate the text in a diff from a string of line hashes to real lines of\n * text.\n * @param {!Array.<!diff_match_patch.Diff>} diffs Array of diff tuples.\n * @param {!Array.<string>} lineArray Array of unique strings.\n * @private\n */\ndiff_match_patch.prototype.diff_charsToLines_ = function(diffs, lineArray) {\n  for (var i = 0; i < diffs.length; i++) {\n    var chars = diffs[i][1];\n    var text = [];\n    for (var j = 0; j < chars.length; j++) {\n      text[j] = lineArray[chars.charCodeAt(j)];\n    }\n    diffs[i][1] = text.join('');\n  }\n};\n\n\n/**\n * Determine the common prefix of two strings.\n * @param {string} text1 First string.\n * @param {string} text2 Second string.\n * @return {number} The number of characters common to the start of each\n *     string.\n */\ndiff_match_patch.prototype.diff_commonPrefix = function(text1, text2) {\n  // Quick check for common null cases.\n  if (!text1 || !text2 || text1.charAt(0) != text2.charAt(0)) {\n    return 0;\n  }\n  // Binary search.\n  // Performance analysis: https://neil.fraser.name/news/2007/10/09/\n  var pointermin = 0;\n  var pointermax = Math.min(text1.length, text2.length);\n  var pointermid = pointermax;\n  var pointerstart = 0;\n  while (pointermin < pointermid) {\n    if (text1.substring(pointerstart, pointermid) ==\n        text2.substring(pointerstart, pointermid)) {\n      pointermin = pointermid;\n      pointerstart = pointermin;\n    } else {\n      pointermax = pointermid;\n    }\n    pointermid = Math.floor((pointermax - pointermin) / 2 + pointermin);\n  }\n  return pointermid;\n};\n\n\n/**\n * Determine the common suffix of two strings.\n * @param {string} text1 First string.\n * @param {string} text2 Second string.\n * @return {number} The number of characters common to the end of each string.\n */\ndiff_match_patch.prototype.diff_commonSuffix = function(text1, text2) {\n  // Quick check for common null cases.\n  if (!text1 || !text2 ||\n      text1.charAt(text1.length - 1) != text2.charAt(text2.length - 1)) {\n    return 0;\n  }\n  // Binary search.\n  // Performance analysis: https://neil.fraser.name/news/2007/10/09/\n  var pointermin = 0;\n  var pointermax = Math.min(text1.length, text2.length);\n  var pointermid = pointermax;\n  var pointerend = 0;\n  while (pointermin < pointermid) {\n    if (text1.substring(text1.length - pointermid, text1.length - pointerend) ==\n        text2.substring(text2.length - pointermid, text2.length - pointerend)) {\n      pointermin = pointermid;\n      pointerend = pointermin;\n    } else {\n      pointermax = pointermid;\n    }\n    pointermid = Math.floor((pointermax - pointermin) / 2 + pointermin);\n  }\n  return pointermid;\n};\n\n\n/**\n * Determine if the suffix of one string is the prefix of another.\n * @param {string} text1 First string.\n * @param {string} text2 Second string.\n * @return {number} The number of characters common to the end of the first\n *     string and the start of the second string.\n * @private\n */\ndiff_match_patch.prototype.diff_commonOverlap_ = function(text1, text2) {\n  // Cache the text lengths to prevent multiple calls.\n  var text1_length = text1.length;\n  var text2_length = text2.length;\n  // Eliminate the null case.\n  if (text1_length == 0 || text2_length == 0) {\n    return 0;\n  }\n  // Truncate the longer string.\n  if (text1_length > text2_length) {\n    text1 = text1.substring(text1_length - text2_length);\n  } else if (text1_length < text2_length) {\n    text2 = text2.substring(0, text1_length);\n  }\n  var text_length = Math.min(text1_length, text2_length);\n  // Quick check for the worst case.\n  if (text1 == text2) {\n    return text_length;\n  }\n\n  // Start by looking for a single character match\n  // and increase length until no match is found.\n  // Performance analysis: https://neil.fraser.name/news/2010/11/04/\n  var best = 0;\n  var length = 1;\n  while (true) {\n    var pattern = text1.substring(text_length - length);\n    var found = text2.indexOf(pattern);\n    if (found == -1) {\n      return best;\n    }\n    length += found;\n    if (found == 0 || text1.substring(text_length - length) ==\n        text2.substring(0, length)) {\n      best = length;\n      length++;\n    }\n  }\n};\n\n\n/**\n * Do the two texts share a substring which is at least half the length of the\n * longer text?\n * This speedup can produce non-minimal diffs.\n * @param {string} text1 First string.\n * @param {string} text2 Second string.\n * @return {Array.<string>} Five element Array, containing the prefix of\n *     text1, the suffix of text1, the prefix of text2, the suffix of\n *     text2 and the common middle.  Or null if there was no match.\n * @private\n */\ndiff_match_patch.prototype.diff_halfMatch_ = function(text1, text2) {\n  if (this.Diff_Timeout <= 0) {\n    // Don't risk returning a non-optimal diff if we have unlimited time.\n    return null;\n  }\n  var longtext = text1.length > text2.length ? text1 : text2;\n  var shorttext = text1.length > text2.length ? text2 : text1;\n  if (longtext.length < 4 || shorttext.length * 2 < longtext.length) {\n    return null;  // Pointless.\n  }\n  var dmp = this;  // 'this' becomes 'window' in a closure.\n\n  /**\n   * Does a substring of shorttext exist within longtext such that the substring\n   * is at least half the length of longtext?\n   * Closure, but does not reference any external variables.\n   * @param {string} longtext Longer string.\n   * @param {string} shorttext Shorter string.\n   * @param {number} i Start index of quarter length substring within longtext.\n   * @return {Array.<string>} Five element Array, containing the prefix of\n   *     longtext, the suffix of longtext, the prefix of shorttext, the suffix\n   *     of shorttext and the common middle.  Or null if there was no match.\n   * @private\n   */\n  function diff_halfMatchI_(longtext, shorttext, i) {\n    // Start with a 1/4 length substring at position i as a seed.\n    var seed = longtext.substring(i, i + Math.floor(longtext.length / 4));\n    var j = -1;\n    var best_common = '';\n    var best_longtext_a, best_longtext_b, best_shorttext_a, best_shorttext_b;\n    while ((j = shorttext.indexOf(seed, j + 1)) != -1) {\n      var prefixLength = dmp.diff_commonPrefix(longtext.substring(i),\n                                               shorttext.substring(j));\n      var suffixLength = dmp.diff_commonSuffix(longtext.substring(0, i),\n                                               shorttext.substring(0, j));\n      if (best_common.length < suffixLength + prefixLength) {\n        best_common = shorttext.substring(j - suffixLength, j) +\n            shorttext.substring(j, j + prefixLength);\n        best_longtext_a = longtext.substring(0, i - suffixLength);\n        best_longtext_b = longtext.substring(i + prefixLength);\n        best_shorttext_a = shorttext.substring(0, j - suffixLength);\n        best_shorttext_b = shorttext.substring(j + prefixLength);\n      }\n    }\n    if (best_common.length * 2 >= longtext.length) {\n      return [best_longtext_a, best_longtext_b,\n              best_shorttext_a, best_shorttext_b, best_common];\n    } else {\n      return null;\n    }\n  }\n\n  // First check if the second quarter is the seed for a half-match.\n  var hm1 = diff_halfMatchI_(longtext, shorttext,\n                             Math.ceil(longtext.length / 4));\n  // Check again based on the third quarter.\n  var hm2 = diff_halfMatchI_(longtext, shorttext,\n                             Math.ceil(longtext.length / 2));\n  var hm;\n  if (!hm1 && !hm2) {\n    return null;\n  } else if (!hm2) {\n    hm = hm1;\n  } else if (!hm1) {\n    hm = hm2;\n  } else {\n    // Both matched.  Select the longest.\n    hm = hm1[4].length > hm2[4].length ? hm1 : hm2;\n  }\n\n  // A half-match was found, sort out the return data.\n  var text1_a, text1_b, text2_a, text2_b;\n  if (text1.length > text2.length) {\n    text1_a = hm[0];\n    text1_b = hm[1];\n    text2_a = hm[2];\n    text2_b = hm[3];\n  } else {\n    text2_a = hm[0];\n    text2_b = hm[1];\n    text1_a = hm[2];\n    text1_b = hm[3];\n  }\n  var mid_common = hm[4];\n  return [text1_a, text1_b, text2_a, text2_b, mid_common];\n};\n\n\n/**\n * Reduce the number of edits by eliminating semantically trivial equalities.\n * @param {!Array.<!diff_match_patch.Diff>} diffs Array of diff tuples.\n */\ndiff_match_patch.prototype.diff_cleanupSemantic = function(diffs) {\n  var changes = false;\n  var equalities = [];  // Stack of indices where equalities are found.\n  var equalitiesLength = 0;  // Keeping our own length var is faster in JS.\n  /** @type {?string} */\n  var lastEquality = null;\n  // Always equal to diffs[equalities[equalitiesLength - 1]][1]\n  var pointer = 0;  // Index of current position.\n  // Number of characters that changed prior to the equality.\n  var length_insertions1 = 0;\n  var length_deletions1 = 0;\n  // Number of characters that changed after the equality.\n  var length_insertions2 = 0;\n  var length_deletions2 = 0;\n  while (pointer < diffs.length) {\n    if (diffs[pointer][0] == DIFF_EQUAL) {  // Equality found.\n      equalities[equalitiesLength++] = pointer;\n      length_insertions1 = length_insertions2;\n      length_deletions1 = length_deletions2;\n      length_insertions2 = 0;\n      length_deletions2 = 0;\n      lastEquality = diffs[pointer][1];\n    } else {  // An insertion or deletion.\n      if (diffs[pointer][0] == DIFF_INSERT) {\n        length_insertions2 += diffs[pointer][1].length;\n      } else {\n        length_deletions2 += diffs[pointer][1].length;\n      }\n      // Eliminate an equality that is smaller or equal to the edits on both\n      // sides of it.\n      if (lastEquality && (lastEquality.length <=\n          Math.max(length_insertions1, length_deletions1)) &&\n          (lastEquality.length <= Math.max(length_insertions2,\n                                           length_deletions2))) {\n        // Duplicate record.\n        diffs.splice(equalities[equalitiesLength - 1], 0,\n                     new diff_match_patch.Diff(DIFF_DELETE, lastEquality));\n        // Change second copy to insert.\n        diffs[equalities[equalitiesLength - 1] + 1][0] = DIFF_INSERT;\n        // Throw away the equality we just deleted.\n        equalitiesLength--;\n        // Throw away the previous equality (it needs to be reevaluated).\n        equalitiesLength--;\n        pointer = equalitiesLength > 0 ? equalities[equalitiesLength - 1] : -1;\n        length_insertions1 = 0;  // Reset the counters.\n        length_deletions1 = 0;\n        length_insertions2 = 0;\n        length_deletions2 = 0;\n        lastEquality = null;\n        changes = true;\n      }\n    }\n    pointer++;\n  }\n\n  // Normalize the diff.\n  if (changes) {\n    this.diff_cleanupMerge(diffs);\n  }\n  this.diff_cleanupSemanticLossless(diffs);\n\n  // Find any overlaps between deletions and insertions.\n  // e.g: <del>abcxxx</del><ins>xxxdef</ins>\n  //   -> <del>abc</del>xxx<ins>def</ins>\n  // e.g: <del>xxxabc</del><ins>defxxx</ins>\n  //   -> <ins>def</ins>xxx<del>abc</del>\n  // Only extract an overlap if it is as big as the edit ahead or behind it.\n  pointer = 1;\n  while (pointer < diffs.length) {\n    if (diffs[pointer - 1][0] == DIFF_DELETE &&\n        diffs[pointer][0] == DIFF_INSERT) {\n      var deletion = diffs[pointer - 1][1];\n      var insertion = diffs[pointer][1];\n      var overlap_length1 = this.diff_commonOverlap_(deletion, insertion);\n      var overlap_length2 = this.diff_commonOverlap_(insertion, deletion);\n      if (overlap_length1 >= overlap_length2) {\n        if (overlap_length1 >= deletion.length / 2 ||\n            overlap_length1 >= insertion.length / 2) {\n          // Overlap found.  Insert an equality and trim the surrounding edits.\n          diffs.splice(pointer, 0, new diff_match_patch.Diff(DIFF_EQUAL,\n              insertion.substring(0, overlap_length1)));\n          diffs[pointer - 1][1] =\n              deletion.substring(0, deletion.length - overlap_length1);\n          diffs[pointer + 1][1] = insertion.substring(overlap_length1);\n          pointer++;\n        }\n      } else {\n        if (overlap_length2 >= deletion.length / 2 ||\n            overlap_length2 >= insertion.length / 2) {\n          // Reverse overlap found.\n          // Insert an equality and swap and trim the surrounding edits.\n          diffs.splice(pointer, 0, new diff_match_patch.Diff(DIFF_EQUAL,\n              deletion.substring(0, overlap_length2)));\n          diffs[pointer - 1][0] = DIFF_INSERT;\n          diffs[pointer - 1][1] =\n              insertion.substring(0, insertion.length - overlap_length2);\n          diffs[pointer + 1][0] = DIFF_DELETE;\n          diffs[pointer + 1][1] =\n              deletion.substring(overlap_length2);\n          pointer++;\n        }\n      }\n      pointer++;\n    }\n    pointer++;\n  }\n};\n\n\n/**\n * Look for single edits surrounded on both sides by equalities\n * which can be shifted sideways to align the edit to a word boundary.\n * e.g: The c<ins>at c</ins>ame. -> The <ins>cat </ins>came.\n * @param {!Array.<!diff_match_patch.Diff>} diffs Array of diff tuples.\n */\ndiff_match_patch.prototype.diff_cleanupSemanticLossless = function(diffs) {\n  /**\n   * Given two strings, compute a score representing whether the internal\n   * boundary falls on logical boundaries.\n   * Scores range from 6 (best) to 0 (worst).\n   * Closure, but does not reference any external variables.\n   * @param {string} one First string.\n   * @param {string} two Second string.\n   * @return {number} The score.\n   * @private\n   */\n  function diff_cleanupSemanticScore_(one, two) {\n    if (!one || !two) {\n      // Edges are the best.\n      return 6;\n    }\n\n    // Each port of this function behaves slightly differently due to\n    // subtle differences in each language's definition of things like\n    // 'whitespace'.  Since this function's purpose is largely cosmetic,\n    // the choice has been made to use each language's native features\n    // rather than force total conformity.\n    var char1 = one.charAt(one.length - 1);\n    var char2 = two.charAt(0);\n    var nonAlphaNumeric1 = char1.match(diff_match_patch.nonAlphaNumericRegex_);\n    var nonAlphaNumeric2 = char2.match(diff_match_patch.nonAlphaNumericRegex_);\n    var whitespace1 = nonAlphaNumeric1 &&\n        char1.match(diff_match_patch.whitespaceRegex_);\n    var whitespace2 = nonAlphaNumeric2 &&\n        char2.match(diff_match_patch.whitespaceRegex_);\n    var lineBreak1 = whitespace1 &&\n        char1.match(diff_match_patch.linebreakRegex_);\n    var lineBreak2 = whitespace2 &&\n        char2.match(diff_match_patch.linebreakRegex_);\n    var blankLine1 = lineBreak1 &&\n        one.match(diff_match_patch.blanklineEndRegex_);\n    var blankLine2 = lineBreak2 &&\n        two.match(diff_match_patch.blanklineStartRegex_);\n\n    if (blankLine1 || blankLine2) {\n      // Five points for blank lines.\n      return 5;\n    } else if (lineBreak1 || lineBreak2) {\n      // Four points for line breaks.\n      return 4;\n    } else if (nonAlphaNumeric1 && !whitespace1 && whitespace2) {\n      // Three points for end of sentences.\n      return 3;\n    } else if (whitespace1 || whitespace2) {\n      // Two points for whitespace.\n      return 2;\n    } else if (nonAlphaNumeric1 || nonAlphaNumeric2) {\n      // One point for non-alphanumeric.\n      return 1;\n    }\n    return 0;\n  }\n\n  var pointer = 1;\n  // Intentionally ignore the first and last element (don't need checking).\n  while (pointer < diffs.length - 1) {\n    if (diffs[pointer - 1][0] == DIFF_EQUAL &&\n        diffs[pointer + 1][0] == DIFF_EQUAL) {\n      // This is a single edit surrounded by equalities.\n      var equality1 = diffs[pointer - 1][1];\n      var edit = diffs[pointer][1];\n      var equality2 = diffs[pointer + 1][1];\n\n      // First, shift the edit as far left as possible.\n      var commonOffset = this.diff_commonSuffix(equality1, edit);\n      if (commonOffset) {\n        var commonString = edit.substring(edit.length - commonOffset);\n        equality1 = equality1.substring(0, equality1.length - commonOffset);\n        edit = commonString + edit.substring(0, edit.length - commonOffset);\n        equality2 = commonString + equality2;\n      }\n\n      // Second, step character by character right, looking for the best fit.\n      var bestEquality1 = equality1;\n      var bestEdit = edit;\n      var bestEquality2 = equality2;\n      var bestScore = diff_cleanupSemanticScore_(equality1, edit) +\n          diff_cleanupSemanticScore_(edit, equality2);\n      while (edit.charAt(0) === equality2.charAt(0)) {\n        equality1 += edit.charAt(0);\n        edit = edit.substring(1) + equality2.charAt(0);\n        equality2 = equality2.substring(1);\n        var score = diff_cleanupSemanticScore_(equality1, edit) +\n            diff_cleanupSemanticScore_(edit, equality2);\n        // The >= encourages trailing rather than leading whitespace on edits.\n        if (score >= bestScore) {\n          bestScore = score;\n          bestEquality1 = equality1;\n          bestEdit = edit;\n          bestEquality2 = equality2;\n        }\n      }\n\n      if (diffs[pointer - 1][1] != bestEquality1) {\n        // We have an improvement, save it back to the diff.\n        if (bestEquality1) {\n          diffs[pointer - 1][1] = bestEquality1;\n        } else {\n          diffs.splice(pointer - 1, 1);\n          pointer--;\n        }\n        diffs[pointer][1] = bestEdit;\n        if (bestEquality2) {\n          diffs[pointer + 1][1] = bestEquality2;\n        } else {\n          diffs.splice(pointer + 1, 1);\n          pointer--;\n        }\n      }\n    }\n    pointer++;\n  }\n};\n\n// Define some regex patterns for matching boundaries.\ndiff_match_patch.nonAlphaNumericRegex_ = /[^a-zA-Z0-9]/;\ndiff_match_patch.whitespaceRegex_ = /\\s/;\ndiff_match_patch.linebreakRegex_ = /[\\r\\n]/;\ndiff_match_patch.blanklineEndRegex_ = /\\n\\r?\\n$/;\ndiff_match_patch.blanklineStartRegex_ = /^\\r?\\n\\r?\\n/;\n\n/**\n * Reduce the number of edits by eliminating operationally trivial equalities.\n * @param {!Array.<!diff_match_patch.Diff>} diffs Array of diff tuples.\n */\ndiff_match_patch.prototype.diff_cleanupEfficiency = function(diffs) {\n  var changes = false;\n  var equalities = [];  // Stack of indices where equalities are found.\n  var equalitiesLength = 0;  // Keeping our own length var is faster in JS.\n  /** @type {?string} */\n  var lastEquality = null;\n  // Always equal to diffs[equalities[equalitiesLength - 1]][1]\n  var pointer = 0;  // Index of current position.\n  // Is there an insertion operation before the last equality.\n  var pre_ins = false;\n  // Is there a deletion operation before the last equality.\n  var pre_del = false;\n  // Is there an insertion operation after the last equality.\n  var post_ins = false;\n  // Is there a deletion operation after the last equality.\n  var post_del = false;\n  while (pointer < diffs.length) {\n    if (diffs[pointer][0] == DIFF_EQUAL) {  // Equality found.\n      if (diffs[pointer][1].length < this.Diff_EditCost &&\n          (post_ins || post_del)) {\n        // Candidate found.\n        equalities[equalitiesLength++] = pointer;\n        pre_ins = post_ins;\n        pre_del = post_del;\n        lastEquality = diffs[pointer][1];\n      } else {\n        // Not a candidate, and can never become one.\n        equalitiesLength = 0;\n        lastEquality = null;\n      }\n      post_ins = post_del = false;\n    } else {  // An insertion or deletion.\n      if (diffs[pointer][0] == DIFF_DELETE) {\n        post_del = true;\n      } else {\n        post_ins = true;\n      }\n      /*\n       * Five types to be split:\n       * <ins>A</ins><del>B</del>XY<ins>C</ins><del>D</del>\n       * <ins>A</ins>X<ins>C</ins><del>D</del>\n       * <ins>A</ins><del>B</del>X<ins>C</ins>\n       * <ins>A</del>X<ins>C</ins><del>D</del>\n       * <ins>A</ins><del>B</del>X<del>C</del>\n       */\n      if (lastEquality && ((pre_ins && pre_del && post_ins && post_del) ||\n                           ((lastEquality.length < this.Diff_EditCost / 2) &&\n                            (pre_ins + pre_del + post_ins + post_del) == 3))) {\n        // Duplicate record.\n        diffs.splice(equalities[equalitiesLength - 1], 0,\n                     new diff_match_patch.Diff(DIFF_DELETE, lastEquality));\n        // Change second copy to insert.\n        diffs[equalities[equalitiesLength - 1] + 1][0] = DIFF_INSERT;\n        equalitiesLength--;  // Throw away the equality we just deleted;\n        lastEquality = null;\n        if (pre_ins && pre_del) {\n          // No changes made which could affect previous entry, keep going.\n          post_ins = post_del = true;\n          equalitiesLength = 0;\n        } else {\n          equalitiesLength--;  // Throw away the previous equality.\n          pointer = equalitiesLength > 0 ?\n              equalities[equalitiesLength - 1] : -1;\n          post_ins = post_del = false;\n        }\n        changes = true;\n      }\n    }\n    pointer++;\n  }\n\n  if (changes) {\n    this.diff_cleanupMerge(diffs);\n  }\n};\n\n\n/**\n * Reorder and merge like edit sections.  Merge equalities.\n * Any edit section can move as long as it doesn't cross an equality.\n * @param {!Array.<!diff_match_patch.Diff>} diffs Array of diff tuples.\n */\ndiff_match_patch.prototype.diff_cleanupMerge = function(diffs) {\n  // Add a dummy entry at the end.\n  diffs.push(new diff_match_patch.Diff(DIFF_EQUAL, ''));\n  var pointer = 0;\n  var count_delete = 0;\n  var count_insert = 0;\n  var text_delete = '';\n  var text_insert = '';\n  var commonlength;\n  while (pointer < diffs.length) {\n    switch (diffs[pointer][0]) {\n      case DIFF_INSERT:\n        count_insert++;\n        text_insert += diffs[pointer][1];\n        pointer++;\n        break;\n      case DIFF_DELETE:\n        count_delete++;\n        text_delete += diffs[pointer][1];\n        pointer++;\n        break;\n      case DIFF_EQUAL:\n        // Upon reaching an equality, check for prior redundancies.\n        if (count_delete + count_insert > 1) {\n          if (count_delete !== 0 && count_insert !== 0) {\n            // Factor out any common prefixies.\n            commonlength = this.diff_commonPrefix(text_insert, text_delete);\n            if (commonlength !== 0) {\n              if ((pointer - count_delete - count_insert) > 0 &&\n                  diffs[pointer - count_delete - count_insert - 1][0] ==\n                  DIFF_EQUAL) {\n                diffs[pointer - count_delete - count_insert - 1][1] +=\n                    text_insert.substring(0, commonlength);\n              } else {\n                diffs.splice(0, 0, new diff_match_patch.Diff(DIFF_EQUAL,\n                    text_insert.substring(0, commonlength)));\n                pointer++;\n              }\n              text_insert = text_insert.substring(commonlength);\n              text_delete = text_delete.substring(commonlength);\n            }\n            // Factor out any common suffixies.\n            commonlength = this.diff_commonSuffix(text_insert, text_delete);\n            if (commonlength !== 0) {\n              diffs[pointer][1] = text_insert.substring(text_insert.length -\n                  commonlength) + diffs[pointer][1];\n              text_insert = text_insert.substring(0, text_insert.length -\n                  commonlength);\n              text_delete = text_delete.substring(0, text_delete.length -\n                  commonlength);\n            }\n          }\n          // Delete the offending records and add the merged ones.\n          pointer -= count_delete + count_insert;\n          diffs.splice(pointer, count_delete + count_insert);\n          if (text_delete.length) {\n            diffs.splice(pointer, 0,\n                new diff_match_patch.Diff(DIFF_DELETE, text_delete));\n            pointer++;\n          }\n          if (text_insert.length) {\n            diffs.splice(pointer, 0,\n                new diff_match_patch.Diff(DIFF_INSERT, text_insert));\n            pointer++;\n          }\n          pointer++;\n        } else if (pointer !== 0 && diffs[pointer - 1][0] == DIFF_EQUAL) {\n          // Merge this equality with the previous one.\n          diffs[pointer - 1][1] += diffs[pointer][1];\n          diffs.splice(pointer, 1);\n        } else {\n          pointer++;\n        }\n        count_insert = 0;\n        count_delete = 0;\n        text_delete = '';\n        text_insert = '';\n        break;\n    }\n  }\n  if (diffs[diffs.length - 1][1] === '') {\n    diffs.pop();  // Remove the dummy entry at the end.\n  }\n\n  // Second pass: look for single edits surrounded on both sides by equalities\n  // which can be shifted sideways to eliminate an equality.\n  // e.g: A<ins>BA</ins>C -> <ins>AB</ins>AC\n  var changes = false;\n  pointer = 1;\n  // Intentionally ignore the first and last element (don't need checking).\n  while (pointer < diffs.length - 1) {\n    if (diffs[pointer - 1][0] == DIFF_EQUAL &&\n        diffs[pointer + 1][0] == DIFF_EQUAL) {\n      // This is a single edit surrounded by equalities.\n      if (diffs[pointer][1].substring(diffs[pointer][1].length -\n          diffs[pointer - 1][1].length) == diffs[pointer - 1][1]) {\n        // Shift the edit over the previous equality.\n        diffs[pointer][1] = diffs[pointer - 1][1] +\n            diffs[pointer][1].substring(0, diffs[pointer][1].length -\n                                        diffs[pointer - 1][1].length);\n        diffs[pointer + 1][1] = diffs[pointer - 1][1] + diffs[pointer + 1][1];\n        diffs.splice(pointer - 1, 1);\n        changes = true;\n      } else if (diffs[pointer][1].substring(0, diffs[pointer + 1][1].length) ==\n          diffs[pointer + 1][1]) {\n        // Shift the edit over the next equality.\n        diffs[pointer - 1][1] += diffs[pointer + 1][1];\n        diffs[pointer][1] =\n            diffs[pointer][1].substring(diffs[pointer + 1][1].length) +\n            diffs[pointer + 1][1];\n        diffs.splice(pointer + 1, 1);\n        changes = true;\n      }\n    }\n    pointer++;\n  }\n  // If shifts were made, the diff needs reordering and another shift sweep.\n  if (changes) {\n    this.diff_cleanupMerge(diffs);\n  }\n};\n\n\n/**\n * loc is a location in text1, compute and return the equivalent location in\n * text2.\n * e.g. 'The cat' vs 'The big cat', 1->1, 5->8\n * @param {!Array.<!diff_match_patch.Diff>} diffs Array of diff tuples.\n * @param {number} loc Location within text1.\n * @return {number} Location within text2.\n */\ndiff_match_patch.prototype.diff_xIndex = function(diffs, loc) {\n  var chars1 = 0;\n  var chars2 = 0;\n  var last_chars1 = 0;\n  var last_chars2 = 0;\n  var x;\n  for (x = 0; x < diffs.length; x++) {\n    if (diffs[x][0] !== DIFF_INSERT) {  // Equality or deletion.\n      chars1 += diffs[x][1].length;\n    }\n    if (diffs[x][0] !== DIFF_DELETE) {  // Equality or insertion.\n      chars2 += diffs[x][1].length;\n    }\n    if (chars1 > loc) {  // Overshot the location.\n      break;\n    }\n    last_chars1 = chars1;\n    last_chars2 = chars2;\n  }\n  // Was the location was deleted?\n  if (diffs.length != x && diffs[x][0] === DIFF_DELETE) {\n    return last_chars2;\n  }\n  // Add the remaining character length.\n  return last_chars2 + (loc - last_chars1);\n};\n\n\n/**\n * Convert a diff array into a pretty HTML report.\n * @param {!Array.<!diff_match_patch.Diff>} diffs Array of diff tuples.\n * @return {string} HTML representation.\n */\ndiff_match_patch.prototype.diff_prettyHtml = function(diffs) {\n  var html = [];\n  var pattern_amp = /&/g;\n  var pattern_lt = /</g;\n  var pattern_gt = />/g;\n  var pattern_para = /\\n/g;\n  for (var x = 0; x < diffs.length; x++) {\n    var op = diffs[x][0];    // Operation (insert, delete, equal)\n    var data = diffs[x][1];  // Text of change.\n    var text = data.replace(pattern_amp, '&amp;').replace(pattern_lt, '&lt;')\n        .replace(pattern_gt, '&gt;').replace(pattern_para, '&para;<br>');\n    switch (op) {\n      case DIFF_INSERT:\n        html[x] = '<ins style=\"background:#e6ffe6;\">' + text + '</ins>';\n        break;\n      case DIFF_DELETE:\n        html[x] = '<del style=\"background:#ffe6e6;\">' + text + '</del>';\n        break;\n      case DIFF_EQUAL:\n        html[x] = '<span>' + text + '</span>';\n        break;\n    }\n  }\n  return html.join('');\n};\n\n\n/**\n * Compute and return the source text (all equalities and deletions).\n * @param {!Array.<!diff_match_patch.Diff>} diffs Array of diff tuples.\n * @return {string} Source text.\n */\ndiff_match_patch.prototype.diff_text1 = function(diffs) {\n  var text = [];\n  for (var x = 0; x < diffs.length; x++) {\n    if (diffs[x][0] !== DIFF_INSERT) {\n      text[x] = diffs[x][1];\n    }\n  }\n  return text.join('');\n};\n\n\n/**\n * Compute and return the destination text (all equalities and insertions).\n * @param {!Array.<!diff_match_patch.Diff>} diffs Array of diff tuples.\n * @return {string} Destination text.\n */\ndiff_match_patch.prototype.diff_text2 = function(diffs) {\n  var text = [];\n  for (var x = 0; x < diffs.length; x++) {\n    if (diffs[x][0] !== DIFF_DELETE) {\n      text[x] = diffs[x][1];\n    }\n  }\n  return text.join('');\n};\n\n\n/**\n * Compute the Levenshtein distance; the number of inserted, deleted or\n * substituted characters.\n * @param {!Array.<!diff_match_patch.Diff>} diffs Array of diff tuples.\n * @return {number} Number of changes.\n */\ndiff_match_patch.prototype.diff_levenshtein = function(diffs) {\n  var levenshtein = 0;\n  var insertions = 0;\n  var deletions = 0;\n  for (var x = 0; x < diffs.length; x++) {\n    var op = diffs[x][0];\n    var data = diffs[x][1];\n    switch (op) {\n      case DIFF_INSERT:\n        insertions += data.length;\n        break;\n      case DIFF_DELETE:\n        deletions += data.length;\n        break;\n      case DIFF_EQUAL:\n        // A deletion and an insertion is one substitution.\n        levenshtein += Math.max(insertions, deletions);\n        insertions = 0;\n        deletions = 0;\n        break;\n    }\n  }\n  levenshtein += Math.max(insertions, deletions);\n  return levenshtein;\n};\n\n\n/**\n * Crush the diff into an encoded string which describes the operations\n * required to transform text1 into text2.\n * E.g. =3\\t-2\\t+ing  -> Keep 3 chars, delete 2 chars, insert 'ing'.\n * Operations are tab-separated.  Inserted text is escaped using %xx notation.\n * @param {!Array.<!diff_match_patch.Diff>} diffs Array of diff tuples.\n * @return {string} Delta text.\n */\ndiff_match_patch.prototype.diff_toDelta = function(diffs) {\n  var text = [];\n  for (var x = 0; x < diffs.length; x++) {\n    switch (diffs[x][0]) {\n      case DIFF_INSERT:\n        text[x] = '+' + encodeURI(diffs[x][1]);\n        break;\n      case DIFF_DELETE:\n        text[x] = '-' + diffs[x][1].length;\n        break;\n      case DIFF_EQUAL:\n        text[x] = '=' + diffs[x][1].length;\n        break;\n    }\n  }\n  return text.join('\\t').replace(/%20/g, ' ');\n};\n\n\n/**\n * Given the original text1, and an encoded string which describes the\n * operations required to transform text1 into text2, compute the full diff.\n * @param {string} text1 Source string for the diff.\n * @param {string} delta Delta text.\n * @return {!Array.<!diff_match_patch.Diff>} Array of diff tuples.\n * @throws {!Error} If invalid input.\n */\ndiff_match_patch.prototype.diff_fromDelta = function(text1, delta) {\n  var diffs = [];\n  var diffsLength = 0;  // Keeping our own length var is faster in JS.\n  var pointer = 0;  // Cursor in text1\n  var tokens = delta.split(/\\t/g);\n  for (var x = 0; x < tokens.length; x++) {\n    // Each token begins with a one character parameter which specifies the\n    // operation of this token (delete, insert, equality).\n    var param = tokens[x].substring(1);\n    switch (tokens[x].charAt(0)) {\n      case '+':\n        try {\n          diffs[diffsLength++] =\n              new diff_match_patch.Diff(DIFF_INSERT, decodeURI(param));\n        } catch (ex) {\n          // Malformed URI sequence.\n          throw new Error('Illegal escape in diff_fromDelta: ' + param);\n        }\n        break;\n      case '-':\n        // Fall through.\n      case '=':\n        var n = parseInt(param, 10);\n        if (isNaN(n) || n < 0) {\n          throw new Error('Invalid number in diff_fromDelta: ' + param);\n        }\n        var text = text1.substring(pointer, pointer += n);\n        if (tokens[x].charAt(0) == '=') {\n          diffs[diffsLength++] = new diff_match_patch.Diff(DIFF_EQUAL, text);\n        } else {\n          diffs[diffsLength++] = new diff_match_patch.Diff(DIFF_DELETE, text);\n        }\n        break;\n      default:\n        // Blank tokens are ok (from a trailing \\t).\n        // Anything else is an error.\n        if (tokens[x]) {\n          throw new Error('Invalid diff operation in diff_fromDelta: ' +\n                          tokens[x]);\n        }\n    }\n  }\n  if (pointer != text1.length) {\n    throw new Error('Delta length (' + pointer +\n        ') does not equal source text length (' + text1.length + ').');\n  }\n  return diffs;\n};\n\n\n//  MATCH FUNCTIONS\n\n\n/**\n * Locate the best instance of 'pattern' in 'text' near 'loc'.\n * @param {string} text The text to search.\n * @param {string} pattern The pattern to search for.\n * @param {number} loc The location to search around.\n * @return {number} Best match index or -1.\n */\ndiff_match_patch.prototype.match_main = function(text, pattern, loc) {\n  // Check for null inputs.\n  if (text == null || pattern == null || loc == null) {\n    throw new Error('Null input. (match_main)');\n  }\n\n  loc = Math.max(0, Math.min(loc, text.length));\n  if (text == pattern) {\n    // Shortcut (potentially not guaranteed by the algorithm)\n    return 0;\n  } else if (!text.length) {\n    // Nothing to match.\n    return -1;\n  } else if (text.substring(loc, loc + pattern.length) == pattern) {\n    // Perfect match at the perfect spot!  (Includes case of null pattern)\n    return loc;\n  } else {\n    // Do a fuzzy compare.\n    return this.match_bitap_(text, pattern, loc);\n  }\n};\n\n\n/**\n * Locate the best instance of 'pattern' in 'text' near 'loc' using the\n * Bitap algorithm.\n * @param {string} text The text to search.\n * @param {string} pattern The pattern to search for.\n * @param {number} loc The location to search around.\n * @return {number} Best match index or -1.\n * @private\n */\ndiff_match_patch.prototype.match_bitap_ = function(text, pattern, loc) {\n  if (pattern.length > this.Match_MaxBits) {\n    throw new Error('Pattern too long for this browser.');\n  }\n\n  // Initialise the alphabet.\n  var s = this.match_alphabet_(pattern);\n\n  var dmp = this;  // 'this' becomes 'window' in a closure.\n\n  /**\n   * Compute and return the score for a match with e errors and x location.\n   * Accesses loc and pattern through being a closure.\n   * @param {number} e Number of errors in match.\n   * @param {number} x Location of match.\n   * @return {number} Overall score for match (0.0 = good, 1.0 = bad).\n   * @private\n   */\n  function match_bitapScore_(e, x) {\n    var accuracy = e / pattern.length;\n    var proximity = Math.abs(loc - x);\n    if (!dmp.Match_Distance) {\n      // Dodge divide by zero error.\n      return proximity ? 1.0 : accuracy;\n    }\n    return accuracy + (proximity / dmp.Match_Distance);\n  }\n\n  // Highest score beyond which we give up.\n  var score_threshold = this.Match_Threshold;\n  // Is there a nearby exact match? (speedup)\n  var best_loc = text.indexOf(pattern, loc);\n  if (best_loc != -1) {\n    score_threshold = Math.min(match_bitapScore_(0, best_loc), score_threshold);\n    // What about in the other direction? (speedup)\n    best_loc = text.lastIndexOf(pattern, loc + pattern.length);\n    if (best_loc != -1) {\n      score_threshold =\n          Math.min(match_bitapScore_(0, best_loc), score_threshold);\n    }\n  }\n\n  // Initialise the bit arrays.\n  var matchmask = 1 << (pattern.length - 1);\n  best_loc = -1;\n\n  var bin_min, bin_mid;\n  var bin_max = pattern.length + text.length;\n  var last_rd;\n  for (var d = 0; d < pattern.length; d++) {\n    // Scan for the best match; each iteration allows for one more error.\n    // Run a binary search to determine how far from 'loc' we can stray at this\n    // error level.\n    bin_min = 0;\n    bin_mid = bin_max;\n    while (bin_min < bin_mid) {\n      if (match_bitapScore_(d, loc + bin_mid) <= score_threshold) {\n        bin_min = bin_mid;\n      } else {\n        bin_max = bin_mid;\n      }\n      bin_mid = Math.floor((bin_max - bin_min) / 2 + bin_min);\n    }\n    // Use the result from this iteration as the maximum for the next.\n    bin_max = bin_mid;\n    var start = Math.max(1, loc - bin_mid + 1);\n    var finish = Math.min(loc + bin_mid, text.length) + pattern.length;\n\n    var rd = Array(finish + 2);\n    rd[finish + 1] = (1 << d) - 1;\n    for (var j = finish; j >= start; j--) {\n      // The alphabet (s) is a sparse hash, so the following line generates\n      // warnings.\n      var charMatch = s[text.charAt(j - 1)];\n      if (d === 0) {  // First pass: exact match.\n        rd[j] = ((rd[j + 1] << 1) | 1) & charMatch;\n      } else {  // Subsequent passes: fuzzy match.\n        rd[j] = (((rd[j + 1] << 1) | 1) & charMatch) |\n                (((last_rd[j + 1] | last_rd[j]) << 1) | 1) |\n                last_rd[j + 1];\n      }\n      if (rd[j] & matchmask) {\n        var score = match_bitapScore_(d, j - 1);\n        // This match will almost certainly be better than any existing match.\n        // But check anyway.\n        if (score <= score_threshold) {\n          // Told you so.\n          score_threshold = score;\n          best_loc = j - 1;\n          if (best_loc > loc) {\n            // When passing loc, don't exceed our current distance from loc.\n            start = Math.max(1, 2 * loc - best_loc);\n          } else {\n            // Already passed loc, downhill from here on in.\n            break;\n          }\n        }\n      }\n    }\n    // No hope for a (better) match at greater error levels.\n    if (match_bitapScore_(d + 1, loc) > score_threshold) {\n      break;\n    }\n    last_rd = rd;\n  }\n  return best_loc;\n};\n\n\n/**\n * Initialise the alphabet for the Bitap algorithm.\n * @param {string} pattern The text to encode.\n * @return {!Object} Hash of character locations.\n * @private\n */\ndiff_match_patch.prototype.match_alphabet_ = function(pattern) {\n  var s = {};\n  for (var i = 0; i < pattern.length; i++) {\n    s[pattern.charAt(i)] = 0;\n  }\n  for (var i = 0; i < pattern.length; i++) {\n    s[pattern.charAt(i)] |= 1 << (pattern.length - i - 1);\n  }\n  return s;\n};\n\n\n//  PATCH FUNCTIONS\n\n\n/**\n * Increase the context until it is unique,\n * but don't let the pattern expand beyond Match_MaxBits.\n * @param {!diff_match_patch.patch_obj} patch The patch to grow.\n * @param {string} text Source text.\n * @private\n */\ndiff_match_patch.prototype.patch_addContext_ = function(patch, text) {\n  if (text.length == 0) {\n    return;\n  }\n  if (patch.start2 === null) {\n    throw Error('patch not initialized');\n  }\n  var pattern = text.substring(patch.start2, patch.start2 + patch.length1);\n  var padding = 0;\n\n  // Look for the first and last matches of pattern in text.  If two different\n  // matches are found, increase the pattern length.\n  while (text.indexOf(pattern) != text.lastIndexOf(pattern) &&\n         pattern.length < this.Match_MaxBits - this.Patch_Margin -\n         this.Patch_Margin) {\n    padding += this.Patch_Margin;\n    pattern = text.substring(patch.start2 - padding,\n                             patch.start2 + patch.length1 + padding);\n  }\n  // Add one chunk for good luck.\n  padding += this.Patch_Margin;\n\n  // Add the prefix.\n  var prefix = text.substring(patch.start2 - padding, patch.start2);\n  if (prefix) {\n    patch.diffs.unshift(new diff_match_patch.Diff(DIFF_EQUAL, prefix));\n  }\n  // Add the suffix.\n  var suffix = text.substring(patch.start2 + patch.length1,\n                              patch.start2 + patch.length1 + padding);\n  if (suffix) {\n    patch.diffs.push(new diff_match_patch.Diff(DIFF_EQUAL, suffix));\n  }\n\n  // Roll back the start points.\n  patch.start1 -= prefix.length;\n  patch.start2 -= prefix.length;\n  // Extend the lengths.\n  patch.length1 += prefix.length + suffix.length;\n  patch.length2 += prefix.length + suffix.length;\n};\n\n\n/**\n * Compute a list of patches to turn text1 into text2.\n * Use diffs if provided, otherwise compute it ourselves.\n * There are four ways to call this function, depending on what data is\n * available to the caller:\n * Method 1:\n * a = text1, b = text2\n * Method 2:\n * a = diffs\n * Method 3 (optimal):\n * a = text1, b = diffs\n * Method 4 (deprecated, use method 3):\n * a = text1, b = text2, c = diffs\n *\n * @param {string|!Array.<!diff_match_patch.Diff>} a text1 (methods 1,3,4) or\n * Array of diff tuples for text1 to text2 (method 2).\n * @param {string|!Array.<!diff_match_patch.Diff>=} opt_b text2 (methods 1,4) or\n * Array of diff tuples for text1 to text2 (method 3) or undefined (method 2).\n * @param {string|!Array.<!diff_match_patch.Diff>=} opt_c Array of diff tuples\n * for text1 to text2 (method 4) or undefined (methods 1,2,3).\n * @return {!Array.<!diff_match_patch.patch_obj>} Array of Patch objects.\n */\ndiff_match_patch.prototype.patch_make = function(a, opt_b, opt_c) {\n  var text1, diffs;\n  if (typeof a == 'string' && typeof opt_b == 'string' &&\n      typeof opt_c == 'undefined') {\n    // Method 1: text1, text2\n    // Compute diffs from text1 and text2.\n    text1 = /** @type {string} */(a);\n    diffs = this.diff_main(text1, /** @type {string} */(opt_b), true);\n    if (diffs.length > 2) {\n      this.diff_cleanupSemantic(diffs);\n      this.diff_cleanupEfficiency(diffs);\n    }\n  } else if (a && typeof a == 'object' && typeof opt_b == 'undefined' &&\n      typeof opt_c == 'undefined') {\n    // Method 2: diffs\n    // Compute text1 from diffs.\n    diffs = /** @type {!Array.<!diff_match_patch.Diff>} */(a);\n    text1 = this.diff_text1(diffs);\n  } else if (typeof a == 'string' && opt_b && typeof opt_b == 'object' &&\n      typeof opt_c == 'undefined') {\n    // Method 3: text1, diffs\n    text1 = /** @type {string} */(a);\n    diffs = /** @type {!Array.<!diff_match_patch.Diff>} */(opt_b);\n  } else if (typeof a == 'string' && typeof opt_b == 'string' &&\n      opt_c && typeof opt_c == 'object') {\n    // Method 4: text1, text2, diffs\n    // text2 is not used.\n    text1 = /** @type {string} */(a);\n    diffs = /** @type {!Array.<!diff_match_patch.Diff>} */(opt_c);\n  } else {\n    throw new Error('Unknown call format to patch_make.');\n  }\n\n  if (diffs.length === 0) {\n    return [];  // Get rid of the null case.\n  }\n  var patches = [];\n  var patch = new diff_match_patch.patch_obj();\n  var patchDiffLength = 0;  // Keeping our own length var is faster in JS.\n  var char_count1 = 0;  // Number of characters into the text1 string.\n  var char_count2 = 0;  // Number of characters into the text2 string.\n  // Start with text1 (prepatch_text) and apply the diffs until we arrive at\n  // text2 (postpatch_text).  We recreate the patches one by one to determine\n  // context info.\n  var prepatch_text = text1;\n  var postpatch_text = text1;\n  for (var x = 0; x < diffs.length; x++) {\n    var diff_type = diffs[x][0];\n    var diff_text = diffs[x][1];\n\n    if (!patchDiffLength && diff_type !== DIFF_EQUAL) {\n      // A new patch starts here.\n      patch.start1 = char_count1;\n      patch.start2 = char_count2;\n    }\n\n    switch (diff_type) {\n      case DIFF_INSERT:\n        patch.diffs[patchDiffLength++] = diffs[x];\n        patch.length2 += diff_text.length;\n        postpatch_text = postpatch_text.substring(0, char_count2) + diff_text +\n                         postpatch_text.substring(char_count2);\n        break;\n      case DIFF_DELETE:\n        patch.length1 += diff_text.length;\n        patch.diffs[patchDiffLength++] = diffs[x];\n        postpatch_text = postpatch_text.substring(0, char_count2) +\n                         postpatch_text.substring(char_count2 +\n                             diff_text.length);\n        break;\n      case DIFF_EQUAL:\n        if (diff_text.length <= 2 * this.Patch_Margin &&\n            patchDiffLength && diffs.length != x + 1) {\n          // Small equality inside a patch.\n          patch.diffs[patchDiffLength++] = diffs[x];\n          patch.length1 += diff_text.length;\n          patch.length2 += diff_text.length;\n        } else if (diff_text.length >= 2 * this.Patch_Margin) {\n          // Time for a new patch.\n          if (patchDiffLength) {\n            this.patch_addContext_(patch, prepatch_text);\n            patches.push(patch);\n            patch = new diff_match_patch.patch_obj();\n            patchDiffLength = 0;\n            // Unlike Unidiff, our patch lists have a rolling context.\n            // https://github.com/google/diff-match-patch/wiki/Unidiff\n            // Update prepatch text & pos to reflect the application of the\n            // just completed patch.\n            prepatch_text = postpatch_text;\n            char_count1 = char_count2;\n          }\n        }\n        break;\n    }\n\n    // Update the current character count.\n    if (diff_type !== DIFF_INSERT) {\n      char_count1 += diff_text.length;\n    }\n    if (diff_type !== DIFF_DELETE) {\n      char_count2 += diff_text.length;\n    }\n  }\n  // Pick up the leftover patch if not empty.\n  if (patchDiffLength) {\n    this.patch_addContext_(patch, prepatch_text);\n    patches.push(patch);\n  }\n\n  return patches;\n};\n\n\n/**\n * Given an array of patches, return another array that is identical.\n * @param {!Array.<!diff_match_patch.patch_obj>} patches Array of Patch objects.\n * @return {!Array.<!diff_match_patch.patch_obj>} Array of Patch objects.\n */\ndiff_match_patch.prototype.patch_deepCopy = function(patches) {\n  // Making deep copies is hard in JavaScript.\n  var patchesCopy = [];\n  for (var x = 0; x < patches.length; x++) {\n    var patch = patches[x];\n    var patchCopy = new diff_match_patch.patch_obj();\n    patchCopy.diffs = [];\n    for (var y = 0; y < patch.diffs.length; y++) {\n      patchCopy.diffs[y] =\n          new diff_match_patch.Diff(patch.diffs[y][0], patch.diffs[y][1]);\n    }\n    patchCopy.start1 = patch.start1;\n    patchCopy.start2 = patch.start2;\n    patchCopy.length1 = patch.length1;\n    patchCopy.length2 = patch.length2;\n    patchesCopy[x] = patchCopy;\n  }\n  return patchesCopy;\n};\n\n\n/**\n * Merge a set of patches onto the text.  Return a patched text, as well\n * as a list of true/false values indicating which patches were applied.\n * @param {!Array.<!diff_match_patch.patch_obj>} patches Array of Patch objects.\n * @param {string} text Old text.\n * @return {!Array.<string|!Array.<boolean>>} Two element Array, containing the\n *      new text and an array of boolean values.\n */\ndiff_match_patch.prototype.patch_apply = function(patches, text) {\n  if (patches.length == 0) {\n    return [text, []];\n  }\n\n  // Deep copy the patches so that no changes are made to originals.\n  patches = this.patch_deepCopy(patches);\n\n  var nullPadding = this.patch_addPadding(patches);\n  text = nullPadding + text + nullPadding;\n\n  this.patch_splitMax(patches);\n  // delta keeps track of the offset between the expected and actual location\n  // of the previous patch.  If there are patches expected at positions 10 and\n  // 20, but the first patch was found at 12, delta is 2 and the second patch\n  // has an effective expected position of 22.\n  var delta = 0;\n  var results = [];\n  for (var x = 0; x < patches.length; x++) {\n    var expected_loc = patches[x].start2 + delta;\n    var text1 = this.diff_text1(patches[x].diffs);\n    var start_loc;\n    var end_loc = -1;\n    if (text1.length > this.Match_MaxBits) {\n      // patch_splitMax will only provide an oversized pattern in the case of\n      // a monster delete.\n      start_loc = this.match_main(text, text1.substring(0, this.Match_MaxBits),\n                                  expected_loc);\n      if (start_loc != -1) {\n        end_loc = this.match_main(text,\n            text1.substring(text1.length - this.Match_MaxBits),\n            expected_loc + text1.length - this.Match_MaxBits);\n        if (end_loc == -1 || start_loc >= end_loc) {\n          // Can't find valid trailing context.  Drop this patch.\n          start_loc = -1;\n        }\n      }\n    } else {\n      start_loc = this.match_main(text, text1, expected_loc);\n    }\n    if (start_loc == -1) {\n      // No match found.  :(\n      results[x] = false;\n      // Subtract the delta for this failed patch from subsequent patches.\n      delta -= patches[x].length2 - patches[x].length1;\n    } else {\n      // Found a match.  :)\n      results[x] = true;\n      delta = start_loc - expected_loc;\n      var text2;\n      if (end_loc == -1) {\n        text2 = text.substring(start_loc, start_loc + text1.length);\n      } else {\n        text2 = text.substring(start_loc, end_loc + this.Match_MaxBits);\n      }\n      if (text1 == text2) {\n        // Perfect match, just shove the replacement text in.\n        text = text.substring(0, start_loc) +\n               this.diff_text2(patches[x].diffs) +\n               text.substring(start_loc + text1.length);\n      } else {\n        // Imperfect match.  Run a diff to get a framework of equivalent\n        // indices.\n        var diffs = this.diff_main(text1, text2, false);\n        if (text1.length > this.Match_MaxBits &&\n            this.diff_levenshtein(diffs) / text1.length >\n            this.Patch_DeleteThreshold) {\n          // The end points match, but the content is unacceptably bad.\n          results[x] = false;\n        } else {\n          this.diff_cleanupSemanticLossless(diffs);\n          var index1 = 0;\n          var index2;\n          for (var y = 0; y < patches[x].diffs.length; y++) {\n            var mod = patches[x].diffs[y];\n            if (mod[0] !== DIFF_EQUAL) {\n              index2 = this.diff_xIndex(diffs, index1);\n            }\n            if (mod[0] === DIFF_INSERT) {  // Insertion\n              text = text.substring(0, start_loc + index2) + mod[1] +\n                     text.substring(start_loc + index2);\n            } else if (mod[0] === DIFF_DELETE) {  // Deletion\n              text = text.substring(0, start_loc + index2) +\n                     text.substring(start_loc + this.diff_xIndex(diffs,\n                         index1 + mod[1].length));\n            }\n            if (mod[0] !== DIFF_DELETE) {\n              index1 += mod[1].length;\n            }\n          }\n        }\n      }\n    }\n  }\n  // Strip the padding off.\n  text = text.substring(nullPadding.length, text.length - nullPadding.length);\n  return [text, results];\n};\n\n\n/**\n * Add some padding on text start and end so that edges can match something.\n * Intended to be called only from within patch_apply.\n * @param {!Array.<!diff_match_patch.patch_obj>} patches Array of Patch objects.\n * @return {string} The padding string added to each side.\n */\ndiff_match_patch.prototype.patch_addPadding = function(patches) {\n  var paddingLength = this.Patch_Margin;\n  var nullPadding = '';\n  for (var x = 1; x <= paddingLength; x++) {\n    nullPadding += String.fromCharCode(x);\n  }\n\n  // Bump all the patches forward.\n  for (var x = 0; x < patches.length; x++) {\n    patches[x].start1 += paddingLength;\n    patches[x].start2 += paddingLength;\n  }\n\n  // Add some padding on start of first diff.\n  var patch = patches[0];\n  var diffs = patch.diffs;\n  if (diffs.length == 0 || diffs[0][0] != DIFF_EQUAL) {\n    // Add nullPadding equality.\n    diffs.unshift(new diff_match_patch.Diff(DIFF_EQUAL, nullPadding));\n    patch.start1 -= paddingLength;  // Should be 0.\n    patch.start2 -= paddingLength;  // Should be 0.\n    patch.length1 += paddingLength;\n    patch.length2 += paddingLength;\n  } else if (paddingLength > diffs[0][1].length) {\n    // Grow first equality.\n    var extraLength = paddingLength - diffs[0][1].length;\n    diffs[0][1] = nullPadding.substring(diffs[0][1].length) + diffs[0][1];\n    patch.start1 -= extraLength;\n    patch.start2 -= extraLength;\n    patch.length1 += extraLength;\n    patch.length2 += extraLength;\n  }\n\n  // Add some padding on end of last diff.\n  patch = patches[patches.length - 1];\n  diffs = patch.diffs;\n  if (diffs.length == 0 || diffs[diffs.length - 1][0] != DIFF_EQUAL) {\n    // Add nullPadding equality.\n    diffs.push(new diff_match_patch.Diff(DIFF_EQUAL, nullPadding));\n    patch.length1 += paddingLength;\n    patch.length2 += paddingLength;\n  } else if (paddingLength > diffs[diffs.length - 1][1].length) {\n    // Grow last equality.\n    var extraLength = paddingLength - diffs[diffs.length - 1][1].length;\n    diffs[diffs.length - 1][1] += nullPadding.substring(0, extraLength);\n    patch.length1 += extraLength;\n    patch.length2 += extraLength;\n  }\n\n  return nullPadding;\n};\n\n\n/**\n * Look through the patches and break up any which are longer than the maximum\n * limit of the match algorithm.\n * Intended to be called only from within patch_apply.\n * @param {!Array.<!diff_match_patch.patch_obj>} patches Array of Patch objects.\n */\ndiff_match_patch.prototype.patch_splitMax = function(patches) {\n  var patch_size = this.Match_MaxBits;\n  for (var x = 0; x < patches.length; x++) {\n    if (patches[x].length1 <= patch_size) {\n      continue;\n    }\n    var bigpatch = patches[x];\n    // Remove the big old patch.\n    patches.splice(x--, 1);\n    var start1 = bigpatch.start1;\n    var start2 = bigpatch.start2;\n    var precontext = '';\n    while (bigpatch.diffs.length !== 0) {\n      // Create one of several smaller patches.\n      var patch = new diff_match_patch.patch_obj();\n      var empty = true;\n      patch.start1 = start1 - precontext.length;\n      patch.start2 = start2 - precontext.length;\n      if (precontext !== '') {\n        patch.length1 = patch.length2 = precontext.length;\n        patch.diffs.push(new diff_match_patch.Diff(DIFF_EQUAL, precontext));\n      }\n      while (bigpatch.diffs.length !== 0 &&\n             patch.length1 < patch_size - this.Patch_Margin) {\n        var diff_type = bigpatch.diffs[0][0];\n        var diff_text = bigpatch.diffs[0][1];\n        if (diff_type === DIFF_INSERT) {\n          // Insertions are harmless.\n          patch.length2 += diff_text.length;\n          start2 += diff_text.length;\n          patch.diffs.push(bigpatch.diffs.shift());\n          empty = false;\n        } else if (diff_type === DIFF_DELETE && patch.diffs.length == 1 &&\n                   patch.diffs[0][0] == DIFF_EQUAL &&\n                   diff_text.length > 2 * patch_size) {\n          // This is a large deletion.  Let it pass in one chunk.\n          patch.length1 += diff_text.length;\n          start1 += diff_text.length;\n          empty = false;\n          patch.diffs.push(new diff_match_patch.Diff(diff_type, diff_text));\n          bigpatch.diffs.shift();\n        } else {\n          // Deletion or equality.  Only take as much as we can stomach.\n          diff_text = diff_text.substring(0,\n              patch_size - patch.length1 - this.Patch_Margin);\n          patch.length1 += diff_text.length;\n          start1 += diff_text.length;\n          if (diff_type === DIFF_EQUAL) {\n            patch.length2 += diff_text.length;\n            start2 += diff_text.length;\n          } else {\n            empty = false;\n          }\n          patch.diffs.push(new diff_match_patch.Diff(diff_type, diff_text));\n          if (diff_text == bigpatch.diffs[0][1]) {\n            bigpatch.diffs.shift();\n          } else {\n            bigpatch.diffs[0][1] =\n                bigpatch.diffs[0][1].substring(diff_text.length);\n          }\n        }\n      }\n      // Compute the head context for the next patch.\n      precontext = this.diff_text2(patch.diffs);\n      precontext =\n          precontext.substring(precontext.length - this.Patch_Margin);\n      // Append the end context for this patch.\n      var postcontext = this.diff_text1(bigpatch.diffs)\n                            .substring(0, this.Patch_Margin);\n      if (postcontext !== '') {\n        patch.length1 += postcontext.length;\n        patch.length2 += postcontext.length;\n        if (patch.diffs.length !== 0 &&\n            patch.diffs[patch.diffs.length - 1][0] === DIFF_EQUAL) {\n          patch.diffs[patch.diffs.length - 1][1] += postcontext;\n        } else {\n          patch.diffs.push(new diff_match_patch.Diff(DIFF_EQUAL, postcontext));\n        }\n      }\n      if (!empty) {\n        patches.splice(++x, 0, patch);\n      }\n    }\n  }\n};\n\n\n/**\n * Take a list of patches and return a textual representation.\n * @param {!Array.<!diff_match_patch.patch_obj>} patches Array of Patch objects.\n * @return {string} Text representation of patches.\n */\ndiff_match_patch.prototype.patch_toText = function(patches) {\n  var text = [];\n  for (var x = 0; x < patches.length; x++) {\n    text[x] = patches[x];\n  }\n  return text.join('');\n};\n\n\n/**\n * Parse a textual representation of patches and return a list of Patch objects.\n * @param {string} textline Text representation of patches.\n * @return {!Array.<!diff_match_patch.patch_obj>} Array of Patch objects.\n * @throws {!Error} If invalid input.\n */\ndiff_match_patch.prototype.patch_fromText = function(textline) {\n  var patches = [];\n  if (!textline) {\n    return patches;\n  }\n  var text = textline.split('\\n');\n  var textPointer = 0;\n  var patchHeader = /^@@ -(\\d+),?(\\d*) \\+(\\d+),?(\\d*) @@$/;\n  while (textPointer < text.length) {\n    var m = text[textPointer].match(patchHeader);\n    if (!m) {\n      throw new Error('Invalid patch string: ' + text[textPointer]);\n    }\n    var patch = new diff_match_patch.patch_obj();\n    patches.push(patch);\n    patch.start1 = parseInt(m[1], 10);\n    if (m[2] === '') {\n      patch.start1--;\n      patch.length1 = 1;\n    } else if (m[2] == '0') {\n      patch.length1 = 0;\n    } else {\n      patch.start1--;\n      patch.length1 = parseInt(m[2], 10);\n    }\n\n    patch.start2 = parseInt(m[3], 10);\n    if (m[4] === '') {\n      patch.start2--;\n      patch.length2 = 1;\n    } else if (m[4] == '0') {\n      patch.length2 = 0;\n    } else {\n      patch.start2--;\n      patch.length2 = parseInt(m[4], 10);\n    }\n    textPointer++;\n\n    while (textPointer < text.length) {\n      var sign = text[textPointer].charAt(0);\n      try {\n        var line = decodeURI(text[textPointer].substring(1));\n      } catch (ex) {\n        // Malformed URI sequence.\n        throw new Error('Illegal escape in patch_fromText: ' + line);\n      }\n      if (sign == '-') {\n        // Deletion.\n        patch.diffs.push(new diff_match_patch.Diff(DIFF_DELETE, line));\n      } else if (sign == '+') {\n        // Insertion.\n        patch.diffs.push(new diff_match_patch.Diff(DIFF_INSERT, line));\n      } else if (sign == ' ') {\n        // Minor equality.\n        patch.diffs.push(new diff_match_patch.Diff(DIFF_EQUAL, line));\n      } else if (sign == '@') {\n        // Start of next patch.\n        break;\n      } else if (sign === '') {\n        // Blank line?  Whatever.\n      } else {\n        // WTF?\n        throw new Error('Invalid patch mode \"' + sign + '\" in: ' + line);\n      }\n      textPointer++;\n    }\n  }\n  return patches;\n};\n\n\n/**\n * Class representing one patch operation.\n * @constructor\n */\ndiff_match_patch.patch_obj = function() {\n  /** @type {!Array.<!diff_match_patch.Diff>} */\n  this.diffs = [];\n  /** @type {?number} */\n  this.start1 = null;\n  /** @type {?number} */\n  this.start2 = null;\n  /** @type {number} */\n  this.length1 = 0;\n  /** @type {number} */\n  this.length2 = 0;\n};\n\n\n/**\n * Emulate GNU diff's format.\n * Header: @@ -382,8 +481,9 @@\n * Indices are printed as 1-based, not 0-based.\n * @return {string} The GNU diff string.\n */\ndiff_match_patch.patch_obj.prototype.toString = function() {\n  var coords1, coords2;\n  if (this.length1 === 0) {\n    coords1 = this.start1 + ',0';\n  } else if (this.length1 == 1) {\n    coords1 = this.start1 + 1;\n  } else {\n    coords1 = (this.start1 + 1) + ',' + this.length1;\n  }\n  if (this.length2 === 0) {\n    coords2 = this.start2 + ',0';\n  } else if (this.length2 == 1) {\n    coords2 = this.start2 + 1;\n  } else {\n    coords2 = (this.start2 + 1) + ',' + this.length2;\n  }\n  var text = ['@@ -' + coords1 + ' +' + coords2 + ' @@\\n'];\n  var op;\n  // Escape the body of the patch with %xx notation.\n  for (var x = 0; x < this.diffs.length; x++) {\n    switch (this.diffs[x][0]) {\n      case DIFF_INSERT:\n        op = '+';\n        break;\n      case DIFF_DELETE:\n        op = '-';\n        break;\n      case DIFF_EQUAL:\n        op = ' ';\n        break;\n    }\n    text[x + 1] = op + encodeURI(this.diffs[x][1]) + '\\n';\n  }\n  return text.join('').replace(/%20/g, ' ');\n};\n\n// CLOSURE:begin_strip\n// Lines below here will not be included in the Closure-compatible library.\n\n// Export these global variables so that they survive Google's JS compiler.\n// In a browser, 'this' will be 'window'.\n// Users of node.js should 'require' the uncompressed version since Google's\n// JS compiler may break the following exports for non-browser environments.\n/** @suppress {globalThis} */\nthis['diff_match_patch'] = diff_match_patch;\n/** @suppress {globalThis} */\nthis['DIFF_DELETE'] = DIFF_DELETE;\n/** @suppress {globalThis} */\nthis['DIFF_INSERT'] = DIFF_INSERT;\n/** @suppress {globalThis} */\nthis['DIFF_EQUAL'] = DIFF_EQUAL;\n"
  },
  {
    "path": "javascript/tests/diff_match_patch_test.html",
    "content": "<!DOCTYPE html PUBLIC \"-//W3C//DTD HTML 4.01//EN\" \"http://www.w3.org/TR/html4/strict.dtd\">\n<!--\n  Test Harness for diff_match_patch.js and diff_match_patch_uncompressed.js\n\n  Copyright 2018 The diff-match-patch Authors.\n  https://github.com/google/diff-match-patch\n\n  Licensed under the Apache License, Version 2.0 (the \"License\");\n  you may not use this file except in compliance with the License.\n  You may obtain a copy of the License at\n\n    http://www.apache.org/licenses/LICENSE-2.0\n\n  Unless required by applicable law or agreed to in writing, software\n  distributed under the License is distributed on an \"AS IS\" BASIS,\n  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  See the License for the specific language governing permissions and\n  limitations under the License.\n-->\n\n<html>\n  <head>\n    <script type=\"text/javascript\">\n      // Depending on the URL argument, test the compressed or uncompressed version.\n      var compressed = (document.location.search == '?compressed');\n      if (compressed) {\n        document.write('<TITLE>Test harness for diff_match_patch.js</TITLE>');\n        document.write('<scr'+'ipt type=\"text/javascript\" src=\"../diff_match_patch.js\"></scr'+'ipt>');\n      } else {\n        document.write('<TITLE>Test harness for diff_match_patch_uncompressed.js</TITLE>');\n        document.write('<scr'+'ipt type=\"text/javascript\" src=\"../diff_match_patch_uncompressed.js\"></scr'+'ipt>');\n      }\n    </script>\n\n    <script type=\"text/javascript\" src=\"diff_match_patch_test.js\"></script>\n\n    <script type=\"text/javascript\">\n      // Counters for unit test results.\n      var test_good = 0;\n      var test_bad = 0;\n\n      // If expected and actual are the identical, print 'Ok', otherwise 'Fail!'\n      function assertEquals(msg, expected, actual) {\n        if (typeof actual == 'undefined') {\n          // msg is optional.\n          actual = expected;\n          expected = msg;\n          msg = 'Expected: \\'' + expected + '\\' Actual: \\'' + actual + '\\'';\n        }\n        if (expected === actual) {\n          document.write('<FONT COLOR=\"#009900\">Ok</FONT><BR>');\n          test_good++;\n          return true;\n        } else {\n          document.write('<FONT COLOR=\"#990000\"><BIG>Fail!</BIG></FONT><BR>');\n          msg = msg.replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;');\n          document.write('<code>' + msg + '</code><BR>');\n          test_bad++;\n          return false;\n        }\n      }\n\n      function assertTrue(msg, actual) {\n        if (typeof actual == 'undefined') {\n          // msg is optional.\n          actual = msg;\n          return assertEquals(true, actual);\n        } else {\n          return assertEquals(msg, true, actual);\n        }\n      }\n\n      function assertFalse(msg, actual) {\n        if (typeof actual == 'undefined') {\n          // msg is optional.\n          actual = msg;\n          return assertEquals(false, actual);\n        } else {\n          return assertEquals(msg, false, actual);\n        }\n      }\n\n      function runTests() {\n        for (var x = 0; x < tests.length; x++) {\n          document.write('<H3>' + tests[x] + ':</H3>');\n          eval(tests[x] + '()');\n        }\n      }\n\n      var tests = [\n          'testDiffCommonPrefix',\n          'testDiffCommonSuffix',\n          'testDiffCommonOverlap',\n          'testDiffHalfMatch',\n          'testDiffLinesToChars',\n          'testDiffCharsToLines',\n          'testDiffCleanupMerge',\n          'testDiffCleanupSemanticLossless',\n          'testDiffCleanupSemantic',\n          'testDiffCleanupEfficiency',\n          'testDiffPrettyHtml',\n          'testDiffText',\n          'testDiffDelta',\n          'testDiffXIndex',\n          'testDiffLevenshtein',\n          'testDiffBisect',\n          'testDiffMain',\n\n          'testMatchAlphabet',\n          'testMatchBitap',\n          'testMatchMain',\n\n          'testPatchObj',\n          'testPatchFromText',\n          'testPatchToText',\n          'testPatchAddContext',\n          'testPatchMake',\n          'testPatchSplitMax',\n          'testPatchAddPadding',\n          'testPatchApply'];\n\n    </script>\n  </head>\n  <body>\n\n    <script type=\"text/javascript\">\n      if (compressed) {\n        document.write('<H1>Test harness for diff_match_patch.js</H1>');\n        document.write('[ Switch to <A HREF=\"?uncompressed\">Uncompressed</A>. ]');\n      } else {\n        document.write('<H1>Test harness for diff_match_patch_uncompressed.js</H1>');\n        document.write('[ Switch to <A HREF=\"?compressed\">Compressed</A>. ]');\n      }\n    </script>\n\n    <P>If debugging errors, start with the first reported error,\n    subsequent tests often rely on earlier ones.</P>\n\n    <script type=\"text/javascript\">\n      var startTime = (new Date()).getTime();\n      runTests();\n      var endTime = (new Date()).getTime();\n      document.write('<H3>Done.</H3>');\n      document.write('<P>Tests passed: ' + test_good + '<BR>Tests failed: ' + test_bad + '</P>');\n      document.write('<P>Total time: ' + (endTime - startTime) + ' ms</P>');\n    </script>\n  </body>\n</html>\n"
  },
  {
    "path": "javascript/tests/diff_match_patch_test.js",
    "content": "/**\n * Diff Match and Patch -- Test Harness\n * Copyright 2018 The diff-match-patch Authors.\n * https://github.com/google/diff-match-patch\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *   http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n\n// If expected and actual are the equivalent, pass the test.\nfunction assertEquivalent(msg, expected, actual) {\n  if (typeof actual == 'undefined') {\n    // msg is optional.\n    actual = expected;\n    expected = msg;\n    msg = 'Expected: \\'' + expected + '\\' Actual: \\'' + actual + '\\'';\n  }\n  if (_equivalent(expected, actual)) {\n    return assertEquals(msg, String(expected), String(actual));\n  } else {\n    return assertEquals(msg, expected, actual);\n  }\n}\n\n\n// Are a and b the equivalent? -- Recursive.\nfunction _equivalent(a, b) {\n  if (a == b) {\n    return true;\n  }\n  if (typeof a == 'object' && typeof b == 'object' && a !== null && b !== null) {\n    if (a.toString() != b.toString()) {\n      return false;\n    }\n    for (var p in a) {\n      if (a.hasOwnProperty(p) && !_equivalent(a[p], b[p])) {\n        return false;\n      }\n    }\n    for (var p in b) {\n      if (a.hasOwnProperty(p) && !_equivalent(a[p], b[p])) {\n        return false;\n      }\n    }\n    return true;\n  }\n  return false;\n}\n\n\nfunction diff_rebuildtexts(diffs) {\n  // Construct the two texts which made up the diff originally.\n  var text1 = '';\n  var text2 = '';\n  for (var x = 0; x < diffs.length; x++) {\n    if (diffs[x][0] != DIFF_INSERT) {\n      text1 += diffs[x][1];\n    }\n    if (diffs[x][0] != DIFF_DELETE) {\n      text2 += diffs[x][1];\n    }\n  }\n  return [text1, text2];\n}\n\nvar dmp = new diff_match_patch();\n\n\n// DIFF TEST FUNCTIONS\n\n\nfunction testDiffCommonPrefix() {\n  // Detect any common prefix.\n  // Null case.\n  assertEquals(0, dmp.diff_commonPrefix('abc', 'xyz'));\n\n  // Non-null case.\n  assertEquals(4, dmp.diff_commonPrefix('1234abcdef', '1234xyz'));\n\n  // Whole case.\n  assertEquals(4, dmp.diff_commonPrefix('1234', '1234xyz'));\n}\n\nfunction testDiffCommonSuffix() {\n  // Detect any common suffix.\n  // Null case.\n  assertEquals(0, dmp.diff_commonSuffix('abc', 'xyz'));\n\n  // Non-null case.\n  assertEquals(4, dmp.diff_commonSuffix('abcdef1234', 'xyz1234'));\n\n  // Whole case.\n  assertEquals(4, dmp.diff_commonSuffix('1234', 'xyz1234'));\n}\n\nfunction testDiffCommonOverlap() {\n  // Detect any suffix/prefix overlap.\n  // Null case.\n  assertEquals(0, dmp.diff_commonOverlap_('', 'abcd'));\n\n  // Whole case.\n  assertEquals(3, dmp.diff_commonOverlap_('abc', 'abcd'));\n\n  // No overlap.\n  assertEquals(0, dmp.diff_commonOverlap_('123456', 'abcd'));\n\n  // Overlap.\n  assertEquals(3, dmp.diff_commonOverlap_('123456xxx', 'xxxabcd'));\n\n  // Unicode.\n  // Some overly clever languages (C#) may treat ligatures as equal to their\n  // component letters.  E.g. U+FB01 == 'fi'\n  assertEquals(0, dmp.diff_commonOverlap_('fi', '\\ufb01i'));\n}\n\nfunction testDiffHalfMatch() {\n  // Detect a halfmatch.\n  dmp.Diff_Timeout = 1;\n  // No match.\n  assertEquals(null, dmp.diff_halfMatch_('1234567890', 'abcdef'));\n\n  assertEquals(null, dmp.diff_halfMatch_('12345', '23'));\n\n  // Single Match.\n  assertEquivalent(['12', '90', 'a', 'z', '345678'], dmp.diff_halfMatch_('1234567890', 'a345678z'));\n\n  assertEquivalent(['a', 'z', '12', '90', '345678'], dmp.diff_halfMatch_('a345678z', '1234567890'));\n\n  assertEquivalent(['abc', 'z', '1234', '0', '56789'], dmp.diff_halfMatch_('abc56789z', '1234567890'));\n\n  assertEquivalent(['a', 'xyz', '1', '7890', '23456'], dmp.diff_halfMatch_('a23456xyz', '1234567890'));\n\n  // Multiple Matches.\n  assertEquivalent(['12123', '123121', 'a', 'z', '1234123451234'], dmp.diff_halfMatch_('121231234123451234123121', 'a1234123451234z'));\n\n  assertEquivalent(['', '-=-=-=-=-=', 'x', '', 'x-=-=-=-=-=-=-='], dmp.diff_halfMatch_('x-=-=-=-=-=-=-=-=-=-=-=-=', 'xx-=-=-=-=-=-=-='));\n\n  assertEquivalent(['-=-=-=-=-=', '', '', 'y', '-=-=-=-=-=-=-=y'], dmp.diff_halfMatch_('-=-=-=-=-=-=-=-=-=-=-=-=y', '-=-=-=-=-=-=-=yy'));\n\n  // Non-optimal halfmatch.\n  // Optimal diff would be -q+x=H-i+e=lloHe+Hu=llo-Hew+y not -qHillo+x=HelloHe-w+Hulloy\n  assertEquivalent(['qHillo', 'w', 'x', 'Hulloy', 'HelloHe'], dmp.diff_halfMatch_('qHilloHelloHew', 'xHelloHeHulloy'));\n\n  // Optimal no halfmatch.\n  dmp.Diff_Timeout = 0;\n  assertEquals(null, dmp.diff_halfMatch_('qHilloHelloHew', 'xHelloHeHulloy'));\n}\n\nfunction testDiffLinesToChars() {\n  function assertLinesToCharsResultEquals(a, b) {\n    assertEquals(a.chars1, b.chars1);\n    assertEquals(a.chars2, b.chars2);\n    assertEquivalent(a.lineArray, b.lineArray);\n  }\n\n  // Convert lines down to characters.\n  assertLinesToCharsResultEquals({chars1: '\\x01\\x02\\x01', chars2: '\\x02\\x01\\x02', lineArray: ['', 'alpha\\n', 'beta\\n']}, dmp.diff_linesToChars_('alpha\\nbeta\\nalpha\\n', 'beta\\nalpha\\nbeta\\n'));\n\n  assertLinesToCharsResultEquals({chars1: '', chars2: '\\x01\\x02\\x03\\x03', lineArray: ['', 'alpha\\r\\n', 'beta\\r\\n', '\\r\\n']}, dmp.diff_linesToChars_('', 'alpha\\r\\nbeta\\r\\n\\r\\n\\r\\n'));\n\n  assertLinesToCharsResultEquals({chars1: '\\x01', chars2: '\\x02', lineArray: ['', 'a', 'b']}, dmp.diff_linesToChars_('a', 'b'));\n\n  // More than 256 to reveal any 8-bit limitations.\n  var n = 300;\n  var lineList = [];\n  var charList = [];\n  for (var i = 1; i < n + 1; i++) {\n    lineList[i - 1] = i + '\\n';\n    charList[i - 1] = String.fromCharCode(i);\n  }\n  assertEquals(n, lineList.length);\n  var lines = lineList.join('');\n  var chars = charList.join('');\n  assertEquals(n, chars.length);\n  lineList.unshift('');\n  assertLinesToCharsResultEquals({chars1: chars, chars2: '', lineArray: lineList}, dmp.diff_linesToChars_(lines, ''));\n}\n\nfunction testDiffCharsToLines() {\n  // Convert chars up to lines.\n  var diffs = [[DIFF_EQUAL, '\\x01\\x02\\x01'], [DIFF_INSERT, '\\x02\\x01\\x02']];\n  dmp.diff_charsToLines_(diffs, ['', 'alpha\\n', 'beta\\n']);\n  assertEquivalent([[DIFF_EQUAL, 'alpha\\nbeta\\nalpha\\n'], [DIFF_INSERT, 'beta\\nalpha\\nbeta\\n']], diffs);\n\n  // More than 256 to reveal any 8-bit limitations.\n  var n = 300;\n  var lineList = [];\n  var charList = [];\n  for (var i = 1; i < n + 1; i++) {\n    lineList[i - 1] = i + '\\n';\n    charList[i - 1] = String.fromCharCode(i);\n  }\n  assertEquals(n, lineList.length);\n  var lines = lineList.join('');\n  var chars = charList.join('');\n  assertEquals(n, chars.length);\n  lineList.unshift('');\n  var diffs = [[DIFF_DELETE, chars]];\n  dmp.diff_charsToLines_(diffs, lineList);\n  assertEquivalent([[DIFF_DELETE, lines]], diffs);\n\n  // More than 65536 to verify any 16-bit limitation.\n  lineList = [];\n  for (var i = 0; i < 66000; i++) {\n    lineList[i] = i + '\\n';\n  }\n  chars = lineList.join('');\n  var results = dmp.diff_linesToChars_(chars, '');\n  diffs = [[DIFF_INSERT, results.chars1]];\n  dmp.diff_charsToLines_(diffs, results.lineArray);\n  assertEquals(chars, diffs[0][1]);\n}\n\nfunction testDiffCleanupMerge() {\n  // Cleanup a messy diff.\n  // Null case.\n  var diffs = [];\n  dmp.diff_cleanupMerge(diffs);\n  assertEquivalent([], diffs);\n\n  // No change case.\n  diffs = [[DIFF_EQUAL, 'a'], [DIFF_DELETE, 'b'], [DIFF_INSERT, 'c']];\n  dmp.diff_cleanupMerge(diffs);\n  assertEquivalent([[DIFF_EQUAL, 'a'], [DIFF_DELETE, 'b'], [DIFF_INSERT, 'c']], diffs);\n\n  // Merge equalities.\n  diffs = [[DIFF_EQUAL, 'a'], [DIFF_EQUAL, 'b'], [DIFF_EQUAL, 'c']];\n  dmp.diff_cleanupMerge(diffs);\n  assertEquivalent([[DIFF_EQUAL, 'abc']], diffs);\n\n  // Merge deletions.\n  diffs = [[DIFF_DELETE, 'a'], [DIFF_DELETE, 'b'], [DIFF_DELETE, 'c']];\n  dmp.diff_cleanupMerge(diffs);\n  assertEquivalent([[DIFF_DELETE, 'abc']], diffs);\n\n  // Merge insertions.\n  diffs = [[DIFF_INSERT, 'a'], [DIFF_INSERT, 'b'], [DIFF_INSERT, 'c']];\n  dmp.diff_cleanupMerge(diffs);\n  assertEquivalent([[DIFF_INSERT, 'abc']], diffs);\n\n  // Merge interweave.\n  diffs = [[DIFF_DELETE, 'a'], [DIFF_INSERT, 'b'], [DIFF_DELETE, 'c'], [DIFF_INSERT, 'd'], [DIFF_EQUAL, 'e'], [DIFF_EQUAL, 'f']];\n  dmp.diff_cleanupMerge(diffs);\n  assertEquivalent([[DIFF_DELETE, 'ac'], [DIFF_INSERT, 'bd'], [DIFF_EQUAL, 'ef']], diffs);\n\n  // Prefix and suffix detection.\n  diffs = [[DIFF_DELETE, 'a'], [DIFF_INSERT, 'abc'], [DIFF_DELETE, 'dc']];\n  dmp.diff_cleanupMerge(diffs);\n  assertEquivalent([[DIFF_EQUAL, 'a'], [DIFF_DELETE, 'd'], [DIFF_INSERT, 'b'], [DIFF_EQUAL, 'c']], diffs);\n\n  // Prefix and suffix detection with equalities.\n  diffs = [[DIFF_EQUAL, 'x'], [DIFF_DELETE, 'a'], [DIFF_INSERT, 'abc'], [DIFF_DELETE, 'dc'], [DIFF_EQUAL, 'y']];\n  dmp.diff_cleanupMerge(diffs);\n  assertEquivalent([[DIFF_EQUAL, 'xa'], [DIFF_DELETE, 'd'], [DIFF_INSERT, 'b'], [DIFF_EQUAL, 'cy']], diffs);\n\n  // Slide edit left.\n  diffs = [[DIFF_EQUAL, 'a'], [DIFF_INSERT, 'ba'], [DIFF_EQUAL, 'c']];\n  dmp.diff_cleanupMerge(diffs);\n  assertEquivalent([[DIFF_INSERT, 'ab'], [DIFF_EQUAL, 'ac']], diffs);\n\n  // Slide edit right.\n  diffs = [[DIFF_EQUAL, 'c'], [DIFF_INSERT, 'ab'], [DIFF_EQUAL, 'a']];\n  dmp.diff_cleanupMerge(diffs);\n  assertEquivalent([[DIFF_EQUAL, 'ca'], [DIFF_INSERT, 'ba']], diffs);\n\n  // Slide edit left recursive.\n  diffs = [[DIFF_EQUAL, 'a'], [DIFF_DELETE, 'b'], [DIFF_EQUAL, 'c'], [DIFF_DELETE, 'ac'], [DIFF_EQUAL, 'x']];\n  dmp.diff_cleanupMerge(diffs);\n  assertEquivalent([[DIFF_DELETE, 'abc'], [DIFF_EQUAL, 'acx']], diffs);\n\n  // Slide edit right recursive.\n  diffs = [[DIFF_EQUAL, 'x'], [DIFF_DELETE, 'ca'], [DIFF_EQUAL, 'c'], [DIFF_DELETE, 'b'], [DIFF_EQUAL, 'a']];\n  dmp.diff_cleanupMerge(diffs);\n  assertEquivalent([[DIFF_EQUAL, 'xca'], [DIFF_DELETE, 'cba']], diffs);\n\n  // Empty merge.\n  diffs = [[DIFF_DELETE, 'b'], [DIFF_INSERT, 'ab'], [DIFF_EQUAL, 'c']];\n  dmp.diff_cleanupMerge(diffs);\n  assertEquivalent([[DIFF_INSERT, 'a'], [DIFF_EQUAL, 'bc']], diffs);\n\n  // Empty equality.\n  diffs = [[DIFF_EQUAL, ''], [DIFF_INSERT, 'a'], [DIFF_EQUAL, 'b']];\n  dmp.diff_cleanupMerge(diffs);\n  assertEquivalent([[DIFF_INSERT, 'a'], [DIFF_EQUAL, 'b']], diffs);\n}\n\nfunction testDiffCleanupSemanticLossless() {\n  // Slide diffs to match logical boundaries.\n  // Null case.\n  var diffs = [];\n  dmp.diff_cleanupSemanticLossless(diffs);\n  assertEquivalent([], diffs);\n\n  // Blank lines.\n  diffs = [[DIFF_EQUAL, 'AAA\\r\\n\\r\\nBBB'], [DIFF_INSERT, '\\r\\nDDD\\r\\n\\r\\nBBB'], [DIFF_EQUAL, '\\r\\nEEE']];\n  dmp.diff_cleanupSemanticLossless(diffs);\n  assertEquivalent([[DIFF_EQUAL, 'AAA\\r\\n\\r\\n'], [DIFF_INSERT, 'BBB\\r\\nDDD\\r\\n\\r\\n'], [DIFF_EQUAL, 'BBB\\r\\nEEE']], diffs);\n\n  // Line boundaries.\n  diffs = [[DIFF_EQUAL, 'AAA\\r\\nBBB'], [DIFF_INSERT, ' DDD\\r\\nBBB'], [DIFF_EQUAL, ' EEE']];\n  dmp.diff_cleanupSemanticLossless(diffs);\n  assertEquivalent([[DIFF_EQUAL, 'AAA\\r\\n'], [DIFF_INSERT, 'BBB DDD\\r\\n'], [DIFF_EQUAL, 'BBB EEE']], diffs);\n\n  // Word boundaries.\n  diffs = [[DIFF_EQUAL, 'The c'], [DIFF_INSERT, 'ow and the c'], [DIFF_EQUAL, 'at.']];\n  dmp.diff_cleanupSemanticLossless(diffs);\n  assertEquivalent([[DIFF_EQUAL, 'The '], [DIFF_INSERT, 'cow and the '], [DIFF_EQUAL, 'cat.']], diffs);\n\n  // Alphanumeric boundaries.\n  diffs = [[DIFF_EQUAL, 'The-c'], [DIFF_INSERT, 'ow-and-the-c'], [DIFF_EQUAL, 'at.']];\n  dmp.diff_cleanupSemanticLossless(diffs);\n  assertEquivalent([[DIFF_EQUAL, 'The-'], [DIFF_INSERT, 'cow-and-the-'], [DIFF_EQUAL, 'cat.']], diffs);\n\n  // Hitting the start.\n  diffs = [[DIFF_EQUAL, 'a'], [DIFF_DELETE, 'a'], [DIFF_EQUAL, 'ax']];\n  dmp.diff_cleanupSemanticLossless(diffs);\n  assertEquivalent([[DIFF_DELETE, 'a'], [DIFF_EQUAL, 'aax']], diffs);\n\n  // Hitting the end.\n  diffs = [[DIFF_EQUAL, 'xa'], [DIFF_DELETE, 'a'], [DIFF_EQUAL, 'a']];\n  dmp.diff_cleanupSemanticLossless(diffs);\n  assertEquivalent([[DIFF_EQUAL, 'xaa'], [DIFF_DELETE, 'a']], diffs);\n\n  // Sentence boundaries.\n  diffs = [[DIFF_EQUAL, 'The xxx. The '], [DIFF_INSERT, 'zzz. The '], [DIFF_EQUAL, 'yyy.']];\n  dmp.diff_cleanupSemanticLossless(diffs);\n  assertEquivalent([[DIFF_EQUAL, 'The xxx.'], [DIFF_INSERT, ' The zzz.'], [DIFF_EQUAL, ' The yyy.']], diffs);\n}\n\nfunction testDiffCleanupSemantic() {\n  // Cleanup semantically trivial equalities.\n  // Null case.\n  var diffs = [];\n  dmp.diff_cleanupSemantic(diffs);\n  assertEquivalent([], diffs);\n\n  // No elimination #1.\n  diffs = [[DIFF_DELETE, 'ab'], [DIFF_INSERT, 'cd'], [DIFF_EQUAL, '12'], [DIFF_DELETE, 'e']];\n  dmp.diff_cleanupSemantic(diffs);\n  assertEquivalent([[DIFF_DELETE, 'ab'], [DIFF_INSERT, 'cd'], [DIFF_EQUAL, '12'], [DIFF_DELETE, 'e']], diffs);\n\n  // No elimination #2.\n  diffs = [[DIFF_DELETE, 'abc'], [DIFF_INSERT, 'ABC'], [DIFF_EQUAL, '1234'], [DIFF_DELETE, 'wxyz']];\n  dmp.diff_cleanupSemantic(diffs);\n  assertEquivalent([[DIFF_DELETE, 'abc'], [DIFF_INSERT, 'ABC'], [DIFF_EQUAL, '1234'], [DIFF_DELETE, 'wxyz']], diffs);\n\n  // Simple elimination.\n  diffs = [[DIFF_DELETE, 'a'], [DIFF_EQUAL, 'b'], [DIFF_DELETE, 'c']];\n  dmp.diff_cleanupSemantic(diffs);\n  assertEquivalent([[DIFF_DELETE, 'abc'], [DIFF_INSERT, 'b']], diffs);\n\n  // Backpass elimination.\n  diffs = [[DIFF_DELETE, 'ab'], [DIFF_EQUAL, 'cd'], [DIFF_DELETE, 'e'], [DIFF_EQUAL, 'f'], [DIFF_INSERT, 'g']];\n  dmp.diff_cleanupSemantic(diffs);\n  assertEquivalent([[DIFF_DELETE, 'abcdef'], [DIFF_INSERT, 'cdfg']], diffs);\n\n  // Multiple eliminations.\n  diffs = [[DIFF_INSERT, '1'], [DIFF_EQUAL, 'A'], [DIFF_DELETE, 'B'], [DIFF_INSERT, '2'], [DIFF_EQUAL, '_'], [DIFF_INSERT, '1'], [DIFF_EQUAL, 'A'], [DIFF_DELETE, 'B'], [DIFF_INSERT, '2']];\n  dmp.diff_cleanupSemantic(diffs);\n  assertEquivalent([[DIFF_DELETE, 'AB_AB'], [DIFF_INSERT, '1A2_1A2']], diffs);\n\n  // Word boundaries.\n  diffs = [[DIFF_EQUAL, 'The c'], [DIFF_DELETE, 'ow and the c'], [DIFF_EQUAL, 'at.']];\n  dmp.diff_cleanupSemantic(diffs);\n  assertEquivalent([[DIFF_EQUAL, 'The '], [DIFF_DELETE, 'cow and the '], [DIFF_EQUAL, 'cat.']], diffs);\n\n  // No overlap elimination.\n  diffs = [[DIFF_DELETE, 'abcxx'], [DIFF_INSERT, 'xxdef']];\n  dmp.diff_cleanupSemantic(diffs);\n  assertEquivalent([[DIFF_DELETE, 'abcxx'], [DIFF_INSERT, 'xxdef']], diffs);\n\n  // Overlap elimination.\n  diffs = [[DIFF_DELETE, 'abcxxx'], [DIFF_INSERT, 'xxxdef']];\n  dmp.diff_cleanupSemantic(diffs);\n  assertEquivalent([[DIFF_DELETE, 'abc'], [DIFF_EQUAL, 'xxx'], [DIFF_INSERT, 'def']], diffs);\n\n  // Reverse overlap elimination.\n  diffs = [[DIFF_DELETE, 'xxxabc'], [DIFF_INSERT, 'defxxx']];\n  dmp.diff_cleanupSemantic(diffs);\n  assertEquivalent([[DIFF_INSERT, 'def'], [DIFF_EQUAL, 'xxx'], [DIFF_DELETE, 'abc']], diffs);\n\n  // Two overlap eliminations.\n  diffs = [[DIFF_DELETE, 'abcd1212'], [DIFF_INSERT, '1212efghi'], [DIFF_EQUAL, '----'], [DIFF_DELETE, 'A3'], [DIFF_INSERT, '3BC']];\n  dmp.diff_cleanupSemantic(diffs);\n  assertEquivalent([[DIFF_DELETE, 'abcd'], [DIFF_EQUAL, '1212'], [DIFF_INSERT, 'efghi'], [DIFF_EQUAL, '----'], [DIFF_DELETE, 'A'], [DIFF_EQUAL, '3'], [DIFF_INSERT, 'BC']], diffs);\n}\n\nfunction testDiffCleanupEfficiency() {\n  // Cleanup operationally trivial equalities.\n  dmp.Diff_EditCost = 4;\n  // Null case.\n  var diffs = [];\n  dmp.diff_cleanupEfficiency(diffs);\n  assertEquivalent([], diffs);\n\n  // No elimination.\n  diffs = [[DIFF_DELETE, 'ab'], [DIFF_INSERT, '12'], [DIFF_EQUAL, 'wxyz'], [DIFF_DELETE, 'cd'], [DIFF_INSERT, '34']];\n  dmp.diff_cleanupEfficiency(diffs);\n  assertEquivalent([[DIFF_DELETE, 'ab'], [DIFF_INSERT, '12'], [DIFF_EQUAL, 'wxyz'], [DIFF_DELETE, 'cd'], [DIFF_INSERT, '34']], diffs);\n\n  // Four-edit elimination.\n  diffs = [[DIFF_DELETE, 'ab'], [DIFF_INSERT, '12'], [DIFF_EQUAL, 'xyz'], [DIFF_DELETE, 'cd'], [DIFF_INSERT, '34']];\n  dmp.diff_cleanupEfficiency(diffs);\n  assertEquivalent([[DIFF_DELETE, 'abxyzcd'], [DIFF_INSERT, '12xyz34']], diffs);\n\n  // Three-edit elimination.\n  diffs = [[DIFF_INSERT, '12'], [DIFF_EQUAL, 'x'], [DIFF_DELETE, 'cd'], [DIFF_INSERT, '34']];\n  dmp.diff_cleanupEfficiency(diffs);\n  assertEquivalent([[DIFF_DELETE, 'xcd'], [DIFF_INSERT, '12x34']], diffs);\n\n  // Backpass elimination.\n  diffs = [[DIFF_DELETE, 'ab'], [DIFF_INSERT, '12'], [DIFF_EQUAL, 'xy'], [DIFF_INSERT, '34'], [DIFF_EQUAL, 'z'], [DIFF_DELETE, 'cd'], [DIFF_INSERT, '56']];\n  dmp.diff_cleanupEfficiency(diffs);\n  assertEquivalent([[DIFF_DELETE, 'abxyzcd'], [DIFF_INSERT, '12xy34z56']], diffs);\n\n  // High cost elimination.\n  dmp.Diff_EditCost = 5;\n  diffs = [[DIFF_DELETE, 'ab'], [DIFF_INSERT, '12'], [DIFF_EQUAL, 'wxyz'], [DIFF_DELETE, 'cd'], [DIFF_INSERT, '34']];\n  dmp.diff_cleanupEfficiency(diffs);\n  assertEquivalent([[DIFF_DELETE, 'abwxyzcd'], [DIFF_INSERT, '12wxyz34']], diffs);\n  dmp.Diff_EditCost = 4;\n}\n\nfunction testDiffPrettyHtml() {\n  // Pretty print.\n  var diffs = [[DIFF_EQUAL, 'a\\n'], [DIFF_DELETE, '<B>b</B>'], [DIFF_INSERT, 'c&d']];\n  assertEquals('<span>a&para;<br></span><del style=\"background:#ffe6e6;\">&lt;B&gt;b&lt;/B&gt;</del><ins style=\"background:#e6ffe6;\">c&amp;d</ins>', dmp.diff_prettyHtml(diffs));\n}\n\nfunction testDiffText() {\n  // Compute the source and destination texts.\n  var diffs = [[DIFF_EQUAL, 'jump'], [DIFF_DELETE, 's'], [DIFF_INSERT, 'ed'], [DIFF_EQUAL, ' over '], [DIFF_DELETE, 'the'], [DIFF_INSERT, 'a'], [DIFF_EQUAL, ' lazy']];\n  assertEquals('jumps over the lazy', dmp.diff_text1(diffs));\n\n  assertEquals('jumped over a lazy', dmp.diff_text2(diffs));\n}\n\nfunction testDiffDelta() {\n  // Convert a diff into delta string.\n  var diffs = [[DIFF_EQUAL, 'jump'], [DIFF_DELETE, 's'], [DIFF_INSERT, 'ed'], [DIFF_EQUAL, ' over '], [DIFF_DELETE, 'the'], [DIFF_INSERT, 'a'], [DIFF_EQUAL, ' lazy'], [DIFF_INSERT, 'old dog']];\n  var text1 = dmp.diff_text1(diffs);\n  assertEquals('jumps over the lazy', text1);\n\n  var delta = dmp.diff_toDelta(diffs);\n  assertEquals('=4\\t-1\\t+ed\\t=6\\t-3\\t+a\\t=5\\t+old dog', delta);\n\n  // Convert delta string into a diff.\n  assertEquivalent(diffs, dmp.diff_fromDelta(text1, delta));\n\n  // Generates error (19 != 20).\n  try {\n    dmp.diff_fromDelta(text1 + 'x', delta);\n    assertEquals(Error, null);\n  } catch (e) {\n    // Exception expected.\n  }\n\n  // Generates error (19 != 18).\n  try {\n    dmp.diff_fromDelta(text1.substring(1), delta);\n    assertEquals(Error, null);\n  } catch (e) {\n    // Exception expected.\n  }\n\n  // Generates error (%c3%xy invalid Unicode).\n  try {\n    dmp.diff_fromDelta('', '+%c3%xy');\n    assertEquals(Error, null);\n  } catch (e) {\n    // Exception expected.\n  }\n\n  // Test deltas with special characters.\n  diffs = [[DIFF_EQUAL, '\\u0680 \\x00 \\t %'], [DIFF_DELETE, '\\u0681 \\x01 \\n ^'], [DIFF_INSERT, '\\u0682 \\x02 \\\\ |']];\n  text1 = dmp.diff_text1(diffs);\n  assertEquals('\\u0680 \\x00 \\t %\\u0681 \\x01 \\n ^', text1);\n\n  delta = dmp.diff_toDelta(diffs);\n  assertEquals('=7\\t-7\\t+%DA%82 %02 %5C %7C', delta);\n\n  // Convert delta string into a diff.\n  assertEquivalent(diffs, dmp.diff_fromDelta(text1, delta));\n\n  // Verify pool of unchanged characters.\n  diffs = [[DIFF_INSERT, 'A-Z a-z 0-9 - _ . ! ~ * \\' ( ) ; / ? : @ & = + $ , # ']];\n  var text2 = dmp.diff_text2(diffs);\n  assertEquals('A-Z a-z 0-9 - _ . ! ~ * \\' ( ) ; / ? : @ & = + $ , # ', text2);\n\n  delta = dmp.diff_toDelta(diffs);\n  assertEquals('+A-Z a-z 0-9 - _ . ! ~ * \\' ( ) ; / ? : @ & = + $ , # ', delta);\n\n  // Convert delta string into a diff.\n  assertEquivalent(diffs, dmp.diff_fromDelta('', delta));\n\n  // 160 kb string.\n  var a = 'abcdefghij';\n  for (var i = 0; i < 14; i++) {\n    a += a;\n  }\n  diffs = [[DIFF_INSERT, a]];\n  delta = dmp.diff_toDelta(diffs);\n  assertEquals('+' + a, delta);\n\n  // Convert delta string into a diff.\n  assertEquivalent(diffs, dmp.diff_fromDelta('', delta));\n}\n\nfunction testDiffXIndex() {\n  // Translate a location in text1 to text2.\n  // Translation on equality.\n  assertEquals(5, dmp.diff_xIndex([[DIFF_DELETE, 'a'], [DIFF_INSERT, '1234'], [DIFF_EQUAL, 'xyz']], 2));\n\n  // Translation on deletion.\n  assertEquals(1, dmp.diff_xIndex([[DIFF_EQUAL, 'a'], [DIFF_DELETE, '1234'], [DIFF_EQUAL, 'xyz']], 3));\n}\n\nfunction testDiffLevenshtein() {\n  // Levenshtein with trailing equality.\n  assertEquals(4, dmp.diff_levenshtein([[DIFF_DELETE, 'abc'], [DIFF_INSERT, '1234'], [DIFF_EQUAL, 'xyz']]));\n  // Levenshtein with leading equality.\n  assertEquals(4, dmp.diff_levenshtein([[DIFF_EQUAL, 'xyz'], [DIFF_DELETE, 'abc'], [DIFF_INSERT, '1234']]));\n  // Levenshtein with middle equality.\n  assertEquals(7, dmp.diff_levenshtein([[DIFF_DELETE, 'abc'], [DIFF_EQUAL, 'xyz'], [DIFF_INSERT, '1234']]));\n}\n\nfunction testDiffBisect() {\n  // Normal.\n  var a = 'cat';\n  var b = 'map';\n  // Since the resulting diff hasn't been normalized, it would be ok if\n  // the insertion and deletion pairs are swapped.\n  // If the order changes, tweak this test as required.\n  assertEquivalent([[DIFF_DELETE, 'c'], [DIFF_INSERT, 'm'], [DIFF_EQUAL, 'a'], [DIFF_DELETE, 't'], [DIFF_INSERT, 'p']], dmp.diff_bisect_(a, b, Number.MAX_VALUE));\n\n  // Timeout.\n  assertEquivalent([[DIFF_DELETE, 'cat'], [DIFF_INSERT, 'map']], dmp.diff_bisect_(a, b, 0));\n}\n\nfunction testDiffMain() {\n  // Perform a trivial diff.\n  // Null case.\n  assertEquivalent([], dmp.diff_main('', '', false));\n\n  // Equality.\n  assertEquivalent([[DIFF_EQUAL, 'abc']], dmp.diff_main('abc', 'abc', false));\n\n  // Simple insertion.\n  assertEquivalent([[DIFF_EQUAL, 'ab'], [DIFF_INSERT, '123'], [DIFF_EQUAL, 'c']], dmp.diff_main('abc', 'ab123c', false));\n\n  // Simple deletion.\n  assertEquivalent([[DIFF_EQUAL, 'a'], [DIFF_DELETE, '123'], [DIFF_EQUAL, 'bc']], dmp.diff_main('a123bc', 'abc', false));\n\n  // Two insertions.\n  assertEquivalent([[DIFF_EQUAL, 'a'], [DIFF_INSERT, '123'], [DIFF_EQUAL, 'b'], [DIFF_INSERT, '456'], [DIFF_EQUAL, 'c']], dmp.diff_main('abc', 'a123b456c', false));\n\n  // Two deletions.\n  assertEquivalent([[DIFF_EQUAL, 'a'], [DIFF_DELETE, '123'], [DIFF_EQUAL, 'b'], [DIFF_DELETE, '456'], [DIFF_EQUAL, 'c']], dmp.diff_main('a123b456c', 'abc', false));\n\n  // Perform a real diff.\n  // Switch off the timeout.\n  dmp.Diff_Timeout = 0;\n  // Simple cases.\n  assertEquivalent([[DIFF_DELETE, 'a'], [DIFF_INSERT, 'b']], dmp.diff_main('a', 'b', false));\n\n  assertEquivalent([[DIFF_DELETE, 'Apple'], [DIFF_INSERT, 'Banana'], [DIFF_EQUAL, 's are a'], [DIFF_INSERT, 'lso'], [DIFF_EQUAL, ' fruit.']], dmp.diff_main('Apples are a fruit.', 'Bananas are also fruit.', false));\n\n  assertEquivalent([[DIFF_DELETE, 'a'], [DIFF_INSERT, '\\u0680'], [DIFF_EQUAL, 'x'], [DIFF_DELETE, '\\t'], [DIFF_INSERT, '\\0']], dmp.diff_main('ax\\t', '\\u0680x\\0', false));\n\n  // Overlaps.\n  assertEquivalent([[DIFF_DELETE, '1'], [DIFF_EQUAL, 'a'], [DIFF_DELETE, 'y'], [DIFF_EQUAL, 'b'], [DIFF_DELETE, '2'], [DIFF_INSERT, 'xab']], dmp.diff_main('1ayb2', 'abxab', false));\n\n  assertEquivalent([[DIFF_INSERT, 'xaxcx'], [DIFF_EQUAL, 'abc'], [DIFF_DELETE, 'y']], dmp.diff_main('abcy', 'xaxcxabc', false));\n\n  assertEquivalent([[DIFF_DELETE, 'ABCD'], [DIFF_EQUAL, 'a'], [DIFF_DELETE, '='], [DIFF_INSERT, '-'], [DIFF_EQUAL, 'bcd'], [DIFF_DELETE, '='], [DIFF_INSERT, '-'], [DIFF_EQUAL, 'efghijklmnopqrs'], [DIFF_DELETE, 'EFGHIJKLMNOefg']], dmp.diff_main('ABCDa=bcd=efghijklmnopqrsEFGHIJKLMNOefg', 'a-bcd-efghijklmnopqrs', false));\n\n  // Large equality.\n  assertEquivalent([[DIFF_INSERT, ' '], [DIFF_EQUAL, 'a'], [DIFF_INSERT, 'nd'], [DIFF_EQUAL, ' [[Pennsylvania]]'], [DIFF_DELETE, ' and [[New']], dmp.diff_main('a [[Pennsylvania]] and [[New', ' and [[Pennsylvania]]', false));\n\n  // Timeout.\n  dmp.Diff_Timeout = 0.1;  // 100ms\n  var a = '`Twas brillig, and the slithy toves\\nDid gyre and gimble in the wabe:\\nAll mimsy were the borogoves,\\nAnd the mome raths outgrabe.\\n';\n  var b = 'I am the very model of a modern major general,\\nI\\'ve information vegetable, animal, and mineral,\\nI know the kings of England, and I quote the fights historical,\\nFrom Marathon to Waterloo, in order categorical.\\n';\n  // Increase the text lengths by 1024 times to ensure a timeout.\n  for (var i = 0; i < 10; i++) {\n    a += a;\n    b += b;\n  }\n  var startTime = (new Date()).getTime();\n  dmp.diff_main(a, b);\n  var endTime = (new Date()).getTime();\n  // Test that we took at least the timeout period.\n  assertTrue(dmp.Diff_Timeout * 1000 <= endTime - startTime);\n  // Test that we didn't take forever (be forgiving).\n  // Theoretically this test could fail very occasionally if the\n  // OS task swaps or locks up for a second at the wrong moment.\n  assertTrue(dmp.Diff_Timeout * 1000 * 2 > endTime - startTime);\n  dmp.Diff_Timeout = 0;\n\n  // Test the linemode speedup.\n  // Must be long to pass the 100 char cutoff.\n  // Simple line-mode.\n  a = '1234567890\\n1234567890\\n1234567890\\n1234567890\\n1234567890\\n1234567890\\n1234567890\\n1234567890\\n1234567890\\n1234567890\\n1234567890\\n1234567890\\n1234567890\\n';\n  b = 'abcdefghij\\nabcdefghij\\nabcdefghij\\nabcdefghij\\nabcdefghij\\nabcdefghij\\nabcdefghij\\nabcdefghij\\nabcdefghij\\nabcdefghij\\nabcdefghij\\nabcdefghij\\nabcdefghij\\n';\n  assertEquivalent(dmp.diff_main(a, b, false), dmp.diff_main(a, b, true));\n\n  // Single line-mode.\n  a = '1234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890';\n  b = 'abcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghij';\n  assertEquivalent(dmp.diff_main(a, b, false), dmp.diff_main(a, b, true));\n\n  // Overlap line-mode.\n  a = '1234567890\\n1234567890\\n1234567890\\n1234567890\\n1234567890\\n1234567890\\n1234567890\\n1234567890\\n1234567890\\n1234567890\\n1234567890\\n1234567890\\n1234567890\\n';\n  b = 'abcdefghij\\n1234567890\\n1234567890\\n1234567890\\nabcdefghij\\n1234567890\\n1234567890\\n1234567890\\nabcdefghij\\n1234567890\\n1234567890\\n1234567890\\nabcdefghij\\n';\n  var texts_linemode = diff_rebuildtexts(dmp.diff_main(a, b, true));\n  var texts_textmode = diff_rebuildtexts(dmp.diff_main(a, b, false));\n  assertEquivalent(texts_textmode, texts_linemode);\n\n  // Test null inputs.\n  try {\n    dmp.diff_main(null, null);\n    assertEquals(Error, null);\n  } catch (e) {\n    // Exception expected.\n  }\n}\n\n\n// MATCH TEST FUNCTIONS\n\n\nfunction testMatchAlphabet() {\n  // Initialise the bitmasks for Bitap.\n  // Unique.\n  assertEquivalent({'a':4, 'b':2, 'c':1}, dmp.match_alphabet_('abc'));\n\n  // Duplicates.\n  assertEquivalent({'a':37, 'b':18, 'c':8}, dmp.match_alphabet_('abcaba'));\n}\n\nfunction testMatchBitap() {\n  // Bitap algorithm.\n  dmp.Match_Distance = 100;\n  dmp.Match_Threshold = 0.5;\n  // Exact matches.\n  assertEquals(5, dmp.match_bitap_('abcdefghijk', 'fgh', 5));\n\n  assertEquals(5, dmp.match_bitap_('abcdefghijk', 'fgh', 0));\n\n  // Fuzzy matches.\n  assertEquals(4, dmp.match_bitap_('abcdefghijk', 'efxhi', 0));\n\n  assertEquals(2, dmp.match_bitap_('abcdefghijk', 'cdefxyhijk', 5));\n\n  assertEquals(-1, dmp.match_bitap_('abcdefghijk', 'bxy', 1));\n\n  // Overflow.\n  assertEquals(2, dmp.match_bitap_('123456789xx0', '3456789x0', 2));\n\n  // Threshold test.\n  dmp.Match_Threshold = 0.4;\n  assertEquals(4, dmp.match_bitap_('abcdefghijk', 'efxyhi', 1));\n\n  dmp.Match_Threshold = 0.3;\n  assertEquals(-1, dmp.match_bitap_('abcdefghijk', 'efxyhi', 1));\n\n  dmp.Match_Threshold = 0.0;\n  assertEquals(1, dmp.match_bitap_('abcdefghijk', 'bcdef', 1));\n  dmp.Match_Threshold = 0.5;\n\n  // Multiple select.\n  assertEquals(0, dmp.match_bitap_('abcdexyzabcde', 'abccde', 3));\n\n  assertEquals(8, dmp.match_bitap_('abcdexyzabcde', 'abccde', 5));\n\n  // Distance test.\n  dmp.Match_Distance = 10;  // Strict location.\n  assertEquals(-1, dmp.match_bitap_('abcdefghijklmnopqrstuvwxyz', 'abcdefg', 24));\n\n  assertEquals(0, dmp.match_bitap_('abcdefghijklmnopqrstuvwxyz', 'abcdxxefg', 1));\n\n  dmp.Match_Distance = 1000;  // Loose location.\n  assertEquals(0, dmp.match_bitap_('abcdefghijklmnopqrstuvwxyz', 'abcdefg', 24));\n}\n\nfunction testMatchMain() {\n  // Full match.\n  // Shortcut matches.\n  assertEquals(0, dmp.match_main('abcdef', 'abcdef', 1000));\n\n  assertEquals(-1, dmp.match_main('', 'abcdef', 1));\n\n  assertEquals(3, dmp.match_main('abcdef', '', 3));\n\n  assertEquals(3, dmp.match_main('abcdef', 'de', 3));\n\n  // Beyond end match.\n  assertEquals(3, dmp.match_main(\"abcdef\", \"defy\", 4));\n\n  // Oversized pattern.\n  assertEquals(0, dmp.match_main(\"abcdef\", \"abcdefy\", 0));\n\n  // Complex match.\n  assertEquals(4, dmp.match_main('I am the very model of a modern major general.', ' that berry ', 5));\n\n  // Test null inputs.\n  try {\n    dmp.match_main(null, null, 0);\n    assertEquals(Error, null);\n  } catch (e) {\n    // Exception expected.\n  }\n}\n\n\n// PATCH TEST FUNCTIONS\n\n\nfunction testPatchObj() {\n  // Patch Object.\n  var p = new diff_match_patch.patch_obj();\n  p.start1 = 20;\n  p.start2 = 21;\n  p.length1 = 18;\n  p.length2 = 17;\n  p.diffs = [[DIFF_EQUAL, 'jump'], [DIFF_DELETE, 's'], [DIFF_INSERT, 'ed'], [DIFF_EQUAL, ' over '], [DIFF_DELETE, 'the'], [DIFF_INSERT, 'a'], [DIFF_EQUAL, '\\nlaz']];\n  var strp = p.toString();\n  assertEquals('@@ -21,18 +22,17 @@\\n jump\\n-s\\n+ed\\n  over \\n-the\\n+a\\n %0Alaz\\n', strp);\n}\n\nfunction testPatchFromText() {\n  assertEquivalent([], dmp.patch_fromText(strp));\n\n  var strp = '@@ -21,18 +22,17 @@\\n jump\\n-s\\n+ed\\n  over \\n-the\\n+a\\n %0Alaz\\n';\n  assertEquals(strp, dmp.patch_fromText(strp)[0].toString());\n\n  assertEquals('@@ -1 +1 @@\\n-a\\n+b\\n', dmp.patch_fromText('@@ -1 +1 @@\\n-a\\n+b\\n')[0].toString());\n\n  assertEquals('@@ -1,3 +0,0 @@\\n-abc\\n', dmp.patch_fromText('@@ -1,3 +0,0 @@\\n-abc\\n')[0].toString());\n\n  assertEquals('@@ -0,0 +1,3 @@\\n+abc\\n', dmp.patch_fromText('@@ -0,0 +1,3 @@\\n+abc\\n')[0].toString());\n\n  // Generates error.\n  try {\n    dmp.patch_fromText('Bad\\nPatch\\n');\n    assertEquals(Error, null);\n  } catch (e) {\n    // Exception expected.\n  }\n}\n\nfunction testPatchToText() {\n  var strp = '@@ -21,18 +22,17 @@\\n jump\\n-s\\n+ed\\n  over \\n-the\\n+a\\n  laz\\n';\n  var p = dmp.patch_fromText(strp);\n  assertEquals(strp, dmp.patch_toText(p));\n\n  strp = '@@ -1,9 +1,9 @@\\n-f\\n+F\\n oo+fooba\\n@@ -7,9 +7,9 @@\\n obar\\n-,\\n+.\\n  tes\\n';\n  p = dmp.patch_fromText(strp);\n  assertEquals(strp, dmp.patch_toText(p));\n}\n\nfunction testPatchAddContext() {\n  dmp.Patch_Margin = 4;\n  var p = dmp.patch_fromText('@@ -21,4 +21,10 @@\\n-jump\\n+somersault\\n')[0];\n  dmp.patch_addContext_(p, 'The quick brown fox jumps over the lazy dog.');\n  assertEquals('@@ -17,12 +17,18 @@\\n fox \\n-jump\\n+somersault\\n s ov\\n', p.toString());\n\n  // Same, but not enough trailing context.\n  p = dmp.patch_fromText('@@ -21,4 +21,10 @@\\n-jump\\n+somersault\\n')[0];\n  dmp.patch_addContext_(p, 'The quick brown fox jumps.');\n  assertEquals('@@ -17,10 +17,16 @@\\n fox \\n-jump\\n+somersault\\n s.\\n', p.toString());\n\n  // Same, but not enough leading context.\n  p = dmp.patch_fromText('@@ -3 +3,2 @@\\n-e\\n+at\\n')[0];\n  dmp.patch_addContext_(p, 'The quick brown fox jumps.');\n  assertEquals('@@ -1,7 +1,8 @@\\n Th\\n-e\\n+at\\n  qui\\n', p.toString());\n\n  // Same, but with ambiguity.\n  p = dmp.patch_fromText('@@ -3 +3,2 @@\\n-e\\n+at\\n')[0];\n  dmp.patch_addContext_(p, 'The quick brown fox jumps.  The quick brown fox crashes.');\n  assertEquals('@@ -1,27 +1,28 @@\\n Th\\n-e\\n+at\\n  quick brown fox jumps. \\n', p.toString());\n}\n\nfunction testPatchMake() {\n  // Null case.\n  var patches = dmp.patch_make('', '');\n  assertEquals('', dmp.patch_toText(patches));\n\n  var text1 = 'The quick brown fox jumps over the lazy dog.';\n  var text2 = 'That quick brown fox jumped over a lazy dog.';\n  // Text2+Text1 inputs.\n  var expectedPatch = '@@ -1,8 +1,7 @@\\n Th\\n-at\\n+e\\n  qui\\n@@ -21,17 +21,18 @@\\n jump\\n-ed\\n+s\\n  over \\n-a\\n+the\\n  laz\\n';\n  // The second patch must be \"-21,17 +21,18\", not \"-22,17 +21,18\" due to rolling context.\n  patches = dmp.patch_make(text2, text1);\n  assertEquals(expectedPatch, dmp.patch_toText(patches));\n\n  // Text1+Text2 inputs.\n  expectedPatch = '@@ -1,11 +1,12 @@\\n Th\\n-e\\n+at\\n  quick b\\n@@ -22,18 +22,17 @@\\n jump\\n-s\\n+ed\\n  over \\n-the\\n+a\\n  laz\\n';\n  patches = dmp.patch_make(text1, text2);\n  assertEquals(expectedPatch, dmp.patch_toText(patches));\n\n  // Diff input.\n  var diffs = dmp.diff_main(text1, text2, false);\n  patches = dmp.patch_make(diffs);\n  assertEquals(expectedPatch, dmp.patch_toText(patches));\n\n  // Text1+Diff inputs.\n  patches = dmp.patch_make(text1, diffs);\n  assertEquals(expectedPatch, dmp.patch_toText(patches));\n\n  // Text1+Text2+Diff inputs (deprecated).\n  patches = dmp.patch_make(text1, text2, diffs);\n  assertEquals(expectedPatch, dmp.patch_toText(patches));\n\n  // Character encoding.\n  patches = dmp.patch_make('`1234567890-=[]\\\\;\\',./', '~!@#$%^&*()_+{}|:\"<>?');\n  assertEquals('@@ -1,21 +1,21 @@\\n-%601234567890-=%5B%5D%5C;\\',./\\n+~!@#$%25%5E&*()_+%7B%7D%7C:%22%3C%3E?\\n', dmp.patch_toText(patches));\n\n  // Character decoding.\n  diffs = [[DIFF_DELETE, '`1234567890-=[]\\\\;\\',./'], [DIFF_INSERT, '~!@#$%^&*()_+{}|:\"<>?']];\n  assertEquivalent(diffs, dmp.patch_fromText('@@ -1,21 +1,21 @@\\n-%601234567890-=%5B%5D%5C;\\',./\\n+~!@#$%25%5E&*()_+%7B%7D%7C:%22%3C%3E?\\n')[0].diffs);\n\n  // Long string with repeats.\n  text1 = '';\n  for (var x = 0; x < 100; x++) {\n    text1 += 'abcdef';\n  }\n  text2 = text1 + '123';\n  expectedPatch = '@@ -573,28 +573,31 @@\\n cdefabcdefabcdefabcdefabcdef\\n+123\\n';\n  patches = dmp.patch_make(text1, text2);\n  assertEquals(expectedPatch, dmp.patch_toText(patches));\n\n  // Test null inputs.\n  try {\n    dmp.patch_make(null);\n    assertEquals(Error, null);\n  } catch (e) {\n    // Exception expected.\n  }\n}\n\nfunction testPatchSplitMax() {\n  // Assumes that dmp.Match_MaxBits is 32.\n  var patches = dmp.patch_make('abcdefghijklmnopqrstuvwxyz01234567890', 'XabXcdXefXghXijXklXmnXopXqrXstXuvXwxXyzX01X23X45X67X89X0');\n  dmp.patch_splitMax(patches);\n  assertEquals('@@ -1,32 +1,46 @@\\n+X\\n ab\\n+X\\n cd\\n+X\\n ef\\n+X\\n gh\\n+X\\n ij\\n+X\\n kl\\n+X\\n mn\\n+X\\n op\\n+X\\n qr\\n+X\\n st\\n+X\\n uv\\n+X\\n wx\\n+X\\n yz\\n+X\\n 012345\\n@@ -25,13 +39,18 @@\\n zX01\\n+X\\n 23\\n+X\\n 45\\n+X\\n 67\\n+X\\n 89\\n+X\\n 0\\n', dmp.patch_toText(patches));\n\n  patches = dmp.patch_make('abcdef1234567890123456789012345678901234567890123456789012345678901234567890uvwxyz', 'abcdefuvwxyz');\n  var oldToText = dmp.patch_toText(patches);\n  dmp.patch_splitMax(patches);\n  assertEquals(oldToText, dmp.patch_toText(patches));\n\n  patches = dmp.patch_make('1234567890123456789012345678901234567890123456789012345678901234567890', 'abc');\n  dmp.patch_splitMax(patches);\n  assertEquals('@@ -1,32 +1,4 @@\\n-1234567890123456789012345678\\n 9012\\n@@ -29,32 +1,4 @@\\n-9012345678901234567890123456\\n 7890\\n@@ -57,14 +1,3 @@\\n-78901234567890\\n+abc\\n', dmp.patch_toText(patches));\n\n  patches = dmp.patch_make('abcdefghij , h : 0 , t : 1 abcdefghij , h : 0 , t : 1 abcdefghij , h : 0 , t : 1', 'abcdefghij , h : 1 , t : 1 abcdefghij , h : 1 , t : 1 abcdefghij , h : 0 , t : 1');\n  dmp.patch_splitMax(patches);\n  assertEquals('@@ -2,32 +2,32 @@\\n bcdefghij , h : \\n-0\\n+1\\n  , t : 1 abcdef\\n@@ -29,32 +29,32 @@\\n bcdefghij , h : \\n-0\\n+1\\n  , t : 1 abcdef\\n', dmp.patch_toText(patches));\n}\n\nfunction testPatchAddPadding() {\n  // Both edges full.\n  var patches = dmp.patch_make('', 'test');\n  assertEquals('@@ -0,0 +1,4 @@\\n+test\\n', dmp.patch_toText(patches));\n  dmp.patch_addPadding(patches);\n  assertEquals('@@ -1,8 +1,12 @@\\n %01%02%03%04\\n+test\\n %01%02%03%04\\n', dmp.patch_toText(patches));\n\n  // Both edges partial.\n  patches = dmp.patch_make('XY', 'XtestY');\n  assertEquals('@@ -1,2 +1,6 @@\\n X\\n+test\\n Y\\n', dmp.patch_toText(patches));\n  dmp.patch_addPadding(patches);\n  assertEquals('@@ -2,8 +2,12 @@\\n %02%03%04X\\n+test\\n Y%01%02%03\\n', dmp.patch_toText(patches));\n\n  // Both edges none.\n  patches = dmp.patch_make('XXXXYYYY', 'XXXXtestYYYY');\n  assertEquals('@@ -1,8 +1,12 @@\\n XXXX\\n+test\\n YYYY\\n', dmp.patch_toText(patches));\n  dmp.patch_addPadding(patches);\n  assertEquals('@@ -5,8 +5,12 @@\\n XXXX\\n+test\\n YYYY\\n', dmp.patch_toText(patches));\n}\n\nfunction testPatchApply() {\n  dmp.Match_Distance = 1000;\n  dmp.Match_Threshold = 0.5;\n  dmp.Patch_DeleteThreshold = 0.5;\n  // Null case.\n  var patches = dmp.patch_make('', '');\n  var results = dmp.patch_apply(patches, 'Hello world.');\n  assertEquivalent(['Hello world.', []], results);\n\n  // Exact match.\n  patches = dmp.patch_make('The quick brown fox jumps over the lazy dog.', 'That quick brown fox jumped over a lazy dog.');\n  results = dmp.patch_apply(patches, 'The quick brown fox jumps over the lazy dog.');\n  assertEquivalent(['That quick brown fox jumped over a lazy dog.', [true, true]], results);\n\n  // Partial match.\n  results = dmp.patch_apply(patches, 'The quick red rabbit jumps over the tired tiger.');\n  assertEquivalent(['That quick red rabbit jumped over a tired tiger.', [true, true]], results);\n\n  // Failed match.\n  results = dmp.patch_apply(patches, 'I am the very model of a modern major general.');\n  assertEquivalent(['I am the very model of a modern major general.', [false, false]], results);\n\n  // Big delete, small change.\n  patches = dmp.patch_make('x1234567890123456789012345678901234567890123456789012345678901234567890y', 'xabcy');\n  results = dmp.patch_apply(patches, 'x123456789012345678901234567890-----++++++++++-----123456789012345678901234567890y');\n  assertEquivalent(['xabcy', [true, true]], results);\n\n  // Big delete, big change 1.\n  patches = dmp.patch_make('x1234567890123456789012345678901234567890123456789012345678901234567890y', 'xabcy');\n  results = dmp.patch_apply(patches, 'x12345678901234567890---------------++++++++++---------------12345678901234567890y');\n  assertEquivalent(['xabc12345678901234567890---------------++++++++++---------------12345678901234567890y', [false, true]], results);\n\n  // Big delete, big change 2.\n  dmp.Patch_DeleteThreshold = 0.6;\n  patches = dmp.patch_make('x1234567890123456789012345678901234567890123456789012345678901234567890y', 'xabcy');\n  results = dmp.patch_apply(patches, 'x12345678901234567890---------------++++++++++---------------12345678901234567890y');\n  assertEquivalent(['xabcy', [true, true]], results);\n  dmp.Patch_DeleteThreshold = 0.5;\n\n  // Compensate for failed patch.\n  dmp.Match_Threshold = 0.0;\n  dmp.Match_Distance = 0;\n  patches = dmp.patch_make('abcdefghijklmnopqrstuvwxyz--------------------1234567890', 'abcXXXXXXXXXXdefghijklmnopqrstuvwxyz--------------------1234567YYYYYYYYYY890');\n  results = dmp.patch_apply(patches, 'ABCDEFGHIJKLMNOPQRSTUVWXYZ--------------------1234567890');\n  assertEquivalent(['ABCDEFGHIJKLMNOPQRSTUVWXYZ--------------------1234567YYYYYYYYYY890', [false, true]], results);\n  dmp.Match_Threshold = 0.5;\n  dmp.Match_Distance = 1000;\n\n  // No side effects.\n  patches = dmp.patch_make('', 'test');\n  var patchstr = dmp.patch_toText(patches);\n  dmp.patch_apply(patches, '');\n  assertEquals(patchstr, dmp.patch_toText(patches));\n\n  // No side effects with major delete.\n  patches = dmp.patch_make('The quick brown fox jumps over the lazy dog.', 'Woof');\n  patchstr = dmp.patch_toText(patches);\n  dmp.patch_apply(patches, 'The quick brown fox jumps over the lazy dog.');\n  assertEquals(patchstr, dmp.patch_toText(patches));\n\n  // Edge exact match.\n  patches = dmp.patch_make('', 'test');\n  results = dmp.patch_apply(patches, '');\n  assertEquivalent(['test', [true]], results);\n\n  // Near edge exact match.\n  patches = dmp.patch_make('XY', 'XtestY');\n  results = dmp.patch_apply(patches, 'XY');\n  assertEquivalent(['XtestY', [true]], results);\n\n  // Edge partial match.\n  patches = dmp.patch_make('y', 'y123');\n  results = dmp.patch_apply(patches, 'x');\n  assertEquivalent(['x123', [true]], results);\n}\n"
  },
  {
    "path": "javascript/tests/speedtest.html",
    "content": "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01 Transitional//EN\" \"http://www.w3.org/TR/html4/loose.dtd\">\n<HTML>\n<HEAD>\n  <TITLE>Diff Speed Test</TITLE>\n  <SCRIPT TYPE=\"text/javascript\" LANGUAGE=\"JavaScript\" SRC=\"../diff_match_patch_uncompressed.js\"></SCRIPT>\n</HEAD>\n\n<BODY>\n<H1>Diff Speed Test</H1>\n\n<SCRIPT TYPE=\"text/javascript\" LANGUAGE=\"JavaScript\">\n\nfunction launch() {\n  var text1 = document.getElementById('text1').value;\n  var text2 = document.getElementById('text2').value;\n\n  var dmp = new diff_match_patch();\n  dmp.Diff_Timeout = 0;\n\n  // No warmup loop since it risks triggering an 'unresponsive script' dialog\n  // in client-side JavaScript\n  var ms_start = (new Date()).getTime();\n  var d = dmp.diff_main(text1, text2, false);\n  var ms_end = (new Date()).getTime();\n\n  var ds = dmp.diff_prettyHtml(d);\n  document.getElementById('outputdiv').innerHTML = ds + '<BR>Time: ' + (ms_end - ms_start) / 1000 + 's';\n}\n</SCRIPT>\n\n<FORM action=\"#\" onsubmit=\"return false;\">\n<TABLE WIDTH=\"100%\"><TR>\n  <TD WIDTH=\"50%\">\n<H3>Text Version 1:</H3>\n<TEXTAREA ID=\"text1\" STYLE=\"width: 100%\" ROWS=10>This is a '''list of newspapers published by [[Journal Register Company]]'''.\n\nThe company owns daily and weekly newspapers, other print media properties and newspaper-affiliated local Websites in the [[U.S.]] states of [[Connecticut]], [[Michigan]], [[New York]], [[Ohio]] and [[Pennsylvania]], organized in six geographic \"clusters\":&lt;ref&gt;[http://www.journalregister.com/newspapers.html Journal Register Company: Our Newspapers], accessed February 10, 2008.&lt;/ref&gt;\n\n== Capital-Saratoga ==\nThree dailies, associated weeklies and [[pennysaver]]s in greater [[Albany, New York]]; also [http://www.capitalcentral.com capitalcentral.com] and [http://www.jobsinnewyork.com JobsInNewYork.com].\n\n* ''The Oneida Daily Dispatch'' {{WS|oneidadispatch.com}} of [[Oneida, New York]]\n* ''[[The Record (Troy)|The Record]]'' {{WS|troyrecord.com}} of [[Troy, New York]]\n* ''[[The Saratogian]]'' {{WS|saratogian.com}} of [[Saratoga Springs, New York]]\n* Weeklies:\n** ''Community News'' {{WS|cnweekly.com}} weekly of [[Clifton Park, New York]]\n** ''Rome Observer'' of [[Rome, New York]]\n** ''Life &amp; Times of Utica'' of [[Utica, New York]]\n\n== Connecticut ==\nFive dailies, associated weeklies and [[pennysaver]]s in the state of [[Connecticut]]; also [http://www.ctcentral.com CTcentral.com], [http://www.ctcarsandtrucks.com CTCarsAndTrucks.com] and [http://www.jobsinct.com JobsInCT.com].\n\n* ''The Middletown Press'' {{WS|middletownpress.com}} of [[Middletown, Connecticut|Middletown]]\n* ''[[New Haven Register]]'' {{WS|newhavenregister.com}} of [[New Haven, Connecticut|New Haven]]\n* ''The Register Citizen'' {{WS|registercitizen.com}} of [[Torrington, Connecticut|Torrington]]\n\n* [[New Haven Register#Competitors|Elm City Newspapers]] {{WS|ctcentral.com}}\n** ''The Advertiser'' of [[East Haven, Connecticut|East Haven]]\n** ''Hamden Chronicle'' of [[Hamden, Connecticut|Hamden]]\n** ''Milford Weekly'' of [[Milford, Connecticut|Milford]]\n** ''The Orange Bulletin'' of [[Orange, Connecticut|Orange]]\n** ''The Post'' of [[North Haven, Connecticut|North Haven]]\n** ''Shelton Weekly'' of [[Shelton, Connecticut|Shelton]]\n** ''The Stratford Bard'' of [[Stratford, Connecticut|Stratford]]\n** ''Wallingford Voice'' of [[Wallingford, Connecticut|Wallingford]]\n** ''West Haven News'' of [[West Haven, Connecticut|West Haven]]\n* Housatonic Publications\n** ''The New Milford Times'' {{WS|newmilfordtimes.com}} of [[New Milford, Connecticut|New Milford]]\n** ''The Brookfield Journal'' of [[Brookfield, Connecticut|Brookfield]]\n** ''The Kent Good Times Dispatch'' of [[Kent, Connecticut|Kent]]\n** ''The Bethel Beacon'' of [[Bethel, Connecticut|Bethel]]\n** ''The Litchfield Enquirer'' of [[Litchfield, Connecticut|Litchfield]]\n** ''Litchfield County Times'' of [[Litchfield, Connecticut|Litchfield]]\n* Imprint Newspapers {{WS|imprintnewspapers.com}}\n** ''West Hartford News'' of [[West Hartford, Connecticut|West Hartford]]\n** ''Windsor Journal'' of [[Windsor, Connecticut|Windsor]]\n** ''Windsor Locks Journal'' of [[Windsor Locks, Connecticut|Windsor Locks]]\n** ''Avon Post'' of [[Avon, Connecticut|Avon]]\n** ''Farmington Post'' of [[Farmington, Connecticut|Farmington]]\n** ''Simsbury Post'' of [[Simsbury, Connecticut|Simsbury]]\n** ''Tri-Town Post'' of [[Burlington, Connecticut|Burlington]], [[Canton, Connecticut|Canton]] and [[Harwinton, Connecticut|Harwinton]]\n* Minuteman Publications\n** ''[[Fairfield Minuteman]]'' of [[Fairfield, Connecticut|Fairfield]]\n** ''The Westport Minuteman'' {{WS|westportminuteman.com}} of [[Westport, Connecticut|Westport]]\n* Shoreline Newspapers weeklies:\n** ''Branford Review'' of [[Branford, Connecticut|Branford]]\n** ''Clinton Recorder'' of [[Clinton, Connecticut|Clinton]]\n** ''The Dolphin'' of [[Naval Submarine Base New London]] in [[New London, Connecticut|New London]]\n** ''Main Street News'' {{WS|ctmainstreetnews.com}} of [[Essex, Connecticut|Essex]]\n** ''Pictorial Gazette'' of [[Old Saybrook, Connecticut|Old Saybrook]]\n** ''Regional Express'' of [[Colchester, Connecticut|Colchester]]\n** ''Regional Standard'' of [[Colchester, Connecticut|Colchester]]\n** ''Shoreline Times'' {{WS|shorelinetimes.com}} of [[Guilford, Connecticut|Guilford]]\n** ''Shore View East'' of [[Madison, Connecticut|Madison]]\n** ''Shore View West'' of [[Guilford, Connecticut|Guilford]]\n* Other weeklies:\n** ''Registro'' {{WS|registroct.com}} of [[New Haven, Connecticut|New Haven]]\n** ''Thomaston Express'' {{WS|thomastownexpress.com}} of [[Thomaston, Connecticut|Thomaston]]\n** ''Foothills Traders'' {{WS|foothillstrader.com}} of Torrington, Bristol, Canton\n\n== Michigan ==\nFour dailies, associated weeklies and [[pennysaver]]s in the state of [[Michigan]]; also [http://www.micentralhomes.com MIcentralhomes.com] and [http://www.micentralautos.com MIcentralautos.com]\n* ''[[Oakland Press]]'' {{WS|theoaklandpress.com}} of [[Oakland, Michigan|Oakland]]\n* ''Daily Tribune'' {{WS|dailytribune.com}} of [[Royal Oak, Michigan|Royal Oak]]\n* ''Macomb Daily'' {{WS|macombdaily.com}} of [[Mt. Clemens, Michigan|Mt. Clemens]]\n* ''[[Morning Sun]]'' {{WS|themorningsun.com}} of  [[Mount Pleasant, Michigan|Mount Pleasant]]\n* Heritage Newspapers {{WS|heritage.com}}\n** ''Belleville View''\n** ''Ile Camera''\n** ''Monroe Guardian''\n** ''Ypsilanti Courier''\n** ''News-Herald''\n** ''Press &amp; Guide''\n** ''Chelsea Standard &amp; Dexter Leader''\n** ''Manchester Enterprise''\n** ''Milan News-Leader''\n** ''Saline Reporter''\n* Independent Newspapers {{WS|sourcenewspapers.com}}\n** ''Advisor''\n** ''Source''\n* Morning Star {{WS|morningstarpublishing.com}}\n** ''Alma Reminder''\n** ''Alpena Star''\n** ''Antrim County News''\n** ''Carson City Reminder''\n** ''The Leader &amp; Kalkaskian''\n** ''Ogemaw/Oscoda County Star''\n** ''Petoskey/Charlevoix Star''\n** ''Presque Isle Star''\n** ''Preview Community Weekly''\n** ''Roscommon County Star''\n** ''St. Johns Reminder''\n** ''Straits Area Star''\n** ''The (Edmore) Advertiser''\n* Voice Newspapers {{WS|voicenews.com}}\n** ''Armada Times''\n** ''Bay Voice''\n** ''Blue Water Voice''\n** ''Downriver Voice''\n** ''Macomb Township Voice''\n** ''North Macomb Voice''\n** ''Weekend Voice''\n** ''Suburban Lifestyles'' {{WS|suburbanlifestyles.com}}\n\n== Mid-Hudson ==\nOne daily, associated magazines in the [[Hudson River Valley]] of [[New York]]; also [http://www.midhudsoncentral.com MidHudsonCentral.com] and [http://www.jobsinnewyork.com JobsInNewYork.com].\n\n* ''[[Daily Freeman]]'' {{WS|dailyfreeman.com}} of [[Kingston, New York]]\n\n== Ohio ==\nTwo dailies, associated magazines and three shared Websites, all in the state of [[Ohio]]: [http://www.allaroundcleveland.com AllAroundCleveland.com], [http://www.allaroundclevelandcars.com AllAroundClevelandCars.com] and [http://www.allaroundclevelandjobs.com AllAroundClevelandJobs.com].\n\n* ''[[The News-Herald (Ohio)|The News-Herald]]'' {{WS|news-herald.com}} of [[Willoughby, Ohio|Willoughby]]\n* ''[[The Morning Journal]]'' {{WS|morningjournal.com}} of [[Lorain, Ohio|Lorain]]\n\n== Philadelphia area ==\nSeven dailies and associated weeklies and magazines in [[Pennsylvania]] and [[New Jersey]], and associated Websites: [http://www.allaroundphilly.com AllAroundPhilly.com], [http://www.jobsinnj.com JobsInNJ.com], [http://www.jobsinpa.com JobsInPA.com], and [http://www.phillycarsearch.com PhillyCarSearch.com].\n\n* ''The Daily Local'' {{WS|dailylocal.com}} of [[West Chester, Pennsylvania|West Chester]]\n* ''[[Delaware County Daily and Sunday Times]] {{WS|delcotimes.com}} of Primos\n* ''[[The Mercury (Pennsylvania)|The Mercury]]'' {{WS|pottstownmercury.com}} of [[Pottstown, Pennsylvania|Pottstown]]\n* ''The Phoenix'' {{WS|phoenixvillenews.com}} of [[Phoenixville, Pennsylvania|Phoenixville]]\n* ''[[The Reporter (Lansdale)|The Reporter]]'' {{WS|thereporteronline.com}} of [[Lansdale, Pennsylvania|Lansdale]]\n* ''The Times Herald'' {{WS|timesherald.com}} of [[Norristown, Pennsylvania|Norristown]]\n* ''[[The Trentonian]]'' {{WS|trentonian.com}} of [[Trenton, New Jersey]]\n\n* Weeklies\n** ''El Latino Expreso'' of [[Trenton, New Jersey]]\n** ''La Voz'' of [[Norristown, Pennsylvania]]\n** ''The Village News'' of [[Downingtown, Pennsylvania]]\n** ''The Times Record'' of [[Kennett Square, Pennsylvania]]\n** ''The Tri-County Record'' {{WS|tricountyrecord.com}} of [[Morgantown, Pennsylvania]]\n** ''News of Delaware County'' {{WS|newsofdelawarecounty.com}}of [[Havertown, Pennsylvania]]\n** ''Main Line Times'' {{WS|mainlinetimes.com}}of [[Ardmore, Pennsylvania]]\n** ''Penny Pincher'' of [[Pottstown, Pennsylvania]]\n** ''Town Talk'' {{WS|towntalknews.com}} of [[Ridley, Pennsylvania]]\n* Chesapeake Publishing {{WS|pa8newsgroup.com}}\n** ''Solanco Sun Ledger'' of [[Quarryville, Pennsylvania]]\n** ''Columbia Ledger'' of [[Columbia, Pennsylvania]]\n** ''Coatesville Ledger'' of [[Downingtown, Pennsylvania]]\n** ''Parkesburg Post Ledger'' of [[Quarryville, Pennsylvania]]\n** ''Downingtown Ledger'' of [[Downingtown, Pennsylvania]]\n** ''The Kennett Paper'' of [[Kennett Square, Pennsylvania]]\n** ''Avon Grove Sun'' of [[West Grove, Pennsylvania]]\n** ''Oxford Tribune'' of [[Oxford, Pennsylvania]]\n** ''Elizabethtown Chronicle'' of [[Elizabethtown, Pennsylvania]]\n** ''Donegal Ledger'' of [[Donegal, Pennsylvania]]\n** ''Chadds Ford Post'' of [[Chadds Ford, Pennsylvania]]\n** ''The Central Record'' of [[Medford, New Jersey]]\n** ''Maple Shade Progress'' of [[Maple Shade, New Jersey]]\n* Intercounty Newspapers {{WS|buckslocalnews.com}}\n** ''The Review'' of Roxborough, Pennsylvania\n** ''The Recorder'' of [[Conshohocken, Pennsylvania]]\n** ''The Leader'' of [[Mount Airy, Pennsylvania|Mount Airy]] and West Oak Lake, Pennsylvania\n** ''The Pennington Post'' of [[Pennington, New Jersey]]\n** ''The Bristol Pilot'' of [[Bristol, Pennsylvania]]\n** ''Yardley News'' of [[Yardley, Pennsylvania]]\n** ''New Hope Gazette'' of [[New Hope, Pennsylvania]]\n** ''Doylestown Patriot'' of [[Doylestown, Pennsylvania]]\n** ''Newtown Advance'' of [[Newtown, Pennsylvania]]\n** ''The Plain Dealer'' of [[Williamstown, New Jersey]]\n** ''News Report'' of [[Sewell, New Jersey]]\n** ''Record Breeze'' of [[Berlin, New Jersey]]\n** ''Newsweekly'' of [[Moorestown, New Jersey]]\n** ''Haddon Herald'' of [[Haddonfield, New Jersey]]\n** ''New Egypt Press'' of [[New Egypt, New Jersey]]\n** ''Community News'' of [[Pemberton, New Jersey]]\n** ''Plymouth Meeting Journal'' of [[Plymouth Meeting, Pennsylvania]]\n** ''Lafayette Hill Journal'' of [[Lafayette Hill, Pennsylvania]]\n* Montgomery Newspapers {{WS|montgomerynews.com}}\n** ''Ambler Gazette'' of [[Ambler, Pennsylvania]]\n** ''Central Bucks Life'' of [[Bucks County, Pennsylvania]]\n** ''The Colonial'' of [[Plymouth Meeting, Pennsylvania]]\n** ''Glenside News'' of [[Glenside, Pennsylvania]]\n** ''The Globe'' of [[Lower Moreland Township, Pennsylvania]]\n** ''Main Line Life'' of [[Ardmore, Pennsylvania]]\n** ''Montgomery Life'' of [[Fort Washington, Pennsylvania]]\n** ''North Penn Life'' of [[Lansdale, Pennsylvania]]\n** ''Perkasie News Herald'' of [[Perkasie, Pennsylvania]]\n** ''Public Spirit'' of [[Hatboro, Pennsylvania]]\n** ''Souderton Independent'' of [[Souderton, Pennsylvania]]\n** ''Springfield Sun'' of [[Springfield, Pennsylvania]]\n** ''Spring-Ford Reporter'' of [[Royersford, Pennsylvania]]\n** ''Times Chronicle'' of [[Jenkintown, Pennsylvania]]\n** ''Valley Item'' of [[Perkiomenville, Pennsylvania]]\n** ''Willow Grove Guide'' of [[Willow Grove, Pennsylvania]]\n* News Gleaner Publications (closed December 2008) {{WS|newsgleaner.com}}\n** ''Life Newspapers'' of [[Philadelphia, Pennsylvania]]\n* Suburban Publications\n** ''The Suburban &amp; Wayne Times'' {{WS|waynesuburban.com}} of [[Wayne, Pennsylvania]]\n** ''The Suburban Advertiser'' of [[Exton, Pennsylvania]]\n** ''The King of Prussia Courier'' of [[King of Prussia, Pennsylvania]]\n* Press Newspapers {{WS|countypressonline.com}}\n** ''County Press'' of [[Newtown Square, Pennsylvania]]\n** ''Garnet Valley Press'' of [[Glen Mills, Pennsylvania]]\n** ''Haverford Press'' of [[Newtown Square, Pennsylvania]] (closed January 2009)\n** ''Hometown Press'' of [[Glen Mills, Pennsylvania]] (closed January 2009)\n** ''Media Press'' of [[Newtown Square, Pennsylvania]] (closed January 2009)\n** ''Springfield Press'' of [[Springfield, Pennsylvania]]\n* Berks-Mont Newspapers {{WS|berksmontnews.com}}\n** ''The Boyertown Area Times'' of [[Boyertown, Pennsylvania]]\n** ''The Kutztown Area Patriot'' of [[Kutztown, Pennsylvania]]\n** ''The Hamburg Area Item'' of [[Hamburg, Pennsylvania]]\n** ''The Southern Berks News'' of [[Exeter Township, Berks County, Pennsylvania]]\n** ''The Free Press'' of [[Quakertown, Pennsylvania]]\n** ''The Saucon News'' of [[Quakertown, Pennsylvania]]\n** ''Westside Weekly'' of [[Reading, Pennsylvania]]\n\n* Magazines\n** ''Bucks Co. Town &amp; Country Living''\n** ''Chester Co. Town &amp; Country Living''\n** ''Montomgery Co. Town &amp; Country Living''\n** ''Garden State Town &amp; Country Living''\n** ''Montgomery Homes''\n** ''Philadelphia Golfer''\n** ''Parents Express''\n** ''Art Matters''\n\n{{JRC}}\n\n==References==\n&lt;references /&gt;\n\n[[Category:Journal Register publications|*]]\n</TEXTAREA></TD>\n  <TD WIDTH=\"50%\">\n<H3>Text Version 2:</H3>\n\n<TEXTAREA ID=\"text2\" STYLE=\"width: 100%\" ROWS=10>This is a '''list of newspapers published by [[Journal Register Company]]'''.\n\nThe company owns daily and weekly newspapers, other print media properties and newspaper-affiliated local Websites in the [[U.S.]] states of [[Connecticut]], [[Michigan]], [[New York]], [[Ohio]], [[Pennsylvania]] and [[New Jersey]], organized in six geographic \"clusters\":&lt;ref&gt;[http://www.journalregister.com/publications.html Journal Register Company: Our Publications], accessed April 21, 2010.&lt;/ref&gt;\n\n== Capital-Saratoga ==\nThree dailies, associated weeklies and [[pennysaver]]s in greater [[Albany, New York]]; also [http://www.capitalcentral.com capitalcentral.com] and [http://www.jobsinnewyork.com JobsInNewYork.com].\n\n* ''The Oneida Daily Dispatch'' {{WS|oneidadispatch.com}} of [[Oneida, New York]]\n* ''[[The Record (Troy)|The Record]]'' {{WS|troyrecord.com}} of [[Troy, New York]]\n* ''[[The Saratogian]]'' {{WS|saratogian.com}} of [[Saratoga Springs, New York]]\n* Weeklies:\n** ''Community News'' {{WS|cnweekly.com}} weekly of [[Clifton Park, New York]]\n** ''Rome Observer'' {{WS|romeobserver.com}} of [[Rome, New York]]\n** ''WG Life '' {{WS|saratogian.com/wglife/}} of [[Wilton, New York]]\n** ''Ballston Spa Life '' {{WS|saratogian.com/bspalife}} of [[Ballston Spa, New York]]\n** ''Greenbush Life'' {{WS|troyrecord.com/greenbush}} of [[Troy, New York]]\n** ''Latham Life'' {{WS|troyrecord.com/latham}} of [[Latham, New York]]\n** ''River Life'' {{WS|troyrecord.com/river}} of [[Troy, New York]]\n\n== Connecticut ==\nThree dailies, associated weeklies and [[pennysaver]]s in the state of [[Connecticut]]; also [http://www.ctcentral.com CTcentral.com], [http://www.ctcarsandtrucks.com CTCarsAndTrucks.com] and [http://www.jobsinct.com JobsInCT.com].\n\n* ''The Middletown Press'' {{WS|middletownpress.com}} of [[Middletown, Connecticut|Middletown]]\n* ''[[New Haven Register]]'' {{WS|newhavenregister.com}} of [[New Haven, Connecticut|New Haven]]\n* ''The Register Citizen'' {{WS|registercitizen.com}} of [[Torrington, Connecticut|Torrington]]\n\n* Housatonic Publications\n** ''The Housatonic Times'' {{WS|housatonictimes.com}} of [[New Milford, Connecticut|New Milford]]\n** ''Litchfield County Times'' {{WS|countytimes.com}} of [[Litchfield, Connecticut|Litchfield]]\n\n* Minuteman Publications\n** ''[[Fairfield Minuteman]]'' {{WS|fairfieldminuteman.com}}of [[Fairfield, Connecticut|Fairfield]]\n** ''The Westport Minuteman'' {{WS|westportminuteman.com}} of [[Westport, Connecticut|Westport]]\n\n* Shoreline Newspapers\n** ''The Dolphin'' {{WS|dolphin-news.com}} of [[Naval Submarine Base New London]] in [[New London, Connecticut|New London]]\n** ''Shoreline Times'' {{WS|shorelinetimes.com}} of [[Guilford, Connecticut|Guilford]]\n\n* Foothills Media Group {{WS|foothillsmediagroup.com}}\n** ''Thomaston Express'' {{WS|thomastonexpress.com}} of [[Thomaston, Connecticut|Thomaston]]\n** ''Good News About Torrington'' {{WS|goodnewsabouttorrington.com}} of [[Torrington, Connecticut|Torrington]]\n** ''Granby News'' {{WS|foothillsmediagroup.com/granby}} of [[Granby, Connecticut|Granby]]\n** ''Canton News'' {{WS|foothillsmediagroup.com/canton}} of [[Canton, Connecticut|Canton]]\n** ''Avon News'' {{WS|foothillsmediagroup.com/avon}} of [[Avon, Connecticut|Avon]]\n** ''Simsbury News'' {{WS|foothillsmediagroup.com/simsbury}} of [[Simsbury, Connecticut|Simsbury]]\n** ''Litchfield News'' {{WS|foothillsmediagroup.com/litchfield}} of [[Litchfield, Connecticut|Litchfield]]\n** ''Foothills Trader'' {{WS|foothillstrader.com}} of Torrington, Bristol, Canton\n\n* Other weeklies\n** ''The Milford-Orange Bulletin'' {{WS|ctbulletin.com}} of [[Orange, Connecticut|Orange]]\n** ''The Post-Chronicle'' {{WS|ctpostchronicle.com}} of [[North Haven, Connecticut|North Haven]]\n** ''West Hartford News'' {{WS|westhartfordnews.com}} of [[West Hartford, Connecticut|West Hartford]]\n\n* Magazines\n** ''The Connecticut Bride'' {{WS|connecticutmag.com}}\n** ''Connecticut Magazine'' {{WS|theconnecticutbride.com}}\n** ''Passport Magazine'' {{WS|passport-mag.com}}\n\n== Michigan ==\nFour dailies, associated weeklies and [[pennysaver]]s in the state of [[Michigan]]; also [http://www.micentralhomes.com MIcentralhomes.com] and [http://www.micentralautos.com MIcentralautos.com]\n* ''[[Oakland Press]]'' {{WS|theoaklandpress.com}} of [[Oakland, Michigan|Oakland]]\n* ''Daily Tribune'' {{WS|dailytribune.com}} of [[Royal Oak, Michigan|Royal Oak]]\n* ''Macomb Daily'' {{WS|macombdaily.com}} of [[Mt. Clemens, Michigan|Mt. Clemens]]\n* ''[[Morning Sun]]'' {{WS|themorningsun.com}} of  [[Mount Pleasant, Michigan|Mount Pleasant]]\n\n* Heritage Newspapers {{WS|heritage.com}}\n** ''Belleville View'' {{WS|bellevilleview.com}}\n** ''Ile Camera'' {{WS|thenewsherald.com/ile_camera}}\n** ''Monroe Guardian''  {{WS|monreguardian.com}}\n** ''Ypsilanti Courier'' {{WS|ypsilanticourier.com}}\n** ''News-Herald'' {{WS|thenewsherald.com}}\n** ''Press &amp; Guide'' {{WS|pressandguide.com}}\n** ''Chelsea Standard &amp; Dexter Leader'' {{WS|chelseastandard.com}}\n** ''Manchester Enterprise'' {{WS|manchesterguardian.com}}\n** ''Milan News-Leader'' {{WS|milannews.com}}\n** ''Saline Reporter'' {{WS|salinereporter.com}}\n* Independent Newspapers\n** ''Advisor'' {{WS|sourcenewspapers.com}}\n** ''Source'' {{WS|sourcenewspapers.com}}\n* Morning Star {{WS|morningstarpublishing.com}}\n** ''The Leader &amp; Kalkaskian'' {{WS|leaderandkalkaskian.com}}\n** ''Grand Traverse Insider'' {{WS|grandtraverseinsider.com}}\n** ''Alma Reminder''\n** ''Alpena Star''\n** ''Ogemaw/Oscoda County Star''\n** ''Presque Isle Star''\n** ''St. Johns Reminder''\n\n* Voice Newspapers {{WS|voicenews.com}}\n** ''Armada Times''\n** ''Bay Voice''\n** ''Blue Water Voice''\n** ''Downriver Voice''\n** ''Macomb Township Voice''\n** ''North Macomb Voice''\n** ''Weekend Voice''\n\n== Mid-Hudson ==\nOne daily, associated magazines in the [[Hudson River Valley]] of [[New York]]; also [http://www.midhudsoncentral.com MidHudsonCentral.com] and [http://www.jobsinnewyork.com JobsInNewYork.com].\n\n* ''[[Daily Freeman]]'' {{WS|dailyfreeman.com}} of [[Kingston, New York]]\n* ''Las Noticias'' {{WS|lasnoticiasny.com}} of [[Kingston, New York]]\n\n== Ohio ==\nTwo dailies, associated magazines and three shared Websites, all in the state of [[Ohio]]: [http://www.allaroundcleveland.com AllAroundCleveland.com], [http://www.allaroundclevelandcars.com AllAroundClevelandCars.com] and [http://www.allaroundclevelandjobs.com AllAroundClevelandJobs.com].\n\n* ''[[The News-Herald (Ohio)|The News-Herald]]'' {{WS|news-herald.com}} of [[Willoughby, Ohio|Willoughby]]\n* ''[[The Morning Journal]]'' {{WS|morningjournal.com}} of [[Lorain, Ohio|Lorain]]\n* ''El Latino Expreso'' {{WS|lorainlatino.com}} of [[Lorain, Ohio|Lorain]]\n\n== Philadelphia area ==\nSeven dailies and associated weeklies and magazines in [[Pennsylvania]] and [[New Jersey]], and associated Websites: [http://www.allaroundphilly.com AllAroundPhilly.com], [http://www.jobsinnj.com JobsInNJ.com], [http://www.jobsinpa.com JobsInPA.com], and [http://www.phillycarsearch.com PhillyCarSearch.com].\n\n* ''[[The Daily Local News]]'' {{WS|dailylocal.com}} of [[West Chester, Pennsylvania|West Chester]]\n* ''[[Delaware County Daily and Sunday Times]] {{WS|delcotimes.com}} of Primos [[Upper Darby Township, Pennsylvania]]\n* ''[[The Mercury (Pennsylvania)|The Mercury]]'' {{WS|pottstownmercury.com}} of [[Pottstown, Pennsylvania|Pottstown]]\n* ''[[The Reporter (Lansdale)|The Reporter]]'' {{WS|thereporteronline.com}} of [[Lansdale, Pennsylvania|Lansdale]]\n* ''The Times Herald'' {{WS|timesherald.com}} of [[Norristown, Pennsylvania|Norristown]]\n* ''[[The Trentonian]]'' {{WS|trentonian.com}} of [[Trenton, New Jersey]]\n\n* Weeklies\n* ''The Phoenix'' {{WS|phoenixvillenews.com}} of [[Phoenixville, Pennsylvania]]\n** ''El Latino Expreso'' {{WS|njexpreso.com}} of [[Trenton, New Jersey]]\n** ''La Voz'' {{WS|lavozpa.com}} of [[Norristown, Pennsylvania]]\n** ''The Tri County Record'' {{WS|tricountyrecord.com}} of [[Morgantown, Pennsylvania]]\n** ''Penny Pincher'' {{WS|pennypincherpa.com}}of [[Pottstown, Pennsylvania]]\n\n* Chesapeake Publishing  {{WS|southernchestercountyweeklies.com}}\n** ''The Kennett Paper'' {{WS|kennettpaper.com}} of [[Kennett Square, Pennsylvania]]\n** ''Avon Grove Sun'' {{WS|avongrovesun.com}} of [[West Grove, Pennsylvania]]\n** ''The Central Record'' {{WS|medfordcentralrecord.com}} of [[Medford, New Jersey]]\n** ''Maple Shade Progress'' {{WS|mapleshadeprogress.com}} of [[Maple Shade, New Jersey]]\n\n* Intercounty Newspapers {{WS|buckslocalnews.com}} {{WS|southjerseylocalnews.com}}\n** ''The Pennington Post'' {{WS|penningtonpost.com}} of [[Pennington, New Jersey]]\n** ''The Bristol Pilot'' {{WS|bristolpilot.com}} of [[Bristol, Pennsylvania]]\n** ''Yardley News'' {{WS|yardleynews.com}} of [[Yardley, Pennsylvania]]\n** ''Advance of Bucks County'' {{WS|advanceofbucks.com}} of [[Newtown, Pennsylvania]]\n** ''Record Breeze'' {{WS|recordbreeze.com}} of [[Berlin, New Jersey]]\n** ''Community News'' {{WS|sjcommunitynews.com}} of [[Pemberton, New Jersey]]\n\n* Montgomery Newspapers {{WS|montgomerynews.com}}\n** ''Ambler Gazette'' {{WS|amblergazette.com}} of [[Ambler, Pennsylvania]]\n** ''The Colonial'' {{WS|colonialnews.com}} of [[Plymouth Meeting, Pennsylvania]]\n** ''Glenside News'' {{WS|glensidenews.com}} of [[Glenside, Pennsylvania]]\n** ''The Globe'' {{WS|globenewspaper.com}} of [[Lower Moreland Township, Pennsylvania]]\n** ''Montgomery Life'' {{WS|montgomerylife.com}} of [[Fort Washington, Pennsylvania]]\n** ''North Penn Life'' {{WS|northpennlife.com}} of [[Lansdale, Pennsylvania]]\n** ''Perkasie News Herald'' {{WS|perkasienewsherald.com}} of [[Perkasie, Pennsylvania]]\n** ''Public Spirit'' {{WS|thepublicspirit.com}} of [[Hatboro, Pennsylvania]]\n** ''Souderton Independent'' {{WS|soudertonindependent.com}} of [[Souderton, Pennsylvania]]\n** ''Springfield Sun'' {{WS|springfieldsun.com}} of [[Springfield, Pennsylvania]]\n** ''Spring-Ford Reporter'' {{WS|springfordreporter.com}} of [[Royersford, Pennsylvania]]\n** ''Times Chronicle'' {{WS|thetimeschronicle.com}} of [[Jenkintown, Pennsylvania]]\n** ''Valley Item'' {{WS|valleyitem.com}} of [[Perkiomenville, Pennsylvania]]\n** ''Willow Grove Guide'' {{WS|willowgroveguide.com}} of [[Willow Grove, Pennsylvania]]\n** ''The Review'' {{WS|roxreview.com}} of [[Roxborough, Philadelphia, Pennsylvania]]\n\n* Main Line Media News {{WS|mainlinemedianews.com}}\n** ''Main Line Times'' {{WS|mainlinetimes.com}} of [[Ardmore, Pennsylvania]]\n** ''Main Line Life'' {{WS|mainlinelife.com}} of [[Ardmore, Pennsylvania]]\n** ''The King of Prussia Courier'' {{WS|kingofprussiacourier.com}} of [[King of Prussia, Pennsylvania]]\n\n* Delaware County News Network {{WS|delconewsnetwork.com}}\n** ''News of Delaware County'' {{WS|newsofdelawarecounty.com}} of [[Havertown, Pennsylvania]]\n** ''County Press'' {{WS|countypressonline.com}} of [[Newtown Square, Pennsylvania]]\n** ''Garnet Valley Press'' {{WS|countypressonline.com}} of [[Glen Mills, Pennsylvania]]\n** ''Springfield Press'' {{WS|countypressonline.com}} of [[Springfield, Pennsylvania]]\n** ''Town Talk'' {{WS|towntalknews.com}} of [[Ridley, Pennsylvania]]\n\n* Berks-Mont Newspapers {{WS|berksmontnews.com}}\n** ''The Boyertown Area Times'' {{WS|berksmontnews.com/boyertown_area_times}} of [[Boyertown, Pennsylvania]]\n** ''The Kutztown Area Patriot'' {{WS|berksmontnews.com/kutztown_area_patriot}} of [[Kutztown, Pennsylvania]]\n** ''The Hamburg Area Item'' {{WS|berksmontnews.com/hamburg_area_item}} of [[Hamburg, Pennsylvania]]\n** ''The Southern Berks News'' {{WS|berksmontnews.com/southern_berks_news}} of [[Exeter Township, Berks County, Pennsylvania]]\n** ''Community Connection'' {{WS|berksmontnews.com/community_connection}} of [[Boyertown, Pennsylvania]]\n\n* Magazines\n** ''Bucks Co. Town &amp; Country Living'' {{WS|buckscountymagazine.com}}\n** ''Parents Express'' {{WS|parents-express.com}}\n** ''Real Men, Rednecks'' {{WS|realmenredneck.com}}\n\n{{JRC}}\n\n==References==\n&lt;references /&gt;\n\n[[Category:Journal Register publications|*]]\n</TEXTAREA></TD>\n</TR></TABLE>\n\n<P><INPUT TYPE=\"button\" onClick=\"launch();\" VALUE=\"Compute Diff\"></P>\n</FORM>\n\n<DIV ID=\"outputdiv\"></DIV>\n\n</BODY>\n</HTML>\n"
  },
  {
    "path": "lua/diff_match_patch.lua",
    "content": "--[[\n* Diff Match and Patch\n* Copyright 2018 The diff-match-patch Authors.\n* https://github.com/google/diff-match-patch\n*\n* Based on the JavaScript implementation by Neil Fraser.\n* Ported to Lua by Duncan Cross.\n*\n* Licensed under the Apache License, Version 2.0 (the \"License\");\n* you may not use this file except in compliance with the License.\n* You may obtain a copy of the License at\n*\n*   http://www.apache.org/licenses/LICENSE-2.0\n*\n* Unless required by applicable law or agreed to in writing, software\n* distributed under the License is distributed on an \"AS IS\" BASIS,\n* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n* See the License for the specific language governing permissions and\n* limitations under the License.\n--]]\n\n--[[\n-- Lua 5.1 and earlier requires the external BitOp library.\n-- This library is built-in from Lua 5.2 and later as 'bit32'.\nrequire 'bit'   -- <https://bitop.luajit.org/>\nlocal band, bor, lshift\n    = bit.band, bit.bor, bit.lshift\n--]]\n\nlocal band, bor, lshift\n    = bit32.band, bit32.bor, bit32.lshift\nlocal type, setmetatable, ipairs, select\n    = type, setmetatable, ipairs, select\nlocal unpack, tonumber, error\n    = unpack, tonumber, error\nlocal strsub, strbyte, strchar, gmatch, gsub\n    = string.sub, string.byte, string.char, string.gmatch, string.gsub\nlocal strmatch, strfind, strformat\n    = string.match, string.find, string.format\nlocal tinsert, tremove, tconcat\n    = table.insert, table.remove, table.concat\nlocal max, min, floor, ceil, abs\n    = math.max, math.min, math.floor, math.ceil, math.abs\nlocal clock = os.clock\n\n\n-- Utility functions.\n\nlocal percentEncode_pattern = '[^A-Za-z0-9%-=;\\',./~!@#$%&*%(%)_%+ %?]'\nlocal function percentEncode_replace(v)\n  return strformat('%%%02X', strbyte(v))\nend\n\nlocal function indexOf(a, b, start)\n  if (#b == 0) then\n    return nil\n  end\n  return strfind(a, b, start, true)\nend\n\nlocal htmlEncode_pattern = '[&<>\\n]'\nlocal htmlEncode_replace = {\n  ['&'] = '&amp;', ['<'] = '&lt;', ['>'] = '&gt;', ['\\n'] = '&para;<br>'\n}\n\n-- Public API Functions\n-- (Exported at the end of the script)\n\nlocal diff_main,\n      diff_cleanupSemantic,\n      diff_cleanupEfficiency,\n      diff_levenshtein,\n      diff_prettyHtml\n\nlocal match_main\n\nlocal patch_make,\n      patch_toText,\n      patch_fromText,\n      patch_apply\n\n--[[\n* The data structure representing a diff is an array of tuples:\n* {{DIFF_DELETE, 'Hello'}, {DIFF_INSERT, 'Goodbye'}, {DIFF_EQUAL, ' world.'}}\n* which means: delete 'Hello', add 'Goodbye' and keep ' world.'\n--]]\nlocal DIFF_DELETE = -1\nlocal DIFF_INSERT = 1\nlocal DIFF_EQUAL = 0\n\n-- Number of seconds to map a diff before giving up (0 for infinity).\nlocal Diff_Timeout = 1.0\n-- Cost of an empty edit operation in terms of edit characters.\nlocal Diff_EditCost = 4\n-- At what point is no match declared (0.0 = perfection, 1.0 = very loose).\nlocal Match_Threshold = 0.5\n-- How far to search for a match (0 = exact location, 1000+ = broad match).\n-- A match this many characters away from the expected location will add\n-- 1.0 to the score (0.0 is a perfect match).\nlocal Match_Distance = 1000\n-- When deleting a large block of text (over ~64 characters), how close do\n-- the contents have to be to match the expected contents. (0.0 = perfection,\n-- 1.0 = very loose).  Note that Match_Threshold controls how closely the\n-- end points of a delete need to match.\nlocal Patch_DeleteThreshold = 0.5\n-- Chunk size for context length.\nlocal Patch_Margin = 4\n-- The number of bits in an int.\nlocal Match_MaxBits = 32\n\nfunction settings(new)\n  if new then\n    Diff_Timeout = new.Diff_Timeout or Diff_Timeout\n    Diff_EditCost = new.Diff_EditCost or Diff_EditCost\n    Match_Threshold = new.Match_Threshold or Match_Threshold\n    Match_Distance = new.Match_Distance or Match_Distance\n    Patch_DeleteThreshold = new.Patch_DeleteThreshold or Patch_DeleteThreshold\n    Patch_Margin = new.Patch_Margin or Patch_Margin\n    Match_MaxBits = new.Match_MaxBits or Match_MaxBits\n  else\n    return {\n      Diff_Timeout = Diff_Timeout;\n      Diff_EditCost = Diff_EditCost;\n      Match_Threshold = Match_Threshold;\n      Match_Distance = Match_Distance;\n      Patch_DeleteThreshold = Patch_DeleteThreshold;\n      Patch_Margin = Patch_Margin;\n      Match_MaxBits = Match_MaxBits;\n    }\n  end\nend\n\n-- ---------------------------------------------------------------------------\n--  DIFF API\n-- ---------------------------------------------------------------------------\n\n-- The private diff functions\nlocal _diff_compute,\n      _diff_bisect,\n      _diff_halfMatchI,\n      _diff_halfMatch,\n      _diff_cleanupSemanticScore,\n      _diff_cleanupSemanticLossless,\n      _diff_cleanupMerge,\n      _diff_commonPrefix,\n      _diff_commonSuffix,\n      _diff_commonOverlap,\n      _diff_xIndex,\n      _diff_text1,\n      _diff_text2,\n      _diff_toDelta,\n      _diff_fromDelta\n\n--[[\n* Find the differences between two texts.  Simplifies the problem by stripping\n* any common prefix or suffix off the texts before diffing.\n* @param {string} text1 Old string to be diffed.\n* @param {string} text2 New string to be diffed.\n* @param {boolean} opt_checklines Has no effect in Lua.\n* @param {number} opt_deadline Optional time when the diff should be complete\n*     by.  Used internally for recursive calls.  Users should set DiffTimeout\n*     instead.\n* @return {Array.<Array.<number|string>>} Array of diff tuples.\n--]]\nfunction diff_main(text1, text2, opt_checklines, opt_deadline)\n  -- Set a deadline by which time the diff must be complete.\n  if opt_deadline == nil then\n    if Diff_Timeout <= 0 then\n      opt_deadline = 2 ^ 31\n    else\n      opt_deadline = clock() + Diff_Timeout\n    end\n  end\n  local deadline = opt_deadline\n\n  -- Check for null inputs.\n  if text1 == nil or text1 == nil then\n    error('Null inputs. (diff_main)')\n  end\n\n  -- Check for equality (speedup).\n  if text1 == text2 then\n    if #text1 > 0 then\n      return {{DIFF_EQUAL, text1}}\n    end\n    return {}\n  end\n\n  -- LUANOTE: Due to the lack of Unicode support, Lua is incapable of\n  -- implementing the line-mode speedup.\n  local checklines = false\n\n  -- Trim off common prefix (speedup).\n  local commonlength = _diff_commonPrefix(text1, text2)\n  local commonprefix\n  if commonlength > 0 then\n    commonprefix = strsub(text1, 1, commonlength)\n    text1 = strsub(text1, commonlength + 1)\n    text2 = strsub(text2, commonlength + 1)\n  end\n\n  -- Trim off common suffix (speedup).\n  commonlength = _diff_commonSuffix(text1, text2)\n  local commonsuffix\n  if commonlength > 0 then\n    commonsuffix = strsub(text1, -commonlength)\n    text1 = strsub(text1, 1, -commonlength - 1)\n    text2 = strsub(text2, 1, -commonlength - 1)\n  end\n\n  -- Compute the diff on the middle block.\n  local diffs = _diff_compute(text1, text2, checklines, deadline)\n\n  -- Restore the prefix and suffix.\n  if commonprefix then\n    tinsert(diffs, 1, {DIFF_EQUAL, commonprefix})\n  end\n  if commonsuffix then\n    diffs[#diffs + 1] = {DIFF_EQUAL, commonsuffix}\n  end\n\n  _diff_cleanupMerge(diffs)\n  return diffs\nend\n\n--[[\n* Reduce the number of edits by eliminating semantically trivial equalities.\n* @param {Array.<Array.<number|string>>} diffs Array of diff tuples.\n--]]\nfunction diff_cleanupSemantic(diffs)\n  local changes = false\n  local equalities = {}  -- Stack of indices where equalities are found.\n  local equalitiesLength = 0  -- Keeping our own length var is faster.\n  local lastEquality = nil\n  -- Always equal to diffs[equalities[equalitiesLength]][2]\n  local pointer = 1  -- Index of current position.\n  -- Number of characters that changed prior to the equality.\n  local length_insertions1 = 0\n  local length_deletions1 = 0\n  -- Number of characters that changed after the equality.\n  local length_insertions2 = 0\n  local length_deletions2 = 0\n\n  while diffs[pointer] do\n    if diffs[pointer][1] == DIFF_EQUAL then  -- Equality found.\n      equalitiesLength = equalitiesLength + 1\n      equalities[equalitiesLength] = pointer\n      length_insertions1 = length_insertions2\n      length_deletions1 = length_deletions2\n      length_insertions2 = 0\n      length_deletions2 = 0\n      lastEquality = diffs[pointer][2]\n    else  -- An insertion or deletion.\n      if diffs[pointer][1] == DIFF_INSERT then\n        length_insertions2 = length_insertions2 + #(diffs[pointer][2])\n      else\n        length_deletions2 = length_deletions2 + #(diffs[pointer][2])\n      end\n      -- Eliminate an equality that is smaller or equal to the edits on both\n      -- sides of it.\n      if lastEquality\n          and (#lastEquality <= max(length_insertions1, length_deletions1))\n          and (#lastEquality <= max(length_insertions2, length_deletions2)) then\n        -- Duplicate record.\n        tinsert(diffs, equalities[equalitiesLength],\n         {DIFF_DELETE, lastEquality})\n        -- Change second copy to insert.\n        diffs[equalities[equalitiesLength] + 1][1] = DIFF_INSERT\n        -- Throw away the equality we just deleted.\n        equalitiesLength = equalitiesLength - 1\n        -- Throw away the previous equality (it needs to be reevaluated).\n        equalitiesLength = equalitiesLength - 1\n        pointer = (equalitiesLength > 0) and equalities[equalitiesLength] or 0\n        length_insertions1, length_deletions1 = 0, 0  -- Reset the counters.\n        length_insertions2, length_deletions2 = 0, 0\n        lastEquality = nil\n        changes = true\n      end\n    end\n    pointer = pointer + 1\n  end\n\n  -- Normalize the diff.\n  if changes then\n    _diff_cleanupMerge(diffs)\n  end\n  _diff_cleanupSemanticLossless(diffs)\n\n  -- Find any overlaps between deletions and insertions.\n  -- e.g: <del>abcxxx</del><ins>xxxdef</ins>\n  --   -> <del>abc</del>xxx<ins>def</ins>\n  -- e.g: <del>xxxabc</del><ins>defxxx</ins>\n  --   -> <ins>def</ins>xxx<del>abc</del>\n  -- Only extract an overlap if it is as big as the edit ahead or behind it.\n  pointer = 2\n  while diffs[pointer] do\n    if (diffs[pointer - 1][1] == DIFF_DELETE and\n        diffs[pointer][1] == DIFF_INSERT) then\n      local deletion = diffs[pointer - 1][2]\n      local insertion = diffs[pointer][2]\n      local overlap_length1 = _diff_commonOverlap(deletion, insertion)\n      local overlap_length2 = _diff_commonOverlap(insertion, deletion)\n      if (overlap_length1 >= overlap_length2) then\n        if (overlap_length1 >= #deletion / 2 or\n            overlap_length1 >= #insertion / 2) then\n          -- Overlap found.  Insert an equality and trim the surrounding edits.\n          tinsert(diffs, pointer,\n              {DIFF_EQUAL, strsub(insertion, 1, overlap_length1)})\n          diffs[pointer - 1][2] =\n              strsub(deletion, 1, #deletion - overlap_length1)\n          diffs[pointer + 1][2] = strsub(insertion, overlap_length1 + 1)\n          pointer = pointer + 1\n        end\n      else\n        if (overlap_length2 >= #deletion / 2 or\n            overlap_length2 >= #insertion / 2) then\n          -- Reverse overlap found.\n          -- Insert an equality and swap and trim the surrounding edits.\n          tinsert(diffs, pointer,\n              {DIFF_EQUAL, strsub(deletion, 1, overlap_length2)})\n          diffs[pointer - 1] = {DIFF_INSERT,\n              strsub(insertion, 1, #insertion - overlap_length2)}\n          diffs[pointer + 1] = {DIFF_DELETE,\n              strsub(deletion, overlap_length2 + 1)}\n          pointer = pointer + 1\n        end\n      end\n      pointer = pointer + 1\n    end\n    pointer = pointer + 1\n  end\nend\n\n--[[\n* Reduce the number of edits by eliminating operationally trivial equalities.\n* @param {Array.<Array.<number|string>>} diffs Array of diff tuples.\n--]]\nfunction diff_cleanupEfficiency(diffs)\n  local changes = false\n  -- Stack of indices where equalities are found.\n  local equalities = {}\n  -- Keeping our own length var is faster.\n  local equalitiesLength = 0\n  -- Always equal to diffs[equalities[equalitiesLength]][2]\n  local lastEquality = nil\n  -- Index of current position.\n  local pointer = 1\n\n  -- The following four are really booleans but are stored as numbers because\n  -- they are used at one point like this:\n  --\n  -- (pre_ins + pre_del + post_ins + post_del) == 3\n  --\n  -- ...i.e. checking that 3 of them are true and 1 of them is false.\n\n  -- Is there an insertion operation before the last equality.\n  local pre_ins = 0\n  -- Is there a deletion operation before the last equality.\n  local pre_del = 0\n  -- Is there an insertion operation after the last equality.\n  local post_ins = 0\n  -- Is there a deletion operation after the last equality.\n  local post_del = 0\n\n  while diffs[pointer] do\n    if diffs[pointer][1] == DIFF_EQUAL then  -- Equality found.\n      local diffText = diffs[pointer][2]\n      if (#diffText < Diff_EditCost) and (post_ins == 1 or post_del == 1) then\n        -- Candidate found.\n        equalitiesLength = equalitiesLength + 1\n        equalities[equalitiesLength] = pointer\n        pre_ins, pre_del = post_ins, post_del\n        lastEquality = diffText\n      else\n        -- Not a candidate, and can never become one.\n        equalitiesLength = 0\n        lastEquality = nil\n      end\n      post_ins, post_del = 0, 0\n    else  -- An insertion or deletion.\n      if diffs[pointer][1] == DIFF_DELETE then\n        post_del = 1\n      else\n        post_ins = 1\n      end\n      --[[\n      * Five types to be split:\n      * <ins>A</ins><del>B</del>XY<ins>C</ins><del>D</del>\n      * <ins>A</ins>X<ins>C</ins><del>D</del>\n      * <ins>A</ins><del>B</del>X<ins>C</ins>\n      * <ins>A</del>X<ins>C</ins><del>D</del>\n      * <ins>A</ins><del>B</del>X<del>C</del>\n      --]]\n      if lastEquality and (\n          (pre_ins+pre_del+post_ins+post_del == 4)\n          or\n          (\n            (#lastEquality < Diff_EditCost / 2)\n            and\n            (pre_ins+pre_del+post_ins+post_del == 3)\n          )) then\n        -- Duplicate record.\n        tinsert(diffs, equalities[equalitiesLength],\n            {DIFF_DELETE, lastEquality})\n        -- Change second copy to insert.\n        diffs[equalities[equalitiesLength] + 1][1] = DIFF_INSERT\n        -- Throw away the equality we just deleted.\n        equalitiesLength = equalitiesLength - 1\n        lastEquality = nil\n        if (pre_ins == 1) and (pre_del == 1) then\n          -- No changes made which could affect previous entry, keep going.\n          post_ins, post_del = 1, 1\n          equalitiesLength = 0\n        else\n          -- Throw away the previous equality.\n          equalitiesLength = equalitiesLength - 1\n          pointer = (equalitiesLength > 0) and equalities[equalitiesLength] or 0\n          post_ins, post_del = 0, 0\n        end\n        changes = true\n      end\n    end\n    pointer = pointer + 1\n  end\n\n  if changes then\n    _diff_cleanupMerge(diffs)\n  end\nend\n\n--[[\n* Compute the Levenshtein distance; the number of inserted, deleted or\n* substituted characters.\n* @param {Array.<Array.<number|string>>} diffs Array of diff tuples.\n* @return {number} Number of changes.\n--]]\nfunction diff_levenshtein(diffs)\n  local levenshtein = 0\n  local insertions, deletions = 0, 0\n  for x, diff in ipairs(diffs) do\n    local op, data = diff[1], diff[2]\n    if (op == DIFF_INSERT) then\n      insertions = insertions + #data\n    elseif (op == DIFF_DELETE) then\n      deletions = deletions + #data\n    elseif (op == DIFF_EQUAL) then\n      -- A deletion and an insertion is one substitution.\n      levenshtein = levenshtein + max(insertions, deletions)\n      insertions = 0\n      deletions = 0\n    end\n  end\n  levenshtein = levenshtein + max(insertions, deletions)\n  return levenshtein\nend\n\n--[[\n* Convert a diff array into a pretty HTML report.\n* @param {Array.<Array.<number|string>>} diffs Array of diff tuples.\n* @return {string} HTML representation.\n--]]\nfunction diff_prettyHtml(diffs)\n  local html = {}\n  for x, diff in ipairs(diffs) do\n    local op = diff[1]   -- Operation (insert, delete, equal)\n    local data = diff[2]  -- Text of change.\n    local text = gsub(data, htmlEncode_pattern, htmlEncode_replace)\n    if op == DIFF_INSERT then\n      html[x] = '<ins style=\"background:#e6ffe6;\">' .. text .. '</ins>'\n    elseif op == DIFF_DELETE then\n      html[x] = '<del style=\"background:#ffe6e6;\">' .. text .. '</del>'\n    elseif op == DIFF_EQUAL then\n      html[x] = '<span>' .. text .. '</span>'\n    end\n  end\n  return tconcat(html)\nend\n\n-- ---------------------------------------------------------------------------\n-- UNOFFICIAL/PRIVATE DIFF FUNCTIONS\n-- ---------------------------------------------------------------------------\n\n--[[\n* Find the differences between two texts.  Assumes that the texts do not\n* have any common prefix or suffix.\n* @param {string} text1 Old string to be diffed.\n* @param {string} text2 New string to be diffed.\n* @param {boolean} checklines Has no effect in Lua.\n* @param {number} deadline Time when the diff should be complete by.\n* @return {Array.<Array.<number|string>>} Array of diff tuples.\n* @private\n--]]\nfunction _diff_compute(text1, text2, checklines, deadline)\n  if #text1 == 0 then\n    -- Just add some text (speedup).\n    return {{DIFF_INSERT, text2}}\n  end\n\n  if #text2 == 0 then\n    -- Just delete some text (speedup).\n    return {{DIFF_DELETE, text1}}\n  end\n\n  local diffs\n\n  local longtext = (#text1 > #text2) and text1 or text2\n  local shorttext = (#text1 > #text2) and text2 or text1\n  local i = indexOf(longtext, shorttext)\n\n  if i ~= nil then\n    -- Shorter text is inside the longer text (speedup).\n    diffs = {\n      {DIFF_INSERT, strsub(longtext, 1, i - 1)},\n      {DIFF_EQUAL, shorttext},\n      {DIFF_INSERT, strsub(longtext, i + #shorttext)}\n    }\n    -- Swap insertions for deletions if diff is reversed.\n    if #text1 > #text2 then\n      diffs[1][1], diffs[3][1] = DIFF_DELETE, DIFF_DELETE\n    end\n    return diffs\n  end\n\n  if #shorttext == 1 then\n    -- Single character string.\n    -- After the previous speedup, the character can't be an equality.\n    return {{DIFF_DELETE, text1}, {DIFF_INSERT, text2}}\n  end\n\n  -- Check to see if the problem can be split in two.\n  do\n    local\n     text1_a, text1_b,\n     text2_a, text2_b,\n     mid_common        = _diff_halfMatch(text1, text2)\n\n    if text1_a then\n      -- A half-match was found, sort out the return data.\n      -- Send both pairs off for separate processing.\n      local diffs_a = diff_main(text1_a, text2_a, checklines, deadline)\n      local diffs_b = diff_main(text1_b, text2_b, checklines, deadline)\n      -- Merge the results.\n      local diffs_a_len = #diffs_a\n      diffs = diffs_a\n      diffs[diffs_a_len + 1] = {DIFF_EQUAL, mid_common}\n      for i, b_diff in ipairs(diffs_b) do\n        diffs[diffs_a_len + 1 + i] = b_diff\n      end\n      return diffs\n    end\n  end\n\n  return _diff_bisect(text1, text2, deadline)\nend\n\n--[[\n* Find the 'middle snake' of a diff, split the problem in two\n* and return the recursively constructed diff.\n* See Myers 1986 paper: An O(ND) Difference Algorithm and Its Variations.\n* @param {string} text1 Old string to be diffed.\n* @param {string} text2 New string to be diffed.\n* @param {number} deadline Time at which to bail if not yet complete.\n* @return {Array.<Array.<number|string>>} Array of diff tuples.\n* @private\n--]]\nfunction _diff_bisect(text1, text2, deadline)\n  -- Cache the text lengths to prevent multiple calls.\n  local text1_length = #text1\n  local text2_length = #text2\n  local _sub, _element\n  local max_d = ceil((text1_length + text2_length) / 2)\n  local v_offset = max_d\n  local v_length = 2 * max_d\n  local v1 = {}\n  local v2 = {}\n  -- Setting all elements to -1 is faster in Lua than mixing integers and nil.\n  for x = 0, v_length - 1 do\n    v1[x] = -1\n    v2[x] = -1\n  end\n  v1[v_offset + 1] = 0\n  v2[v_offset + 1] = 0\n  local delta = text1_length - text2_length\n  -- If the total number of characters is odd, then\n  -- the front path will collide with the reverse path.\n  local front = (delta % 2 ~= 0)\n  -- Offsets for start and end of k loop.\n  -- Prevents mapping of space beyond the grid.\n  local k1start = 0\n  local k1end = 0\n  local k2start = 0\n  local k2end = 0\n  for d = 0, max_d - 1 do\n    -- Bail out if deadline is reached.\n    if clock() > deadline then\n      break\n    end\n\n    -- Walk the front path one step.\n    for k1 = -d + k1start, d - k1end, 2 do\n      local k1_offset = v_offset + k1\n      local x1\n      if (k1 == -d) or ((k1 ~= d) and\n          (v1[k1_offset - 1] < v1[k1_offset + 1])) then\n        x1 = v1[k1_offset + 1]\n      else\n        x1 = v1[k1_offset - 1] + 1\n      end\n      local y1 = x1 - k1\n      while (x1 <= text1_length) and (y1 <= text2_length)\n          and (strsub(text1, x1, x1) == strsub(text2, y1, y1)) do\n        x1 = x1 + 1\n        y1 = y1 + 1\n      end\n      v1[k1_offset] = x1\n      if x1 > text1_length + 1 then\n        -- Ran off the right of the graph.\n        k1end = k1end + 2\n      elseif y1 > text2_length + 1 then\n        -- Ran off the bottom of the graph.\n        k1start = k1start + 2\n      elseif front then\n        local k2_offset = v_offset + delta - k1\n        if k2_offset >= 0 and k2_offset < v_length and v2[k2_offset] ~= -1 then\n          -- Mirror x2 onto top-left coordinate system.\n          local x2 = text1_length - v2[k2_offset] + 1\n          if x1 > x2 then\n            -- Overlap detected.\n            return _diff_bisectSplit(text1, text2, x1, y1, deadline)\n          end\n        end\n      end\n    end\n\n    -- Walk the reverse path one step.\n    for k2 = -d + k2start, d - k2end, 2 do\n      local k2_offset = v_offset + k2\n      local x2\n      if (k2 == -d) or ((k2 ~= d) and\n          (v2[k2_offset - 1] < v2[k2_offset + 1])) then\n        x2 = v2[k2_offset + 1]\n      else\n        x2 = v2[k2_offset - 1] + 1\n      end\n      local y2 = x2 - k2\n      while (x2 <= text1_length) and (y2 <= text2_length)\n          and (strsub(text1, -x2, -x2) == strsub(text2, -y2, -y2)) do\n        x2 = x2 + 1\n        y2 = y2 + 1\n      end\n      v2[k2_offset] = x2\n      if x2 > text1_length + 1 then\n        -- Ran off the left of the graph.\n        k2end = k2end + 2\n      elseif y2 > text2_length + 1 then\n        -- Ran off the top of the graph.\n        k2start = k2start + 2\n      elseif not front then\n        local k1_offset = v_offset + delta - k2\n        if k1_offset >= 0 and k1_offset < v_length and v1[k1_offset] ~= -1 then\n          local x1 = v1[k1_offset]\n          local y1 = v_offset + x1 - k1_offset\n          -- Mirror x2 onto top-left coordinate system.\n          x2 = text1_length - x2 + 1\n          if x1 > x2 then\n            -- Overlap detected.\n            return _diff_bisectSplit(text1, text2, x1, y1, deadline)\n          end\n        end\n      end\n    end\n  end\n  -- Diff took too long and hit the deadline or\n  -- number of diffs equals number of characters, no commonality at all.\n  return {{DIFF_DELETE, text1}, {DIFF_INSERT, text2}}\nend\n\n--[[\n * Given the location of the 'middle snake', split the diff in two parts\n * and recurse.\n * @param {string} text1 Old string to be diffed.\n * @param {string} text2 New string to be diffed.\n * @param {number} x Index of split point in text1.\n * @param {number} y Index of split point in text2.\n * @param {number} deadline Time at which to bail if not yet complete.\n * @return {Array.<Array.<number|string>>} Array of diff tuples.\n * @private\n--]]\nfunction _diff_bisectSplit(text1, text2, x, y, deadline)\n  local text1a = strsub(text1, 1, x - 1)\n  local text2a = strsub(text2, 1, y - 1)\n  local text1b = strsub(text1, x)\n  local text2b = strsub(text2, y)\n\n  -- Compute both diffs serially.\n  local diffs = diff_main(text1a, text2a, false, deadline)\n  local diffsb = diff_main(text1b, text2b, false, deadline)\n\n  local diffs_len = #diffs\n  for i, v in ipairs(diffsb) do\n    diffs[diffs_len + i] = v\n  end\n  return diffs\nend\n\n--[[\n* Determine the common prefix of two strings.\n* @param {string} text1 First string.\n* @param {string} text2 Second string.\n* @return {number} The number of characters common to the start of each\n*    string.\n--]]\nfunction _diff_commonPrefix(text1, text2)\n  -- Quick check for common null cases.\n  if (#text1 == 0) or (#text2 == 0) or (strbyte(text1, 1) ~= strbyte(text2, 1))\n      then\n    return 0\n  end\n  -- Binary search.\n  -- Performance analysis: https://neil.fraser.name/news/2007/10/09/\n  local pointermin = 1\n  local pointermax = min(#text1, #text2)\n  local pointermid = pointermax\n  local pointerstart = 1\n  while (pointermin < pointermid) do\n    if (strsub(text1, pointerstart, pointermid)\n        == strsub(text2, pointerstart, pointermid)) then\n      pointermin = pointermid\n      pointerstart = pointermin\n    else\n      pointermax = pointermid\n    end\n    pointermid = floor(pointermin + (pointermax - pointermin) / 2)\n  end\n  return pointermid\nend\n\n--[[\n* Determine the common suffix of two strings.\n* @param {string} text1 First string.\n* @param {string} text2 Second string.\n* @return {number} The number of characters common to the end of each string.\n--]]\nfunction _diff_commonSuffix(text1, text2)\n  -- Quick check for common null cases.\n  if (#text1 == 0) or (#text2 == 0)\n      or (strbyte(text1, -1) ~= strbyte(text2, -1)) then\n    return 0\n  end\n  -- Binary search.\n  -- Performance analysis: https://neil.fraser.name/news/2007/10/09/\n  local pointermin = 1\n  local pointermax = min(#text1, #text2)\n  local pointermid = pointermax\n  local pointerend = 1\n  while (pointermin < pointermid) do\n    if (strsub(text1, -pointermid, -pointerend)\n        == strsub(text2, -pointermid, -pointerend)) then\n      pointermin = pointermid\n      pointerend = pointermin\n    else\n      pointermax = pointermid\n    end\n    pointermid = floor(pointermin + (pointermax - pointermin) / 2)\n  end\n  return pointermid\nend\n\n--[[\n* Determine if the suffix of one string is the prefix of another.\n* @param {string} text1 First string.\n* @param {string} text2 Second string.\n* @return {number} The number of characters common to the end of the first\n*     string and the start of the second string.\n* @private\n--]]\nfunction _diff_commonOverlap(text1, text2)\n  -- Cache the text lengths to prevent multiple calls.\n  local text1_length = #text1\n  local text2_length = #text2\n  -- Eliminate the null case.\n  if text1_length == 0 or text2_length == 0 then\n    return 0\n  end\n  -- Truncate the longer string.\n  if text1_length > text2_length then\n    text1 = strsub(text1, text1_length - text2_length + 1)\n  elseif text1_length < text2_length then\n    text2 = strsub(text2, 1, text1_length)\n  end\n  local text_length = min(text1_length, text2_length)\n  -- Quick check for the worst case.\n  if text1 == text2 then\n    return text_length\n  end\n\n  -- Start by looking for a single character match\n  -- and increase length until no match is found.\n  -- Performance analysis: https://neil.fraser.name/news/2010/11/04/\n  local best = 0\n  local length = 1\n  while true do\n    local pattern = strsub(text1, text_length - length + 1)\n    local found = strfind(text2, pattern, 1, true)\n    if found == nil then\n      return best\n    end\n    length = length + found - 1\n    if found == 1 or strsub(text1, text_length - length + 1) ==\n                     strsub(text2, 1, length) then\n      best = length\n      length = length + 1\n    end\n  end\nend\n\n--[[\n* Does a substring of shorttext exist within longtext such that the substring\n* is at least half the length of longtext?\n* This speedup can produce non-minimal diffs.\n* Closure, but does not reference any external variables.\n* @param {string} longtext Longer string.\n* @param {string} shorttext Shorter string.\n* @param {number} i Start index of quarter length substring within longtext.\n* @return {?Array.<string>} Five element Array, containing the prefix of\n*    longtext, the suffix of longtext, the prefix of shorttext, the suffix\n*    of shorttext and the common middle.  Or nil if there was no match.\n* @private\n--]]\nfunction _diff_halfMatchI(longtext, shorttext, i)\n  -- Start with a 1/4 length substring at position i as a seed.\n  local seed = strsub(longtext, i, i + floor(#longtext / 4))\n  local j = 0  -- LUANOTE: do not change to 1, was originally -1\n  local best_common = ''\n  local best_longtext_a, best_longtext_b, best_shorttext_a, best_shorttext_b\n  while true do\n    j = indexOf(shorttext, seed, j + 1)\n    if (j == nil) then\n      break\n    end\n    local prefixLength = _diff_commonPrefix(strsub(longtext, i),\n        strsub(shorttext, j))\n    local suffixLength = _diff_commonSuffix(strsub(longtext, 1, i - 1),\n        strsub(shorttext, 1, j - 1))\n    if #best_common < suffixLength + prefixLength then\n      best_common = strsub(shorttext, j - suffixLength, j - 1)\n          .. strsub(shorttext, j, j + prefixLength - 1)\n      best_longtext_a = strsub(longtext, 1, i - suffixLength - 1)\n      best_longtext_b = strsub(longtext, i + prefixLength)\n      best_shorttext_a = strsub(shorttext, 1, j - suffixLength - 1)\n      best_shorttext_b = strsub(shorttext, j + prefixLength)\n    end\n  end\n  if #best_common * 2 >= #longtext then\n    return {best_longtext_a, best_longtext_b,\n            best_shorttext_a, best_shorttext_b, best_common}\n  else\n    return nil\n  end\nend\n\n--[[\n* Do the two texts share a substring which is at least half the length of the\n* longer text?\n* @param {string} text1 First string.\n* @param {string} text2 Second string.\n* @return {?Array.<string>} Five element Array, containing the prefix of\n*    text1, the suffix of text1, the prefix of text2, the suffix of\n*    text2 and the common middle.  Or nil if there was no match.\n* @private\n--]]\nfunction _diff_halfMatch(text1, text2)\n  if Diff_Timeout <= 0 then\n    -- Don't risk returning a non-optimal diff if we have unlimited time.\n    return nil\n  end\n  local longtext = (#text1 > #text2) and text1 or text2\n  local shorttext = (#text1 > #text2) and text2 or text1\n  if (#longtext < 4) or (#shorttext * 2 < #longtext) then\n    return nil  -- Pointless.\n  end\n\n  -- First check if the second quarter is the seed for a half-match.\n  local hm1 = _diff_halfMatchI(longtext, shorttext, ceil(#longtext / 4))\n  -- Check again based on the third quarter.\n  local hm2 = _diff_halfMatchI(longtext, shorttext, ceil(#longtext / 2))\n  local hm\n  if not hm1 and not hm2 then\n    return nil\n  elseif not hm2 then\n    hm = hm1\n  elseif not hm1 then\n    hm = hm2\n  else\n    -- Both matched.  Select the longest.\n    hm = (#hm1[5] > #hm2[5]) and hm1 or hm2\n  end\n\n  -- A half-match was found, sort out the return data.\n  local text1_a, text1_b, text2_a, text2_b\n  if (#text1 > #text2) then\n    text1_a, text1_b = hm[1], hm[2]\n    text2_a, text2_b = hm[3], hm[4]\n  else\n    text2_a, text2_b = hm[1], hm[2]\n    text1_a, text1_b = hm[3], hm[4]\n  end\n  local mid_common = hm[5]\n  return text1_a, text1_b, text2_a, text2_b, mid_common\nend\n\n--[[\n* Given two strings, compute a score representing whether the internal\n* boundary falls on logical boundaries.\n* Scores range from 6 (best) to 0 (worst).\n* @param {string} one First string.\n* @param {string} two Second string.\n* @return {number} The score.\n* @private\n--]]\nfunction _diff_cleanupSemanticScore(one, two)\n  if (#one == 0) or (#two == 0) then\n    -- Edges are the best.\n    return 6\n  end\n\n  -- Each port of this function behaves slightly differently due to\n  -- subtle differences in each language's definition of things like\n  -- 'whitespace'.  Since this function's purpose is largely cosmetic,\n  -- the choice has been made to use each language's native features\n  -- rather than force total conformity.\n  local char1 = strsub(one, -1)\n  local char2 = strsub(two, 1, 1)\n  local nonAlphaNumeric1 = strmatch(char1, '%W')\n  local nonAlphaNumeric2 = strmatch(char2, '%W')\n  local whitespace1 = nonAlphaNumeric1 and strmatch(char1, '%s')\n  local whitespace2 = nonAlphaNumeric2 and strmatch(char2, '%s')\n  local lineBreak1 = whitespace1 and strmatch(char1, '%c')\n  local lineBreak2 = whitespace2 and strmatch(char2, '%c')\n  local blankLine1 = lineBreak1 and strmatch(one, '\\n\\r?\\n$')\n  local blankLine2 = lineBreak2 and strmatch(two, '^\\r?\\n\\r?\\n')\n\n  if blankLine1 or blankLine2 then\n    -- Five points for blank lines.\n    return 5\n  elseif lineBreak1 or lineBreak2 then\n    -- Four points for line breaks.\n    return 4\n  elseif nonAlphaNumeric1 and not whitespace1 and whitespace2 then\n    -- Three points for end of sentences.\n    return 3\n  elseif whitespace1 or whitespace2 then\n    -- Two points for whitespace.\n    return 2\n  elseif nonAlphaNumeric1 or nonAlphaNumeric2 then\n    -- One point for non-alphanumeric.\n    return 1\n  end\n  return 0\nend\n\n--[[\n* Look for single edits surrounded on both sides by equalities\n* which can be shifted sideways to align the edit to a word boundary.\n* e.g: The c<ins>at c</ins>ame. -> The <ins>cat </ins>came.\n* @param {Array.<Array.<number|string>>} diffs Array of diff tuples.\n--]]\nfunction _diff_cleanupSemanticLossless(diffs)\n  local pointer = 2\n  -- Intentionally ignore the first and last element (don't need checking).\n  while diffs[pointer + 1] do\n    local prevDiff, nextDiff = diffs[pointer - 1], diffs[pointer + 1]\n    if (prevDiff[1] == DIFF_EQUAL) and (nextDiff[1] == DIFF_EQUAL) then\n      -- This is a single edit surrounded by equalities.\n      local diff = diffs[pointer]\n\n      local equality1 = prevDiff[2]\n      local edit = diff[2]\n      local equality2 = nextDiff[2]\n\n      -- First, shift the edit as far left as possible.\n      local commonOffset = _diff_commonSuffix(equality1, edit)\n      if commonOffset > 0 then\n        local commonString = strsub(edit, -commonOffset)\n        equality1 = strsub(equality1, 1, -commonOffset - 1)\n        edit = commonString .. strsub(edit, 1, -commonOffset - 1)\n        equality2 = commonString .. equality2\n      end\n\n      -- Second, step character by character right, looking for the best fit.\n      local bestEquality1 = equality1\n      local bestEdit = edit\n      local bestEquality2 = equality2\n      local bestScore = _diff_cleanupSemanticScore(equality1, edit)\n          + _diff_cleanupSemanticScore(edit, equality2)\n\n      while strbyte(edit, 1) == strbyte(equality2, 1) do\n        equality1 = equality1 .. strsub(edit, 1, 1)\n        edit = strsub(edit, 2) .. strsub(equality2, 1, 1)\n        equality2 = strsub(equality2, 2)\n        local score = _diff_cleanupSemanticScore(equality1, edit)\n            + _diff_cleanupSemanticScore(edit, equality2)\n        -- The >= encourages trailing rather than leading whitespace on edits.\n        if score >= bestScore then\n          bestScore = score\n          bestEquality1 = equality1\n          bestEdit = edit\n          bestEquality2 = equality2\n        end\n      end\n      if prevDiff[2] ~= bestEquality1 then\n        -- We have an improvement, save it back to the diff.\n        if #bestEquality1 > 0 then\n          diffs[pointer - 1][2] = bestEquality1\n        else\n          tremove(diffs, pointer - 1)\n          pointer = pointer - 1\n        end\n        diffs[pointer][2] = bestEdit\n        if #bestEquality2 > 0 then\n          diffs[pointer + 1][2] = bestEquality2\n        else\n          tremove(diffs, pointer + 1, 1)\n          pointer = pointer - 1\n        end\n      end\n    end\n    pointer = pointer + 1\n  end\nend\n\n--[[\n* Reorder and merge like edit sections.  Merge equalities.\n* Any edit section can move as long as it doesn't cross an equality.\n* @param {Array.<Array.<number|string>>} diffs Array of diff tuples.\n--]]\nfunction _diff_cleanupMerge(diffs)\n  diffs[#diffs + 1] = {DIFF_EQUAL, ''}  -- Add a dummy entry at the end.\n  local pointer = 1\n  local count_delete, count_insert = 0, 0\n  local text_delete, text_insert = '', ''\n  local commonlength\n  while diffs[pointer] do\n    local diff_type = diffs[pointer][1]\n    if diff_type == DIFF_INSERT then\n      count_insert = count_insert + 1\n      text_insert = text_insert .. diffs[pointer][2]\n      pointer = pointer + 1\n    elseif diff_type == DIFF_DELETE then\n      count_delete = count_delete + 1\n      text_delete = text_delete .. diffs[pointer][2]\n      pointer = pointer + 1\n    elseif diff_type == DIFF_EQUAL then\n      -- Upon reaching an equality, check for prior redundancies.\n      if count_delete + count_insert > 1 then\n        if (count_delete > 0) and (count_insert > 0) then\n          -- Factor out any common prefixies.\n          commonlength = _diff_commonPrefix(text_insert, text_delete)\n          if commonlength > 0 then\n            local back_pointer = pointer - count_delete - count_insert\n            if (back_pointer > 1) and (diffs[back_pointer - 1][1] == DIFF_EQUAL)\n                then\n              diffs[back_pointer - 1][2] = diffs[back_pointer - 1][2]\n                  .. strsub(text_insert, 1, commonlength)\n            else\n              tinsert(diffs, 1,\n                  {DIFF_EQUAL, strsub(text_insert, 1, commonlength)})\n              pointer = pointer + 1\n            end\n            text_insert = strsub(text_insert, commonlength + 1)\n            text_delete = strsub(text_delete, commonlength + 1)\n          end\n          -- Factor out any common suffixies.\n          commonlength = _diff_commonSuffix(text_insert, text_delete)\n          if commonlength ~= 0 then\n            diffs[pointer][2] =\n                strsub(text_insert, -commonlength) .. diffs[pointer][2]\n            text_insert = strsub(text_insert, 1, -commonlength - 1)\n            text_delete = strsub(text_delete, 1, -commonlength - 1)\n          end\n        end\n        -- Delete the offending records and add the merged ones.\n        pointer = pointer - count_delete - count_insert\n        for i = 1, count_delete + count_insert do\n          tremove(diffs, pointer)\n        end\n        if #text_delete > 0 then\n          tinsert(diffs, pointer, {DIFF_DELETE, text_delete})\n          pointer = pointer + 1\n        end\n        if #text_insert > 0 then\n          tinsert(diffs, pointer, {DIFF_INSERT, text_insert})\n          pointer = pointer + 1\n        end\n        pointer = pointer + 1\n      elseif (pointer > 1) and (diffs[pointer - 1][1] == DIFF_EQUAL) then\n        -- Merge this equality with the previous one.\n        diffs[pointer - 1][2] = diffs[pointer - 1][2] .. diffs[pointer][2]\n        tremove(diffs, pointer)\n      else\n        pointer = pointer + 1\n      end\n      count_insert, count_delete = 0, 0\n      text_delete, text_insert = '', ''\n    end\n  end\n  if diffs[#diffs][2] == '' then\n    diffs[#diffs] = nil  -- Remove the dummy entry at the end.\n  end\n\n  -- Second pass: look for single edits surrounded on both sides by equalities\n  -- which can be shifted sideways to eliminate an equality.\n  -- e.g: A<ins>BA</ins>C -> <ins>AB</ins>AC\n  local changes = false\n  pointer = 2\n  -- Intentionally ignore the first and last element (don't need checking).\n  while pointer < #diffs do\n    local prevDiff, nextDiff = diffs[pointer - 1], diffs[pointer + 1]\n    if (prevDiff[1] == DIFF_EQUAL) and (nextDiff[1] == DIFF_EQUAL) then\n      -- This is a single edit surrounded by equalities.\n      local diff = diffs[pointer]\n      local currentText = diff[2]\n      local prevText = prevDiff[2]\n      local nextText = nextDiff[2]\n      if #prevText == 0 then\n        tremove(diffs, pointer - 1)\n        changes = true\n      elseif strsub(currentText, -#prevText) == prevText then\n        -- Shift the edit over the previous equality.\n        diff[2] = prevText .. strsub(currentText, 1, -#prevText - 1)\n        nextDiff[2] = prevText .. nextDiff[2]\n        tremove(diffs, pointer - 1)\n        changes = true\n      elseif strsub(currentText, 1, #nextText) == nextText then\n        -- Shift the edit over the next equality.\n        prevDiff[2] = prevText .. nextText\n        diff[2] = strsub(currentText, #nextText + 1) .. nextText\n        tremove(diffs, pointer + 1)\n        changes = true\n      end\n    end\n    pointer = pointer + 1\n  end\n  -- If shifts were made, the diff needs reordering and another shift sweep.\n  if changes then\n    -- LUANOTE: no return value, but necessary to use 'return' to get\n    -- tail calls.\n    return _diff_cleanupMerge(diffs)\n  end\nend\n\n--[[\n* loc is a location in text1, compute and return the equivalent location in\n* text2.\n* e.g. 'The cat' vs 'The big cat', 1->1, 5->8\n* @param {Array.<Array.<number|string>>} diffs Array of diff tuples.\n* @param {number} loc Location within text1.\n* @return {number} Location within text2.\n--]]\nfunction _diff_xIndex(diffs, loc)\n  local chars1 = 1\n  local chars2 = 1\n  local last_chars1 = 1\n  local last_chars2 = 1\n  local x\n  for _x, diff in ipairs(diffs) do\n    x = _x\n    if diff[1] ~= DIFF_INSERT then   -- Equality or deletion.\n      chars1 = chars1 + #diff[2]\n    end\n    if diff[1] ~= DIFF_DELETE then   -- Equality or insertion.\n      chars2 = chars2 + #diff[2]\n    end\n    if chars1 > loc then   -- Overshot the location.\n      break\n    end\n    last_chars1 = chars1\n    last_chars2 = chars2\n  end\n  -- Was the location deleted?\n  if diffs[x + 1] and (diffs[x][1] == DIFF_DELETE) then\n    return last_chars2\n  end\n  -- Add the remaining character length.\n  return last_chars2 + (loc - last_chars1)\nend\n\n--[[\n* Compute and return the source text (all equalities and deletions).\n* @param {Array.<Array.<number|string>>} diffs Array of diff tuples.\n* @return {string} Source text.\n--]]\nfunction _diff_text1(diffs)\n  local text = {}\n  for x, diff in ipairs(diffs) do\n    if diff[1] ~= DIFF_INSERT then\n      text[#text + 1] = diff[2]\n    end\n  end\n  return tconcat(text)\nend\n\n--[[\n* Compute and return the destination text (all equalities and insertions).\n* @param {Array.<Array.<number|string>>} diffs Array of diff tuples.\n* @return {string} Destination text.\n--]]\nfunction _diff_text2(diffs)\n  local text = {}\n  for x, diff in ipairs(diffs) do\n    if diff[1] ~= DIFF_DELETE then\n      text[#text + 1] = diff[2]\n    end\n  end\n  return tconcat(text)\nend\n\n--[[\n* Crush the diff into an encoded string which describes the operations\n* required to transform text1 into text2.\n* E.g. =3\\t-2\\t+ing  -> Keep 3 chars, delete 2 chars, insert 'ing'.\n* Operations are tab-separated.  Inserted text is escaped using %xx notation.\n* @param {Array.<Array.<number|string>>} diffs Array of diff tuples.\n* @return {string} Delta text.\n--]]\nfunction _diff_toDelta(diffs)\n  local text = {}\n  for x, diff in ipairs(diffs) do\n    local op, data = diff[1], diff[2]\n    if op == DIFF_INSERT then\n      text[x] = '+' .. gsub(data, percentEncode_pattern, percentEncode_replace)\n    elseif op == DIFF_DELETE then\n      text[x] = '-' .. #data\n    elseif op == DIFF_EQUAL then\n      text[x] = '=' .. #data\n    end\n  end\n  return tconcat(text, '\\t')\nend\n\n--[[\n* Given the original text1, and an encoded string which describes the\n* operations required to transform text1 into text2, compute the full diff.\n* @param {string} text1 Source string for the diff.\n* @param {string} delta Delta text.\n* @return {Array.<Array.<number|string>>} Array of diff tuples.\n* @throws {Errorend If invalid input.\n--]]\nfunction _diff_fromDelta(text1, delta)\n  local diffs = {}\n  local diffsLength = 0  -- Keeping our own length var is faster\n  local pointer = 1  -- Cursor in text1\n  for token in gmatch(delta, '[^\\t]+') do\n    -- Each token begins with a one character parameter which specifies the\n    -- operation of this token (delete, insert, equality).\n    local tokenchar, param = strsub(token, 1, 1), strsub(token, 2)\n    if (tokenchar == '+') then\n      local invalidDecode = false\n      local decoded = gsub(param, '%%(.?.?)',\n          function(c)\n            local n = tonumber(c, 16)\n            if (#c ~= 2) or (n == nil) then\n              invalidDecode = true\n              return ''\n            end\n            return strchar(n)\n          end)\n      if invalidDecode then\n        -- Malformed URI sequence.\n        error('Illegal escape in _diff_fromDelta: ' .. param)\n      end\n      diffsLength = diffsLength + 1\n      diffs[diffsLength] = {DIFF_INSERT, decoded}\n    elseif (tokenchar == '-') or (tokenchar == '=') then\n      local n = tonumber(param)\n      if (n == nil) or (n < 0) then\n        error('Invalid number in _diff_fromDelta: ' .. param)\n      end\n      local text = strsub(text1, pointer, pointer + n - 1)\n      pointer = pointer + n\n      if (tokenchar == '=') then\n        diffsLength = diffsLength + 1\n        diffs[diffsLength] = {DIFF_EQUAL, text}\n      else\n        diffsLength = diffsLength + 1\n        diffs[diffsLength] = {DIFF_DELETE, text}\n      end\n    else\n      error('Invalid diff operation in _diff_fromDelta: ' .. token)\n    end\n  end\n  if (pointer ~= #text1 + 1) then\n    error('Delta length (' .. (pointer - 1)\n        .. ') does not equal source text length (' .. #text1 .. ').')\n  end\n  return diffs\nend\n\n-- ---------------------------------------------------------------------------\n--  MATCH API\n-- ---------------------------------------------------------------------------\n\nlocal _match_bitap, _match_alphabet\n\n--[[\n* Locate the best instance of 'pattern' in 'text' near 'loc'.\n* @param {string} text The text to search.\n* @param {string} pattern The pattern to search for.\n* @param {number} loc The location to search around.\n* @return {number} Best match index or -1.\n--]]\nfunction match_main(text, pattern, loc)\n  -- Check for null inputs.\n  if text == nil or pattern == nil or loc == nil then\n    error('Null inputs. (match_main)')\n  end\n\n  if text == pattern then\n    -- Shortcut (potentially not guaranteed by the algorithm)\n    return 1\n  elseif #text == 0 then\n    -- Nothing to match.\n    return -1\n  end\n  loc = max(1, min(loc, #text))\n  if strsub(text, loc, loc + #pattern - 1) == pattern then\n    -- Perfect match at the perfect spot!  (Includes case of null pattern)\n    return loc\n  else\n    -- Do a fuzzy compare.\n    return _match_bitap(text, pattern, loc)\n  end\nend\n\n-- ---------------------------------------------------------------------------\n-- UNOFFICIAL/PRIVATE MATCH FUNCTIONS\n-- ---------------------------------------------------------------------------\n\n--[[\n* Initialise the alphabet for the Bitap algorithm.\n* @param {string} pattern The text to encode.\n* @return {Object} Hash of character locations.\n* @private\n--]]\nfunction _match_alphabet(pattern)\n  local s = {}\n  local i = 0\n  for c in gmatch(pattern, '.') do\n    s[c] = bor(s[c] or 0, lshift(1, #pattern - i - 1))\n    i = i + 1\n  end\n  return s\nend\n\n--[[\n* Locate the best instance of 'pattern' in 'text' near 'loc' using the\n* Bitap algorithm.\n* @param {string} text The text to search.\n* @param {string} pattern The pattern to search for.\n* @param {number} loc The location to search around.\n* @return {number} Best match index or -1.\n* @private\n--]]\nfunction _match_bitap(text, pattern, loc)\n  if #pattern > Match_MaxBits then\n    error('Pattern too long.')\n  end\n\n  -- Initialise the alphabet.\n  local s = _match_alphabet(pattern)\n\n  --[[\n  * Compute and return the score for a match with e errors and x location.\n  * Accesses loc and pattern through being a closure.\n  * @param {number} e Number of errors in match.\n  * @param {number} x Location of match.\n  * @return {number} Overall score for match (0.0 = good, 1.0 = bad).\n  * @private\n  --]]\n  local function _match_bitapScore(e, x)\n    local accuracy = e / #pattern\n    local proximity = abs(loc - x)\n    if (Match_Distance == 0) then\n      -- Dodge divide by zero error.\n      return (proximity == 0) and 1 or accuracy\n    end\n    return accuracy + (proximity / Match_Distance)\n  end\n\n  -- Highest score beyond which we give up.\n  local score_threshold = Match_Threshold\n  -- Is there a nearby exact match? (speedup)\n  local best_loc = indexOf(text, pattern, loc)\n  if best_loc then\n    score_threshold = min(_match_bitapScore(0, best_loc), score_threshold)\n    -- LUANOTE: Ideally we'd also check from the other direction, but Lua\n    -- doesn't have an efficent lastIndexOf function.\n  end\n\n  -- Initialise the bit arrays.\n  local matchmask = lshift(1, #pattern - 1)\n  best_loc = -1\n\n  local bin_min, bin_mid\n  local bin_max = #pattern + #text\n  local last_rd\n  for d = 0, #pattern - 1, 1 do\n    -- Scan for the best match; each iteration allows for one more error.\n    -- Run a binary search to determine how far from 'loc' we can stray at this\n    -- error level.\n    bin_min = 0\n    bin_mid = bin_max\n    while (bin_min < bin_mid) do\n      if (_match_bitapScore(d, loc + bin_mid) <= score_threshold) then\n        bin_min = bin_mid\n      else\n        bin_max = bin_mid\n      end\n      bin_mid = floor(bin_min + (bin_max - bin_min) / 2)\n    end\n    -- Use the result from this iteration as the maximum for the next.\n    bin_max = bin_mid\n    local start = max(1, loc - bin_mid + 1)\n    local finish = min(loc + bin_mid, #text) + #pattern\n\n    local rd = {}\n    for j = start, finish do\n      rd[j] = 0\n    end\n    rd[finish + 1] = lshift(1, d) - 1\n    for j = finish, start, -1 do\n      local charMatch = s[strsub(text, j - 1, j - 1)] or 0\n      if (d == 0) then  -- First pass: exact match.\n        rd[j] = band(bor((rd[j + 1] * 2), 1), charMatch)\n      else\n        -- Subsequent passes: fuzzy match.\n        -- Functions instead of operators make this hella messy.\n        rd[j] = bor(\n                band(\n                  bor(\n                    lshift(rd[j + 1], 1),\n                    1\n                  ),\n                  charMatch\n                ),\n                bor(\n                  bor(\n                    lshift(bor(last_rd[j + 1], last_rd[j]), 1),\n                    1\n                  ),\n                  last_rd[j + 1]\n                )\n              )\n      end\n      if (band(rd[j], matchmask) ~= 0) then\n        local score = _match_bitapScore(d, j - 1)\n        -- This match will almost certainly be better than any existing match.\n        -- But check anyway.\n        if (score <= score_threshold) then\n          -- Told you so.\n          score_threshold = score\n          best_loc = j - 1\n          if (best_loc > loc) then\n            -- When passing loc, don't exceed our current distance from loc.\n            start = max(1, loc * 2 - best_loc)\n          else\n            -- Already passed loc, downhill from here on in.\n            break\n          end\n        end\n      end\n    end\n    -- No hope for a (better) match at greater error levels.\n    if (_match_bitapScore(d + 1, loc) > score_threshold) then\n      break\n    end\n    last_rd = rd\n  end\n  return best_loc\nend\n\n-- -----------------------------------------------------------------------------\n-- PATCH API\n-- -----------------------------------------------------------------------------\n\nlocal _patch_addContext,\n      _patch_deepCopy,\n      _patch_addPadding,\n      _patch_splitMax,\n      _patch_appendText,\n      _new_patch_obj\n\n--[[\n* Compute a list of patches to turn text1 into text2.\n* Use diffs if provided, otherwise compute it ourselves.\n* There are four ways to call this function, depending on what data is\n* available to the caller:\n* Method 1:\n* a = text1, b = text2\n* Method 2:\n* a = diffs\n* Method 3 (optimal):\n* a = text1, b = diffs\n* Method 4 (deprecated, use method 3):\n* a = text1, b = text2, c = diffs\n*\n* @param {string|Array.<Array.<number|string>>} a text1 (methods 1,3,4) or\n* Array of diff tuples for text1 to text2 (method 2).\n* @param {string|Array.<Array.<number|string>>} opt_b text2 (methods 1,4) or\n* Array of diff tuples for text1 to text2 (method 3) or undefined (method 2).\n* @param {string|Array.<Array.<number|string>>} opt_c Array of diff tuples for\n* text1 to text2 (method 4) or undefined (methods 1,2,3).\n* @return {Array.<_new_patch_obj>} Array of patch objects.\n--]]\nfunction patch_make(a, opt_b, opt_c)\n  local text1, diffs\n  local type_a, type_b, type_c = type(a), type(opt_b), type(opt_c)\n  if (type_a == 'string') and (type_b == 'string') and (type_c == 'nil') then\n    -- Method 1: text1, text2\n    -- Compute diffs from text1 and text2.\n    text1 = a\n    diffs = diff_main(text1, opt_b, true)\n    if (#diffs > 2) then\n      diff_cleanupSemantic(diffs)\n      diff_cleanupEfficiency(diffs)\n    end\n  elseif (type_a == 'table') and (type_b == 'nil') and (type_c == 'nil') then\n    -- Method 2: diffs\n    -- Compute text1 from diffs.\n    diffs = a\n    text1 = _diff_text1(diffs)\n  elseif (type_a == 'string') and (type_b == 'table') and (type_c == 'nil') then\n    -- Method 3: text1, diffs\n    text1 = a\n    diffs = opt_b\n  elseif (type_a == 'string') and (type_b == 'string') and (type_c == 'table')\n      then\n    -- Method 4: text1, text2, diffs\n    -- text2 is not used.\n    text1 = a\n    diffs = opt_c\n  else\n    error('Unknown call format to patch_make.')\n  end\n\n  if (diffs[1] == nil) then\n    return {}  -- Get rid of the null case.\n  end\n\n  local patches = {}\n  local patch = _new_patch_obj()\n  local patchDiffLength = 0  -- Keeping our own length var is faster.\n  local char_count1 = 0  -- Number of characters into the text1 string.\n  local char_count2 = 0  -- Number of characters into the text2 string.\n  -- Start with text1 (prepatch_text) and apply the diffs until we arrive at\n  -- text2 (postpatch_text).  We recreate the patches one by one to determine\n  -- context info.\n  local prepatch_text, postpatch_text = text1, text1\n  for x, diff in ipairs(diffs) do\n    local diff_type, diff_text = diff[1], diff[2]\n\n    if (patchDiffLength == 0) and (diff_type ~= DIFF_EQUAL) then\n      -- A new patch starts here.\n      patch.start1 = char_count1 + 1\n      patch.start2 = char_count2 + 1\n    end\n\n    if (diff_type == DIFF_INSERT) then\n      patchDiffLength = patchDiffLength + 1\n      patch.diffs[patchDiffLength] = diff\n      patch.length2 = patch.length2 + #diff_text\n      postpatch_text = strsub(postpatch_text, 1, char_count2)\n          .. diff_text .. strsub(postpatch_text, char_count2 + 1)\n    elseif (diff_type == DIFF_DELETE) then\n      patch.length1 = patch.length1 + #diff_text\n      patchDiffLength = patchDiffLength + 1\n      patch.diffs[patchDiffLength] = diff\n      postpatch_text = strsub(postpatch_text, 1, char_count2)\n          .. strsub(postpatch_text, char_count2 + #diff_text + 1)\n    elseif (diff_type == DIFF_EQUAL) then\n      if (#diff_text <= Patch_Margin * 2)\n          and (patchDiffLength ~= 0) and (#diffs ~= x) then\n        -- Small equality inside a patch.\n        patchDiffLength = patchDiffLength + 1\n        patch.diffs[patchDiffLength] = diff\n        patch.length1 = patch.length1 + #diff_text\n        patch.length2 = patch.length2 + #diff_text\n      elseif (#diff_text >= Patch_Margin * 2) then\n        -- Time for a new patch.\n        if (patchDiffLength ~= 0) then\n          _patch_addContext(patch, prepatch_text)\n          patches[#patches + 1] = patch\n          patch = _new_patch_obj()\n          patchDiffLength = 0\n          -- Unlike Unidiff, our patch lists have a rolling context.\n          -- https://github.com/google/diff-match-patch/wiki/Unidiff\n          -- Update prepatch text & pos to reflect the application of the\n          -- just completed patch.\n          prepatch_text = postpatch_text\n          char_count1 = char_count2\n        end\n      end\n    end\n\n    -- Update the current character count.\n    if (diff_type ~= DIFF_INSERT) then\n      char_count1 = char_count1 + #diff_text\n    end\n    if (diff_type ~= DIFF_DELETE) then\n      char_count2 = char_count2 + #diff_text\n    end\n  end\n\n  -- Pick up the leftover patch if not empty.\n  if (patchDiffLength > 0) then\n    _patch_addContext(patch, prepatch_text)\n    patches[#patches + 1] = patch\n  end\n\n  return patches\nend\n\n--[[\n* Merge a set of patches onto the text.  Return a patched text, as well\n* as a list of true/false values indicating which patches were applied.\n* @param {Array.<_new_patch_obj>} patches Array of patch objects.\n* @param {string} text Old text.\n* @return {Array.<string|Array.<boolean>>} Two return values, the\n*     new text and an array of boolean values.\n--]]\nfunction patch_apply(patches, text)\n  if patches[1] == nil then\n    return text, {}\n  end\n\n  -- Deep copy the patches so that no changes are made to originals.\n  patches = _patch_deepCopy(patches)\n\n  local nullPadding = _patch_addPadding(patches)\n  text = nullPadding .. text .. nullPadding\n\n  _patch_splitMax(patches)\n  -- delta keeps track of the offset between the expected and actual location\n  -- of the previous patch. If there are patches expected at positions 10 and\n  -- 20, but the first patch was found at 12, delta is 2 and the second patch\n  -- has an effective expected position of 22.\n  local delta = 0\n  local results = {}\n  for x, patch in ipairs(patches) do\n    local expected_loc = patch.start2 + delta\n    local text1 = _diff_text1(patch.diffs)\n    local start_loc\n    local end_loc = -1\n    if #text1 > Match_MaxBits then\n      -- _patch_splitMax will only provide an oversized pattern in\n      -- the case of a monster delete.\n      start_loc = match_main(text,\n          strsub(text1, 1, Match_MaxBits), expected_loc)\n      if start_loc ~= -1 then\n        end_loc = match_main(text, strsub(text1, -Match_MaxBits),\n            expected_loc + #text1 - Match_MaxBits)\n        if end_loc == -1 or start_loc >= end_loc then\n          -- Can't find valid trailing context.  Drop this patch.\n          start_loc = -1\n        end\n      end\n    else\n      start_loc = match_main(text, text1, expected_loc)\n    end\n    if start_loc == -1 then\n      -- No match found.  :(\n      results[x] = false\n      -- Subtract the delta for this failed patch from subsequent patches.\n      delta = delta - patch.length2 - patch.length1\n    else\n      -- Found a match.  :)\n      results[x] = true\n      delta = start_loc - expected_loc\n      local text2\n      if end_loc == -1 then\n        text2 = strsub(text, start_loc, start_loc + #text1 - 1)\n      else\n        text2 = strsub(text, start_loc, end_loc + Match_MaxBits - 1)\n      end\n      if text1 == text2 then\n        -- Perfect match, just shove the replacement text in.\n        text = strsub(text, 1, start_loc - 1) .. _diff_text2(patch.diffs)\n            .. strsub(text, start_loc + #text1)\n      else\n        -- Imperfect match.  Run a diff to get a framework of equivalent\n        -- indices.\n        local diffs = diff_main(text1, text2, false)\n        if (#text1 > Match_MaxBits)\n            and (diff_levenshtein(diffs) / #text1 > Patch_DeleteThreshold) then\n          -- The end points match, but the content is unacceptably bad.\n          results[x] = false\n        else\n          _diff_cleanupSemanticLossless(diffs)\n          local index1 = 1\n          local index2\n          for y, mod in ipairs(patch.diffs) do\n            if mod[1] ~= DIFF_EQUAL then\n              index2 = _diff_xIndex(diffs, index1)\n            end\n            if mod[1] == DIFF_INSERT then\n              text = strsub(text, 1, start_loc + index2 - 2)\n                  .. mod[2] .. strsub(text, start_loc + index2 - 1)\n            elseif mod[1] == DIFF_DELETE then\n              text = strsub(text, 1, start_loc + index2 - 2) .. strsub(text,\n                  start_loc + _diff_xIndex(diffs, index1 + #mod[2] - 1))\n            end\n            if mod[1] ~= DIFF_DELETE then\n              index1 = index1 + #mod[2]\n            end\n          end\n        end\n      end\n    end\n  end\n  -- Strip the padding off.\n  text = strsub(text, #nullPadding + 1, -#nullPadding - 1)\n  return text, results\nend\n\n--[[\n* Take a list of patches and return a textual representation.\n* @param {Array.<_new_patch_obj>} patches Array of patch objects.\n* @return {string} Text representation of patches.\n--]]\nfunction patch_toText(patches)\n  local text = {}\n  for x, patch in ipairs(patches) do\n    _patch_appendText(patch, text)\n  end\n  return tconcat(text)\nend\n\n--[[\n* Parse a textual representation of patches and return a list of patch objects.\n* @param {string} textline Text representation of patches.\n* @return {Array.<_new_patch_obj>} Array of patch objects.\n* @throws {Error} If invalid input.\n--]]\nfunction patch_fromText(textline)\n  local patches = {}\n  if (#textline == 0) then\n    return patches\n  end\n  local text = {}\n  for line in gmatch(textline, '([^\\n]*)') do\n    text[#text + 1] = line\n  end\n  local textPointer = 1\n  while (textPointer <= #text) do\n    local start1, length1, start2, length2\n     = strmatch(text[textPointer], '^@@ %-(%d+),?(%d*) %+(%d+),?(%d*) @@$')\n    if (start1 == nil) then\n      error('Invalid patch string: \"' .. text[textPointer] .. '\"')\n    end\n    local patch = _new_patch_obj()\n    patches[#patches + 1] = patch\n\n    start1 = tonumber(start1)\n    length1 = tonumber(length1) or 1\n    if (length1 == 0) then\n      start1 = start1 + 1\n    end\n    patch.start1 = start1\n    patch.length1 = length1\n\n    start2 = tonumber(start2)\n    length2 = tonumber(length2) or 1\n    if (length2 == 0) then\n      start2 = start2 + 1\n    end\n    patch.start2 = start2\n    patch.length2 = length2\n\n    textPointer = textPointer + 1\n\n    while true do\n      local line = text[textPointer]\n      if (line == nil) then\n        break\n      end\n      local sign; sign, line = strsub(line, 1, 1), strsub(line, 2)\n\n      local invalidDecode = false\n      local decoded = gsub(line, '%%(.?.?)',\n          function(c)\n            local n = tonumber(c, 16)\n            if (#c ~= 2) or (n == nil) then\n              invalidDecode = true\n              return ''\n            end\n            return strchar(n)\n          end)\n      if invalidDecode then\n        -- Malformed URI sequence.\n        error('Illegal escape in patch_fromText: ' .. line)\n      end\n\n      line = decoded\n\n      if (sign == '-') then\n        -- Deletion.\n        patch.diffs[#patch.diffs + 1] = {DIFF_DELETE, line}\n      elseif (sign == '+') then\n        -- Insertion.\n        patch.diffs[#patch.diffs + 1] = {DIFF_INSERT, line}\n      elseif (sign == ' ') then\n        -- Minor equality.\n        patch.diffs[#patch.diffs + 1] = {DIFF_EQUAL, line}\n      elseif (sign == '@') then\n        -- Start of next patch.\n        break\n      elseif (sign == '') then\n        -- Blank line?  Whatever.\n      else\n        -- WTF?\n        error('Invalid patch mode \"' .. sign .. '\" in: ' .. line)\n      end\n      textPointer = textPointer + 1\n    end\n  end\n  return patches\nend\n\n-- ---------------------------------------------------------------------------\n-- UNOFFICIAL/PRIVATE PATCH FUNCTIONS\n-- ---------------------------------------------------------------------------\n\nlocal patch_meta = {\n  __tostring = function(patch)\n    local buf = {}\n    _patch_appendText(patch, buf)\n    return tconcat(buf)\n  end\n}\n\n--[[\n* Class representing one patch operation.\n* @constructor\n--]]\nfunction _new_patch_obj()\n  return setmetatable({\n    --[[ @type {Array.<Array.<number|string>>} ]]\n    diffs = {};\n    --[[ @type {?number} ]]\n    start1 = 1;  -- nil;\n    --[[ @type {?number} ]]\n    start2 = 1;  -- nil;\n    --[[ @type {number} ]]\n    length1 = 0;\n    --[[ @type {number} ]]\n    length2 = 0;\n  }, patch_meta)\nend\n\n--[[\n* Increase the context until it is unique,\n* but don't let the pattern expand beyond Match_MaxBits.\n* @param {_new_patch_obj} patch The patch to grow.\n* @param {string} text Source text.\n* @private\n--]]\nfunction _patch_addContext(patch, text)\n  if (#text == 0) then\n    return\n  end\n  local pattern = strsub(text, patch.start2, patch.start2 + patch.length1 - 1)\n  local padding = 0\n\n  -- LUANOTE: Lua's lack of a lastIndexOf function results in slightly\n  -- different logic here than in other language ports.\n  -- Look for the first two matches of pattern in text.  If two are found,\n  -- increase the pattern length.\n  local firstMatch = indexOf(text, pattern)\n  local secondMatch = nil\n  if (firstMatch ~= nil) then\n    secondMatch = indexOf(text, pattern, firstMatch + 1)\n  end\n  while (#pattern == 0 or secondMatch ~= nil)\n      and (#pattern < Match_MaxBits - Patch_Margin - Patch_Margin) do\n    padding = padding + Patch_Margin\n    pattern = strsub(text, max(1, patch.start2 - padding),\n    patch.start2 + patch.length1 - 1 + padding)\n    firstMatch = indexOf(text, pattern)\n    if (firstMatch ~= nil) then\n      secondMatch = indexOf(text, pattern, firstMatch + 1)\n    else\n      secondMatch = nil\n    end\n  end\n  -- Add one chunk for good luck.\n  padding = padding + Patch_Margin\n\n  -- Add the prefix.\n  local prefix = strsub(text, max(1, patch.start2 - padding), patch.start2 - 1)\n  if (#prefix > 0) then\n    tinsert(patch.diffs, 1, {DIFF_EQUAL, prefix})\n  end\n  -- Add the suffix.\n  local suffix = strsub(text, patch.start2 + patch.length1,\n  patch.start2 + patch.length1 - 1 + padding)\n  if (#suffix > 0) then\n    patch.diffs[#patch.diffs + 1] = {DIFF_EQUAL, suffix}\n  end\n\n  -- Roll back the start points.\n  patch.start1 = patch.start1 - #prefix\n  patch.start2 = patch.start2 - #prefix\n  -- Extend the lengths.\n  patch.length1 = patch.length1 + #prefix + #suffix\n  patch.length2 = patch.length2 + #prefix + #suffix\nend\n\n--[[\n* Given an array of patches, return another array that is identical.\n* @param {Array.<_new_patch_obj>} patches Array of patch objects.\n* @return {Array.<_new_patch_obj>} Array of patch objects.\n--]]\nfunction _patch_deepCopy(patches)\n  local patchesCopy = {}\n  for x, patch in ipairs(patches) do\n    local patchCopy = _new_patch_obj()\n    local diffsCopy = {}\n    for i, diff in ipairs(patch.diffs) do\n      diffsCopy[i] = {diff[1], diff[2]}\n    end\n    patchCopy.diffs = diffsCopy\n    patchCopy.start1 = patch.start1\n    patchCopy.start2 = patch.start2\n    patchCopy.length1 = patch.length1\n    patchCopy.length2 = patch.length2\n    patchesCopy[x] = patchCopy\n  end\n  return patchesCopy\nend\n\n--[[\n* Add some padding on text start and end so that edges can match something.\n* Intended to be called only from within patch_apply.\n* @param {Array.<_new_patch_obj>} patches Array of patch objects.\n* @return {string} The padding string added to each side.\n--]]\nfunction _patch_addPadding(patches)\n  local paddingLength = Patch_Margin\n  local nullPadding = ''\n  for x = 1, paddingLength do\n    nullPadding = nullPadding .. strchar(x)\n  end\n\n  -- Bump all the patches forward.\n  for x, patch in ipairs(patches) do\n    patch.start1 = patch.start1 + paddingLength\n    patch.start2 = patch.start2 + paddingLength\n  end\n\n  -- Add some padding on start of first diff.\n  local patch = patches[1]\n  local diffs = patch.diffs\n  local firstDiff = diffs[1]\n  if (firstDiff == nil) or (firstDiff[1] ~= DIFF_EQUAL) then\n    -- Add nullPadding equality.\n    tinsert(diffs, 1, {DIFF_EQUAL, nullPadding})\n    patch.start1 = patch.start1 - paddingLength  -- Should be 0.\n    patch.start2 = patch.start2 - paddingLength  -- Should be 0.\n    patch.length1 = patch.length1 + paddingLength\n    patch.length2 = patch.length2 + paddingLength\n  elseif (paddingLength > #firstDiff[2]) then\n    -- Grow first equality.\n    local extraLength = paddingLength - #firstDiff[2]\n    firstDiff[2] = strsub(nullPadding, #firstDiff[2] + 1) .. firstDiff[2]\n    patch.start1 = patch.start1 - extraLength\n    patch.start2 = patch.start2 - extraLength\n    patch.length1 = patch.length1 + extraLength\n    patch.length2 = patch.length2 + extraLength\n  end\n\n  -- Add some padding on end of last diff.\n  patch = patches[#patches]\n  diffs = patch.diffs\n  local lastDiff = diffs[#diffs]\n  if (lastDiff == nil) or (lastDiff[1] ~= DIFF_EQUAL) then\n    -- Add nullPadding equality.\n    diffs[#diffs + 1] = {DIFF_EQUAL, nullPadding}\n    patch.length1 = patch.length1 + paddingLength\n    patch.length2 = patch.length2 + paddingLength\n  elseif (paddingLength > #lastDiff[2]) then\n    -- Grow last equality.\n    local extraLength = paddingLength - #lastDiff[2]\n    lastDiff[2] = lastDiff[2] .. strsub(nullPadding, 1, extraLength)\n    patch.length1 = patch.length1 + extraLength\n    patch.length2 = patch.length2 + extraLength\n  end\n\n  return nullPadding\nend\n\n--[[\n* Look through the patches and break up any which are longer than the maximum\n* limit of the match algorithm.\n* Intended to be called only from within patch_apply.\n* @param {Array.<_new_patch_obj>} patches Array of patch objects.\n--]]\nfunction _patch_splitMax(patches)\n  local patch_size = Match_MaxBits\n  local x = 1\n  while true do\n    local patch = patches[x]\n    if patch == nil then\n      return\n    end\n    if patch.length1 > patch_size then\n      local bigpatch = patch\n      -- Remove the big old patch.\n      tremove(patches, x)\n      x = x - 1\n      local start1 = bigpatch.start1\n      local start2 = bigpatch.start2\n      local precontext = ''\n      while bigpatch.diffs[1] do\n        -- Create one of several smaller patches.\n        local patch = _new_patch_obj()\n        local empty = true\n        patch.start1 = start1 - #precontext\n        patch.start2 = start2 - #precontext\n        if precontext ~= '' then\n          patch.length1, patch.length2 = #precontext, #precontext\n          patch.diffs[#patch.diffs + 1] = {DIFF_EQUAL, precontext}\n        end\n        while bigpatch.diffs[1] and (patch.length1 < patch_size-Patch_Margin) do\n          local diff_type = bigpatch.diffs[1][1]\n          local diff_text = bigpatch.diffs[1][2]\n          if (diff_type == DIFF_INSERT) then\n            -- Insertions are harmless.\n            patch.length2 = patch.length2 + #diff_text\n            start2 = start2 + #diff_text\n            patch.diffs[#(patch.diffs) + 1] = bigpatch.diffs[1]\n            tremove(bigpatch.diffs, 1)\n            empty = false\n          elseif (diff_type == DIFF_DELETE) and (#patch.diffs == 1)\n           and (patch.diffs[1][1] == DIFF_EQUAL)\n           and (#diff_text > 2 * patch_size) then\n            -- This is a large deletion.  Let it pass in one chunk.\n            patch.length1 = patch.length1 + #diff_text\n            start1 = start1 + #diff_text\n            empty = false\n            patch.diffs[#patch.diffs + 1] = {diff_type, diff_text}\n            tremove(bigpatch.diffs, 1)\n          else\n            -- Deletion or equality.\n            -- Only take as much as we can stomach.\n            diff_text = strsub(diff_text, 1,\n            patch_size - patch.length1 - Patch_Margin)\n            patch.length1 = patch.length1 + #diff_text\n            start1 = start1 + #diff_text\n            if (diff_type == DIFF_EQUAL) then\n              patch.length2 = patch.length2 + #diff_text\n              start2 = start2 + #diff_text\n            else\n              empty = false\n            end\n            patch.diffs[#patch.diffs + 1] = {diff_type, diff_text}\n            if (diff_text == bigpatch.diffs[1][2]) then\n              tremove(bigpatch.diffs, 1)\n            else\n              bigpatch.diffs[1][2]\n                  = strsub(bigpatch.diffs[1][2], #diff_text + 1)\n            end\n          end\n        end\n        -- Compute the head context for the next patch.\n        precontext = _diff_text2(patch.diffs)\n        precontext = strsub(precontext, -Patch_Margin)\n        -- Append the end context for this patch.\n        local postcontext = strsub(_diff_text1(bigpatch.diffs), 1, Patch_Margin)\n        if postcontext ~= '' then\n          patch.length1 = patch.length1 + #postcontext\n          patch.length2 = patch.length2 + #postcontext\n          if patch.diffs[1]\n              and (patch.diffs[#patch.diffs][1] == DIFF_EQUAL) then\n            patch.diffs[#patch.diffs][2] = patch.diffs[#patch.diffs][2]\n                .. postcontext\n          else\n            patch.diffs[#patch.diffs + 1] = {DIFF_EQUAL, postcontext}\n          end\n        end\n        if not empty then\n          x = x + 1\n          tinsert(patches, x, patch)\n        end\n      end\n    end\n    x = x + 1\n  end\nend\n\n--[[\n* Emulate GNU diff's format.\n* Header: @@ -382,8 +481,9 @@\n* @return {string} The GNU diff string.\n--]]\nfunction _patch_appendText(patch, text)\n  local coords1, coords2\n  local length1, length2 = patch.length1, patch.length2\n  local start1, start2 = patch.start1, patch.start2\n  local diffs = patch.diffs\n\n  if length1 == 1 then\n    coords1 = start1\n  else\n    coords1 = ((length1 == 0) and (start1 - 1) or start1) .. ',' .. length1\n  end\n\n  if length2 == 1 then\n    coords2 = start2\n  else\n    coords2 = ((length2 == 0) and (start2 - 1) or start2) .. ',' .. length2\n  end\n  text[#text + 1] = '@@ -' .. coords1 .. ' +' .. coords2 .. ' @@\\n'\n\n  local op\n  -- Escape the body of the patch with %xx notation.\n  for x, diff in ipairs(patch.diffs) do\n    local diff_type = diff[1]\n    if diff_type == DIFF_INSERT then\n      op = '+'\n    elseif diff_type == DIFF_DELETE then\n      op = '-'\n    elseif diff_type == DIFF_EQUAL then\n      op = ' '\n    end\n    text[#text + 1] = op\n        .. gsub(diffs[x][2], percentEncode_pattern, percentEncode_replace)\n        .. '\\n'\n  end\n\n  return text\nend\n\n-- Expose the API\nlocal _M = {}\n\n_M.DIFF_DELETE = DIFF_DELETE\n_M.DIFF_INSERT = DIFF_INSERT\n_M.DIFF_EQUAL = DIFF_EQUAL\n\n_M.diff_main = diff_main\n_M.diff_cleanupSemantic = diff_cleanupSemantic\n_M.diff_cleanupEfficiency = diff_cleanupEfficiency\n_M.diff_levenshtein = diff_levenshtein\n_M.diff_prettyHtml = diff_prettyHtml\n\n_M.match_main = match_main\n\n_M.patch_make = patch_make\n_M.patch_toText = patch_toText\n_M.patch_fromText = patch_fromText\n_M.patch_apply = patch_apply\n\n-- Expose some non-API functions as well, for testing purposes etc.\n_M.diff_commonPrefix = _diff_commonPrefix\n_M.diff_commonSuffix = _diff_commonSuffix\n_M.diff_commonOverlap = _diff_commonOverlap\n_M.diff_halfMatch = _diff_halfMatch\n_M.diff_bisect = _diff_bisect\n_M.diff_cleanupMerge = _diff_cleanupMerge\n_M.diff_cleanupSemanticLossless = _diff_cleanupSemanticLossless\n_M.diff_text1 = _diff_text1\n_M.diff_text2 = _diff_text2\n_M.diff_toDelta = _diff_toDelta\n_M.diff_fromDelta = _diff_fromDelta\n_M.diff_xIndex = _diff_xIndex\n_M.match_alphabet = _match_alphabet\n_M.match_bitap = _match_bitap\n_M.new_patch_obj = _new_patch_obj\n_M.patch_addContext = _patch_addContext\n_M.patch_splitMax = _patch_splitMax\n_M.patch_addPadding = _patch_addPadding\n_M.settings = settings\n\nreturn _M\n"
  },
  {
    "path": "lua/tests/diff_match_patch_test.lua",
    "content": "--[[\n* Diff Match and Patch -- Test Harness\n* Copyright 2018 The diff-match-patch Authors.\n* https://github.com/google/diff-match-patch\n*\n* Based on the JavaScript implementation by Neil Fraser\n* Ported to Lua by Duncan Cross\n*\n* Licensed under the Apache License, Version 2.0 (the \"License\");\n* you may not use this file except in compliance with the License.\n* You may obtain a copy of the License at\n*\n*   http://www.apache.org/licenses/LICENSE-2.0\n*\n* Unless required by applicable law or agreed to in writing, software\n* distributed under the License is distributed on an \"AS IS\" BASIS,\n* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n* See the License for the specific language governing permissions and\n* limitations under the License.\n--]]\n\npackage.path = package.path .. ';../?.lua'\nlocal dmp = require 'diff_match_patch'\n\nlocal DIFF_INSERT = dmp.DIFF_INSERT\nlocal DIFF_DELETE = dmp.DIFF_DELETE\nlocal DIFF_EQUAL = dmp.DIFF_EQUAL\n\n-- Utility functions.\n\nlocal function pretty(v)\n  if (type(v) == 'string') then\n    return string.format('%q', v):gsub('\\\\\\n', '\\\\n')\n  elseif (type(v) == 'table') then\n    local str = {}\n    local next_i = 1\n    for i, v in pairs(v) do\n      if (i == next_i) then\n        next_i = next_i + 1\n        str[#str + 1] = pretty(v)\n      else\n        str[#str + 1] = '[' .. pretty(i) .. ']=' .. pretty(v)\n      end\n    end\n    return '{' .. table.concat(str, ',') .. '}'\n  else\n    return tostring(v)\n  end\nend\n\nfunction assertEquals(...)\n  local msg, expected, actual\n  if (select('#', ...) == 2) then\n    expected, actual = ...\n    msg = 'Expected: \\'' .. pretty(expected)\n        .. '\\' Actual: \\'' .. pretty(actual) .. '\\''\n  else\n    msg, expected, actual = ...\n  end\n  assert(expected == actual, msg)\nend\n\nfunction assertTrue(...)\n  local msg, actual\n  if (select('#', ...) == 1) then\n    actual = ...\n    assertEquals(true, actual)\n  else\n    msg, actual = ...\n    assertEquals(msg, true, actual)\n  end\nend\n\nfunction assertFalse(...)\n  local msg, actual\n  if (select('#', ...) == 1) then\n    actual = ...\n    assertEquals(flase, actual)\n  else\n    msg, actual = ...\n    assertEquals(msg, false, actual)\n  end\nend\n\n-- If expected and actual are the equivalent, pass the test.\nfunction assertEquivalent(...)\n  local msg, expected, actual\n  expected, actual = ...\n  msg = 'Expected: \\'' .. pretty(expected)\n      .. '\\' Actual: \\'' .. pretty(actual) .. '\\''\n  if (_equivalent(expected, actual)) then\n    assertEquals(msg, pretty(expected), pretty(actual))\n  else\n    assertEquals(msg, expected, actual)\n  end\nend\n\n-- Are a and b the equivalent? -- Recursive.\nfunction _equivalent(a, b)\n  if (a == b) then\n    return true\n  end\n  if (type(a) == 'table') and (type(b) == 'table') then\n    for k, v in pairs(a) do\n      if not _equivalent(v, b[k]) then\n        return false\n      end\n    end\n    for k, v in pairs(b) do\n      if not _equivalent(v, a[k]) then\n        return false\n      end\n    end\n    return true\n  end\n  return false\nend\n\nfunction diff_rebuildtexts(diffs)\n  -- Construct the two texts which made up the diff originally.\n  local text1, text2 = {}, {}\n  for x, diff in ipairs(diffs) do\n    local op, data = diff[1], diff[2]\n    if (op ~= DIFF_INSERT) then\n      text1[#text1 + 1] = data\n    end\n    if (op ~= DIFF_DELETE) then\n      text2[#text2 + 1] = data\n    end\n  end\n  return table.concat(text1), table.concat(text2)\nend\n\n\n-- DIFF TEST FUNCTIONS\n\n\nfunction testDiffCommonPrefix()\n  -- Detect any common prefix.\n\n  -- Null case.\n  assertEquals(0, dmp.diff_commonPrefix('abc', 'xyz'))\n  -- Non-null case.\n  assertEquals(4, dmp.diff_commonPrefix('1234abcdef', '1234xyz'))\n  -- Whole case.\n  assertEquals(4, dmp.diff_commonPrefix('1234', '1234xyz'))\nend\n\nfunction testDiffCommonSuffix()\n  -- Detect any common suffix.\n\n  -- Null case.\n  assertEquals(0, dmp.diff_commonSuffix('abc', 'xyz'))\n  -- Non-null case.\n  assertEquals(4, dmp.diff_commonSuffix('abcdef1234', 'xyz1234'))\n  -- Whole case.\n  assertEquals(4, dmp.diff_commonSuffix('1234', 'xyz1234'))\nend\n\nfunction testDiffCommonOverlap()\n  -- Detect any suffix/prefix overlap.\n\n  -- Null case.\n  assertEquals(0, dmp.diff_commonOverlap('', 'abcd'));\n  -- Whole case.\n  assertEquals(3, dmp.diff_commonOverlap('abc', 'abcd'));\n  -- No overlap.\n  assertEquals(0, dmp.diff_commonOverlap('123456', 'abcd'));\n  -- Overlap.\n  assertEquals(3, dmp.diff_commonOverlap('123456xxx', 'xxxabcd'));\n  --[[\n  -- Unicode.\n  -- Some overly clever languages (C#) may treat ligatures as equal to their\n  -- component letters.  E.g. U+FB01 == 'fi'\n  -- LUANOTE: No ability to handle Unicode.\n  assertEquals(0, dmp.diff_commonOverlap('fi', '\\ufb01i'));\n  --]]\nend\n\nfunction testDiffHalfMatch()\n  -- Detect a halfmatch.\n  dmp.settings{Diff_Timeout = 1}\n\n  -- No match.\n  assertEquivalent({nil}, {dmp.diff_halfMatch('1234567890', 'abcdef')})\n  assertEquivalent({nil}, {dmp.diff_halfMatch('12345', '23')})\n  -- Single Match.\n  assertEquivalent({'12', '90', 'a', 'z', '345678'},\n      {dmp.diff_halfMatch('1234567890', 'a345678z')})\n  assertEquivalent({'a', 'z', '12', '90', '345678'},\n      {dmp.diff_halfMatch('a345678z', '1234567890')})\n  assertEquivalent({'abc', 'z', '1234', '0', '56789'},\n      {dmp.diff_halfMatch('abc56789z', '1234567890')})\n  assertEquivalent({'a', 'xyz', '1', '7890', '23456'},\n      {dmp.diff_halfMatch('a23456xyz', '1234567890')})\n  -- Multiple Matches.\n  assertEquivalent({'12123', '123121', 'a', 'z', '1234123451234'},\n      {dmp.diff_halfMatch('121231234123451234123121', 'a1234123451234z')})\n  assertEquivalent({'', '-=-=-=-=-=', 'x', '', 'x-=-=-=-=-=-=-='},\n      {dmp.diff_halfMatch('x-=-=-=-=-=-=-=-=-=-=-=-=', 'xx-=-=-=-=-=-=-=')})\n  assertEquivalent({'-=-=-=-=-=', '', '', 'y', '-=-=-=-=-=-=-=y'},\n      {dmp.diff_halfMatch('-=-=-=-=-=-=-=-=-=-=-=-=y', '-=-=-=-=-=-=-=yy')})\n\n  -- Non-optimal halfmatch.\n  -- Optimal diff would be -q+x=H-i+e=lloHe+Hu=llo-Hew+y not -qHillo+x=HelloHe-w+Hulloy\n  assertEquivalent({'qHillo', 'w', 'x', 'Hulloy', 'HelloHe'},\n      {dmp.diff_halfMatch('qHilloHelloHew', 'xHelloHeHulloy')})\n  -- Optimal no halfmatch.\n  dmp.settings{Diff_Timeout = 0}\n  assertEquivalent({nill}, {dmp.diff_halfMatch('qHilloHelloHew', 'xHelloHeHulloy')})\nend\n\nfunction testDiffCleanupMerge()\n  -- Cleanup a messy diff.\n\n  -- Null case.\n  local diffs = {}\n  dmp.diff_cleanupMerge(diffs)\n  assertEquivalent({}, diffs)\n  -- No change case.\n  diffs = {{DIFF_EQUAL, 'a'}, {DIFF_DELETE, 'b'}, {DIFF_INSERT, 'c'}}\n  dmp.diff_cleanupMerge(diffs)\n  assertEquivalent({{DIFF_EQUAL, 'a'}, {DIFF_DELETE, 'b'}, {DIFF_INSERT, 'c'}},\n      diffs)\n  -- Merge equalities.\n  diffs = {{DIFF_EQUAL, 'a'}, {DIFF_EQUAL, 'b'}, {DIFF_EQUAL, 'c'}}\n  dmp.diff_cleanupMerge(diffs)\n  assertEquivalent({{DIFF_EQUAL, 'abc'}}, diffs)\n  -- Merge deletions.\n  diffs = {{DIFF_DELETE, 'a'}, {DIFF_DELETE, 'b'}, {DIFF_DELETE, 'c'}}\n  dmp.diff_cleanupMerge(diffs)\n  assertEquivalent({{DIFF_DELETE, 'abc'}}, diffs)\n  -- Merge insertions.\n  diffs = {{DIFF_INSERT, 'a'}, {DIFF_INSERT, 'b'}, {DIFF_INSERT, 'c'}}\n  dmp.diff_cleanupMerge(diffs)\n  assertEquivalent({{DIFF_INSERT, 'abc'}}, diffs)\n  -- Merge interweave.\n  diffs = {{DIFF_DELETE, 'a'}, {DIFF_INSERT, 'b'}, {DIFF_DELETE, 'c'},\n      {DIFF_INSERT, 'd'}, {DIFF_EQUAL, 'e'}, {DIFF_EQUAL, 'f'}}\n  dmp.diff_cleanupMerge(diffs)\n  assertEquivalent({{DIFF_DELETE, 'ac'}, {DIFF_INSERT, 'bd'}, {DIFF_EQUAL, 'ef'}},\n      diffs)\n  -- Prefix and suffix detection.\n  diffs = {{DIFF_DELETE, 'a'}, {DIFF_INSERT, 'abc'}, {DIFF_DELETE, 'dc'}}\n  dmp.diff_cleanupMerge(diffs)\n  assertEquivalent({{DIFF_EQUAL, 'a'}, {DIFF_DELETE, 'd'},\n      {DIFF_INSERT, 'b'}, {DIFF_EQUAL, 'c'}}, diffs)\n  -- Prefix and suffix detection with equalities.\n  diffs = {{DIFF_EQUAL, 'x'}, {DIFF_DELETE, 'a'}, {DIFF_INSERT, 'abc'},\n      {DIFF_DELETE, 'dc'}, {DIFF_EQUAL, 'y'}}\n  dmp.diff_cleanupMerge(diffs)\n  assertEquivalent({{DIFF_EQUAL, 'xa'}, {DIFF_DELETE, 'd'},\n      {DIFF_INSERT, 'b'}, {DIFF_EQUAL, 'cy'}}, diffs)\n  -- Slide edit left.\n  diffs = {{DIFF_EQUAL, 'a'}, {DIFF_INSERT, 'ba'}, {DIFF_EQUAL, 'c'}}\n  dmp.diff_cleanupMerge(diffs)\n  assertEquivalent({{DIFF_INSERT, 'ab'}, {DIFF_EQUAL, 'ac'}}, diffs)\n  -- Slide edit right.\n  diffs = {{DIFF_EQUAL, 'c'}, {DIFF_INSERT, 'ab'}, {DIFF_EQUAL, 'a'}}\n  dmp.diff_cleanupMerge(diffs)\n  assertEquivalent({{DIFF_EQUAL, 'ca'}, {DIFF_INSERT, 'ba'}}, diffs)\n  -- Slide edit left recursive.\n  diffs = {{DIFF_EQUAL, 'a'}, {DIFF_DELETE, 'b'}, {DIFF_EQUAL, 'c'},\n      {DIFF_DELETE, 'ac'}, {DIFF_EQUAL, 'x'}}\n  dmp.diff_cleanupMerge(diffs)\n  assertEquivalent({{DIFF_DELETE, 'abc'}, {DIFF_EQUAL, 'acx'}}, diffs)\n  -- Slide edit right recursive.\n  diffs = {{DIFF_EQUAL, 'x'}, {DIFF_DELETE, 'ca'}, {DIFF_EQUAL, 'c'},\n      {DIFF_DELETE, 'b'}, {DIFF_EQUAL, 'a'}}\n  dmp.diff_cleanupMerge(diffs)\n  assertEquivalent({{DIFF_EQUAL, 'xca'}, {DIFF_DELETE, 'cba'}}, diffs)\n  -- Empty merge.\n  diffs = {{DIFF_DELETE, 'b'}, {DIFF_INSERT, 'ab'}, {DIFF_EQUAL, 'c'}}\n  dmp.diff_cleanupMerge(diffs)\n  assertEquivalent({{DIFF_INSERT, 'a'}, {DIFF_EQUAL, 'bc'}}, diffs)\n  -- Empty equality.\n  diffs = {{DIFF_EQUAL, ''}, {DIFF_INSERT, 'a'}, {DIFF_EQUAL, 'b'}}\n  dmp.diff_cleanupMerge(diffs)\n  assertEquivalent({{DIFF_INSERT, 'a'}, {DIFF_EQUAL, 'b'}}, diffs)\nend\n\nfunction testDiffCleanupSemanticLossless()\n  -- Slide diffs to match logical boundaries.\n\n  -- Null case.\n  local diffs = {}\n  dmp.diff_cleanupSemanticLossless(diffs)\n  assertEquivalent({}, diffs)\n  -- Blank lines.\n  diffs = {{DIFF_EQUAL, 'AAA\\r\\n\\r\\nBBB'}, {DIFF_INSERT, '\\r\\nDDD\\r\\n\\r\\nBBB'},\n      {DIFF_EQUAL, '\\r\\nEEE'}}\n  dmp.diff_cleanupSemanticLossless(diffs)\n  assertEquivalent({{DIFF_EQUAL, 'AAA\\r\\n\\r\\n'},\n      {DIFF_INSERT, 'BBB\\r\\nDDD\\r\\n\\r\\n'}, {DIFF_EQUAL, 'BBB\\r\\nEEE'}}, diffs)\n  -- Line boundaries.\n  diffs = {{DIFF_EQUAL, 'AAA\\r\\nBBB'}, {DIFF_INSERT, ' DDD\\r\\nBBB'},\n      {DIFF_EQUAL, ' EEE'}}\n  dmp.diff_cleanupSemanticLossless(diffs)\n  assertEquivalent({{DIFF_EQUAL, 'AAA\\r\\n'}, {DIFF_INSERT, 'BBB DDD\\r\\n'},\n      {DIFF_EQUAL, 'BBB EEE'}}, diffs)\n  -- Word boundaries.\n  diffs = {{DIFF_EQUAL, 'The c'}, {DIFF_INSERT, 'ow and the c'},\n      {DIFF_EQUAL, 'at.'}}\n  dmp.diff_cleanupSemanticLossless(diffs)\n  assertEquivalent({{DIFF_EQUAL, 'The '}, {DIFF_INSERT, 'cow and the '},\n      {DIFF_EQUAL, 'cat.'}}, diffs)\n  -- Alphanumeric boundaries.\n  diffs = {{DIFF_EQUAL, 'The-c'}, {DIFF_INSERT, 'ow-and-the-c'},\n      {DIFF_EQUAL, 'at.'}}\n  dmp.diff_cleanupSemanticLossless(diffs)\n  assertEquivalent({{DIFF_EQUAL, 'The-'}, {DIFF_INSERT, 'cow-and-the-'},\n      {DIFF_EQUAL, 'cat.'}}, diffs)\n  -- Hitting the start.\n  diffs = {{DIFF_EQUAL, 'a'}, {DIFF_DELETE, 'a'}, {DIFF_EQUAL, 'ax'}}\n  dmp.diff_cleanupSemanticLossless(diffs)\n  assertEquivalent({{DIFF_DELETE, 'a'}, {DIFF_EQUAL, 'aax'}}, diffs)\n  -- Hitting the end.\n  diffs = {{DIFF_EQUAL, 'xa'}, {DIFF_DELETE, 'a'}, {DIFF_EQUAL, 'a'}}\n  dmp.diff_cleanupSemanticLossless(diffs)\n  assertEquivalent({{DIFF_EQUAL, 'xaa'}, {DIFF_DELETE, 'a'}}, diffs)\n  -- Sentence boundaries.\n  diffs = {{DIFF_EQUAL, 'The xxx. The '}, {DIFF_INSERT, 'zzz. The '},\n      {DIFF_EQUAL, 'yyy.'}}\n  dmp.diff_cleanupSemanticLossless(diffs)\n  assertEquivalent({{DIFF_EQUAL, 'The xxx.'}, {DIFF_INSERT, ' The zzz.'},\n      {DIFF_EQUAL, ' The yyy.'}}, diffs)\nend\n\nfunction testDiffCleanupSemantic()\n  -- Cleanup semantically trivial equalities.\n\n  -- Null case.\n  local diffs = {}\n  dmp.diff_cleanupSemantic(diffs)\n  assertEquivalent({}, diffs)\n  -- No elimination #1.\n  diffs = {{DIFF_DELETE, 'ab'}, {DIFF_INSERT, 'cd'}, {DIFF_EQUAL, '12'},\n      {DIFF_DELETE, 'e'}}\n  dmp.diff_cleanupSemantic(diffs)\n  assertEquivalent({{DIFF_DELETE, 'ab'}, {DIFF_INSERT, 'cd'}, {DIFF_EQUAL, '12'},\n      {DIFF_DELETE, 'e'}}, diffs)\n  -- No elimination #2.\n  diffs = {{DIFF_DELETE, 'abc'}, {DIFF_INSERT, 'ABC'}, {DIFF_EQUAL, '1234'},\n      {DIFF_DELETE, 'wxyz'}}\n  dmp.diff_cleanupSemantic(diffs)\n  assertEquivalent({{DIFF_DELETE, 'abc'}, {DIFF_INSERT, 'ABC'}, {DIFF_EQUAL, '1234'},\n      {DIFF_DELETE, 'wxyz'}}, diffs)\n  -- Simple elimination.\n  diffs = {{DIFF_DELETE, 'a'}, {DIFF_EQUAL, 'b'}, {DIFF_DELETE, 'c'}}\n  dmp.diff_cleanupSemantic(diffs)\n  assertEquivalent({{DIFF_DELETE, 'abc'}, {DIFF_INSERT, 'b'}}, diffs)\n  -- Backpass elimination.\n  diffs = {{DIFF_DELETE, 'ab'}, {DIFF_EQUAL, 'cd'}, {DIFF_DELETE, 'e'},\n      {DIFF_EQUAL, 'f'}, {DIFF_INSERT, 'g'}}\n  dmp.diff_cleanupSemantic(diffs)\n  assertEquivalent({{DIFF_DELETE, 'abcdef'}, {DIFF_INSERT, 'cdfg'}}, diffs)\n  -- Multiple eliminations.\n  diffs = {{DIFF_INSERT, '1'}, {DIFF_EQUAL, 'A'}, {DIFF_DELETE, 'B'},\n      {DIFF_INSERT, '2'}, {DIFF_EQUAL, '_'}, {DIFF_INSERT, '1'},\n      {DIFF_EQUAL, 'A'}, {DIFF_DELETE, 'B'}, {DIFF_INSERT, '2'}}\n  dmp.diff_cleanupSemantic(diffs)\n  assertEquivalent({{DIFF_DELETE, 'AB_AB'}, {DIFF_INSERT, '1A2_1A2'}}, diffs)\n  -- Word boundaries.\n  diffs = {{DIFF_EQUAL, 'The c'}, {DIFF_DELETE, 'ow and the c'},\n      {DIFF_EQUAL, 'at.'}}\n  dmp.diff_cleanupSemantic(diffs)\n  assertEquivalent({{DIFF_EQUAL, 'The '}, {DIFF_DELETE, 'cow and the '},\n      {DIFF_EQUAL, 'cat.'}}, diffs)\n  -- No overlap elimination.\n  diffs = {{DIFF_DELETE, 'abcxx'}, {DIFF_INSERT, 'xxdef'}}\n  dmp.diff_cleanupSemantic(diffs)\n  assertEquivalent({{DIFF_DELETE, 'abcxx'}, {DIFF_INSERT, 'xxdef'}}, diffs)\n  -- Overlap elimination.\n  diffs = {{DIFF_DELETE, 'abcxxx'}, {DIFF_INSERT, 'xxxdef'}}\n  dmp.diff_cleanupSemantic(diffs)\n  assertEquivalent({{DIFF_DELETE, 'abc'}, {DIFF_EQUAL, 'xxx'}, {DIFF_INSERT, 'def'}}, diffs)\n  -- Reverse overlap elimination.\n  diffs = {{DIFF_DELETE, 'xxxabc'}, {DIFF_INSERT, 'defxxx'}}\n  dmp.diff_cleanupSemantic(diffs)\n  assertEquivalent({{DIFF_INSERT, 'def'}, {DIFF_EQUAL, 'xxx'}, {DIFF_DELETE, 'abc'}}, diffs)\n  -- Two overlap eliminations.\n  diffs = {{DIFF_DELETE, 'abcd1212'}, {DIFF_INSERT, '1212efghi'}, {DIFF_EQUAL, '----'}, {DIFF_DELETE, 'A3'}, {DIFF_INSERT, '3BC'}}\n  dmp.diff_cleanupSemantic(diffs)\n  assertEquivalent({{DIFF_DELETE, 'abcd'}, {DIFF_EQUAL, '1212'}, {DIFF_INSERT, 'efghi'}, {DIFF_EQUAL, '----'}, {DIFF_DELETE, 'A'}, {DIFF_EQUAL, '3'}, {DIFF_INSERT, 'BC'}}, diffs)\nend\n\nfunction testDiffCleanupEfficiency()\n  -- Cleanup operationally trivial equalities.\n  local diffs\n  dmp.settings{Diff_EditCost = 4}\n\n  -- Null case.\n  diffs = {}\n  dmp.diff_cleanupEfficiency(diffs)\n  assertEquivalent({}, diffs)\n  -- No elimination.\n  diffs = {{DIFF_DELETE, 'ab'}, {DIFF_INSERT, '12'}, {DIFF_EQUAL, 'wxyz'},\n      {DIFF_DELETE, 'cd'}, {DIFF_INSERT, '34'}}\n  dmp.diff_cleanupEfficiency(diffs)\n  assertEquivalent({{DIFF_DELETE, 'ab'}, {DIFF_INSERT, '12'},\n      {DIFF_EQUAL, 'wxyz'}, {DIFF_DELETE, 'cd'}, {DIFF_INSERT, '34'}}, diffs)\n  -- Four-edit elimination.\n  diffs = {{DIFF_DELETE, 'ab'}, {DIFF_INSERT, '12'}, {DIFF_EQUAL, 'xyz'},\n      {DIFF_DELETE, 'cd'}, {DIFF_INSERT, '34'}}\n  dmp.diff_cleanupEfficiency(diffs)\n  assertEquivalent({\n        {DIFF_DELETE, 'abxyzcd'},\n        {DIFF_INSERT, '12xyz34'}\n      }, diffs)\n\n  -- Three-edit elimination.\n  diffs = {\n        {DIFF_INSERT, '12'},\n        {DIFF_EQUAL, 'x'},\n        {DIFF_DELETE, 'cd'},\n        {DIFF_INSERT, '34'}\n      }\n  dmp.diff_cleanupEfficiency(diffs)\n  assertEquivalent({\n        {DIFF_DELETE, 'xcd'},\n        {DIFF_INSERT, '12x34'}\n      }, diffs)\n\n  -- Backpass elimination.\n  diffs = {\n        {DIFF_DELETE, 'ab'},\n        {DIFF_INSERT, '12'},\n        {DIFF_EQUAL, 'xy'},\n        {DIFF_INSERT, '34'},\n        {DIFF_EQUAL, 'z'},\n        {DIFF_DELETE, 'cd'},\n        {DIFF_INSERT, '56'}\n      }\n  dmp.diff_cleanupEfficiency(diffs)\n  assertEquivalent({\n        {DIFF_DELETE, 'abxyzcd'},\n        {DIFF_INSERT, '12xy34z56'}\n      }, diffs)\n\n  -- High cost elimination.\n  dmp.settings{Diff_EditCost = 5}\n  diffs = {\n        {DIFF_DELETE, 'ab'},\n        {DIFF_INSERT, '12'},\n        {DIFF_EQUAL, 'wxyz'},\n        {DIFF_DELETE, 'cd'},\n        {DIFF_INSERT, '34'}\n      }\n  dmp.diff_cleanupEfficiency(diffs)\n  assertEquivalent({\n        {DIFF_DELETE, 'abwxyzcd'},\n        {DIFF_INSERT, '12wxyz34'}\n      }, diffs)\n\n  dmp.settings{Diff_EditCost = 4}\nend\n\nfunction testDiffPrettyHtml()\n  -- Pretty print.\n  local diffs = {\n        {DIFF_EQUAL, 'a\\n'},\n        {DIFF_DELETE, '<B>b</B>'},\n        {DIFF_INSERT, 'c&d'}\n      }\n  assertEquals(\n        '<span>a&para;<br></span>'\n        .. '<del style=\"background:#ffe6e6;\">&lt;B&gt;b&lt;/B&gt;'\n        .. '</del><ins style=\"background:#e6ffe6;\">c&amp;d</ins>',\n        dmp.diff_prettyHtml(diffs)\n      )\nend\n\nfunction testDiffText()\n  -- Compute the source and destination texts.\n  local diffs = {\n        {DIFF_EQUAL, 'jump'},\n        {DIFF_DELETE, 's'},\n        {DIFF_INSERT, 'ed'},\n        {DIFF_EQUAL, ' over '},\n        {DIFF_DELETE, 'the'},\n        {DIFF_INSERT, 'a'},\n        {DIFF_EQUAL, ' lazy'}\n      }\n  assertEquals('jumps over the lazy', dmp.diff_text1(diffs))\n  assertEquals('jumped over a lazy', dmp.diff_text2(diffs))\nend\n\nfunction testDiffDelta()\n  -- Convert a diff into delta string.\n  local diffs = {\n        {DIFF_EQUAL, 'jump'},\n        {DIFF_DELETE, 's'},\n        {DIFF_INSERT, 'ed'},\n        {DIFF_EQUAL, ' over '},\n        {DIFF_DELETE, 'the'},\n        {DIFF_INSERT, 'a'},\n        {DIFF_EQUAL, ' lazy'},\n        {DIFF_INSERT, 'old dog'}\n      }\n  local text1 = dmp.diff_text1(diffs)\n  assertEquals('jumps over the lazy', text1)\n\n  local delta = dmp.diff_toDelta(diffs)\n  assertEquals('=4\\t-1\\t+ed\\t=6\\t-3\\t+a\\t=5\\t+old dog', delta)\n\n  -- Convert delta string into a diff.\n  assertEquivalent(diffs, dmp.diff_fromDelta(text1, delta))\n\n  -- Generates error (19 ~= 20).\n  success, result = pcall(dmp.diff_fromDelta, text1 .. 'x', delta)\n  assertEquals(false, success)\n\n  -- Generates error (19 ~= 18).\n  success, result = pcall(dmp.diff_fromDelta, string.sub(text1, 2), delta)\n  assertEquals(false, success)\n\n  -- Generates error (%c3%xy invalid Unicode).\n  success, result = pcall(dmp.patch_fromDelta, '', '+%c3%xy')\n  assertEquals(false, success)\n\n  --[[\n  -- Test deltas with special characters.\n  -- LUANOTE: No ability to handle Unicode.\n  diffs = {{DIFF_EQUAL, '\\u0680 \\000 \\t %'}, {DIFF_DELETE, '\\u0681 \\x01 \\n ^'}, {DIFF_INSERT, '\\u0682 \\x02 \\\\ |'}}\n  text1 = dmp.diff_text1(diffs)\n  assertEquals('\\u0680 \\x00 \\t %\\u0681 \\x01 \\n ^', text1)\n\n  delta = dmp.diff_toDelta(diffs)\n  assertEquals('=7\\t-7\\t+%DA%82 %02 %5C %7C', delta)\n  --]]\n\n  -- Convert delta string into a diff.\n  assertEquivalent(diffs, dmp.diff_fromDelta(text1, delta))\n\n  -- Verify pool of unchanged characters.\n  diffs = {\n        {DIFF_INSERT, 'A-Z a-z 0-9 - _ . ! ~ * \\' ( ) ; / ? = @ & = + $ , # '}\n      }\n  local text2 = dmp.diff_text2(diffs)\n  assertEquals(\n        'A-Z a-z 0-9 - _ . ! ~ * \\' ( ) ; / ? = @ & = + $ , # ',\n        text2\n      )\n\n  delta = dmp.diff_toDelta(diffs)\n  assertEquals(\n        '+A-Z a-z 0-9 - _ . ! ~ * \\' ( ) ; / ? = @ & = + $ , # ',\n        delta\n      )\n\n  -- Convert delta string into a diff.\n  assertEquivalent(diffs, dmp.diff_fromDelta('', delta))\n\n  -- 160 kb string.\n  local a = string.rep('abcdefghij', 16384)\n  diffs = {{DIFF_INSERT, a}};\n  delta = dmp.diff_toDelta(diffs);\n  assertEquals('+' .. a, delta);\n\n  -- Convert delta string into a diff.\n  assertEquivalent(diffs, dmp.diff_fromDelta('', delta));\nend\n\nfunction testDiffXIndex()\n  -- Translate a location in text1 to text2.\n\n  -- Translation on equality.\n  assertEquals(6, dmp.diff_xIndex({\n        {DIFF_DELETE, 'a'},\n        {DIFF_INSERT, '1234'},\n        {DIFF_EQUAL, 'xyz'}\n      }, 3))\n\n  -- Translation on deletion.\n  assertEquals(2, dmp.diff_xIndex({\n        {DIFF_EQUAL, 'a'},\n        {DIFF_DELETE, '1234'},\n        {DIFF_EQUAL, 'xyz'}\n      }, 4))\nend\n\nfunction testDiffLevenshtein()\n  -- Levenshtein with trailing equality.\n  assertEquals(4, dmp.diff_levenshtein({\n        {DIFF_DELETE, 'abc'},\n        {DIFF_INSERT, '1234'},\n        {DIFF_EQUAL, 'xyz'}\n      }))\n  -- Levenshtein with leading equality.\n  assertEquals(4, dmp.diff_levenshtein({\n        {DIFF_EQUAL, 'xyz'},\n        {DIFF_DELETE, 'abc'},\n        {DIFF_INSERT, '1234'}\n      }))\n  -- Levenshtein with middle equality.\n  assertEquals(7, dmp.diff_levenshtein({\n        {DIFF_DELETE, 'abc'},\n        {DIFF_EQUAL, 'xyz'},\n        {DIFF_INSERT, '1234'}\n      }))\nend\n\nfunction testDiffBisect()\n  -- Normal.\n  local a = 'cat'\n  local b = 'map'\n  -- Since the resulting diff hasn't been normalized, it would be ok if\n  -- the insertion and deletion pairs are swapped.\n  -- If the order changes, tweak this test as required.\n  assertEquivalent({\n        {DIFF_DELETE, 'c'},\n        {DIFF_INSERT, 'm'},\n        {DIFF_EQUAL, 'a'},\n        {DIFF_DELETE, 't'},\n        {DIFF_INSERT, 'p'}\n      }, dmp.diff_bisect(a, b, 2 ^ 31))\n\n  -- Timeout.\n  assertEquivalent({\n        {DIFF_DELETE, 'cat'},\n        {DIFF_INSERT, 'map'}\n      }, dmp.diff_bisect(a, b, 0))\nend\n\nfunction testDiffMain()\n  -- Perform a trivial diff.\n  local a,b\n\n  -- Null case.\n  assertEquivalent({}, dmp.diff_main('', '', false))\n\n  -- Equality.\n  assertEquivalent({\n        {DIFF_EQUAL, 'abc'}\n      }, dmp.diff_main('abc', 'abc', false))\n\n  -- Simple insertion.\n  assertEquivalent({\n        {DIFF_EQUAL, 'ab'},\n        {DIFF_INSERT, '123'},\n        {DIFF_EQUAL, 'c'}\n      }, dmp.diff_main('abc', 'ab123c', false))\n\n  -- Simple deletion.\n  assertEquivalent({\n        {DIFF_EQUAL, 'a'},\n        {DIFF_DELETE, '123'},\n        {DIFF_EQUAL, 'bc'}\n      }, dmp.diff_main('a123bc', 'abc', false))\n\n  -- Two insertions.\n  assertEquivalent({\n        {DIFF_EQUAL, 'a'},\n        {DIFF_INSERT, '123'},\n        {DIFF_EQUAL, 'b'},\n        {DIFF_INSERT, '456'},\n        {DIFF_EQUAL, 'c'}\n      }, dmp.diff_main('abc', 'a123b456c', false))\n\n  -- Two deletions.\n  assertEquivalent({\n        {DIFF_EQUAL, 'a'},\n        {DIFF_DELETE, '123'},\n        {DIFF_EQUAL, 'b'},\n        {DIFF_DELETE, '456'},\n        {DIFF_EQUAL, 'c'}\n      }, dmp.diff_main('a123b456c', 'abc', false))\n\n  -- Perform a real diff.\n  -- Switch off the timeout.\n  dmp.settings{ Diff_Timeout=0 }\n\n  -- Simple cases.\n  assertEquivalent({\n        {DIFF_DELETE, 'a'},\n        {DIFF_INSERT, 'b'}\n      }, dmp.diff_main('a', 'b', false))\n\n  assertEquivalent({\n        {DIFF_DELETE, 'Apple'},\n        {DIFF_INSERT, 'Banana'},\n        {DIFF_EQUAL, 's are a'},\n        {DIFF_INSERT, 'lso'},\n        {DIFF_EQUAL, ' fruit.'}\n      }, dmp.diff_main('Apples are a fruit.', 'Bananas are also fruit.', false))\n\n  --[[\n  -- LUANOTE: No ability to handle Unicode.\n  assertEquivalent({\n        {DIFF_DELETE, 'a'},\n        {DIFF_INSERT, '\\u0680'},\n        {DIFF_EQUAL, 'x'},\n        {DIFF_DELETE, '\\t'},\n        {DIFF_INSERT, '\\0'}\n      }, dmp.diff_main('ax\\t', '\\u0680x\\0', false))\n  ]]--\n\n  -- Overlaps.\n  assertEquivalent({\n        {DIFF_DELETE, '1'},\n        {DIFF_EQUAL, 'a'},\n        {DIFF_DELETE, 'y'},\n        {DIFF_EQUAL, 'b'},\n        {DIFF_DELETE, '2'},\n        {DIFF_INSERT, 'xab'}\n      }, dmp.diff_main('1ayb2', 'abxab', false))\n\n  assertEquivalent({\n        {DIFF_INSERT, 'xaxcx'},\n        {DIFF_EQUAL, 'abc'},\n        {DIFF_DELETE, 'y'}\n      }, dmp.diff_main('abcy', 'xaxcxabc', false))\n\n  assertEquivalent({\n        {DIFF_DELETE, 'ABCD'},\n        {DIFF_EQUAL, 'a'},\n        {DIFF_DELETE, '='},\n        {DIFF_INSERT, '-'},\n        {DIFF_EQUAL, 'bcd'},\n        {DIFF_DELETE, '='},\n        {DIFF_INSERT, '-'},\n        {DIFF_EQUAL, 'efghijklmnopqrs'},\n        {DIFF_DELETE, 'EFGHIJKLMNOefg'}\n      }, dmp.diff_main('ABCDa=bcd=efghijklmnopqrsEFGHIJKLMNOefg',\n                       'a-bcd-efghijklmnopqrs', false))\n\n  -- Large equality.\n  assertEquivalent({\n        {DIFF_INSERT, ' '},\n        {DIFF_EQUAL, 'a'},\n        {DIFF_INSERT, 'nd'},\n        {DIFF_EQUAL, ' [[Pennsylvania]]'},\n        {DIFF_DELETE, ' and [[New'}\n      }, dmp.diff_main('a [[Pennsylvania]] and [[New',\n                       ' and [[Pennsylvania]]', false))\n\n  -- Timeout.\n  dmp.settings{Diff_Timeout = 0.1}  -- 100ms\n  -- Increase the text lengths by 1024 times to ensure a timeout.\n  a = string.rep([[\n`Twas brillig, and the slithy toves\nDid gyre and gimble in the wabe:\nAll mimsy were the borogoves,\nAnd the mome raths outgrabe.\n]], 1024)\n  b = string.rep([[\nI am the very model of a modern major general,\nI've information vegetable, animal, and mineral,\nI know the kings of England, and I quote the fights historical,\nFrom Marathon to Waterloo, in order categorical.\n]], 1024)\n  local startTime = os.clock()\n  dmp.diff_main(a, b)\n  local endTime = os.clock()\n  -- Test that we took at least the timeout period.\n  assertTrue(0.1 <= endTime - startTime)\n  -- Test that we didn't take forever (be forgiving).\n  -- Theoretically this test could fail very occasionally if the\n  -- OS task swaps or locks up for a second at the wrong moment.\n  assertTrue(0.1 * 2 > endTime - startTime)\n  dmp.settings{Diff_Timeout = 0}\n\n  -- Test the linemode speedup.\n  -- Must be long to pass the 100 char cutoff.\n  -- Simple line-mode.\n  a = string.rep('1234567890\\n', 13)\n  b = string.rep('abcdefghij\\n', 13)\n  assertEquivalent(dmp.diff_main(a, b, false), dmp.diff_main(a, b, true))\n\n  -- Single line-mode.\n  a = string.rep('1234567890', 13)\n  b = string.rep('abcdefghij', 13)\n  assertEquivalent(dmp.diff_main(a, b, false), dmp.diff_main(a, b, true))\n\n  -- Overlap line-mode.\n  a = string.rep('1234567890\\n', 13)\n  b = [[\nabcdefghij\n1234567890\n1234567890\n1234567890\nabcdefghij\n1234567890\n1234567890\n1234567890\nabcdefghij\n1234567890\n1234567890\n1234567890\nabcdefghij\n]]\n  local texts_linemode = diff_rebuildtexts(dmp.diff_main(a, b, true))\n  local texts_textmode = diff_rebuildtexts(dmp.diff_main(a, b, false))\n  assertEquivalent(texts_textmode, texts_linemode)\n\n  -- Test null inputs.\n  success, result = pcall(dmp.diff_main, nil, nil)\n  assertEquals(false, success)\nend\n\n\n-- MATCH TEST FUNCTIONS\n\n\nfunction testMatchAlphabet()\n  -- Initialise the bitmasks for Bitap.\n  -- Unique.\n  assertEquivalent({a=4, b=2, c=1}, dmp.match_alphabet('abc'))\n\n  -- Duplicates.\n  assertEquivalent({a=37, b=18, c=8}, dmp.match_alphabet('abcaba'))\nend\n\nfunction testMatchBitap()\n  -- Bitap algorithm.\n  dmp.settings{Match_Distance=100, Match_Threshold=0.5}\n\n  -- Exact matches.\n  assertEquals(6, dmp.match_bitap('abcdefghijk', 'fgh', 6))\n\n  assertEquals(6, dmp.match_bitap('abcdefghijk', 'fgh', 1))\n\n  -- Fuzzy matches.\n  assertEquals(5, dmp.match_bitap('abcdefghijk', 'efxhi', 1))\n\n  assertEquals(3, dmp.match_bitap('abcdefghijk', 'cdefxyhijk', 6))\n\n  assertEquals(-1, dmp.match_bitap('abcdefghijk', 'bxy', 2))\n\n  -- Overflow.\n  assertEquals(3, dmp.match_bitap('123456789xx0', '3456789x0', 3))\n\n  -- Threshold test.\n  dmp.settings{Match_Threshold = 0.4}\n  assertEquals(5, dmp.match_bitap('abcdefghijk', 'efxyhi', 2))\n\n  dmp.settings{Match_Threshold = 0.3}\n  assertEquals(-1, dmp.match_bitap('abcdefghijk', 'efxyhi', 2))\n\n  dmp.settings{Match_Threshold = 0.0}\n  assertEquals(2, dmp.match_bitap('abcdefghijk', 'bcdef', 2))\n  dmp.settings{Match_Threshold = 0.5}\n\n  -- Multiple select.\n  assertEquals(1, dmp.match_bitap('abcdexyzabcde', 'abccde', 4))\n\n  assertEquals(9, dmp.match_bitap('abcdexyzabcde', 'abccde', 6))\n\n  -- Distance test.\n\n  dmp.settings{Match_Distance = 10}  -- Strict location.\n\n  assertEquals(-1,\n      dmp.match_bitap('abcdefghijklmnopqrstuvwxyz', 'abcdefg', 25))\n\n  assertEquals(1,\n      dmp.match_bitap('abcdefghijklmnopqrstuvwxyz', 'abcdxxefg', 2))\n\n  dmp.settings{Match_Distance = 1000}  -- Loose location.\n\n  assertEquals(1,\n      dmp.match_bitap('abcdefghijklmnopqrstuvwxyz', 'abcdefg', 25))\nend\n\nfunction testMatchMain()\n  -- Full match.\n  -- Shortcut matches.\n  assertEquals(1, dmp.match_main('abcdef', 'abcdef', 1000))\n\n  assertEquals(-1, dmp.match_main('', 'abcdef', 2))\n\n  assertEquals(4, dmp.match_main('abcdef', '', 4))\n\n  assertEquals(4, dmp.match_main('abcdef', 'de', 4))\n\n  -- Beyond end match.\n  assertEquals(4, dmp.match_main(\"abcdef\", \"defy\", 5))\n\n  -- Oversized pattern.\n  assertEquals(1, dmp.match_main(\"abcdef\", \"abcdefy\", 1))\n\n  -- Complex match.\n  assertEquals(5, dmp.match_main(\n        'I am the very model of a modern major general.',\n        ' that berry ',\n        6\n      ))\n\n  -- Test null inputs.\n  success, result = pcall(dmp.match_main, nil, nil, 0)\n  assertEquals(false, success)\nend\n\n\n-- PATCH TEST FUNCTIONS\n\n\nfunction testPatchObj()\n  -- Patch Object.\n  local p = dmp.new_patch_obj()\n  p.start1 = 21\n  p.start2 = 22\n  p.length1 = 18\n  p.length2 = 17\n  p.diffs = {\n        {DIFF_EQUAL, 'jump'},\n        {DIFF_DELETE, 's'},\n        {DIFF_INSERT, 'ed'},\n        {DIFF_EQUAL, ' over '},\n        {DIFF_DELETE, 'the'},\n        {DIFF_INSERT, 'a'},\n        {DIFF_EQUAL, '\\nlaz'}\n      }\n  local strp = tostring(p)\n  assertEquals(\n        '@@ -21,18 +22,17 @@\\n jump\\n-s\\n+ed\\n  over \\n-the\\n+a\\n %0Alaz\\n',\n        strp\n      )\nend\n\nfunction testPatchFromText()\n  local strp\n\n  strp = ''\n  assertEquivalent({}, dmp.patch_fromText(strp))\n\n  strp = '@@ -21,18 +22,17 @@\\n jump\\n-s\\n+ed\\n  over \\n-the\\n+a\\n %0Alaz\\n'\n  assertEquals(strp, tostring(dmp.patch_fromText(strp)[1]))\n\n  assertEquals(\n        '@@ -1 +1 @@\\n-a\\n+b\\n',\n        tostring(dmp.patch_fromText('@@ -1 +1 @@\\n-a\\n+b\\n')[1])\n      )\n\n  assertEquals(\n        '@@ -1,3 +0,0 @@\\n-abc\\n',\n        tostring(dmp.patch_fromText('@@ -1,3 +0,0 @@\\n-abc\\n')[1])\n      )\n\n  assertEquals(\n        '@@ -0,0 +1,3 @@\\n+abc\\n',\n        tostring(dmp.patch_fromText('@@ -0,0 +1,3 @@\\n+abc\\n')[1])\n      )\n\n  -- Generates error.\n  success, result = pcall(dmp.patch_fromText, 'Bad\\nPatch\\n')\n  assertEquals(false, success)\nend\n\nfunction testPatchToText()\n  local strp, p\n\n  strp = '@@ -21,18 +22,17 @@\\n jump\\n-s\\n+ed\\n  over \\n-the\\n+a\\n  laz\\n'\n  p = dmp.patch_fromText(strp)\n  assertEquals(strp, dmp.patch_toText(p))\n\n  strp = '@@ -1,9 +1,9 @@\\n-f\\n+F\\n oo+fooba\\n'\n      .. '@@ -7,9 +7,9 @@\\n obar\\n-,\\n+.\\n  tes\\n'\n  p = dmp.patch_fromText(strp)\n  assertEquals(strp, dmp.patch_toText(p))\nend\n\nfunction testPatchAddContext()\n  local p\n  dmp.settings{Patch_Margin = 4}\n\n  p = dmp.patch_fromText('@@ -21,4 +21,10 @@\\n-jump\\n+somersault\\n')[1]\n\n  dmp.patch_addContext(p, 'The quick brown fox jumps over the lazy dog.')\n\n  assertEquals(\n        '@@ -17,12 +17,18 @@\\n fox \\n-jump\\n+somersault\\n s ov\\n',\n        tostring(p)\n      )\n\n  -- Same, but not enough trailing context.\n  p = dmp.patch_fromText('@@ -21,4 +21,10 @@\\n-jump\\n+somersault\\n')[1]\n  dmp.patch_addContext(p, 'The quick brown fox jumps.')\n  assertEquals(\n        '@@ -17,10 +17,16 @@\\n fox \\n-jump\\n+somersault\\n s.\\n',\n        tostring(p)\n      )\n\n  -- Same, but not enough leading context.\n  p = dmp.patch_fromText('@@ -3 +3,2 @@\\n-e\\n+at\\n')[1]\n  dmp.patch_addContext(p, 'The quick brown fox jumps.')\n  assertEquals('@@ -1,7 +1,8 @@\\n Th\\n-e\\n+at\\n  qui\\n', tostring(p))\n\n  -- Same, but with ambiguity.\n  p = dmp.patch_fromText('@@ -3 +3,2 @@\\n-e\\n+at\\n')[1]\n  dmp.patch_addContext(p, 'The quick brown fox jumps.  The quick brown fox crashes.')\n  assertEquals('@@ -1,27 +1,28 @@\\n Th\\n-e\\n+at\\n  quick brown fox jumps. \\n', tostring(p))\nend\n\nfunction testPatchMake()\n  -- Null case.\n  local patches = dmp.patch_make('', '')\n  assertEquals('', dmp.patch_toText(patches))\n\n  local text1 = 'The quick brown fox jumps over the lazy dog.'\n  local text2 = 'That quick brown fox jumped over a lazy dog.'\n  -- Text2+Text1 inputs.\n  local expectedPatch = '@@ -1,8 +1,7 @@\\n Th\\n-at\\n+e\\n  qui\\n'\n      .. '@@ -21,17 +21,18 @@\\n jump\\n-ed\\n+s\\n  over \\n-a\\n+the\\n  laz\\n'\n  -- The second patch must be \"-21,17 +21,18\",\n  -- not \"-22,17 +21,18\" due to rolling context.\n  patches = dmp.patch_make(text2, text1)\n  assertEquals(expectedPatch, dmp.patch_toText(patches))\n\n  -- Text1+Text2 inputs.\n  expectedPatch = '@@ -1,11 +1,12 @@\\n Th\\n-e\\n+at\\n  quick b\\n'\n      .. '@@ -22,18 +22,17 @@\\n jump\\n-s\\n+ed\\n  over \\n-the\\n+a\\n  laz\\n'\n  patches = dmp.patch_make(text1, text2)\n  assertEquals(expectedPatch, dmp.patch_toText(patches))\n\n  -- Diff input.\n  local diffs = dmp.diff_main(text1, text2, false)\n  patches = dmp.patch_make(diffs)\n  assertEquals(expectedPatch, dmp.patch_toText(patches))\n\n  -- Text1+Diff inputs.\n  patches = dmp.patch_make(text1, diffs)\n  assertEquals(expectedPatch, dmp.patch_toText(patches))\n\n  -- Text1+Text2+Diff inputs (deprecated).\n  patches = dmp.patch_make(text1, text2, diffs)\n  assertEquals(expectedPatch, dmp.patch_toText(patches))\n\n  -- Character encoding.\n  patches = dmp.patch_make('`1234567890-=[]\\\\;\\',./', '~!@#$%^&*()_+{}|=\"<>?')\n  assertEquals('@@ -1,21 +1,21 @@\\n'\n      .. '-%601234567890-=%5B%5D%5C;\\',./\\n'\n      .. '+~!@#$%25%5E&*()_+%7B%7D%7C=%22%3C%3E?\\n', dmp.patch_toText(patches))\n\n  -- Character decoding.\n  diffs = {\n        {DIFF_DELETE, '`1234567890-=[]\\\\;\\',./'},\n        {DIFF_INSERT, '~!@#$%^&*()_+{}|=\"<>?'}\n      }\n  assertEquivalent(diffs, dmp.patch_fromText(\n        '@@ -1,21 +1,21 @@'\n         .. '\\n-%601234567890-=%5B%5D%5C;\\',./'\n         .. '\\n+~!@#$%25%5E&*()_+%7B%7D%7C=%22%3C%3E?\\n'\n      )[1].diffs)\n\n  -- Long string with repeats.\n  text1 = string.rep('abcdef', 100)\n  text2 = text1 .. '123'\n  expectedPatch = '@@ -573,28 +573,31 @@\\n'\n      .. ' cdefabcdefabcdefabcdefabcdef\\n+123\\n'\n  patches = dmp.patch_make(text1, text2)\n  assertEquals(expectedPatch, dmp.patch_toText(patches))\n\n  -- Test null inputs.\n  success, result = pcall(dmp.patch_make, nil, nil)\n  assertEquals(false, success)\nend\n\nfunction testPatchSplitMax()\n  -- Assumes that dmp.Match_MaxBits is 32.\n  local patches = dmp.patch_make('abcdefghijklmnopqrstuvwxyz01234567890',\n      'XabXcdXefXghXijXklXmnXopXqrXstXuvXwxXyzX01X23X45X67X89X0')\n  dmp.patch_splitMax(patches)\n  assertEquals('@@ -1,32 +1,46 @@\\n+X\\n ab\\n+X\\n cd\\n+X\\n ef\\n+X\\n gh\\n+X\\n ij\\n+X\\n kl\\n+X\\n mn\\n+X\\n op\\n+X\\n qr\\n+X\\n st\\n+X\\n uv\\n+X\\n wx\\n+X\\n yz\\n+X\\n 012345\\n@@ -25,13 +39,18 @@\\n zX01\\n+X\\n 23\\n+X\\n 45\\n+X\\n 67\\n+X\\n 89\\n+X\\n 0\\n', dmp.patch_toText(patches))\n\n  patches = dmp.patch_make('abcdef1234567890123456789012345678901234567890123456789012345678901234567890uvwxyz', 'abcdefuvwxyz')\n  local oldToText = dmp.patch_toText(patches)\n  dmp.patch_splitMax(patches)\n  assertEquals(oldToText, dmp.patch_toText(patches))\n\n  patches = dmp.patch_make('1234567890123456789012345678901234567890123456789012345678901234567890', 'abc')\n  dmp.patch_splitMax(patches)\n  assertEquals('@@ -1,32 +1,4 @@\\n-1234567890123456789012345678\\n 9012\\n@@ -29,32 +1,4 @@\\n-9012345678901234567890123456\\n 7890\\n@@ -57,14 +1,3 @@\\n-78901234567890\\n+abc\\n', dmp.patch_toText(patches))\n\n  patches = dmp.patch_make('abcdefghij , h = 0 , t = 1 abcdefghij , h = 0 , t = 1 abcdefghij , h = 0 , t = 1', 'abcdefghij , h = 1 , t = 1 abcdefghij , h = 1 , t = 1 abcdefghij , h = 0 , t = 1')\n  dmp.patch_splitMax(patches)\n  assertEquals('@@ -2,32 +2,32 @@\\n bcdefghij , h = \\n-0\\n+1\\n  , t = 1 abcdef\\n@@ -29,32 +29,32 @@\\n bcdefghij , h = \\n-0\\n+1\\n  , t = 1 abcdef\\n', dmp.patch_toText(patches))\nend\n\nfunction testPatchAddPadding()\n  -- Both edges full.\n  local patches = dmp.patch_make('', 'test')\n  assertEquals('@@ -0,0 +1,4 @@\\n+test\\n', dmp.patch_toText(patches))\n  dmp.patch_addPadding(patches)\n  assertEquals('@@ -1,8 +1,12 @@\\n %01%02%03%04\\n+test\\n %01%02%03%04\\n', dmp.patch_toText(patches))\n\n  -- Both edges partial.\n  patches = dmp.patch_make('XY', 'XtestY')\n  assertEquals('@@ -1,2 +1,6 @@\\n X\\n+test\\n Y\\n', dmp.patch_toText(patches))\n  dmp.patch_addPadding(patches)\n  assertEquals('@@ -2,8 +2,12 @@\\n %02%03%04X\\n+test\\n Y%01%02%03\\n', dmp.patch_toText(patches))\n\n  -- Both edges none.\n  patches = dmp.patch_make('XXXXYYYY', 'XXXXtestYYYY')\n  assertEquals('@@ -1,8 +1,12 @@\\n XXXX\\n+test\\n YYYY\\n', dmp.patch_toText(patches))\n  dmp.patch_addPadding(patches)\n  assertEquals('@@ -5,8 +5,12 @@\\n XXXX\\n+test\\n YYYY\\n', dmp.patch_toText(patches))\nend\n\nfunction testPatchApply()\n  local patches\n\n  dmp.settings{Match_Distance = 1000}\n  dmp.settings{Match_Threshold = 0.5}\n  dmp.settings{Patch_DeleteThreshold = 0.5}\n  -- Null case.\n  patches = dmp.patch_make('', '')\n  assertEquivalent({'Hello world.', {}},\n      {dmp.patch_apply(patches, 'Hello world.')})\n\n  -- Exact match.\n  patches = dmp.patch_make('The quick brown fox jumps over the lazy dog.',\n      'That quick brown fox jumped over a lazy dog.')\n  assertEquivalent(\n      {'That quick brown fox jumped over a lazy dog.', {true, true}},\n      {dmp.patch_apply(patches, 'The quick brown fox jumps over the lazy dog.')})\n  -- Partial match.\n  assertEquivalent(\n      {'That quick red rabbit jumped over a tired tiger.', {true, true}},\n      {dmp.patch_apply(patches, 'The quick red rabbit jumps over the tired tiger.')})\n  -- Failed match.\n  assertEquivalent(\n      {'I am the very model of a modern major general.', {false, false}},\n      {dmp.patch_apply(patches, 'I am the very model of a modern major general.')})\n  -- Big delete, small change.\n  patches = dmp.patch_make(\n      'x1234567890123456789012345678901234567890123456789012345678901234567890y',\n      'xabcy')\n  assertEquivalent({'xabcy', {true, true}}, {dmp.patch_apply(patches,\n      'x123456789012345678901234567890-----++++++++++-----'\n      .. '123456789012345678901234567890y')})\n  -- Big delete, big change 1.\n  patches = dmp.patch_make('x1234567890123456789012345678901234567890123456789'\n      .. '012345678901234567890y', 'xabcy')\n  assertEquivalent({'xabc12345678901234567890'\n      .. '---------------++++++++++---------------'\n      .. '12345678901234567890y', {false, true}},\n      {dmp.patch_apply(patches, 'x12345678901234567890'\n      .. '---------------++++++++++---------------'\n      .. '12345678901234567890y'\n      )})\n  -- Big delete, big change 2.\n  dmp.settings{Patch_DeleteThreshold = 0.6}\n  patches = dmp.patch_make(\n        'x1234567890123456789012345678901234567890123456789'\n        .. '012345678901234567890y',\n        'xabcy'\n      )\n  assertEquivalent({'xabcy', {true, true}},   {dmp.patch_apply(\n        patches,\n        'x12345678901234567890---------------++++++++++---------------'\n        .. '12345678901234567890y'\n      )}\n)\n  dmp.settings{Patch_DeleteThreshold = 0.5}\n\n  -- Compensate for failed patch.\n  dmp.settings{Match_Threshold = 0, Match_Distance = 0}\n  patches = dmp.patch_make(\n        'abcdefghijklmnopqrstuvwxyz--------------------1234567890',\n        'abcXXXXXXXXXXdefghijklmnopqrstuvwxyz--------------------'\n        .. '1234567YYYYYYYYYY890'\n      )\n  assertEquivalent({\n        'ABCDEFGHIJKLMNOPQRSTUVWXYZ--------------------1234567YYYYYYYYYY890',\n        {false, true}\n      }, {dmp.patch_apply(\n        patches,\n        'ABCDEFGHIJKLMNOPQRSTUVWXYZ--------------------1234567890'\n      )})\n\n  dmp.settings{Match_Threshold = 0.5}\n  dmp.settings{Match_Distance = 1000}\n\n  -- No side effects.\n  patches = dmp.patch_make('', 'test')\n  local patchstr = dmp.patch_toText(patches)\n  dmp.patch_apply(patches, '')\n  assertEquals(patchstr, dmp.patch_toText(patches))\n  -- No side effects with major delete.\n  patches = dmp.patch_make('The quick brown fox jumps over the lazy dog.',\n      'Woof')\n  patchstr = dmp.patch_toText(patches)\n  dmp.patch_apply(patches, 'The quick brown fox jumps over the lazy dog.')\n  assertEquals(patchstr, dmp.patch_toText(patches))\n  -- Edge exact match.\n  patches = dmp.patch_make('', 'test')\n  assertEquivalent({'test', {true}}, {dmp.patch_apply(patches, '')})\n  -- Near edge exact match.\n  patches = dmp.patch_make('XY', 'XtestY')\n  assertEquivalent({'XtestY', {true}}, {dmp.patch_apply(patches, 'XY')})\n  -- Edge partial match.\n  patches = dmp.patch_make('y', 'y123')\n  assertEquivalent({'x123', {true}}, {dmp.patch_apply(patches, 'x')})\nend\n\nfunction runTests()\n  local passed = 0\n  local failed = 0\n  for name, func in pairs(_G) do\n    if (type(func) == 'function') and tostring(name):match(\"^test\") then\n      local success, message = pcall(func)\n      if success then\n        print(name .. ' Ok.')\n        passed = passed + 1\n      else\n        print('** ' .. name .. ' FAILED: ' .. tostring(message))\n        failed = failed + 1\n      end\n    end\n  end\n  print('Tests passed: ' .. passed)\n  print('Tests failed: ' .. failed)\n  if failed ~= 0 then\n    os.exit(1)\n  end\nend\n\nrunTests()\n"
  },
  {
    "path": "lua/tests/speedtest.lua",
    "content": "--[[\nCopyright 2010 Google Inc.\nAll Rights Reserved.\n\nDiff Speed Test\n\nfraser@google.com\n--]]\n\npackage.path = package.path .. ';../?.lua'\nlocal dmp = require 'diff_match_patch'\n\nfunction main()\n  text1 = readlines('speedtest1.txt')\n  text2 = readlines('speedtest2.txt')\n\n  dmp.settings{ Diff_Timeout=0 }\n\n  -- Execute one reverse diff as a warmup.\n  dmp.diff_main(text2, text1, false)\n  collectgarbage('collect')\n\n  start_time = os.clock()\n  dmp.diff_main(text1, text2, false)\n  end_time = os.clock()\n  print('Elapsed time: ' .. (end_time - start_time))\nend\n\nfunction readlines(filename)\n  f = io.open(filename, 'rb')\n  text = f:read('*a')\n  f:close()\n  return text\nend\n\nmain()\n"
  },
  {
    "path": "lua/tests/speedtest1.txt",
    "content": "This is a '''list of newspapers published by [[Journal Register Company]]'''.\n\nThe company owns daily and weekly newspapers, other print media properties and newspaper-affiliated local Websites in the [[U.S.]] states of [[Connecticut]], [[Michigan]], [[New York]], [[Ohio]] and [[Pennsylvania]], organized in six geographic \"clusters\":<ref>[http://www.journalregister.com/newspapers.html Journal Register Company: Our Newspapers], accessed February 10, 2008.</ref>\n\n== Capital-Saratoga ==\nThree dailies, associated weeklies and [[pennysaver]]s in greater [[Albany, New York]]; also [http://www.capitalcentral.com capitalcentral.com] and [http://www.jobsinnewyork.com JobsInNewYork.com].\n\n* ''The Oneida Daily Dispatch'' {{WS|oneidadispatch.com}} of [[Oneida, New York]]\n* ''[[The Record (Troy)|The Record]]'' {{WS|troyrecord.com}} of [[Troy, New York]]\n* ''[[The Saratogian]]'' {{WS|saratogian.com}} of [[Saratoga Springs, New York]]\n* Weeklies:\n** ''Community News'' {{WS|cnweekly.com}} weekly of [[Clifton Park, New York]]\n** ''Rome Observer'' of [[Rome, New York]]\n** ''Life & Times of Utica'' of [[Utica, New York]]\n\n== Connecticut ==\nFive dailies, associated weeklies and [[pennysaver]]s in the state of [[Connecticut]]; also [http://www.ctcentral.com CTcentral.com], [http://www.ctcarsandtrucks.com CTCarsAndTrucks.com] and [http://www.jobsinct.com JobsInCT.com].\n\n* ''The Middletown Press'' {{WS|middletownpress.com}} of [[Middletown, Connecticut|Middletown]]\n* ''[[New Haven Register]]'' {{WS|newhavenregister.com}} of [[New Haven, Connecticut|New Haven]]\n* ''The Register Citizen'' {{WS|registercitizen.com}} of [[Torrington, Connecticut|Torrington]]\n\n* [[New Haven Register#Competitors|Elm City Newspapers]] {{WS|ctcentral.com}}\n** ''The Advertiser'' of [[East Haven, Connecticut|East Haven]]\n** ''Hamden Chronicle'' of [[Hamden, Connecticut|Hamden]]\n** ''Milford Weekly'' of [[Milford, Connecticut|Milford]]\n** ''The Orange Bulletin'' of [[Orange, Connecticut|Orange]]\n** ''The Post'' of [[North Haven, Connecticut|North Haven]]\n** ''Shelton Weekly'' of [[Shelton, Connecticut|Shelton]]\n** ''The Stratford Bard'' of [[Stratford, Connecticut|Stratford]]\n** ''Wallingford Voice'' of [[Wallingford, Connecticut|Wallingford]]\n** ''West Haven News'' of [[West Haven, Connecticut|West Haven]]\n* Housatonic Publications \n** ''The New Milford Times'' {{WS|newmilfordtimes.com}} of [[New Milford, Connecticut|New Milford]]\n** ''The Brookfield Journal'' of [[Brookfield, Connecticut|Brookfield]]\n** ''The Kent Good Times Dispatch'' of [[Kent, Connecticut|Kent]]\n** ''The Bethel Beacon'' of [[Bethel, Connecticut|Bethel]]\n** ''The Litchfield Enquirer'' of [[Litchfield, Connecticut|Litchfield]]\n** ''Litchfield County Times'' of [[Litchfield, Connecticut|Litchfield]]\n* Imprint Newspapers {{WS|imprintnewspapers.com}}\n** ''West Hartford News'' of [[West Hartford, Connecticut|West Hartford]]\n** ''Windsor Journal'' of [[Windsor, Connecticut|Windsor]]\n** ''Windsor Locks Journal'' of [[Windsor Locks, Connecticut|Windsor Locks]]\n** ''Avon Post'' of [[Avon, Connecticut|Avon]]\n** ''Farmington Post'' of [[Farmington, Connecticut|Farmington]]\n** ''Simsbury Post'' of [[Simsbury, Connecticut|Simsbury]]\n** ''Tri-Town Post'' of [[Burlington, Connecticut|Burlington]], [[Canton, Connecticut|Canton]] and [[Harwinton, Connecticut|Harwinton]]\n* Minuteman Publications\n** ''[[Fairfield Minuteman]]'' of [[Fairfield, Connecticut|Fairfield]]\n** ''The Westport Minuteman'' {{WS|westportminuteman.com}} of [[Westport, Connecticut|Westport]]\n* Shoreline Newspapers weeklies:\n** ''Branford Review'' of [[Branford, Connecticut|Branford]]\n** ''Clinton Recorder'' of [[Clinton, Connecticut|Clinton]]\n** ''The Dolphin'' of [[Naval Submarine Base New London]] in [[New London, Connecticut|New London]]\n** ''Main Street News'' {{WS|ctmainstreetnews.com}} of [[Essex, Connecticut|Essex]]\n** ''Pictorial Gazette'' of [[Old Saybrook, Connecticut|Old Saybrook]]\n** ''Regional Express'' of [[Colchester, Connecticut|Colchester]]\n** ''Regional Standard'' of [[Colchester, Connecticut|Colchester]]\n** ''Shoreline Times'' {{WS|shorelinetimes.com}} of [[Guilford, Connecticut|Guilford]]\n** ''Shore View East'' of [[Madison, Connecticut|Madison]]\n** ''Shore View West'' of [[Guilford, Connecticut|Guilford]]\n* Other weeklies:\n** ''Registro'' {{WS|registroct.com}} of [[New Haven, Connecticut|New Haven]]\n** ''Thomaston Express'' {{WS|thomastownexpress.com}} of [[Thomaston, Connecticut|Thomaston]]\n** ''Foothills Traders'' {{WS|foothillstrader.com}} of Torrington, Bristol, Canton\n\n== Michigan ==\nFour dailies, associated weeklies and [[pennysaver]]s in the state of [[Michigan]]; also [http://www.micentralhomes.com MIcentralhomes.com] and [http://www.micentralautos.com MIcentralautos.com]\n* ''[[Oakland Press]]'' {{WS|theoaklandpress.com}} of [[Oakland, Michigan|Oakland]]\n* ''Daily Tribune'' {{WS|dailytribune.com}} of [[Royal Oak, Michigan|Royal Oak]]\n* ''Macomb Daily'' {{WS|macombdaily.com}} of [[Mt. Clemens, Michigan|Mt. Clemens]]\n* ''[[Morning Sun]]'' {{WS|themorningsun.com}} of  [[Mount Pleasant, Michigan|Mount Pleasant]]\n* Heritage Newspapers {{WS|heritage.com}}\n** ''Belleville View''\n** ''Ile Camera''\n** ''Monroe Guardian''\n** ''Ypsilanti Courier''\n** ''News-Herald''\n** ''Press & Guide''\n** ''Chelsea Standard & Dexter Leader''\n** ''Manchester Enterprise''\n** ''Milan News-Leader''\n** ''Saline Reporter''\n* Independent Newspapers {{WS|sourcenewspapers.com}}\n** ''Advisor''\n** ''Source''\n* Morning Star {{WS|morningstarpublishing.com}}\n** ''Alma Reminder''\n** ''Alpena Star''\n** ''Antrim County News''\n** ''Carson City Reminder''\n** ''The Leader & Kalkaskian''\n** ''Ogemaw/Oscoda County Star''\n** ''Petoskey/Charlevoix Star''\n** ''Presque Isle Star''\n** ''Preview Community Weekly''\n** ''Roscommon County Star''\n** ''St. Johns Reminder''\n** ''Straits Area Star''\n** ''The (Edmore) Advertiser'' \n* Voice Newspapers {{WS|voicenews.com}}\n** ''Armada Times''\n** ''Bay Voice''\n** ''Blue Water Voice''\n** ''Downriver Voice''\n** ''Macomb Township Voice''\n** ''North Macomb Voice''\n** ''Weekend Voice''\n** ''Suburban Lifestyles'' {{WS|suburbanlifestyles.com}}\n\n== Mid-Hudson ==\nOne daily, associated magazines in the [[Hudson River Valley]] of [[New York]]; also [http://www.midhudsoncentral.com MidHudsonCentral.com] and [http://www.jobsinnewyork.com JobsInNewYork.com].\n\n* ''[[Daily Freeman]]'' {{WS|dailyfreeman.com}} of [[Kingston, New York]]\n\n== Ohio ==\nTwo dailies, associated magazines and three shared Websites, all in the state of [[Ohio]]: [http://www.allaroundcleveland.com AllAroundCleveland.com], [http://www.allaroundclevelandcars.com AllAroundClevelandCars.com] and [http://www.allaroundclevelandjobs.com AllAroundClevelandJobs.com].\n\n* ''[[The News-Herald (Ohio)|The News-Herald]]'' {{WS|news-herald.com}} of [[Willoughby, Ohio|Willoughby]]\n* ''[[The Morning Journal]]'' {{WS|morningjournal.com}} of [[Lorain, Ohio|Lorain]]\n\n== Philadelphia area ==\nSeven dailies and associated weeklies and magazines in [[Pennsylvania]] and [[New Jersey]], and associated Websites: [http://www.allaroundphilly.com AllAroundPhilly.com], [http://www.jobsinnj.com JobsInNJ.com], [http://www.jobsinpa.com JobsInPA.com], and [http://www.phillycarsearch.com PhillyCarSearch.com].\n\n* ''The Daily Local'' {{WS|dailylocal.com}} of [[West Chester, Pennsylvania|West Chester]]\n* ''[[Delaware County Daily and Sunday Times]] {{WS|delcotimes.com}} of Primos\n* ''[[The Mercury (Pennsylvania)|The Mercury]]'' {{WS|pottstownmercury.com}} of [[Pottstown, Pennsylvania|Pottstown]]\n* ''The Phoenix'' {{WS|phoenixvillenews.com}} of [[Phoenixville, Pennsylvania|Phoenixville]]\n* ''[[The Reporter (Lansdale)|The Reporter]]'' {{WS|thereporteronline.com}} of [[Lansdale, Pennsylvania|Lansdale]]\n* ''The Times Herald'' {{WS|timesherald.com}} of [[Norristown, Pennsylvania|Norristown]]\n* ''[[The Trentonian]]'' {{WS|trentonian.com}} of [[Trenton, New Jersey]]\n\n* Weeklies\n** ''El Latino Expreso'' of [[Trenton, New Jersey]]\n** ''La Voz'' of [[Norristown, Pennsylvania]]\n** ''The Village News'' of [[Downingtown, Pennsylvania]]\n** ''The Times Record'' of [[Kennett Square, Pennsylvania]]\n** ''The Tri-County Record'' {{WS|tricountyrecord.com}} of [[Morgantown, Pennsylvania]]\n** ''News of Delaware County'' {{WS|newsofdelawarecounty.com}}of [[Havertown, Pennsylvania]]\n** ''Main Line Times'' {{WS|mainlinetimes.com}}of [[Ardmore, Pennsylvania]]\n** ''Penny Pincher'' of [[Pottstown, Pennsylvania]]\n** ''Town Talk'' {{WS|towntalknews.com}} of [[Ridley, Pennsylvania]]\n* Chesapeake Publishing {{WS|pa8newsgroup.com}} \n** ''Solanco Sun Ledger'' of [[Quarryville, Pennsylvania]]\n** ''Columbia Ledger'' of [[Columbia, Pennsylvania]]\n** ''Coatesville Ledger'' of [[Downingtown, Pennsylvania]]\n** ''Parkesburg Post Ledger'' of [[Quarryville, Pennsylvania]]\n** ''Downingtown Ledger'' of [[Downingtown, Pennsylvania]]\n** ''The Kennett Paper'' of [[Kennett Square, Pennsylvania]]\n** ''Avon Grove Sun'' of [[West Grove, Pennsylvania]]\n** ''Oxford Tribune'' of [[Oxford, Pennsylvania]]\n** ''Elizabethtown Chronicle'' of [[Elizabethtown, Pennsylvania]]\n** ''Donegal Ledger'' of [[Donegal, Pennsylvania]]\n** ''Chadds Ford Post'' of [[Chadds Ford, Pennsylvania]]\n** ''The Central Record'' of [[Medford, New Jersey]]\n** ''Maple Shade Progress'' of [[Maple Shade, New Jersey]]\n* Intercounty Newspapers {{WS|buckslocalnews.com}} \n** ''The Review'' of Roxborough, Pennsylvania\n** ''The Recorder'' of [[Conshohocken, Pennsylvania]]\n** ''The Leader'' of [[Mount Airy, Pennsylvania|Mount Airy]] and West Oak Lake, Pennsylvania\n** ''The Pennington Post'' of [[Pennington, New Jersey]]\n** ''The Bristol Pilot'' of [[Bristol, Pennsylvania]]\n** ''Yardley News'' of [[Yardley, Pennsylvania]]\n** ''New Hope Gazette'' of [[New Hope, Pennsylvania]]\n** ''Doylestown Patriot'' of [[Doylestown, Pennsylvania]]\n** ''Newtown Advance'' of [[Newtown, Pennsylvania]]\n** ''The Plain Dealer'' of [[Williamstown, New Jersey]]\n** ''News Report'' of [[Sewell, New Jersey]]\n** ''Record Breeze'' of [[Berlin, New Jersey]]\n** ''Newsweekly'' of [[Moorestown, New Jersey]]\n** ''Haddon Herald'' of [[Haddonfield, New Jersey]]\n** ''New Egypt Press'' of [[New Egypt, New Jersey]]\n** ''Community News'' of [[Pemberton, New Jersey]]\n** ''Plymouth Meeting Journal'' of [[Plymouth Meeting, Pennsylvania]]\n** ''Lafayette Hill Journal'' of [[Lafayette Hill, Pennsylvania]]\n* Montgomery Newspapers {{WS|montgomerynews.com}} \n** ''Ambler Gazette'' of [[Ambler, Pennsylvania]]\n** ''Central Bucks Life'' of [[Bucks County, Pennsylvania]]\n** ''The Colonial'' of [[Plymouth Meeting, Pennsylvania]]\n** ''Glenside News'' of [[Glenside, Pennsylvania]]\n** ''The Globe'' of [[Lower Moreland Township, Pennsylvania]]\n** ''Main Line Life'' of [[Ardmore, Pennsylvania]]\n** ''Montgomery Life'' of [[Fort Washington, Pennsylvania]]\n** ''North Penn Life'' of [[Lansdale, Pennsylvania]]\n** ''Perkasie News Herald'' of [[Perkasie, Pennsylvania]]\n** ''Public Spirit'' of [[Hatboro, Pennsylvania]]\n** ''Souderton Independent'' of [[Souderton, Pennsylvania]]\n** ''Springfield Sun'' of [[Springfield, Pennsylvania]]\n** ''Spring-Ford Reporter'' of [[Royersford, Pennsylvania]]\n** ''Times Chronicle'' of [[Jenkintown, Pennsylvania]]\n** ''Valley Item'' of [[Perkiomenville, Pennsylvania]]\n** ''Willow Grove Guide'' of [[Willow Grove, Pennsylvania]]\n* News Gleaner Publications (closed December 2008) {{WS|newsgleaner.com}} \n** ''Life Newspapers'' of [[Philadelphia, Pennsylvania]]\n* Suburban Publications\n** ''The Suburban & Wayne Times'' {{WS|waynesuburban.com}} of [[Wayne, Pennsylvania]]\n** ''The Suburban Advertiser'' of [[Exton, Pennsylvania]]\n** ''The King of Prussia Courier'' of [[King of Prussia, Pennsylvania]]\n* Press Newspapers {{WS|countypressonline.com}} \n** ''County Press'' of [[Newtown Square, Pennsylvania]]\n** ''Garnet Valley Press'' of [[Glen Mills, Pennsylvania]]\n** ''Haverford Press'' of [[Newtown Square, Pennsylvania]] (closed January 2009)\n** ''Hometown Press'' of [[Glen Mills, Pennsylvania]] (closed January 2009)\n** ''Media Press'' of [[Newtown Square, Pennsylvania]] (closed January 2009)\n** ''Springfield Press'' of [[Springfield, Pennsylvania]]\n* Berks-Mont Newspapers {{WS|berksmontnews.com}} \n** ''The Boyertown Area Times'' of [[Boyertown, Pennsylvania]]\n** ''The Kutztown Area Patriot'' of [[Kutztown, Pennsylvania]]\n** ''The Hamburg Area Item'' of [[Hamburg, Pennsylvania]]\n** ''The Southern Berks News'' of [[Exeter Township, Berks County, Pennsylvania]]\n** ''The Free Press'' of [[Quakertown, Pennsylvania]]\n** ''The Saucon News'' of [[Quakertown, Pennsylvania]]\n** ''Westside Weekly'' of [[Reading, Pennsylvania]]\n\n* Magazines\n** ''Bucks Co. Town & Country Living''\n** ''Chester Co. Town & Country Living''\n** ''Montomgery Co. Town & Country Living''\n** ''Garden State Town & Country Living''\n** ''Montgomery Homes''\n** ''Philadelphia Golfer''\n** ''Parents Express''\n** ''Art Matters''\n\n{{JRC}}\n\n==References==\n<references />\n\n[[Category:Journal Register publications|*]]\n"
  },
  {
    "path": "lua/tests/speedtest2.txt",
    "content": "This is a '''list of newspapers published by [[Journal Register Company]]'''.\n\nThe company owns daily and weekly newspapers, other print media properties and newspaper-affiliated local Websites in the [[U.S.]] states of [[Connecticut]], [[Michigan]], [[New York]], [[Ohio]], [[Pennsylvania]] and [[New Jersey]], organized in six geographic \"clusters\":<ref>[http://www.journalregister.com/publications.html Journal Register Company: Our Publications], accessed April 21, 2010.</ref>\n\n== Capital-Saratoga ==\nThree dailies, associated weeklies and [[pennysaver]]s in greater [[Albany, New York]]; also [http://www.capitalcentral.com capitalcentral.com] and [http://www.jobsinnewyork.com JobsInNewYork.com].\n\n* ''The Oneida Daily Dispatch'' {{WS|oneidadispatch.com}} of [[Oneida, New York]]\n* ''[[The Record (Troy)|The Record]]'' {{WS|troyrecord.com}} of [[Troy, New York]]\n* ''[[The Saratogian]]'' {{WS|saratogian.com}} of [[Saratoga Springs, New York]]\n* Weeklies:\n** ''Community News'' {{WS|cnweekly.com}} weekly of [[Clifton Park, New York]]\n** ''Rome Observer'' {{WS|romeobserver.com}} of [[Rome, New York]]\n** ''WG Life '' {{WS|saratogian.com/wglife/}} of [[Wilton, New York]]\n** ''Ballston Spa Life '' {{WS|saratogian.com/bspalife}} of [[Ballston Spa, New York]]\n** ''Greenbush Life'' {{WS|troyrecord.com/greenbush}} of [[Troy, New York]]\n** ''Latham Life'' {{WS|troyrecord.com/latham}} of [[Latham, New York]]\n** ''River Life'' {{WS|troyrecord.com/river}} of [[Troy, New York]]\n\n== Connecticut ==\nThree dailies, associated weeklies and [[pennysaver]]s in the state of [[Connecticut]]; also [http://www.ctcentral.com CTcentral.com], [http://www.ctcarsandtrucks.com CTCarsAndTrucks.com] and [http://www.jobsinct.com JobsInCT.com].\n\n* ''The Middletown Press'' {{WS|middletownpress.com}} of [[Middletown, Connecticut|Middletown]]\n* ''[[New Haven Register]]'' {{WS|newhavenregister.com}} of [[New Haven, Connecticut|New Haven]]\n* ''The Register Citizen'' {{WS|registercitizen.com}} of [[Torrington, Connecticut|Torrington]]\n\n* Housatonic Publications \n** ''The Housatonic Times'' {{WS|housatonictimes.com}} of [[New Milford, Connecticut|New Milford]]\n** ''Litchfield County Times'' {{WS|countytimes.com}} of [[Litchfield, Connecticut|Litchfield]]\n\n* Minuteman Publications\n** ''[[Fairfield Minuteman]]'' {{WS|fairfieldminuteman.com}}of [[Fairfield, Connecticut|Fairfield]]\n** ''The Westport Minuteman'' {{WS|westportminuteman.com}} of [[Westport, Connecticut|Westport]]\n\n* Shoreline Newspapers \n** ''The Dolphin'' {{WS|dolphin-news.com}} of [[Naval Submarine Base New London]] in [[New London, Connecticut|New London]]\n** ''Shoreline Times'' {{WS|shorelinetimes.com}} of [[Guilford, Connecticut|Guilford]]\n\n* Foothills Media Group {{WS|foothillsmediagroup.com}}\n** ''Thomaston Express'' {{WS|thomastonexpress.com}} of [[Thomaston, Connecticut|Thomaston]]\n** ''Good News About Torrington'' {{WS|goodnewsabouttorrington.com}} of [[Torrington, Connecticut|Torrington]]\n** ''Granby News'' {{WS|foothillsmediagroup.com/granby}} of [[Granby, Connecticut|Granby]]\n** ''Canton News'' {{WS|foothillsmediagroup.com/canton}} of [[Canton, Connecticut|Canton]]\n** ''Avon News'' {{WS|foothillsmediagroup.com/avon}} of [[Avon, Connecticut|Avon]]\n** ''Simsbury News'' {{WS|foothillsmediagroup.com/simsbury}} of [[Simsbury, Connecticut|Simsbury]]\n** ''Litchfield News'' {{WS|foothillsmediagroup.com/litchfield}} of [[Litchfield, Connecticut|Litchfield]]\n** ''Foothills Trader'' {{WS|foothillstrader.com}} of Torrington, Bristol, Canton\n\n* Other weeklies\n** ''The Milford-Orange Bulletin'' {{WS|ctbulletin.com}} of [[Orange, Connecticut|Orange]]\n** ''The Post-Chronicle'' {{WS|ctpostchronicle.com}} of [[North Haven, Connecticut|North Haven]]\n** ''West Hartford News'' {{WS|westhartfordnews.com}} of [[West Hartford, Connecticut|West Hartford]]\n\n* Magazines\n** ''The Connecticut Bride'' {{WS|connecticutmag.com}}\n** ''Connecticut Magazine'' {{WS|theconnecticutbride.com}}\n** ''Passport Magazine'' {{WS|passport-mag.com}}\n\n== Michigan ==\nFour dailies, associated weeklies and [[pennysaver]]s in the state of [[Michigan]]; also [http://www.micentralhomes.com MIcentralhomes.com] and [http://www.micentralautos.com MIcentralautos.com]\n* ''[[Oakland Press]]'' {{WS|theoaklandpress.com}} of [[Oakland, Michigan|Oakland]]\n* ''Daily Tribune'' {{WS|dailytribune.com}} of [[Royal Oak, Michigan|Royal Oak]]\n* ''Macomb Daily'' {{WS|macombdaily.com}} of [[Mt. Clemens, Michigan|Mt. Clemens]]\n* ''[[Morning Sun]]'' {{WS|themorningsun.com}} of  [[Mount Pleasant, Michigan|Mount Pleasant]]\n\n* Heritage Newspapers {{WS|heritage.com}}\n** ''Belleville View'' {{WS|bellevilleview.com}}\n** ''Ile Camera'' {{WS|thenewsherald.com/ile_camera}}\n** ''Monroe Guardian''  {{WS|monreguardian.com}}\n** ''Ypsilanti Courier'' {{WS|ypsilanticourier.com}}\n** ''News-Herald'' {{WS|thenewsherald.com}}\n** ''Press & Guide'' {{WS|pressandguide.com}}\n** ''Chelsea Standard & Dexter Leader'' {{WS|chelseastandard.com}}\n** ''Manchester Enterprise'' {{WS|manchesterguardian.com}}\n** ''Milan News-Leader'' {{WS|milannews.com}}\n** ''Saline Reporter'' {{WS|salinereporter.com}}\n* Independent Newspapers \n** ''Advisor'' {{WS|sourcenewspapers.com}}\n** ''Source'' {{WS|sourcenewspapers.com}}\n* Morning Star {{WS|morningstarpublishing.com}}\n** ''The Leader & Kalkaskian'' {{WS|leaderandkalkaskian.com}}\n** ''Grand Traverse Insider'' {{WS|grandtraverseinsider.com}}\n** ''Alma Reminder''\n** ''Alpena Star''\n** ''Ogemaw/Oscoda County Star''\n** ''Presque Isle Star''\n** ''St. Johns Reminder''\n\n* Voice Newspapers {{WS|voicenews.com}}\n** ''Armada Times''\n** ''Bay Voice''\n** ''Blue Water Voice''\n** ''Downriver Voice''\n** ''Macomb Township Voice''\n** ''North Macomb Voice''\n** ''Weekend Voice''\n\n== Mid-Hudson ==\nOne daily, associated magazines in the [[Hudson River Valley]] of [[New York]]; also [http://www.midhudsoncentral.com MidHudsonCentral.com] and [http://www.jobsinnewyork.com JobsInNewYork.com].\n\n* ''[[Daily Freeman]]'' {{WS|dailyfreeman.com}} of [[Kingston, New York]]\n* ''Las Noticias'' {{WS|lasnoticiasny.com}} of [[Kingston, New York]]\n\n== Ohio ==\nTwo dailies, associated magazines and three shared Websites, all in the state of [[Ohio]]: [http://www.allaroundcleveland.com AllAroundCleveland.com], [http://www.allaroundclevelandcars.com AllAroundClevelandCars.com] and [http://www.allaroundclevelandjobs.com AllAroundClevelandJobs.com].\n\n* ''[[The News-Herald (Ohio)|The News-Herald]]'' {{WS|news-herald.com}} of [[Willoughby, Ohio|Willoughby]]\n* ''[[The Morning Journal]]'' {{WS|morningjournal.com}} of [[Lorain, Ohio|Lorain]]\n* ''El Latino Expreso'' {{WS|lorainlatino.com}} of [[Lorain, Ohio|Lorain]]\n\n== Philadelphia area ==\nSeven dailies and associated weeklies and magazines in [[Pennsylvania]] and [[New Jersey]], and associated Websites: [http://www.allaroundphilly.com AllAroundPhilly.com], [http://www.jobsinnj.com JobsInNJ.com], [http://www.jobsinpa.com JobsInPA.com], and [http://www.phillycarsearch.com PhillyCarSearch.com].\n\n* ''[[The Daily Local News]]'' {{WS|dailylocal.com}} of [[West Chester, Pennsylvania|West Chester]]\n* ''[[Delaware County Daily and Sunday Times]] {{WS|delcotimes.com}} of Primos [[Upper Darby Township, Pennsylvania]]\n* ''[[The Mercury (Pennsylvania)|The Mercury]]'' {{WS|pottstownmercury.com}} of [[Pottstown, Pennsylvania|Pottstown]]\n* ''[[The Reporter (Lansdale)|The Reporter]]'' {{WS|thereporteronline.com}} of [[Lansdale, Pennsylvania|Lansdale]]\n* ''The Times Herald'' {{WS|timesherald.com}} of [[Norristown, Pennsylvania|Norristown]]\n* ''[[The Trentonian]]'' {{WS|trentonian.com}} of [[Trenton, New Jersey]]\n\n* Weeklies\n* ''The Phoenix'' {{WS|phoenixvillenews.com}} of [[Phoenixville, Pennsylvania]]\n** ''El Latino Expreso'' {{WS|njexpreso.com}} of [[Trenton, New Jersey]]\n** ''La Voz'' {{WS|lavozpa.com}} of [[Norristown, Pennsylvania]]\n** ''The Tri County Record'' {{WS|tricountyrecord.com}} of [[Morgantown, Pennsylvania]]\n** ''Penny Pincher'' {{WS|pennypincherpa.com}}of [[Pottstown, Pennsylvania]]\n\n* Chesapeake Publishing  {{WS|southernchestercountyweeklies.com}}\n** ''The Kennett Paper'' {{WS|kennettpaper.com}} of [[Kennett Square, Pennsylvania]]\n** ''Avon Grove Sun'' {{WS|avongrovesun.com}} of [[West Grove, Pennsylvania]]\n** ''The Central Record'' {{WS|medfordcentralrecord.com}} of [[Medford, New Jersey]]\n** ''Maple Shade Progress'' {{WS|mapleshadeprogress.com}} of [[Maple Shade, New Jersey]]\n\n* Intercounty Newspapers {{WS|buckslocalnews.com}} {{WS|southjerseylocalnews.com}} \n** ''The Pennington Post'' {{WS|penningtonpost.com}} of [[Pennington, New Jersey]]\n** ''The Bristol Pilot'' {{WS|bristolpilot.com}} of [[Bristol, Pennsylvania]]\n** ''Yardley News'' {{WS|yardleynews.com}} of [[Yardley, Pennsylvania]]\n** ''Advance of Bucks County'' {{WS|advanceofbucks.com}} of [[Newtown, Pennsylvania]]\n** ''Record Breeze'' {{WS|recordbreeze.com}} of [[Berlin, New Jersey]]\n** ''Community News'' {{WS|sjcommunitynews.com}} of [[Pemberton, New Jersey]]\n\n* Montgomery Newspapers {{WS|montgomerynews.com}} \n** ''Ambler Gazette'' {{WS|amblergazette.com}} of [[Ambler, Pennsylvania]]\n** ''The Colonial'' {{WS|colonialnews.com}} of [[Plymouth Meeting, Pennsylvania]]\n** ''Glenside News'' {{WS|glensidenews.com}} of [[Glenside, Pennsylvania]]\n** ''The Globe'' {{WS|globenewspaper.com}} of [[Lower Moreland Township, Pennsylvania]]\n** ''Montgomery Life'' {{WS|montgomerylife.com}} of [[Fort Washington, Pennsylvania]]\n** ''North Penn Life'' {{WS|northpennlife.com}} of [[Lansdale, Pennsylvania]]\n** ''Perkasie News Herald'' {{WS|perkasienewsherald.com}} of [[Perkasie, Pennsylvania]]\n** ''Public Spirit'' {{WS|thepublicspirit.com}} of [[Hatboro, Pennsylvania]]\n** ''Souderton Independent'' {{WS|soudertonindependent.com}} of [[Souderton, Pennsylvania]]\n** ''Springfield Sun'' {{WS|springfieldsun.com}} of [[Springfield, Pennsylvania]]\n** ''Spring-Ford Reporter'' {{WS|springfordreporter.com}} of [[Royersford, Pennsylvania]]\n** ''Times Chronicle'' {{WS|thetimeschronicle.com}} of [[Jenkintown, Pennsylvania]]\n** ''Valley Item'' {{WS|valleyitem.com}} of [[Perkiomenville, Pennsylvania]]\n** ''Willow Grove Guide'' {{WS|willowgroveguide.com}} of [[Willow Grove, Pennsylvania]]\n** ''The Review'' {{WS|roxreview.com}} of [[Roxborough, Philadelphia, Pennsylvania]]\n\n* Main Line Media News {{WS|mainlinemedianews.com}}\n** ''Main Line Times'' {{WS|mainlinetimes.com}} of [[Ardmore, Pennsylvania]]\n** ''Main Line Life'' {{WS|mainlinelife.com}} of [[Ardmore, Pennsylvania]]\n** ''The King of Prussia Courier'' {{WS|kingofprussiacourier.com}} of [[King of Prussia, Pennsylvania]]\n\n* Delaware County News Network {{WS|delconewsnetwork.com}} \n** ''News of Delaware County'' {{WS|newsofdelawarecounty.com}} of [[Havertown, Pennsylvania]]\n** ''County Press'' {{WS|countypressonline.com}} of [[Newtown Square, Pennsylvania]]\n** ''Garnet Valley Press'' {{WS|countypressonline.com}} of [[Glen Mills, Pennsylvania]]\n** ''Springfield Press'' {{WS|countypressonline.com}} of [[Springfield, Pennsylvania]]\n** ''Town Talk'' {{WS|towntalknews.com}} of [[Ridley, Pennsylvania]]\n\n* Berks-Mont Newspapers {{WS|berksmontnews.com}} \n** ''The Boyertown Area Times'' {{WS|berksmontnews.com/boyertown_area_times}} of [[Boyertown, Pennsylvania]]\n** ''The Kutztown Area Patriot'' {{WS|berksmontnews.com/kutztown_area_patriot}} of [[Kutztown, Pennsylvania]]\n** ''The Hamburg Area Item'' {{WS|berksmontnews.com/hamburg_area_item}} of [[Hamburg, Pennsylvania]]\n** ''The Southern Berks News'' {{WS|berksmontnews.com/southern_berks_news}} of [[Exeter Township, Berks County, Pennsylvania]]\n** ''Community Connection'' {{WS|berksmontnews.com/community_connection}} of [[Boyertown, Pennsylvania]]\n\n* Magazines\n** ''Bucks Co. Town & Country Living'' {{WS|buckscountymagazine.com}} \n** ''Parents Express'' {{WS|parents-express.com}} \n** ''Real Men, Rednecks'' {{WS|realmenredneck.com}} \n\n{{JRC}}\n\n==References==\n<references />\n\n[[Category:Journal Register publications|*]]\n"
  },
  {
    "path": "objectivec/DiffMatchPatch.h",
    "content": "/*\n * Diff Match and Patch\n * Copyright 2018 The diff-match-patch Authors.\n * https://github.com/google/diff-match-patch\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *   http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n *\n * Author: fraser@google.com (Neil Fraser)\n * ObjC port: jan@geheimwerk.de (Jan Weiß)\n */\n\n#import <Foundation/Foundation.h>\n\n/*\n * Functions for diff, match and patch.\n * Computes the difference between two texts to create a patch.\n * Applies the patch onto another text, allowing for errors.\n */\n\n/*\n * The data structure representing a diff is an NSMutableArray of Diff objects:\n * {Diff(Operation.DIFF_DELETE, \"Hello\"),\n *  Diff(Operation.DIFF_INSERT, \"Goodbye\"),\n *  Diff(Operation.DIFF_EQUAL, \" world.\")}\n * which means: delete \"Hello\", add \"Goodbye\" and keep \" world.\"\n */\n\ntypedef enum {\n  DIFF_DELETE = 1,\n  DIFF_INSERT = 2,\n  DIFF_EQUAL = 3\n} Operation;\n\n\n/*\n * Class representing one diff operation.\n */\n@interface Diff : NSObject <NSCopying> {\n  Operation operation;  // One of: DIFF_INSERT, DIFF_DELETE or DIFF_EQUAL.\n  NSString *text;      // The text associated with this diff operation.\n}\n\n@property (nonatomic, assign) Operation operation;\n@property (nonatomic, copy) NSString *text;\n\n+ (id)diffWithOperation:(Operation)anOperation andText:(NSString *)aText;\n\n- (id)initWithOperation:(Operation)anOperation andText:(NSString *)aText;\n\n@end\n\n/*\n * Class representing one patch operation.\n */\n@interface Patch : NSObject <NSCopying> {\n  NSMutableArray *diffs;\n  NSUInteger start1;\n  NSUInteger start2;\n  NSUInteger length1;\n  NSUInteger length2;\n}\n\n@property (nonatomic, retain) NSMutableArray *diffs;\n@property (nonatomic, assign) NSUInteger start1;\n@property (nonatomic, assign) NSUInteger start2;\n@property (nonatomic, assign) NSUInteger length1;\n@property (nonatomic, assign) NSUInteger length2;\n\n@end\n\n\n/*\n * Class containing the diff, match and patch methods.\n * Also Contains the behaviour settings.\n */\n@interface DiffMatchPatch : NSObject {\n  // Number of seconds to map a diff before giving up (0 for infinity).\n  NSTimeInterval Diff_Timeout;\n\n  // Cost of an empty edit operation in terms of edit characters.\n  NSUInteger Diff_EditCost;\n\n  // At what point is no match declared (0.0 = perfection, 1.0 = very loose).\n  double Match_Threshold;\n\n  // How far to search for a match (0 = exact location, 1000+ = broad match).\n  // A match this many characters away from the expected location will add\n  // 1.0 to the score (0.0 is a perfect match).\n  NSInteger Match_Distance;\n\n  // When deleting a large block of text (over ~64 characters), how close\n  // do the contents have to be to match the expected contents. (0.0 =\n  // perfection, 1.0 = very loose).  Note that Match_Threshold controls\n  // how closely the end points of a delete need to match.\n  float Patch_DeleteThreshold;\n\n  // Chunk size for context length.\n  uint16_t Patch_Margin;\n\n  // The number of bits in an int.\n  NSUInteger Match_MaxBits;\n}\n\n@property (nonatomic, assign) NSTimeInterval Diff_Timeout;\n@property (nonatomic, assign) NSUInteger Diff_EditCost;\n@property (nonatomic, assign) double Match_Threshold;\n@property (nonatomic, assign) NSInteger Match_Distance;\n@property (nonatomic, assign) float Patch_DeleteThreshold;\n@property (nonatomic, assign) uint16_t Patch_Margin;\n\n- (NSMutableArray *)diff_mainOfOldString:(NSString *)text1 andNewString:(NSString *)text2;\n- (NSMutableArray *)diff_mainOfOldString:(NSString *)text1 andNewString:(NSString *)text2 checkLines:(BOOL)checklines;\n- (NSUInteger)diff_commonPrefixOfFirstString:(NSString *)text1 andSecondString:(NSString *)text2;\n- (NSUInteger)diff_commonSuffixOfFirstString:(NSString *)text1 andSecondString:(NSString *)text2;\n- (void)diff_cleanupSemantic:(NSMutableArray *)diffs;\n- (void)diff_cleanupSemanticLossless:(NSMutableArray *)diffs;\n- (void)diff_cleanupEfficiency:(NSMutableArray *)diffs;\n- (void)diff_cleanupMerge:(NSMutableArray *)diffs;\n- (NSUInteger)diff_xIndexIn:(NSMutableArray *)diffs location:(NSUInteger) loc;\n- (NSString *)diff_prettyHtml:(NSMutableArray *)diffs;\n- (NSString *)diff_text1:(NSMutableArray *)diffs;\n- (NSString *)diff_text2:(NSMutableArray *)diffs;\n- (NSUInteger)diff_levenshtein:(NSMutableArray *)diffs;\n- (NSString *)diff_toDelta:(NSMutableArray *)diffs;\n- (NSMutableArray *)diff_fromDeltaWithText:(NSString *)text1 andDelta:(NSString *)delta error:(NSError **)error;\n\n- (NSUInteger)match_mainForText:(NSString *)text pattern:(NSString *)pattern near:(NSUInteger)loc;\n- (NSMutableDictionary *)match_alphabet:(NSString *)pattern;\n\n- (NSMutableArray *)patch_makeFromOldString:(NSString *)text1 andNewString:(NSString *)text2;\n- (NSMutableArray *)patch_makeFromDiffs:(NSMutableArray *)diffs;\n- (NSMutableArray *)patch_makeFromOldString:(NSString *)text1 newString:(NSString *)text2 diffs:(NSMutableArray *)diffs;\n- (NSMutableArray *)patch_makeFromOldString:(NSString *)text1 andDiffs:(NSMutableArray *)diffs;\n- (NSMutableArray *)patch_deepCopy:(NSArray *)patches; // Copy rule applies!\n- (NSArray *)patch_apply:(NSArray *)sourcePatches toString:(NSString *)text;\n- (NSString *)patch_addPadding:(NSMutableArray *)patches;\n- (void)patch_splitMax:(NSMutableArray *)patches;\n- (NSString *)patch_toText:(NSMutableArray *)patches;\n- (NSMutableArray *)patch_fromText:(NSString *)textline error:(NSError **)error;\n\n@end\n\n\n@interface DiffMatchPatch (PrivateMethods)\n\n- (NSMutableArray *)diff_mainOfOldString:(NSString *)text1 andNewString:(NSString *)text2 checkLines:(BOOL)checklines deadline:(NSTimeInterval)deadline;\n- (NSMutableArray *)diff_computeFromOldString:(NSString *)text1 andNewString:(NSString *)text2 checkLines:(BOOL)checklines deadline:(NSTimeInterval)deadline;\n- (NSMutableArray *)diff_lineModeFromOldString:(NSString *)text1 andNewString:(NSString *)text2 deadline:(NSTimeInterval)deadline;\n- (NSArray *)diff_linesToCharsForFirstString:(NSString *)text1 andSecondString:(NSString *)text2;\n- (void)diff_chars:(NSArray *)diffs toLines:(NSMutableArray *)lineArray;\n- (NSMutableArray *)diff_bisectOfOldString:(NSString *)text1 andNewString:(NSString *)text2 deadline:(NSTimeInterval)deadline;\n- (NSMutableArray *)diff_bisectSplitOfOldString:(NSString *)text1 andNewString:(NSString *)text2 x:(NSUInteger)x y:(NSUInteger)y deadline:(NSTimeInterval)deadline;\n- (NSUInteger)diff_commonOverlapOfFirstString:(NSString *)text1 andSecondString:(NSString *)text2;\n- (NSArray *)diff_halfMatchOfFirstString:(NSString *)text1 andSecondString:(NSString *)text2;\n- (NSArray *)diff_halfMatchIOfLongString:(NSString *)longtext andShortString:(NSString *)shorttext;\n- (NSInteger)diff_cleanupSemanticScoreOfFirstString:(NSString *)one andSecondString:(NSString *)two;\n\n- (NSUInteger)match_bitapOfText:(NSString *)text andPattern:(NSString *)pattern near:(NSUInteger)loc;\n- (double)match_bitapScoreForErrorCount:(NSUInteger)e location:(NSUInteger)x near:(NSUInteger)loc pattern:(NSString *)pattern;\n\n- (void)patch_addContextToPatch:(Patch *)patch sourceText:(NSString *)text;\n\n@end\n"
  },
  {
    "path": "objectivec/DiffMatchPatch.m",
    "content": "/*\n * Diff Match and Patch\n * Copyright 2018 The diff-match-patch Authors.\n * https://github.com/google/diff-match-patch\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *   http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n *\n * Author: fraser@google.com (Neil Fraser)\n * ObjC port: jan@geheimwerk.de (Jan Weiß)\n */\n\n#import \"DiffMatchPatch.h\"\n\n#import \"NSString+JavaSubstring.h\"\n#import \"NSString+UriCompatibility.h\"\n#import \"NSMutableDictionary+DMPExtensions.h\"\n#import \"DiffMatchPatchCFUtilities.h\"\n\n\n#if !defined(MAX_OF_CONST_AND_DIFF)\n  // Determines the maximum of two expressions:\n  // The first is a constant (first parameter) while the second expression is\n  // the difference between the second and third parameter.  The way this is\n  // calculated prevents integer overflow in the result of the difference.\n  #define MAX_OF_CONST_AND_DIFF(A,B,C)  ((B) <= (C) ? (A) : (B) - (C) + (A))\n#endif\n\n\n// JavaScript-style splice function\nvoid splice(NSMutableArray *input, NSUInteger start, NSUInteger count, NSArray *objects);\n\n/* NSMutableArray * */ void splice(NSMutableArray *input, NSUInteger start, NSUInteger count, NSArray *objects) {\n  NSRange deletionRange = NSMakeRange(start, count);\n  if (objects == nil) {\n    [input removeObjectsInRange:deletionRange];\n  } else {\n    [input replaceObjectsInRange:deletionRange withObjectsFromArray:objects];\n  }\n}\n\n@implementation Diff\n\n@synthesize operation;\n@synthesize text;\n\n/**\n * Constructor.  Initializes the diff with the provided values.\n * @param anOperation One of DIFF_INSERT, DIFF_DELETE or DIFF_EQUAL.\n * @param aText The text being applied.\n */\n+ (id)diffWithOperation:(Operation)anOperation\n                andText:(NSString *)aText;\n{\n  return [[[self alloc] initWithOperation:anOperation andText:aText] autorelease];\n}\n\n- (id)initWithOperation:(Operation)anOperation\n                andText:(NSString *)aText;\n{\n  self = [super init];\n  if (self) {\n    self.operation = anOperation;\n    self.text = aText;\n  }\n  return self;\n\n}\n\n- (void)dealloc\n{\n  self.text = nil;\n\n  [super dealloc];\n}\n\n- (id)copyWithZone:(NSZone *)zone\n{\n  id newDiff = [[[self class] allocWithZone:zone]\n                initWithOperation:self.operation\n                andText:self.text];\n\n  return newDiff;\n}\n\n\n/**\n * Display a human-readable version of this Diff.\n * @return text version.\n */\n- (NSString *)description\n{\n  NSString *prettyText = [self.text stringByReplacingOccurrencesOfString:@\"\\n\" withString:@\"\\u00b6\"];\n  NSString *operationName = nil;\n  switch (self.operation) {\n    case DIFF_DELETE:\n      operationName = @\"DIFF_DELETE\";\n      break;\n    case DIFF_INSERT:\n      operationName = @\"DIFF_INSERT\";\n      break;\n    case DIFF_EQUAL:\n      operationName = @\"DIFF_EQUAL\";\n      break;\n    default:\n      break;\n  }\n\n  return [NSString stringWithFormat:@\"Diff(%@,\\\"%@\\\")\", operationName, prettyText];\n}\n\n/**\n * Is this Diff equivalent to another Diff?\n * @param obj Another Diff to compare against.\n * @return YES or NO.\n */\n- (BOOL)isEqual:(id)obj\n{\n  // If parameter is nil return NO.\n  if (obj == nil) {\n    return NO;\n  }\n\n  // If parameter cannot be cast to Diff return NO.\n  if (![obj isKindOfClass:[Diff class]]) {\n    return NO;\n  }\n\n  // Return YES if the fields match.\n  Diff *p = (Diff *)obj;\n  return p.operation == self.operation && [p.text isEqualToString:self.text];\n}\n\n- (BOOL)isEqualToDiff:(Diff *)obj\n{\n  // If parameter is nil return NO.\n  if (obj == nil) {\n    return NO;\n  }\n\n  // Return YES if the fields match.\n  return obj.operation == self.operation && [obj.text isEqualToString:self.text];\n}\n\n- (NSUInteger)hash\n{\n  return ([text hash] ^ (NSUInteger)operation);\n}\n\n@end\n\n\n@implementation Patch\n\n@synthesize diffs;\n@synthesize start1;\n@synthesize start2;\n@synthesize length1;\n@synthesize length2;\n\n- (id)init\n{\n  self = [super init];\n\n  if (self) {\n    self.diffs = [NSMutableArray array];\n  }\n\n  return self;\n}\n\n- (void)dealloc\n{\n  self.diffs = nil;\n\n  [super dealloc];\n}\n\n- (id)copyWithZone:(NSZone *)zone\n{\n  Patch *newPatch = [[[self class] allocWithZone:zone] init];\n\n  newPatch.diffs = [[NSMutableArray alloc] initWithArray:self.diffs copyItems:YES];\n  newPatch.start1 = self.start1;\n  newPatch.start2 = self.start2;\n  newPatch.length1 = self.length1;\n  newPatch.length2 = self.length2;\n\n  return newPatch;\n}\n\n\n/**\n * Emulate GNU diff's format.\n * Header: @@ -382,8 +481,9 @@\n * Indices are printed as 1-based, not 0-based.\n * @return The GNU diff NSString.\n */\n- (NSString *)description\n{\n  NSString *coords1;\n  NSString *coords2;\n\n  if (self.length1 == 0) {\n    coords1 = [NSString stringWithFormat:@\"%lu,0\",\n               (unsigned long)self.start1];\n  } else if (self.length1 == 1) {\n    coords1 = [NSString stringWithFormat:@\"%lu\",\n               (unsigned long)self.start1 + 1];\n  } else {\n    coords1 = [NSString stringWithFormat:@\"%lu,%lu\",\n               (unsigned long)self.start1 + 1, (unsigned long)self.length1];\n  }\n  if (self.length2 == 0) {\n    coords2 = [NSString stringWithFormat:@\"%lu,0\",\n               (unsigned long)self.start2];\n  } else if (self.length2 == 1) {\n    coords2 = [NSString stringWithFormat:@\"%lu\",\n               (unsigned long)self.start2 + 1];\n  } else {\n    coords2 = [NSString stringWithFormat:@\"%lu,%lu\",\n               (unsigned long)self.start2 + 1, (unsigned long)self.length2];\n  }\n\n  NSMutableString *text = [NSMutableString stringWithFormat:@\"@@ -%@ +%@ @@\\n\",\n                           coords1, coords2];\n  // Escape the body of the patch with %xx notation.\n  for (Diff *aDiff in self.diffs) {\n    switch (aDiff.operation) {\n      case DIFF_INSERT:\n        [text appendString:@\"+\"];\n        break;\n      case DIFF_DELETE:\n        [text appendString:@\"-\"];\n        break;\n      case DIFF_EQUAL:\n        [text appendString:@\" \"];\n        break;\n    }\n\n    [text appendString:[aDiff.text diff_stringByAddingPercentEscapesForEncodeUriCompatibility]];\n    [text appendString:@\"\\n\"];\n  }\n\n  return text;\n}\n\n@end\n\n\n@implementation DiffMatchPatch\n\n@synthesize Diff_Timeout;\n@synthesize Diff_EditCost;\n@synthesize Match_Threshold;\n@synthesize Match_Distance;\n@synthesize Patch_DeleteThreshold;\n@synthesize Patch_Margin;\n\n- (id)init\n{\n  self = [super init];\n\n  if (self) {\n    Diff_Timeout = 1.0f;\n    Diff_EditCost = 4;\n    Match_Threshold = 0.5f;\n    Match_Distance = 1000;\n    Patch_DeleteThreshold = 0.5f;\n    Patch_Margin = 4;\n\n    Match_MaxBits = 32;\n  }\n\n  return self;\n}\n\n- (void)dealloc\n{\n  [super dealloc];\n}\n\n\n#pragma mark Diff Functions\n//  DIFF FUNCTIONS\n\n\n/**\n * Find the differences between two texts.\n * Run a faster, slightly less optimal diff.\n * This method allows the 'checklines' of diff_main() to be optional.\n * Most of the time checklines is wanted, so default to YES.\n * @param text1 Old NSString to be diffed.\n * @param text2 New NSString to be diffed.\n * @return NSMutableArray of Diff objects.\n */\n- (NSMutableArray *)diff_mainOfOldString:(NSString *)text1\n                            andNewString:(NSString *)text2;\n{\n  return [self diff_mainOfOldString:text1 andNewString:text2 checkLines:YES];\n}\n\n/**\n * Find the differences between two texts.\n * @param text1 Old string to be diffed.\n * @param text2 New string to be diffed.\n * @param checklines Speedup flag.  If NO, then don't run a\n *     line-level diff first to identify the changed areas.\n *     If YES, then run a faster slightly less optimal diff.\n * @return NSMutableArray of Diff objects.\n */\n- (NSMutableArray *)diff_mainOfOldString:(NSString *)text1\n                            andNewString:(NSString *)text2\n                              checkLines:(BOOL)checklines;\n{\n  // Set a deadline by which time the diff must be complete.\n  NSTimeInterval deadline;\n  if (Diff_Timeout <= 0) {\n    deadline = [[NSDate distantFuture] timeIntervalSinceReferenceDate];\n  } else {\n    deadline = [[NSDate dateWithTimeIntervalSinceNow:Diff_Timeout] timeIntervalSinceReferenceDate];\n  }\n  return [self diff_mainOfOldString:text1 andNewString:text2 checkLines:YES deadline:deadline];\n}\n\n/**\n * Find the differences between two texts.  Simplifies the problem by\n * stripping any common prefix or suffix off the texts before diffing.\n * @param text1 Old NSString to be diffed.\n * @param text2 New NSString to be diffed.\n * @param checklines Speedup flag.  If NO, then don't run a\n *     line-level diff first to identify the changed areas.\n *     If YES, then run a faster slightly less optimal diff\n * @param deadline Time when the diff should be complete by.  Used\n *     internally for recursive calls.  Users should set DiffTimeout\n *     instead.\n * @return NSMutableArray of Diff objects.\n */\n- (NSMutableArray *)diff_mainOfOldString:(NSString *)text1\n                            andNewString:(NSString *)text2\n                              checkLines:(BOOL)checklines\n                                deadline:(NSTimeInterval)deadline;\n{\n  // Check for null inputs.\n  if (text1 == nil || text2 == nil) {\n    NSLog(@\"Null inputs. (diff_main)\");\n    return nil;\n  }\n\n  // Check for equality (speedup).\n  NSMutableArray *diffs;\n  if ([text1 isEqualToString:text2]) {\n    diffs = [NSMutableArray array];\n    if (text1.length != 0) {\n      [diffs addObject:[Diff diffWithOperation:DIFF_EQUAL andText:text1]];\n    }\n    return diffs;\n  }\n\n  // Trim off common prefix (speedup).\n  NSUInteger commonlength = (NSUInteger)diff_commonPrefix((CFStringRef)text1, (CFStringRef)text2);\n  NSString *commonprefix = [text1 substringWithRange:NSMakeRange(0, commonlength)];\n  text1 = [text1 substringFromIndex:commonlength];\n  text2 = [text2 substringFromIndex:commonlength];\n\n  // Trim off common suffix (speedup).\n  commonlength = (NSUInteger)diff_commonSuffix((CFStringRef)text1, (CFStringRef)text2);\n  NSString *commonsuffix = [text1 substringFromIndex:text1.length - commonlength];\n  text1 = [text1 substringWithRange:NSMakeRange(0, text1.length - commonlength)];\n  text2 = [text2 substringWithRange:NSMakeRange(0, text2.length - commonlength)];\n\n  // Compute the diff on the middle block.\n  diffs = [self diff_computeFromOldString:text1 andNewString:text2 checkLines:checklines deadline:deadline];\n\n  // Restore the prefix and suffix.\n  if (commonprefix.length != 0) {\n    [diffs insertObject:[Diff diffWithOperation:DIFF_EQUAL andText:commonprefix] atIndex:0];\n  }\n  if (commonsuffix.length != 0) {\n    [diffs addObject:[Diff diffWithOperation:DIFF_EQUAL andText:commonsuffix]];\n  }\n\n  [self diff_cleanupMerge:diffs];\n  return diffs;\n}\n\n/**\n * Determine the common prefix of two strings.\n * @param text1 First string.\n * @param text2 Second string.\n * @return The number of characters common to the start of each string.\n */\n- (NSUInteger)diff_commonPrefixOfFirstString:(NSString *)text1\n                             andSecondString:(NSString *)text2;\n{\n  return (NSUInteger)diff_commonPrefix((CFStringRef)text1, (CFStringRef)text2);\n}\n\n/**\n * Determine the common suffix of two strings.\n * @param text1 First string.\n * @param text2 Second string.\n * @return The number of characters common to the end of each string.\n */\n- (NSUInteger)diff_commonSuffixOfFirstString:(NSString *)text1\n                             andSecondString:(NSString *)text2;\n{\n  return (NSUInteger)diff_commonSuffix((CFStringRef)text1, (CFStringRef)text2);\n}\n\n/**\n * Determine if the suffix of one CFStringRef is the prefix of another.\n * @param text1 First NSString.\n * @param text2 Second NSString.\n * @return The number of characters common to the end of the first\n *     CFStringRef and the start of the second CFStringRef.\n */\n- (NSUInteger)diff_commonOverlapOfFirstString:(NSString *)text1\n                              andSecondString:(NSString *)text2;\n{\n  return (NSUInteger)diff_commonOverlap((CFStringRef)text1, (CFStringRef)text2);\n}\n\n/**\n * Do the two texts share a substring which is at least half the length of\n * the longer text?\n * This speedup can produce non-minimal diffs.\n * @param text1 First NSString.\n * @param text2 Second NSString.\n * @return Five element String array, containing the prefix of text1, the\n *     suffix of text1, the prefix of text2, the suffix of text2 and the\n *     common middle.   Or NULL if there was no match.\n */\n- (NSArray *)diff_halfMatchOfFirstString:(NSString *)text1\n                         andSecondString:(NSString *)text2;\n{\n  return [(NSArray *)diff_halfMatchCreate((CFStringRef)text1, (CFStringRef)text2, Diff_Timeout) autorelease];\n}\n\n/**\n * Does a substring of shorttext exist within longtext such that the\n * substring is at least half the length of longtext?\n * @param longtext Longer NSString.\n * @param shorttext Shorter NSString.\n * @param index Start index of quarter length substring within longtext.\n * @return Five element NSArray, containing the prefix of longtext, the\n *     suffix of longtext, the prefix of shorttext, the suffix of shorttext\n *     and the common middle.  Or nil if there was no match.\n */\n- (NSArray *)diff_halfMatchIOfLongString:(NSString *)longtext\n                          andShortString:(NSString *)shorttext\n                                   index:(NSInteger)index;\n{\n  return [((NSArray *)diff_halfMatchICreate((CFStringRef)longtext, (CFStringRef)shorttext, (CFIndex)index)) autorelease];\n}\n\n/**\n * Find the differences between two texts.  Assumes that the texts do not\n * have any common prefix or suffix.\n * @param text1 Old NSString to be diffed.\n * @param text2 New NSString to be diffed.\n * @param checklines Speedup flag.  If NO, then don't run a\n *     line-level diff first to identify the changed areas.\n *     If YES, then run a faster slightly less optimal diff.\n * @param deadline Time the diff should be complete by.\n * @return NSMutableArray of Diff objects.\n */\n- (NSMutableArray *)diff_computeFromOldString:(NSString *)text1\n                                 andNewString:(NSString *)text2\n                                   checkLines:(BOOL)checklines\n                                     deadline:(NSTimeInterval)deadline;\n{\n  NSMutableArray *diffs = [NSMutableArray array];\n\n  if (text1.length == 0) {\n    // Just add some text (speedup).\n    [diffs addObject:[Diff diffWithOperation:DIFF_INSERT andText:text2]];\n    return diffs;\n  }\n\n  if (text2.length == 0) {\n    // Just delete some text (speedup).\n    [diffs addObject:[Diff diffWithOperation:DIFF_DELETE andText:text1]];\n    return diffs;\n  }\n\n  NSString *longtext = text1.length > text2.length ? text1 : text2;\n  NSString *shorttext = text1.length > text2.length ? text2 : text1;\n  NSUInteger i = [longtext rangeOfString:shorttext].location;\n  if (i != NSNotFound) {\n    // Shorter text is inside the longer text (speedup).\n    Operation op = (text1.length > text2.length) ? DIFF_DELETE : DIFF_INSERT;\n    [diffs addObject:[Diff diffWithOperation:op andText:[longtext substringWithRange:NSMakeRange(0, i)]]];\n    [diffs addObject:[Diff diffWithOperation:DIFF_EQUAL andText:shorttext]];\n    [diffs addObject:[Diff diffWithOperation:op andText:[longtext substringFromIndex:(i + shorttext.length)]]];\n    return diffs;\n  }\n\n  if (shorttext.length == 1) {\n    // Single character string.\n    // After the previous speedup, the character can't be an equality.\n    [diffs addObject:[Diff diffWithOperation:DIFF_DELETE andText:text1]];\n    [diffs addObject:[Diff diffWithOperation:DIFF_INSERT andText:text2]];\n    return diffs;\n  }\n\n  // Check to see if the problem can be split in two.\n  NSArray *hm = [(NSArray *)diff_halfMatchCreate((CFStringRef)text1, (CFStringRef)text2, Diff_Timeout) autorelease];\n  if (hm != nil) {\n    NSAutoreleasePool *splitPool = [NSAutoreleasePool new];\n    // A half-match was found, sort out the return data.\n    NSString *text1_a = [hm objectAtIndex:0];\n    NSString *text1_b = [hm objectAtIndex:1];\n    NSString *text2_a = [hm objectAtIndex:2];\n    NSString *text2_b = [hm objectAtIndex:3];\n    NSString *mid_common = [hm objectAtIndex:4];\n    // Send both pairs off for separate processing.\n    NSMutableArray *diffs_a = [self diff_mainOfOldString:text1_a andNewString:text2_a checkLines:checklines deadline:deadline];\n    NSMutableArray *diffs_b = [self diff_mainOfOldString:text1_b andNewString:text2_b checkLines:checklines deadline:deadline];\n    // Merge the results.\n    diffs = [diffs_a retain];\n    [diffs addObject:[Diff diffWithOperation:DIFF_EQUAL andText:mid_common]];\n    [diffs addObjectsFromArray:diffs_b];\n    [splitPool drain];\n    return [diffs autorelease];\n  }\n\n  if (checklines && text1.length > 100 && text2.length > 100) {\n    return [self diff_lineModeFromOldString:text1 andNewString:text2 deadline:deadline];\n  }\n\n  NSAutoreleasePool *bisectPool = [NSAutoreleasePool new];\n  diffs = [self diff_bisectOfOldString:text1 andNewString:text2 deadline:deadline];\n  [diffs retain];\n  [bisectPool drain];\n\n  return [diffs autorelease];\n}\n\n/**\n * Do a quick line-level diff on both strings, then rediff the parts for\n * greater accuracy.\n * This speedup can produce non-minimal diffs.\n * @param text1 Old NSString to be diffed.\n * @param text2 New NSString to be diffed.\n * @param deadline Time when the diff should be complete by.\n * @return NSMutableArray of Diff objects.\n */\n- (NSMutableArray *)diff_lineModeFromOldString:(NSString *)text1\n                                  andNewString:(NSString *)text2\n                                      deadline:(NSTimeInterval)deadline;\n{\n  // Scan the text on a line-by-line basis first.\n  NSArray *a = [self diff_linesToCharsForFirstString:text1 andSecondString:text2];\n  text1 = (NSString *)[a objectAtIndex:0];\n  text2 = (NSString *)[a objectAtIndex:1];\n  NSMutableArray *linearray = (NSMutableArray *)[a objectAtIndex:2];\n\n  NSAutoreleasePool *recursePool = [NSAutoreleasePool new];\n  NSMutableArray *diffs = [self diff_mainOfOldString:text1 andNewString:text2 checkLines:NO deadline:deadline];\n  [diffs retain];\n  [recursePool drain];\n\n  [diffs autorelease];\n\n  // Convert the diff back to original text.\n  [self diff_chars:diffs toLines:linearray];\n  // Eliminate freak matches (e.g. blank lines)\n  [self diff_cleanupSemantic:diffs];\n\n  // Rediff any Replacement blocks, this time character-by-character.\n  // Add a dummy entry at the end.\n  [diffs addObject:[Diff diffWithOperation:DIFF_EQUAL andText:@\"\"]];\n  NSUInteger thisPointer = 0;\n  NSUInteger count_delete = 0;\n  NSUInteger count_insert = 0;\n  NSString *text_delete = @\"\";\n  NSString *text_insert = @\"\";\n  while (thisPointer < diffs.count) {\n    switch (((Diff *)[diffs objectAtIndex:thisPointer]).operation) {\n      case DIFF_INSERT:\n        count_insert++;\n        text_insert = [text_insert stringByAppendingString:((Diff *)[diffs objectAtIndex:thisPointer]).text];\n        break;\n      case DIFF_DELETE:\n        count_delete++;\n        text_delete = [text_delete stringByAppendingString:((Diff *)[diffs objectAtIndex:thisPointer]).text];\n        break;\n      case DIFF_EQUAL:\n        // Upon reaching an equality, check for prior redundancies.\n        if (count_delete >= 1 && count_insert >= 1) {\n          // Delete the offending records and add the merged ones.\n          NSMutableArray *subDiff = [self diff_mainOfOldString:text_delete andNewString:text_insert checkLines:NO deadline:deadline];\n          [diffs removeObjectsInRange:NSMakeRange(thisPointer - count_delete - count_insert,\n                                                  count_delete + count_insert)];\n          thisPointer = thisPointer - count_delete - count_insert;\n          NSUInteger insertionIndex = thisPointer;\n          for (Diff *thisDiff in subDiff) {\n            [diffs insertObject:thisDiff atIndex:insertionIndex];\n            insertionIndex++;\n          }\n          thisPointer = thisPointer + subDiff.count;\n        }\n        count_insert = 0;\n        count_delete = 0;\n        text_delete = @\"\";\n        text_insert = @\"\";\n        break;\n    }\n    thisPointer++;\n  }\n  [diffs removeLastObject];  // Remove the dummy entry at the end.\n\n  return diffs;\n}\n\n/**\n * Find the 'middle snake' of a diff, split the problem in two\n * and return the recursively constructed diff.\n * See Myers 1986 paper: An O(ND) Difference Algorithm and Its Variations.\n * @param _text1 Old string to be diffed.\n * @param _text2 New string to be diffed.\n * @param deadline Time at which to bail if not yet complete.\n * @return NSMutableArray of Diff objects.\n */\n- (NSMutableArray *)diff_bisectOfOldString:(NSString *)_text1\n                              andNewString:(NSString *)_text2\n                                  deadline:(NSTimeInterval)deadline;\n{\n#define text1CharacterAtIndex(A)  text1_chars[(A)]\n#define text2CharacterAtIndex(A)  text2_chars[(A)]\n#define freeTextBuffers()  if (text1_buffer != NULL) free(text1_buffer);\\\n                           if (text2_buffer != NULL) free(text2_buffer);\n\n  CFStringRef text1 = (CFStringRef)_text1;\n  CFStringRef text2 = (CFStringRef)_text2;\n\n  // Cache the text lengths to prevent multiple calls.\n  CFIndex text1_length = CFStringGetLength(text1);\n  CFIndex text2_length = CFStringGetLength(text2);\n  CFIndex max_d = (text1_length + text2_length + 1) / 2;\n  CFIndex v_offset = max_d;\n  CFIndex v_length = 2 * max_d;\n  CFIndex v1[v_length];\n  CFIndex v2[v_length];\n  for (CFIndex x = 0; x < v_length; x++) {\n    v1[x] = -1;\n    v2[x] = -1;\n  }\n  v1[v_offset + 1] = 0;\n  v2[v_offset + 1] = 0;\n  CFIndex delta = text1_length - text2_length;\n\n  // Prepare access to chars arrays for text1 (massive speedup).\n  const UniChar *text1_chars;\n  UniChar *text1_buffer = NULL;\n  diff_CFStringPrepareUniCharBuffer(text1, &text1_chars, &text1_buffer, CFRangeMake(0, text1_length));\n\n  // Prepare access to chars arrays for text 2 (massive speedup).\n  const UniChar *text2_chars;\n  UniChar *text2_buffer = NULL;\n  diff_CFStringPrepareUniCharBuffer(text2, &text2_chars, &text2_buffer, CFRangeMake(0, text2_length));\n\n  // If the total number of characters is odd, then the front path will\n  // collide with the reverse path.\n  BOOL front = (delta % 2 != 0);\n  // Offsets for start and end of k loop.\n  // Prevents mapping of space beyond the grid.\n  CFIndex k1start = 0;\n  CFIndex k1end = 0;\n  CFIndex k2start = 0;\n  CFIndex k2end = 0;\n  NSMutableArray *diffs;\n  for (CFIndex d = 0; d < max_d; d++) {\n    // Bail out if deadline is reached.\n    if ([NSDate timeIntervalSinceReferenceDate] > deadline) {\n      break;\n    }\n\n    // Walk the front path one step.\n    for (CFIndex k1 = -d + k1start; k1 <= d - k1end; k1 += 2) {\n      CFIndex k1_offset = v_offset + k1;\n      CFIndex x1;\n      if (k1 == -d || (k1 != d && v1[k1_offset - 1] < v1[k1_offset + 1])) {\n        x1 = v1[k1_offset + 1];\n      } else {\n        x1 = v1[k1_offset - 1] + 1;\n      }\n      CFIndex y1 = x1 - k1;\n      while (x1 < text1_length && y1 < text2_length\n           && text1CharacterAtIndex(x1) == text2CharacterAtIndex(y1)) {\n        x1++;\n        y1++;\n      }\n      v1[k1_offset] = x1;\n      if (x1 > text1_length) {\n        // Ran off the right of the graph.\n        k1end += 2;\n      } else if (y1 > text2_length) {\n        // Ran off the bottom of the graph.\n        k1start += 2;\n      } else if (front) {\n        CFIndex k2_offset = v_offset + delta - k1;\n        if (k2_offset >= 0 && k2_offset < v_length && v2[k2_offset] != -1) {\n          // Mirror x2 onto top-left coordinate system.\n          CFIndex x2 = text1_length - v2[k2_offset];\n          if (x1 >= x2) {\n            freeTextBuffers();\n\n            // Overlap detected.\n            return [self diff_bisectSplitOfOldString:_text1\n                                        andNewString:_text2\n                                                   x:x1\n                                                   y:y1\n                                            deadline:deadline];\n          }\n        }\n      }\n    }\n\n    // Walk the reverse path one step.\n    for (CFIndex k2 = -d + k2start; k2 <= d - k2end; k2 += 2) {\n      CFIndex k2_offset = v_offset + k2;\n      CFIndex x2;\n      if (k2 == -d || (k2 != d && v2[k2_offset - 1] < v2[k2_offset + 1])) {\n        x2 = v2[k2_offset + 1];\n      } else {\n        x2 = v2[k2_offset - 1] + 1;\n      }\n      CFIndex y2 = x2 - k2;\n      while (x2 < text1_length && y2 < text2_length\n           && text1CharacterAtIndex(text1_length - x2 - 1)\n           == text2CharacterAtIndex(text2_length - y2 - 1)) {\n        x2++;\n        y2++;\n      }\n      v2[k2_offset] = x2;\n      if (x2 > text1_length) {\n        // Ran off the left of the graph.\n        k2end += 2;\n      } else if (y2 > text2_length) {\n        // Ran off the top of the graph.\n        k2start += 2;\n      } else if (!front) {\n        CFIndex k1_offset = v_offset + delta - k2;\n        if (k1_offset >= 0 && k1_offset < v_length && v1[k1_offset] != -1) {\n          CFIndex x1 = v1[k1_offset];\n          CFIndex y1 = v_offset + x1 - k1_offset;\n          // Mirror x2 onto top-left coordinate system.\n          x2 = text1_length - x2;\n          if (x1 >= x2) {\n            // Overlap detected.\n            freeTextBuffers();\n\n            return [self diff_bisectSplitOfOldString:_text1\n                                        andNewString:_text2\n                                                   x:x1\n                                                   y:y1\n                                            deadline:deadline];\n          }\n        }\n      }\n    }\n  }\n\n  freeTextBuffers();\n\n  // Diff took too long and hit the deadline or\n  // number of diffs equals number of characters, no commonality at all.\n  diffs = [NSMutableArray array];\n  [diffs addObject:[Diff diffWithOperation:DIFF_DELETE andText:_text1]];\n  [diffs addObject:[Diff diffWithOperation:DIFF_INSERT andText:_text2]];\n  return diffs;\n\n#undef freeTextBuffers\n#undef text1CharacterAtIndex\n#undef text2CharacterAtIndex\n}\n\n/**\n * Given the location of the 'middle snake', split the diff in two parts\n * and recurse.\n * @param text1 Old string to be diffed.\n * @param text2 New string to be diffed.\n * @param x Index of split point in text1.\n * @param y Index of split point in text2.\n * @param deadline Time at which to bail if not yet complete.\n * @return NSMutableArray of Diff objects.\n */\n- (NSMutableArray *)diff_bisectSplitOfOldString:(NSString *)text1\n                                   andNewString:(NSString *)text2\n                                              x:(NSUInteger)x\n                                              y:(NSUInteger)y\n                                       deadline:(NSTimeInterval)deadline;\n{\n  NSString *text1a = [text1 substringToIndex:x];\n  NSString *text2a = [text2 substringToIndex:y];\n  NSString *text1b = [text1 substringFromIndex:x];\n  NSString *text2b = [text2 substringFromIndex:y];\n\n  // Compute both diffs serially.\n  NSMutableArray *diffs = [self diff_mainOfOldString:text1a\n                                        andNewString:text2a\n                                          checkLines:NO\n                                            deadline:deadline];\n  NSMutableArray *diffsb = [self diff_mainOfOldString:text1b\n                                         andNewString:text2b\n                                           checkLines:NO\n                                             deadline:deadline];\n\n  [diffs addObjectsFromArray: diffsb];\n  return diffs;\n}\n\n/**\n * Split two texts into a list of strings.  Reduce the texts to a string of\n * hashes where each Unicode character represents one line.\n * @param text1 First NSString.\n * @param text2 Second NSString.\n * @return Three element NSArray, containing the encoded text1, the\n *     encoded text2 and the NSMutableArray of unique strings. The zeroth element\n *     of the NSArray of unique strings is intentionally blank.\n */\n- (NSArray *)diff_linesToCharsForFirstString:(NSString *)text1\n                             andSecondString:(NSString *)text2;\n{\n  NSMutableArray *lineArray = [NSMutableArray array]; // NSString objects\n  NSMutableDictionary *lineHash = [NSMutableDictionary dictionary]; // keys: NSString, values:NSNumber\n  // e.g. [lineArray objectAtIndex:4] == \"Hello\\n\"\n  // e.g. [lineHash objectForKey:\"Hello\\n\"] == 4\n\n  // \"\\x00\" is a valid character, but various debuggers don't like it.\n  // So we'll insert a junk entry to avoid generating a nil character.\n  [lineArray addObject:@\"\"];\n\n  // Allocate 2/3rds of the space for text1, the rest for text2.\n  NSString *chars1 = (NSString *)diff_linesToCharsMungeCFStringCreate((CFStringRef)text1,\n                                                                      (CFMutableArrayRef)lineArray,\n                                                                      (CFMutableDictionaryRef)lineHash,\n                                                                      40000);\n  NSString *chars2 = (NSString *)diff_linesToCharsMungeCFStringCreate((CFStringRef)text2,\n                                                                      (CFMutableArrayRef)lineArray,\n                                                                      (CFMutableDictionaryRef)lineHash,\n                                                                      65535);\n\n  NSArray *result = [NSArray arrayWithObjects:chars1, chars2, lineArray, nil];\n\n  [chars1 release];\n  [chars2 release];\n\n  return result;\n}\n\n/**\n * Rehydrate the text in a diff from an NSString of line hashes to real lines\n * of text.\n * @param diffs NSArray of Diff objects.\n * @param lineArray NSMutableArray of unique strings.\n */\n- (void)diff_chars:(NSArray *)diffs toLines:(NSMutableArray *)lineArray;\n{\n  NSMutableString *text;\n  NSUInteger lineHash;\n  for (Diff *diff in diffs) {\n    text = [NSMutableString string];\n    for (NSUInteger j = 0; j < [diff.text length]; j++) {\n      lineHash = (NSUInteger)[diff.text characterAtIndex:j];\n      [text appendString:[lineArray objectAtIndex:lineHash]];\n    }\n    diff.text = text;\n  }\n}\n\n/**\n * Reorder and merge like edit sections.  Merge equalities.\n * Any edit section can move as long as it doesn't cross an equality.\n * @param diffs NSMutableArray of Diff objects.\n */\n- (void)diff_cleanupMerge:(NSMutableArray *)diffs;\n{\n#define prevDiff ((Diff *)[diffs objectAtIndex:(thisPointer - 1)])\n#define thisDiff ((Diff *)[diffs objectAtIndex:thisPointer])\n#define nextDiff ((Diff *)[diffs objectAtIndex:(thisPointer + 1)])\n\n  if (diffs.count == 0) {\n    return;\n  }\n\n  // Add a dummy entry at the end.\n  [diffs addObject:[Diff diffWithOperation:DIFF_EQUAL andText:@\"\"]];\n  NSUInteger thisPointer = 0;\n  NSUInteger count_delete = 0;\n  NSUInteger count_insert = 0;\n  NSString *text_delete = @\"\";\n  NSString *text_insert = @\"\";\n  NSUInteger commonlength;\n  while (thisPointer < diffs.count) {\n    switch (thisDiff.operation) {\n      case DIFF_INSERT:\n        count_insert++;\n        text_insert = [text_insert stringByAppendingString:thisDiff.text];\n        thisPointer++;\n        break;\n      case DIFF_DELETE:\n        count_delete++;\n        text_delete = [text_delete stringByAppendingString:thisDiff.text];\n        thisPointer++;\n        break;\n      case DIFF_EQUAL:\n        // Upon reaching an equality, check for prior redundancies.\n        if (count_delete + count_insert > 1) {\n          if (count_delete != 0 && count_insert != 0) {\n            // Factor out any common prefixes.\n            commonlength = (NSUInteger)diff_commonPrefix((CFStringRef)text_insert, (CFStringRef)text_delete);\n            if (commonlength != 0) {\n              if ((thisPointer - count_delete - count_insert) > 0 &&\n                  ((Diff *)[diffs objectAtIndex:(thisPointer - count_delete - count_insert - 1)]).operation\n                  == DIFF_EQUAL) {\n                ((Diff *)[diffs objectAtIndex:(thisPointer - count_delete - count_insert - 1)]).text\n                    = [((Diff *)[diffs objectAtIndex:(thisPointer - count_delete - count_insert - 1)]).text\n                       stringByAppendingString:[text_insert substringWithRange:NSMakeRange(0, commonlength)]];\n              } else {\n                [diffs insertObject:[Diff diffWithOperation:DIFF_EQUAL\n                                                    andText:[text_insert substringWithRange:NSMakeRange(0, commonlength)]]\n                            atIndex:0];\n                thisPointer++;\n              }\n              text_insert = [text_insert substringFromIndex:commonlength];\n              text_delete = [text_delete substringFromIndex:commonlength];\n            }\n            // Factor out any common suffixes.\n            commonlength = (NSUInteger)diff_commonSuffix((CFStringRef)text_insert, (CFStringRef)text_delete);\n            if (commonlength != 0) {\n              thisDiff.text = [[text_insert substringFromIndex:(text_insert.length\n                  - commonlength)] stringByAppendingString:thisDiff.text];\n              text_insert = [text_insert substringWithRange:NSMakeRange(0,\n                  text_insert.length - commonlength)];\n              text_delete = [text_delete substringWithRange:NSMakeRange(0,\n                  text_delete.length - commonlength)];\n            }\n          }\n          // Delete the offending records and add the merged ones.\n          thisPointer -= count_delete + count_insert;\n\n          splice(diffs, thisPointer, count_delete + count_insert, nil);\n          if ([text_delete length] != 0) {\n            splice(diffs, thisPointer, 0,\n                   [NSMutableArray arrayWithObject:[Diff diffWithOperation:DIFF_DELETE andText:text_delete]]);\n            thisPointer++;\n          }\n          if ([text_insert length] != 0) {\n            splice(diffs, thisPointer, 0,\n                   [NSMutableArray arrayWithObject:[Diff diffWithOperation:DIFF_INSERT andText:text_insert]]);\n            thisPointer++;\n          }\n          thisPointer++;\n        } else if (thisPointer != 0 && prevDiff.operation == DIFF_EQUAL) {\n          // Merge this equality with the previous one.\n          prevDiff.text = [prevDiff.text stringByAppendingString:thisDiff.text];\n          [diffs removeObjectAtIndex:thisPointer];\n        } else {\n          thisPointer++;\n        }\n        count_insert = 0;\n        count_delete = 0;\n        text_delete = @\"\";\n        text_insert = @\"\";\n        break;\n    }\n  }\n  if (((Diff *)diffs.lastObject).text.length == 0) {\n    [diffs removeLastObject];  // Remove the dummy entry at the end.\n  }\n\n  // Second pass: look for single edits surrounded on both sides by\n  // equalities which can be shifted sideways to eliminate an equality.\n  // e.g: A<ins>BA</ins>C -> <ins>AB</ins>AC\n  BOOL changes = NO;\n  thisPointer = 1;\n  // Intentionally ignore the first and last element (don't need checking).\n  while (thisPointer < (diffs.count - 1)) {\n    if (prevDiff.operation == DIFF_EQUAL &&\n      nextDiff.operation == DIFF_EQUAL) {\n      // This is a single edit surrounded by equalities.\n      if ([prevDiff.text length] == 0) {\n        splice(diffs, thisPointer - 1, 1, nil);\n        changes = YES;\n      } else if ([thisDiff.text hasSuffix:prevDiff.text]) {\n        // Shift the edit over the previous equality.\n        thisDiff.text = [prevDiff.text stringByAppendingString:\n            [thisDiff.text substringWithRange:NSMakeRange(0, thisDiff.text.length - prevDiff.text.length)]];\n        nextDiff.text = [prevDiff.text stringByAppendingString:nextDiff.text];\n        splice(diffs, thisPointer - 1, 1, nil);\n        changes = YES;\n      } else if ([thisDiff.text hasPrefix:nextDiff.text]) {\n        // Shift the edit over the next equality.\n        prevDiff.text = [prevDiff.text stringByAppendingString:nextDiff.text];\n        thisDiff.text = [[thisDiff.text substringFromIndex:nextDiff.text.length] stringByAppendingString:nextDiff.text];\n        splice(diffs, thisPointer + 1, 1, nil);\n        changes = YES;\n      }\n    }\n    thisPointer++;\n  }\n  // If shifts were made, the diff needs reordering and another shift sweep.\n  if (changes) {\n    [self diff_cleanupMerge:diffs];\n  }\n\n#undef prevDiff\n#undef thisDiff\n#undef nextDiff\n}\n\n\n/**\n * Look for single edits surrounded on both sides by equalities\n * which can be shifted sideways to align the edit to a word boundary.\n * e.g: The c<ins>at c</ins>ame. -> The <ins>cat </ins>came.\n * @param diffs NSMutableArray of Diff objects.\n */\n- (void)diff_cleanupSemanticLossless:(NSMutableArray *)diffs;\n{\n#define prevDiff ((Diff *)[diffs objectAtIndex:(thisPointer - 1)])\n#define thisDiff ((Diff *)[diffs objectAtIndex:thisPointer])\n#define nextDiff ((Diff *)[diffs objectAtIndex:(thisPointer + 1)])\n\n  if (diffs.count == 0) {\n    return;\n  }\n\n  NSUInteger thisPointer = 1;\n  // Intentionally ignore the first and last element (don't need checking).\n  while (thisPointer < (diffs.count - 1)) {\n    if (prevDiff.operation == DIFF_EQUAL && nextDiff.operation == DIFF_EQUAL) {\n      // This is a single edit surrounded by equalities.\n      NSString *equality1 = prevDiff.text;\n      NSString *edit = thisDiff.text;\n      NSString *equality2 = nextDiff.text;\n\n      // First, shift the edit as far left as possible.\n      NSUInteger commonOffset = (NSUInteger)diff_commonSuffix((CFStringRef)equality1, (CFStringRef)edit);\n\n      if (commonOffset > 0) {\n        NSString *commonString = [edit substringFromIndex:(edit.length - commonOffset)];\n        equality1 = [equality1 substringWithRange:NSMakeRange(0, (equality1.length - commonOffset))];\n        edit = [commonString stringByAppendingString:[edit substringWithRange:NSMakeRange(0, (edit.length - commonOffset))]];\n        equality2 = [commonString stringByAppendingString:equality2];\n      }\n\n      // Second, step right character by character,\n      // looking for the best fit.\n      NSString *bestEquality1 = equality1;\n      NSString *bestEdit = edit;\n      NSString *bestEquality2 = equality2;\n      CFIndex bestScore = diff_cleanupSemanticScore((CFStringRef)equality1, (CFStringRef)edit) +\n      diff_cleanupSemanticScore((CFStringRef)edit, (CFStringRef)equality2);\n      while (edit.length != 0 && equality2.length != 0\n           && [edit characterAtIndex:0] == [equality2 characterAtIndex:0]) {\n        equality1 = [equality1 stringByAppendingString:[edit substringWithRange:NSMakeRange(0, 1)]];\n        edit = [[edit substringFromIndex:1] stringByAppendingString:[equality2 substringWithRange:NSMakeRange(0, 1)]];\n        equality2 = [equality2 substringFromIndex:1];\n        CFIndex score = diff_cleanupSemanticScore((CFStringRef)equality1, (CFStringRef)edit) +\n        diff_cleanupSemanticScore((CFStringRef)edit, (CFStringRef)equality2);\n        // The >= encourages trailing rather than leading whitespace on edits.\n        if (score >= bestScore) {\n          bestScore = score;\n          bestEquality1 = equality1;\n          bestEdit = edit;\n          bestEquality2 = equality2;\n        }\n      }\n\n      if (prevDiff.text != bestEquality1) {\n        // We have an improvement, save it back to the diff.\n        if (bestEquality1.length != 0) {\n          prevDiff.text = bestEquality1;\n        } else {\n          [diffs removeObjectAtIndex:thisPointer - 1];\n          thisPointer--;\n        }\n        thisDiff.text = bestEdit;\n        if (bestEquality2.length != 0) {\n          nextDiff.text = bestEquality2;\n        } else {\n          [diffs removeObjectAtIndex:thisPointer + 1];\n          thisPointer--;\n        }\n      }\n    }\n    thisPointer++;\n  }\n\n#undef prevDiff\n#undef thisDiff\n#undef nextDiff\n}\n\n/**\n * Given two strings, comAdde a score representing whether the internal\n * boundary falls on logical boundaries.\n * Scores range from 5 (best) to 0 (worst).\n * @param one First string.\n * @param two Second string.\n * @return The score.\n */\n- (NSInteger)diff_cleanupSemanticScoreOfFirstString:(NSString *)one\n                                    andSecondString:(NSString *)two;\n{\n  return diff_cleanupSemanticScore((CFStringRef)one, (CFStringRef)two);\n}\n\n/**\n * Reduce the number of edits by eliminating operationally trivial\n * equalities.\n * @param diffs NSMutableArray of Diff objects.\n */\n- (void)diff_cleanupEfficiency:(NSMutableArray *)diffs;\n{\n#define thisDiff ((Diff *)[diffs objectAtIndex:thisPointer])\n#define equalitiesLastItem ((NSNumber *)equalities.lastObject)\n#define equalitiesLastValue ((NSNumber *)equalities.lastObject).integerValue\n  if (diffs.count == 0) {\n    return;\n  }\n\n  BOOL changes = NO;\n  // Stack of indices where equalities are found.\n  NSMutableArray *equalities = [NSMutableArray array];\n  // Always equal to equalities.lastObject.text\n  NSString *lastEquality = nil;\n  NSInteger thisPointer = 0;  // Index of current position.\n  // Is there an insertion operation before the last equality.\n  BOOL pre_ins = NO;\n  // Is there a deletion operation before the last equality.\n  BOOL pre_del = NO;\n  // Is there an insertion operation after the last equality.\n  BOOL post_ins = NO;\n  // Is there a deletion operation after the last equality.\n  BOOL post_del = NO;\n\n  NSUInteger indexToChange;\n  Diff *diffToChange;\n\n  while (thisPointer < (NSInteger)diffs.count) {\n    if (thisDiff.operation == DIFF_EQUAL) {  // Equality found.\n      if (thisDiff.text.length < Diff_EditCost && (post_ins || post_del)) {\n        // Candidate found.\n        [equalities addObject:[NSNumber numberWithInteger:thisPointer]];\n        pre_ins = post_ins;\n        pre_del = post_del;\n        lastEquality = thisDiff.text;\n      } else {\n        // Not a candidate, and can never become one.\n        [equalities removeAllObjects];\n        lastEquality = nil;\n      }\n      post_ins = post_del = NO;\n    } else {  // An insertion or deletion.\n      if (thisDiff.operation == DIFF_DELETE) {\n        post_del = YES;\n      } else {\n        post_ins = YES;\n      }\n      /*\n       * Five types to be split:\n       * <ins>A</ins><del>B</del>XY<ins>C</ins><del>D</del>\n       * <ins>A</ins>X<ins>C</ins><del>D</del>\n       * <ins>A</ins><del>B</del>X<ins>C</ins>\n       * <ins>A</del>X<ins>C</ins><del>D</del>\n       * <ins>A</ins><del>B</del>X<del>C</del>\n       */\n      if (lastEquality != nil\n          && ((pre_ins && pre_del && post_ins && post_del)\n          || ((lastEquality.length < Diff_EditCost / 2)\n          && ((pre_ins ? 1 : 0) + (pre_del ? 1 : 0) + (post_ins ? 1 : 0)\n          + (post_del ? 1 : 0)) == 3))) {\n        // Duplicate record.\n        [diffs insertObject:[Diff diffWithOperation:DIFF_DELETE andText:lastEquality]\n                    atIndex:equalitiesLastValue];\n        // Change second copy to insert.\n        // Hash values for objects must not change while in a collection\n        indexToChange = equalitiesLastValue + 1;\n        diffToChange = [[diffs objectAtIndex:indexToChange] retain];\n        [diffs replaceObjectAtIndex:indexToChange withObject:[NSNull null]];\n        diffToChange.operation = DIFF_INSERT;\n        [diffs replaceObjectAtIndex:indexToChange withObject:diffToChange];\n        [diffToChange release];\n\n        [equalities removeLastObject];   // Throw away the equality we just deleted.\n        lastEquality = nil;\n        if (pre_ins && pre_del) {\n          // No changes made which could affect previous entry, keep going.\n          post_ins = post_del = YES;\n          [equalities removeAllObjects];\n        } else {\n          if (equalities.count > 0) {\n            [equalities removeLastObject];\n          }\n\n          thisPointer = equalities.count > 0 ? equalitiesLastValue : -1;\n          post_ins = post_del = NO;\n        }\n        changes = YES;\n      }\n    }\n    thisPointer++;\n  }\n\n  if (changes) {\n    [self diff_cleanupMerge:diffs];\n  }\n\n#undef thisDiff\n#undef equalitiesLastItem\n#undef equalitiesLastValue\n}\n\n/**\n * Convert a Diff list into a pretty HTML report.\n * @param diffs NSMutableArray of Diff objects.\n * @return HTML representation.\n */\n- (NSString *)diff_prettyHtml:(NSMutableArray *)diffs;\n{\n  NSMutableString *html = [NSMutableString string];\n  for (Diff *aDiff in diffs) {\n    NSMutableString *text = [[aDiff.text mutableCopy] autorelease];\n    [text replaceOccurrencesOfString:@\"&\" withString:@\"&amp;\" options:NSLiteralSearch range:NSMakeRange(0, text.length)];\n    [text replaceOccurrencesOfString:@\"<\" withString:@\"&lt;\" options:NSLiteralSearch range:NSMakeRange(0, text.length)];\n    [text replaceOccurrencesOfString:@\">\" withString:@\"&gt;\" options:NSLiteralSearch range:NSMakeRange(0, text.length)];\n    [text replaceOccurrencesOfString:@\"\\n\" withString:@\"&para;<br>\" options:NSLiteralSearch range:NSMakeRange(0, text.length)];\n\n    switch (aDiff.operation) {\n      case DIFF_INSERT:\n        [html appendFormat:@\"<ins style=\\\"background:#e6ffe6;\\\">%@</ins>\", text];\n        break;\n      case DIFF_DELETE:\n        [html appendFormat:@\"<del style=\\\"background:#ffe6e6;\\\">%@</del>\", text];\n        break;\n      case DIFF_EQUAL:\n        [html appendFormat:@\"<span>%@</span>\", text];\n        break;\n    }\n  }\n  return html;\n}\n\n/**\n * Compute and return the source text (all equalities and deletions).\n * @param diffs NSMutableArray of Diff objects.\n * @return Source text.\n */\n- (NSString *)diff_text1:(NSMutableArray *)diffs;\n{\n  NSMutableString *text = [NSMutableString string];\n  for (Diff *aDiff in diffs) {\n    if (aDiff.operation != DIFF_INSERT) {\n      [text appendString:aDiff.text];\n    }\n  }\n  return text;\n}\n\n/**\n * Compute and return the destination text (all equalities and insertions).\n * @param diffs NSMutableArray of Diff objects.\n * @return Destination text.\n */\n- (NSString *)diff_text2:(NSMutableArray *)diffs;\n{\n  NSMutableString *text = [NSMutableString string];\n  for (Diff *aDiff in diffs) {\n    if (aDiff.operation != DIFF_DELETE) {\n      [text appendString:aDiff.text];\n    }\n  }\n  return text;\n}\n\n/**\n * Crush the diff into an encoded NSString which describes the operations\n * required to transform text1 into text2.\n * E.g. =3\\t-2\\t+ing  -> Keep 3 chars, delete 2 chars, insert 'ing'.\n * Operations are tab-separated.  Inserted text is escaped using %xx\n * notation.\n * @param diffs NSMutableArray of Diff objects.\n * @return Delta text.\n */\n- (NSString *)diff_toDelta:(NSMutableArray *)diffs;\n{\n  NSMutableString *delta = [NSMutableString string];\n  for (Diff *aDiff in diffs) {\n    switch (aDiff.operation) {\n      case DIFF_INSERT:\n        [delta appendFormat:@\"+%@\\t\", [[aDiff.text diff_stringByAddingPercentEscapesForEncodeUriCompatibility]\n                                       stringByReplacingOccurrencesOfString:@\"%20\" withString:@\" \"]];\n        break;\n      case DIFF_DELETE:\n        [delta appendFormat:@\"-%\" PRId32 \"\\t\", (int32_t)aDiff.text.length];\n        break;\n      case DIFF_EQUAL:\n        [delta appendFormat:@\"=%\" PRId32 \"\\t\", (int32_t)aDiff.text.length];\n        break;\n    }\n  }\n\n  if (delta.length != 0) {\n    // Strip off trailing tab character.\n    return [delta substringWithRange:NSMakeRange(0, delta.length-1)];\n  }\n  return delta;\n}\n\n/**\n * Given the original text1, and an encoded NSString which describes the\n * operations required to transform text1 into text2, compute the full diff.\n * @param text1 Source NSString for the diff.\n * @param delta Delta text.\n * @param error NSError if invalid input.\n * @return NSMutableArray of Diff objects or nil if invalid.\n */\n- (NSMutableArray *)diff_fromDeltaWithText:(NSString *)text1\n                                  andDelta:(NSString *)delta\n                                     error:(NSError **)error;\n{\n  NSMutableArray *diffs = [NSMutableArray array];\n  NSUInteger thisPointer = 0;  // Cursor in text1\n  NSArray *tokens = [delta componentsSeparatedByString:@\"\\t\"];\n  NSInteger n;\n  NSDictionary *errorDetail = nil;\n  for (NSString *token in tokens) {\n    if (token.length == 0) {\n      // Blank tokens are ok (from a trailing \\t).\n      continue;\n    }\n    // Each token begins with a one character parameter which specifies the\n    // operation of this token (delete, insert, equality).\n    NSString *param = [token substringFromIndex:1];\n    switch ([token characterAtIndex:0]) {\n      case '+':\n        param = [param diff_stringByReplacingPercentEscapesForEncodeUriCompatibility];\n        if (param == nil) {\n          if (error != NULL) {\n            errorDetail = [NSDictionary dictionaryWithObjectsAndKeys:\n                [NSString stringWithFormat:NSLocalizedString(@\"Invalid character in diff_fromDelta: %@\", @\"Error\"), param],\n                NSLocalizedDescriptionKey, nil];\n            *error = [NSError errorWithDomain:@\"DiffMatchPatchErrorDomain\" code:99 userInfo:errorDetail];\n          }\n          return nil;\n        }\n        [diffs addObject:[Diff diffWithOperation:DIFF_INSERT andText:param]];\n        break;\n      case '-':\n        // Fall through.\n      case '=':\n        n = [param integerValue];\n        if (n == 0) {\n          if (error != NULL) {\n            errorDetail = [NSDictionary dictionaryWithObjectsAndKeys:\n                [NSString stringWithFormat:NSLocalizedString(@\"Invalid number in diff_fromDelta: %@\", @\"Error\"), param],\n                NSLocalizedDescriptionKey, nil];\n            *error = [NSError errorWithDomain:@\"DiffMatchPatchErrorDomain\" code:100 userInfo:errorDetail];\n          }\n          return nil;\n        } else if (n < 0) {\n          if (error != NULL) {\n            errorDetail = [NSDictionary dictionaryWithObjectsAndKeys:\n                [NSString stringWithFormat:NSLocalizedString(@\"Negative number in diff_fromDelta: %@\", @\"Error\"), param],\n                NSLocalizedDescriptionKey, nil];\n            *error = [NSError errorWithDomain:@\"DiffMatchPatchErrorDomain\" code:101 userInfo:errorDetail];\n          }\n          return nil;\n        }\n        NSString *text;\n        @try {\n          text = [text1 substringWithRange:NSMakeRange(thisPointer, (NSUInteger)n)];\n          thisPointer += (NSUInteger)n;\n        }\n        @catch (NSException *e) {\n          if (error != NULL) {\n            // CHANGME: Pass on the information contained in e\n            errorDetail = [NSDictionary dictionaryWithObjectsAndKeys:\n                [NSString stringWithFormat:NSLocalizedString(@\"Delta length (%lu) larger than source text length (%lu).\", @\"Error\"),\n                 (unsigned long)thisPointer, (unsigned long)text1.length],\n                NSLocalizedDescriptionKey, nil];\n            *error = [NSError errorWithDomain:@\"DiffMatchPatchErrorDomain\" code:102 userInfo:errorDetail];\n          }\n          return nil;\n        }\n        if ([token characterAtIndex:0] == '=') {\n          [diffs addObject:[Diff diffWithOperation:DIFF_EQUAL andText:text]];\n        } else {\n          [diffs addObject:[Diff diffWithOperation:DIFF_DELETE andText:text]];\n        }\n        break;\n      default:\n        // Anything else is an error.\n        if (error != NULL) {\n          errorDetail = [NSDictionary dictionaryWithObjectsAndKeys:\n              [NSString stringWithFormat:NSLocalizedString(@\"Invalid diff operation in diff_fromDelta: %C\", @\"Error\"),\n               [token characterAtIndex:0]],\n              NSLocalizedDescriptionKey, nil];\n          *error = [NSError errorWithDomain:@\"DiffMatchPatchErrorDomain\" code:102 userInfo:errorDetail];\n        }\n        return nil;\n    }\n  }\n  if (thisPointer != text1.length) {\n    if (error != NULL) {\n      errorDetail = [NSDictionary dictionaryWithObjectsAndKeys:\n          [NSString stringWithFormat:NSLocalizedString(@\"Delta length (%lu) smaller than source text length (%lu).\", @\"Error\"),\n           (unsigned long)thisPointer, (unsigned long)text1.length],\n          NSLocalizedDescriptionKey, nil];\n      *error = [NSError errorWithDomain:@\"DiffMatchPatchErrorDomain\" code:103 userInfo:errorDetail];\n    }\n    return nil;\n  }\n  return diffs;\n}\n\n/**\n * loc is a location in text1, compute and return the equivalent location in\n * text2.\n * e.g. \"The cat\" vs \"The big cat\", 1->1, 5->8\n * @param diffs NSMutableArray of Diff objects.\n * @param loc Location within text1.\n * @return Location within text2.\n */\n- (NSUInteger)diff_xIndexIn:(NSMutableArray *)diffs\n                   location:(NSUInteger) loc;\n{\n  NSUInteger chars1 = 0;\n  NSUInteger chars2 = 0;\n  NSUInteger last_chars1 = 0;\n  NSUInteger last_chars2 = 0;\n  Diff *lastDiff = nil;\n  for (Diff *aDiff in diffs) {\n    if (aDiff.operation != DIFF_INSERT) {\n      // Equality or deletion.\n      chars1 += aDiff.text.length;\n    }\n    if (aDiff.operation != DIFF_DELETE) {\n      // Equality or insertion.\n      chars2 += aDiff.text.length;\n    }\n    if (chars1 > loc) {\n      // Overshot the location.\n      lastDiff = aDiff;\n      break;\n    }\n    last_chars1 = chars1;\n    last_chars2 = chars2;\n  }\n  if (lastDiff != nil && lastDiff.operation == DIFF_DELETE) {\n    // The location was deleted.\n    return last_chars2;\n  }\n  // Add the remaining character length.\n  return last_chars2 + (loc - last_chars1);\n}\n\n/**\n * Compute the Levenshtein distance; the number of inserted, deleted or\n * substituted characters.\n * @param diffs NSMutableArray of Diff objects.\n * @return Number of changes.\n */\n- (NSUInteger)diff_levenshtein:(NSMutableArray *)diffs;\n{\n  NSUInteger levenshtein = 0;\n  NSUInteger insertions = 0;\n  NSUInteger deletions = 0;\n  for (Diff *aDiff in diffs) {\n    switch (aDiff.operation) {\n      case DIFF_INSERT:\n        insertions += aDiff.text.length;\n        break;\n      case DIFF_DELETE:\n        deletions += aDiff.text.length;\n        break;\n      case DIFF_EQUAL:\n        // A deletion and an insertion is one substitution.\n        levenshtein += MAX(insertions, deletions);\n        insertions = 0;\n        deletions = 0;\n        break;\n    }\n  }\n  levenshtein += MAX(insertions, deletions);\n  return levenshtein;\n}\n\n/**\n * Reduce the number of edits by eliminating semantically trivial\n * equalities.\n * @param diffs NSMutableArray of Diff objects.\n */\n- (void)diff_cleanupSemantic:(NSMutableArray *)diffs;\n{\n#define prevDiff ((Diff *)[diffs objectAtIndex:(thisPointer - 1)])\n#define thisDiff ((Diff *)[diffs objectAtIndex:thisPointer])\n#define nextDiff ((Diff *)[diffs objectAtIndex:(thisPointer + 1)])\n#define equalitiesLastItem ((NSNumber *)equalities.lastObject)\n#define equalitiesLastValue ((NSNumber *)equalities.lastObject).integerValue\n\n  if (diffs == nil || diffs.count == 0) {\n    return;\n  }\n\n  BOOL changes = NO;\n  // Stack of indices where equalities are found.\n  NSMutableArray *equalities = [NSMutableArray array];\n  // Always equal to equalities.lastObject.text\n  NSString *lastEquality = nil;\n  NSUInteger thisPointer = 0;  // Index of current position.\n  // Number of characters that changed prior to the equality.\n  NSUInteger length_insertions1 = 0;\n  NSUInteger length_deletions1 = 0;\n  // Number of characters that changed after the equality.\n  NSUInteger length_insertions2 = 0;\n  NSUInteger length_deletions2 = 0;\n\n  NSUInteger indexToChange;\n  Diff *diffToChange;\n\n  while (thisPointer < diffs.count) {\n    if (thisDiff.operation == DIFF_EQUAL) {  // Equality found.\n      [equalities addObject:[NSNumber numberWithInteger:thisPointer]];\n      length_insertions1 = length_insertions2;\n      length_deletions1 = length_deletions2;\n      length_insertions2 = 0;\n      length_deletions2 = 0;\n      lastEquality = thisDiff.text;\n    } else {  // an insertion or deletion\n      if (thisDiff.operation == DIFF_INSERT) {\n        length_insertions2 += thisDiff.text.length;\n      } else {\n        length_deletions2 += thisDiff.text.length;\n      }\n      // Eliminate an equality that is smaller or equal to the edits on both\n      // sides of it.\n      if (lastEquality != nil\n          && (lastEquality.length <= MAX(length_insertions1, length_deletions1))\n          && (lastEquality.length <= MAX(length_insertions2, length_deletions2))) {\n        // Duplicate record.\n        [diffs insertObject:[Diff diffWithOperation:DIFF_DELETE andText:lastEquality] atIndex:equalitiesLastValue];\n        // Change second copy to insert.\n        // Hash values for objects must not change while in a collection.\n        indexToChange = equalitiesLastValue + 1;\n        diffToChange = [[diffs objectAtIndex:indexToChange] retain];\n        [diffs replaceObjectAtIndex:indexToChange withObject:[NSNull null]];\n        diffToChange.operation = DIFF_INSERT;\n        [diffs replaceObjectAtIndex:indexToChange withObject:diffToChange];\n        [diffToChange release];\n\n        // Throw away the equality we just deleted.\n        [equalities removeLastObject];\n        if (equalities.count > 0) {\n          [equalities removeLastObject];\n        }\n        // Setting an unsigned value to -1 may seem weird to some,\n        // but we will pass thru a ++ below:\n        // => overflow => 0\n        thisPointer = equalities.count > 0 ? equalitiesLastValue : -1;\n        length_insertions1 = 0; // Reset the counters.\n        length_deletions1 = 0;\n        length_insertions2 = 0;\n        length_deletions2 = 0;\n        lastEquality = nil;\n        changes = YES;\n      }\n    }\n    thisPointer++;\n  }\n\n  // Normalize the diff.\n  if (changes) {\n    [self diff_cleanupMerge:diffs];\n  }\n  [self diff_cleanupSemanticLossless:diffs];\n\n  // Find any overlaps between deletions and insertions.\n  // e.g: <del>abcxxx</del><ins>xxxdef</ins>\n  //   -> <del>abc</del>xxx<ins>def</ins>\n  // e.g: <del>xxxabc</del><ins>defxxx</ins>\n  //   -> <ins>def</ins>xxx<del>abc</del>\n  // Only extract an overlap if it is as big as the edit ahead or behind it.\n  thisPointer = 1;\n  while (thisPointer < diffs.count) {\n    if (prevDiff.operation == DIFF_DELETE && thisDiff.operation == DIFF_INSERT) {\n      NSString *deletion = [prevDiff.text copy];\n      NSString *insertion = [thisDiff.text copy];\n      NSUInteger overlap_length1 = (NSUInteger)diff_commonOverlap((CFStringRef)deletion, (CFStringRef)insertion);\n      NSUInteger overlap_length2 = (NSUInteger)diff_commonOverlap((CFStringRef)insertion, (CFStringRef)deletion);\n      if (overlap_length1 >= overlap_length2) {\n        if (overlap_length1 >= deletion.length / 2.0 ||\n            overlap_length1 >= insertion.length / 2.0) {\n          // Overlap found.\n          // Insert an equality and trim the surrounding edits.\n          [diffs insertObject:[Diff diffWithOperation:DIFF_EQUAL\n              andText:[insertion substringWithRange:NSMakeRange(0, overlap_length1)]]\n              atIndex:thisPointer];\n          prevDiff.text = [deletion substringWithRange:NSMakeRange(0, deletion.length - overlap_length1)];\n          nextDiff.text = [insertion substringFromIndex:overlap_length1];\n          thisPointer++;\n        }\n      } else {\n        if (overlap_length2 >= deletion.length / 2.0 ||\n            overlap_length2 >= insertion.length / 2.0) {\n          // Reverse overlap found.\n          // Insert an equality and swap and trim the surrounding edits.\n          [diffs insertObject:[Diff diffWithOperation:DIFF_EQUAL\n              andText:[deletion substringWithRange:NSMakeRange(0, overlap_length2)]]\n              atIndex:thisPointer];\n          prevDiff.operation = DIFF_INSERT;\n          prevDiff.text = [insertion substringWithRange:NSMakeRange(0, insertion.length - overlap_length2)];\n          nextDiff.operation = DIFF_DELETE;\n          nextDiff.text = [deletion substringFromIndex:overlap_length2];\n          thisPointer++;\n        }\n      }\n      [deletion release];\n      [insertion release];\n      thisPointer++;\n    }\n    thisPointer++;\n  }\n\n#undef prevDiff\n#undef thisDiff\n#undef nextDiff\n#undef equalitiesLastItem\n#undef equalitiesLastValue\n}\n\n#pragma mark Match Functions\n//  MATCH FUNCTIONS\n\n\n/**\n * Locate the best instance of 'pattern' in 'text' near 'loc'.\n * Returns NSNotFound if no match found.\n * @param text The text to search.\n * @param pattern The pattern to search for.\n * @param loc The location to search around.\n * @return Best match index or NSNotFound.\n */\n- (NSUInteger)match_mainForText:(NSString *)text\n                        pattern:(NSString *)pattern\n                           near:(NSUInteger)loc;\n{\n  // Check for null inputs.\n  if (text == nil || pattern == nil) {\n    NSLog(@\"Null inputs. (match_main)\");\n    return NSNotFound;\n  }\n  if (text.length == 0) {\n    NSLog(@\"Empty text. (match_main)\");\n    return NSNotFound;\n  }\n\n  NSUInteger new_loc;\n  new_loc = MIN(loc, text.length);\n  new_loc = MAX((NSUInteger)0, new_loc);\n\n  if ([text isEqualToString:pattern]) {\n    // Shortcut (potentially not guaranteed by the algorithm)\n    return 0;\n  } else if (text.length == 0) {\n    // Nothing to match.\n    return NSNotFound;\n  } else if (new_loc + pattern.length <= text.length\n      && [[text substringWithRange:NSMakeRange(new_loc, pattern.length)] isEqualToString:pattern]) {\n    // Perfect match at the perfect spot!   (Includes case of empty pattern)\n    return new_loc;\n  } else {\n    // Do a fuzzy compare.\n    return [self match_bitapOfText:text andPattern:pattern near:new_loc];\n  }\n}\n\n/**\n * Locate the best instance of 'pattern' in 'text' near 'loc' using the\n * Bitap algorithm.   Returns NSNotFound if no match found.\n * @param text The text to search.\n * @param pattern The pattern to search for.\n * @param loc The location to search around.\n * @return Best match index or NSNotFound.\n */\n- (NSUInteger)match_bitapOfText:(NSString *)text\n                     andPattern:(NSString *)pattern\n                           near:(NSUInteger)loc;\n{\n  NSAssert((Match_MaxBits == 0 || pattern.length <= Match_MaxBits),\n           @\"Pattern too long for this application.\");\n\n  // Initialise the alphabet.\n  NSMutableDictionary *s = [self match_alphabet:pattern];\n\n  // Highest score beyond which we give up.\n  double score_threshold = Match_Threshold;\n  // Is there a nearby exact match? (speedup)\n  NSUInteger best_loc = [text rangeOfString:pattern options:NSLiteralSearch range:NSMakeRange(loc, text.length - loc)].location;\n  if (best_loc != NSNotFound) {\n    score_threshold = MIN([self match_bitapScoreForErrorCount:0 location:best_loc near:loc pattern:pattern], score_threshold);\n    // What about in the other direction? (speedup)\n    NSUInteger searchRangeLoc = MIN(loc + pattern.length, text.length);\n    NSRange searchRange = NSMakeRange(0, searchRangeLoc);\n    best_loc = [text rangeOfString:pattern options:(NSLiteralSearch | NSBackwardsSearch) range:searchRange].location;\n    if (best_loc != NSNotFound) {\n      score_threshold = MIN([self match_bitapScoreForErrorCount:0 location:best_loc near:loc pattern:pattern], score_threshold);\n    }\n  }\n\n  // Initialise the bit arrays.\n  NSUInteger matchmask = 1 << (pattern.length - 1);\n  best_loc = NSNotFound;\n\n  NSUInteger bin_min, bin_mid;\n  NSUInteger bin_max = pattern.length + text.length;\n  NSUInteger *rd = NULL;\n  NSUInteger *last_rd = NULL;\n  for (NSUInteger d = 0; d < pattern.length; d++) {\n    // Scan for the best match; each iteration allows for one more error.\n    // Run a binary search to determine how far from 'loc' we can stray at\n    // this error level.\n    bin_min = 0;\n    bin_mid = bin_max;\n    while (bin_min < bin_mid) {\n      double score = [self match_bitapScoreForErrorCount:d location:(loc + bin_mid) near:loc pattern:pattern];\n      if (score <= score_threshold) {\n        bin_min = bin_mid;\n      } else {\n        bin_max = bin_mid;\n      }\n      bin_mid = (bin_max - bin_min) / 2 + bin_min;\n    }\n    // Use the result from this iteration as the maximum for the next.\n    bin_max = bin_mid;\n    NSUInteger start = MAX_OF_CONST_AND_DIFF(1, loc, bin_mid);\n    NSUInteger finish = MIN(loc + bin_mid, text.length) + pattern.length;\n\n    rd = (NSUInteger *)calloc((finish + 2), sizeof(NSUInteger));\n    rd[finish + 1] = (1 << d) - 1;\n\n    for (NSUInteger j = finish; j >= start; j--) {\n      NSUInteger charMatch;\n      if (text.length <= j - 1 || ![s diff_containsObjectForUnicharKey:[text characterAtIndex:(j - 1)]]) {\n        // Out of range.\n        charMatch = 0;\n      } else {\n        charMatch = [s diff_unsignedIntegerForUnicharKey:[text characterAtIndex:(j - 1)]];\n      }\n      if (d == 0) {\n        // First pass: exact match.\n        rd[j] = ((rd[j + 1] << 1) | 1) & charMatch;\n      } else {\n        // Subsequent passes: fuzzy match.\n        rd[j] = (((rd[j + 1] << 1) | 1) & charMatch)\n            | (((last_rd[j + 1] | last_rd[j]) << 1) | 1) | last_rd[j + 1];\n      }\n      if ((rd[j] & matchmask) != 0) {\n        double score = [self match_bitapScoreForErrorCount:d location:(j - 1) near:loc pattern:pattern];\n        // This match will almost certainly be better than any existing match.\n        // But check anyway.\n        if (score <= score_threshold) {\n          // Told you so.\n          score_threshold = score;\n          best_loc = j - 1;\n          if (best_loc > loc) {\n            // When passing loc, don't exceed our current distance from loc.\n            start = MAX_OF_CONST_AND_DIFF(1, 2 * loc, best_loc);\n          } else {\n            // Already passed loc, downhill from here on in.\n            break;\n          }\n        }\n      }\n    }\n    if ([self match_bitapScoreForErrorCount:(d + 1) location:loc near:loc pattern:pattern] > score_threshold) {\n      // No hope for a (better) match at greater error levels.\n      break;\n    }\n\n    if (last_rd != NULL) {\n      free(last_rd);\n    }\n    last_rd = rd;\n  }\n\n  if (rd != NULL && last_rd != rd) {\n    free(rd);\n  }\n  if (last_rd != NULL) {\n    free(last_rd);\n  }\n\n  return best_loc;\n}\n\n/**\n * Compute and return the score for a match with e errors and x location.\n * @param e Number of errors in match.\n * @param x Location of match.\n * @param loc Expected location of match.\n * @param pattern Pattern being sought.\n * @return Overall score for match (0.0 = good, 1.0 = bad).\n */\n- (double)match_bitapScoreForErrorCount:(NSUInteger)e\n                               location:(NSUInteger)x\n                                   near:(NSUInteger)loc\n                                pattern:(NSString *)pattern;\n{\n  double score;\n\n  double accuracy = (double)e / pattern.length;\n  NSUInteger proximity = (NSUInteger)ABS((long long)loc - (long long)x);\n  if (Match_Distance == 0) {\n    // Dodge divide by zero error.\n    return proximity == 0 ? accuracy : 1.0;\n  }\n  score = accuracy + (proximity / (double) Match_Distance);\n\n  return score;\n}\n\n/**\n * Initialise the alphabet for the Bitap algorithm.\n * @param pattern The text to encode.\n * @return Hash of character locations\n *     (NSMutableDictionary: keys:NSString/unichar, values:NSNumber/NSUInteger).\n */\n- (NSMutableDictionary *)match_alphabet:(NSString *)pattern;\n{\n  NSMutableDictionary *s = [NSMutableDictionary dictionary];\n  CFStringRef str = (CFStringRef)pattern;\n  CFStringInlineBuffer inlineBuffer;\n  CFIndex length;\n  CFIndex cnt;\n\n  length = CFStringGetLength(str);\n  CFStringInitInlineBuffer(str, &inlineBuffer, CFRangeMake(0, length));\n\n  UniChar ch;\n  CFStringRef c;\n  for (cnt = 0; cnt < length; cnt++) {\n    ch = CFStringGetCharacterFromInlineBuffer(&inlineBuffer, cnt);\n    c = diff_CFStringCreateFromUnichar(ch);\n    if (![s diff_containsObjectForKey:(NSString *)c]) {\n      [s diff_setUnsignedIntegerValue:0 forKey:(NSString *)c];\n    }\n    CFRelease(c);\n  }\n\n  NSUInteger i = 0;\n  for (cnt = 0; cnt < length; cnt++) {\n    ch = CFStringGetCharacterFromInlineBuffer(&inlineBuffer, cnt);\n    c = diff_CFStringCreateFromUnichar(ch);\n    NSUInteger value = [s diff_unsignedIntegerForKey:(NSString *)c] | (1 << (pattern.length - i - 1));\n    [s diff_setUnsignedIntegerValue:value forKey:(NSString *)c];\n    i++;\n    CFRelease(c);\n  }\n  return s;\n}\n\n\n#pragma mark Patch Functions\n//  PATCH FUNCTIONS\n\n\n/**\n * Increase the context until it is unique,\n * but don't let the pattern expand beyond Match_MaxBits.\n * @param patch The patch to grow.\n * @param text Source text.\n */\n- (void)patch_addContextToPatch:(Patch *)patch\n                     sourceText:(NSString *)text;\n{\n  if (text.length == 0) {\n    return;\n  }\n  NSString *pattern = [text substringWithRange:NSMakeRange(patch.start2, patch.length1)];\n  NSUInteger padding = 0;\n\n  // Look for the first and last matches of pattern in text.  If two\n  // different matches are found, increase the pattern length.\n  while ([text rangeOfString:pattern options:NSLiteralSearch].location\n      != [text rangeOfString:pattern options:(NSLiteralSearch | NSBackwardsSearch)].location\n      && pattern.length < (Match_MaxBits - Patch_Margin - Patch_Margin)) {\n    padding += Patch_Margin;\n    pattern = [text diff_javaSubstringFromStart:MAX_OF_CONST_AND_DIFF(0, patch.start2, padding)\n        toEnd:MIN(text.length, patch.start2 + patch.length1 + padding)];\n  }\n  // Add one chunk for good luck.\n  padding += Patch_Margin;\n\n  // Add the prefix.\n  NSString *prefix = [text diff_javaSubstringFromStart:MAX_OF_CONST_AND_DIFF(0, patch.start2, padding)\n      toEnd:patch.start2];\n  if (prefix.length != 0) {\n    [patch.diffs insertObject:[Diff diffWithOperation:DIFF_EQUAL andText:prefix] atIndex:0];\n  }\n  // Add the suffix.\n  NSString *suffix = [text diff_javaSubstringFromStart:(patch.start2 + patch.length1)\n      toEnd:MIN(text.length, patch.start2 + patch.length1 + padding)];\n  if (suffix.length != 0) {\n    [patch.diffs addObject:[Diff diffWithOperation:DIFF_EQUAL andText:suffix]];\n  }\n\n  // Roll back the start points.\n  patch.start1 -= prefix.length;\n  patch.start2 -= prefix.length;\n  // Extend the lengths.\n  patch.length1 += prefix.length + suffix.length;\n  patch.length2 += prefix.length + suffix.length;\n}\n\n/**\n * Compute a list of patches to turn text1 into text2.\n * A set of diffs will be computed.\n * @param text1 Old text.\n * @param text2 New text.\n * @return NSMutableArray of Patch objects.\n */\n- (NSMutableArray *)patch_makeFromOldString:(NSString *)text1\n                               andNewString:(NSString *)text2;\n{\n  // Check for null inputs.\n  if (text1 == nil || text2 == nil) {\n    NSLog(@\"Null inputs. (patch_make)\");\n    return nil;\n  }\n\n  // No diffs provided, compute our own.\n  NSMutableArray *diffs = [self diff_mainOfOldString:text1 andNewString:text2 checkLines:YES];\n  if (diffs.count > 2) {\n    [self diff_cleanupSemantic:diffs];\n    [self diff_cleanupEfficiency:diffs];\n  }\n\n  return [self patch_makeFromOldString:text1 andDiffs:diffs];\n}\n\n/**\n * Compute a list of patches to turn text1 into text2.\n * text1 will be derived from the provided diffs.\n * @param diffs NSMutableArray of Diff objects for text1 to text2.\n * @return NSMutableArray of Patch objects.\n */\n- (NSMutableArray *)patch_makeFromDiffs:(NSMutableArray *)diffs;\n{\n  // Check for nil inputs not needed since nil can't be passed in C#.\n  // No origin NSString *provided, comAdde our own.\n  NSString *text1 = [self diff_text1:diffs];\n  return [self patch_makeFromOldString:text1 andDiffs:diffs];\n}\n\n/**\n * Compute a list of patches to turn text1 into text2.\n * text2 is ignored, diffs are the delta between text1 and text2.\n * @param text1 Old text\n * @param text2 New text\n * @param diffs NSMutableArray of Diff objects for text1 to text2.\n * @return NSMutableArray of Patch objects.\n * @deprecated Prefer -patch_makeFromOldString:diffs:.\n */\n- (NSMutableArray *)patch_makeFromOldString:(NSString *)text1\n                                  newString:(NSString *)text2\n                                      diffs:(NSMutableArray *)diffs __deprecated;\n{\n  // Check for null inputs.\n  if (text1 == nil || text2 == nil) {\n    NSLog(@\"Null inputs. (patch_make)\");\n    return nil;\n  }\n\n  return [self patch_makeFromOldString:text1 andDiffs:diffs];\n}\n\n/**\n * Compute a list of patches to turn text1 into text2.\n * text2 is not provided, diffs are the delta between text1 and text2.\n * @param text1 Old text.\n * @param diffs NSMutableArray of Diff objects for text1 to text2.\n * @return NSMutableArray of Patch objects.\n */\n- (NSMutableArray *)patch_makeFromOldString:(NSString *)text1\n                                   andDiffs:(NSMutableArray *)diffs;\n{\n  // Check for null inputs.\n  if (text1 == nil) {\n    NSLog(@\"Null inputs. (patch_make)\");\n    return nil;\n  }\n\n  NSMutableArray *patches = [NSMutableArray array];\n  if (diffs.count == 0) {\n    return patches;   // Get rid of the nil case.\n  }\n  Patch *patch = [[Patch new] autorelease];\n  NSUInteger char_count1 = 0;  // Number of characters into the text1 NSString.\n  NSUInteger char_count2 = 0;  // Number of characters into the text2 NSString.\n  // Start with text1 (prepatch_text) and apply the diffs until we arrive at\n  // text2 (postpatch_text). We recreate the patches one by one to determine\n  // context info.\n  NSString *prepatch_text = [text1 retain];\n  NSMutableString *postpatch_text = [text1 mutableCopy];\n  for (Diff *aDiff in diffs) {\n    if (patch.diffs.count == 0 && aDiff.operation != DIFF_EQUAL) {\n      // A new patch starts here.\n      patch.start1 = char_count1;\n      patch.start2 = char_count2;\n    }\n\n    switch (aDiff.operation) {\n      case DIFF_INSERT:\n        [patch.diffs addObject:aDiff];\n        patch.length2 += aDiff.text.length;\n        [postpatch_text insertString:aDiff.text atIndex:char_count2];\n        break;\n      case DIFF_DELETE:\n        patch.length1 += aDiff.text.length;\n        [patch.diffs addObject:aDiff];\n        [postpatch_text deleteCharactersInRange:NSMakeRange(char_count2, aDiff.text.length)];\n        break;\n      case DIFF_EQUAL:\n        if (aDiff.text.length <= 2 * Patch_Margin\n          && [patch.diffs count] != 0 && aDiff != diffs.lastObject) {\n          // Small equality inside a patch.\n          [patch.diffs addObject:aDiff];\n          patch.length1 += aDiff.text.length;\n          patch.length2 += aDiff.text.length;\n        }\n\n        if (aDiff.text.length >= 2 * Patch_Margin) {\n          // Time for a new patch.\n          if (patch.diffs.count != 0) {\n            [self patch_addContextToPatch:patch sourceText:prepatch_text];\n            [patches addObject:patch];\n            patch = [[Patch new] autorelease];\n            // Unlike Unidiff, our patch lists have a rolling context.\n            // https://github.com/google/diff-match-patch/wiki/Unidiff\n            // Update prepatch text & pos to reflect the application of the\n            // just completed patch.\n            [prepatch_text release];\n            prepatch_text = [postpatch_text copy];\n            char_count1 = char_count2;\n          }\n        }\n        break;\n    }\n\n    // Update the current character count.\n    if (aDiff.operation != DIFF_INSERT) {\n      char_count1 += aDiff.text.length;\n    }\n    if (aDiff.operation != DIFF_DELETE) {\n      char_count2 += aDiff.text.length;\n    }\n  }\n  // Pick up the leftover patch if not empty.\n  if (patch.diffs.count != 0) {\n    [self patch_addContextToPatch:patch sourceText:prepatch_text];\n    [patches addObject:patch];\n  }\n\n  [prepatch_text release];\n  [postpatch_text release];\n\n  return patches;\n}\n\n/**\n * Given an array of patches, return another array that is identical.\n * @param patches NSArray of Patch objects.\n * @return NSMutableArray of Patch objects.\n */\n- (NSMutableArray *)patch_deepCopy:(NSArray *)patches;\n{\n  NSMutableArray *patchesCopy = [[NSMutableArray alloc] initWithArray:patches copyItems:YES];\n  return patchesCopy;\n}\n\n/**\n * Merge a set of patches onto the text.  Return a patched text, as well\n * as an array of YES/NO values indicating which patches were applied.\n * @param sourcePatches NSMutableArray of Patch objects\n * @param text Old text.\n * @return Two element NSArray, containing the new text and an array of\n *      BOOL values.\n */\n- (NSArray *)patch_apply:(NSArray *)sourcePatches\n                toString:(NSString *)text;\n{\n  if (sourcePatches.count == 0) {\n    return [NSArray arrayWithObjects:text, [NSMutableArray array], nil];\n  }\n\n  // Deep copy the patches so that no changes are made to originals.\n  NSMutableArray *patches = [self patch_deepCopy:sourcePatches];\n\n  NSMutableString *textMutable = [[text mutableCopy] autorelease];\n\n  NSString *nullPadding = [self patch_addPadding:patches];\n  [textMutable insertString:nullPadding atIndex:0];\n  [textMutable appendString:nullPadding];\n  [self patch_splitMax:patches];\n\n  NSUInteger x = 0;\n  // delta keeps track of the offset between the expected and actual\n  // location of the previous patch.  If there are patches expected at\n  // positions 10 and 20, but the first patch was found at 12, delta is 2\n  // and the second patch has an effective expected position of 22.\n  NSUInteger delta = 0;\n  BOOL *results = (BOOL *)calloc(patches.count, sizeof(BOOL));\n  for (Patch *aPatch in patches) {\n    NSUInteger expected_loc = aPatch.start2 + delta;\n    NSString *text1 = [self diff_text1:aPatch.diffs];\n    NSUInteger start_loc;\n    NSUInteger end_loc = NSNotFound;\n    if (text1.length > Match_MaxBits) {\n      // patch_splitMax will only provide an oversized pattern\n      // in the case of a monster delete.\n      start_loc = [self match_mainForText:textMutable\n                                  pattern:[text1 substringWithRange:NSMakeRange(0, Match_MaxBits)]\n                                     near:expected_loc];\n      if (start_loc != NSNotFound) {\n        end_loc = [self match_mainForText:textMutable\n            pattern:[text1 substringFromIndex:text1.length - Match_MaxBits]\n            near:(expected_loc + text1.length - Match_MaxBits)];\n        if (end_loc == NSNotFound || start_loc >= end_loc) {\n          // Can't find valid trailing context.   Drop this patch.\n          start_loc = NSNotFound;\n        }\n      }\n    } else {\n      start_loc = [self match_mainForText:textMutable pattern:text1 near:expected_loc];\n    }\n    if (start_loc == NSNotFound) {\n      // No match found.  :(\n      results[x] = NO;\n      // Subtract the delta for this failed patch from subsequent patches.\n      delta -= aPatch.length2 - aPatch.length1;\n    } else {\n      // Found a match.   :)\n      results[x] = YES;\n      delta = start_loc - expected_loc;\n      NSString *text2;\n      if (end_loc == NSNotFound) {\n        text2 = [textMutable diff_javaSubstringFromStart:start_loc\n            toEnd:MIN(start_loc + text1.length, textMutable.length)];\n      } else {\n        text2 = [textMutable diff_javaSubstringFromStart:start_loc\n            toEnd:MIN(end_loc + Match_MaxBits, textMutable.length)];\n      }\n      if (text1 == text2) {\n        // Perfect match, just shove the Replacement text in.\n        [textMutable replaceCharactersInRange:NSMakeRange(start_loc, text1.length) withString:[self diff_text2:aPatch.diffs]];\n      } else {\n        // Imperfect match.   Run a diff to get a framework of equivalent\n        // indices.\n        NSMutableArray *diffs = [self diff_mainOfOldString:text1 andNewString:text2 checkLines:NO];\n        if (text1.length > Match_MaxBits\n            && ([self diff_levenshtein:diffs] / (float)text1.length)\n            > Patch_DeleteThreshold) {\n          // The end points match, but the content is unacceptably bad.\n          results[x] = NO;\n        } else {\n          [self diff_cleanupSemanticLossless:diffs];\n          NSUInteger index1 = 0;\n          for (Diff *aDiff in aPatch.diffs) {\n            if (aDiff.operation != DIFF_EQUAL) {\n              NSUInteger index2 = [self diff_xIndexIn:diffs location:index1];\n              if (aDiff.operation == DIFF_INSERT) {\n                // Insertion\n                [textMutable insertString:aDiff.text atIndex:(start_loc + index2)];\n              } else if (aDiff.operation == DIFF_DELETE) {\n                // Deletion\n                [textMutable deleteCharactersInRange:NSMakeRange(start_loc + index2,\n                    ([self diff_xIndexIn:diffs\n                    location:(index1 + aDiff.text.length)] - index2))];\n              }\n            }\n            if (aDiff.operation != DIFF_DELETE) {\n              index1 += aDiff.text.length;\n            }\n          }\n        }\n      }\n    }\n    x++;\n  }\n\n  NSMutableArray *resultsArray = [NSMutableArray arrayWithCapacity:patches.count];\n  for (NSUInteger i = 0; i < patches.count; i++) {\n    [resultsArray addObject:[NSNumber numberWithBool:(results[i])]];\n  }\n\n  if (results != NULL) {\n    free(results);\n  }\n\n  // Strip the padding off.\n  text = [textMutable substringWithRange:NSMakeRange(nullPadding.length,\n      textMutable.length - 2 * nullPadding.length)];\n  [patches release];\n  return [NSArray arrayWithObjects:text, resultsArray, nil];\n}\n\n/**\n * Add some padding on text start and end so that edges can match something.\n * Intended to be called only from within patch_apply.\n * @param patches NSMutableArray of Patch objects.\n * @return The padding NSString added to each side.\n */\n- (NSString *)patch_addPadding:(NSMutableArray *)patches;\n{\n  uint16_t paddingLength = Patch_Margin;\n  NSMutableString *nullPadding = [NSMutableString string];\n  for (UniChar x = 1; x <= paddingLength; x++) {\n    CFStringAppendCharacters((CFMutableStringRef)nullPadding, &x, 1);\n  }\n\n  // Bump all the patches forward.\n  for (Patch *aPatch in patches) {\n    aPatch.start1 += paddingLength;\n    aPatch.start2 += paddingLength;\n  }\n\n  // Add some padding on start of first diff.\n  Patch *patch = [patches objectAtIndex:0];\n  NSMutableArray *diffs = patch.diffs;\n  if (diffs.count == 0 || ((Diff *)[diffs objectAtIndex:0]).operation != DIFF_EQUAL) {\n    // Add nullPadding equality.\n    [diffs insertObject:[Diff diffWithOperation:DIFF_EQUAL andText:nullPadding] atIndex:0];\n    patch.start1 -= paddingLength;  // Should be 0.\n    patch.start2 -= paddingLength;  // Should be 0.\n    patch.length1 += paddingLength;\n    patch.length2 += paddingLength;\n  } else if (paddingLength > ((Diff *)[diffs objectAtIndex:0]).text.length) {\n    // Grow first equality.\n    Diff *firstDiff = [diffs objectAtIndex:0];\n    NSUInteger extraLength = paddingLength - firstDiff.text.length;\n    firstDiff.text = [[nullPadding substringFromIndex:(firstDiff.text.length)]\n              stringByAppendingString:firstDiff.text];\n    patch.start1 -= extraLength;\n    patch.start2 -= extraLength;\n    patch.length1 += extraLength;\n    patch.length2 += extraLength;\n  }\n\n  // Add some padding on end of last diff.\n  patch = patches.lastObject;\n  diffs = patch.diffs;\n  if (diffs.count == 0 || ((Diff *)diffs.lastObject).operation != DIFF_EQUAL) {\n    // Add nullPadding equality.\n    [diffs addObject:[Diff diffWithOperation:DIFF_EQUAL andText:nullPadding]];\n    patch.length1 += paddingLength;\n    patch.length2 += paddingLength;\n  } else if (paddingLength > ((Diff *)diffs.lastObject).text.length) {\n    // Grow last equality.\n    Diff *lastDiff = diffs.lastObject;\n    NSUInteger extraLength = paddingLength - lastDiff.text.length;\n    lastDiff.text = [lastDiff.text stringByAppendingString:[nullPadding substringWithRange:NSMakeRange(0, extraLength)]];\n    patch.length1 += extraLength;\n    patch.length2 += extraLength;\n  }\n\n  return nullPadding;\n}\n\n/**\n * Look through the patches and break up any which are longer than the\n * maximum limit of the match algorithm.\n * Intended to be called only from within patch_apply.\n * @param patches NSMutableArray of Patch objects.\n */\n- (void)patch_splitMax:(NSMutableArray *)patches;\n{\n  NSUInteger patch_size = Match_MaxBits;\n  for (NSUInteger x = 0; x < patches.count; x++) {\n    if (((Patch *)[patches objectAtIndex:x]).length1 <= patch_size) {\n      continue;\n    }\n    Patch *bigpatch = [[patches objectAtIndex:x] retain];\n    // Remove the big old patch.\n    splice(patches, x--, 1, nil);\n    NSUInteger start1 = bigpatch.start1;\n    NSUInteger start2 = bigpatch.start2;\n    NSString *precontext = @\"\";\n    while (bigpatch.diffs.count != 0) {\n      // Create one of several smaller patches.\n      Patch *patch = [[Patch new] autorelease];\n      BOOL empty = YES;\n      patch.start1 = start1 - precontext.length;\n      patch.start2 = start2 - precontext.length;\n      if (precontext.length != 0) {\n        patch.length1 = patch.length2 = precontext.length;\n        [patch.diffs addObject:[Diff diffWithOperation:DIFF_EQUAL andText:precontext]];\n      }\n      while (bigpatch.diffs.count != 0\n          && patch.length1 < patch_size - self.Patch_Margin) {\n        Operation diff_type = ((Diff *)[bigpatch.diffs objectAtIndex:0]).operation;\n        NSString *diff_text = ((Diff *)[bigpatch.diffs objectAtIndex:0]).text;\n        if (diff_type == DIFF_INSERT) {\n          // Insertions are harmless.\n          patch.length2 += diff_text.length;\n          start2 += diff_text.length;\n          [patch.diffs addObject:[bigpatch.diffs objectAtIndex:0]];\n          [bigpatch.diffs removeObjectAtIndex:0];\n          empty = NO;\n        } else if (diff_type == DIFF_DELETE && patch.diffs.count == 1\n              && ((Diff *)[patch.diffs objectAtIndex:0]).operation == DIFF_EQUAL\n              && diff_text.length > 2 * patch_size) {\n          // This is a large deletion.  Let it pass in one chunk.\n          patch.length1 += diff_text.length;\n          start1 += diff_text.length;\n          empty = NO;\n          [patch.diffs addObject:[Diff diffWithOperation:diff_type andText:diff_text]];\n          [bigpatch.diffs removeObjectAtIndex:0];\n        } else {\n          // Deletion or equality.  Only take as much as we can stomach.\n          diff_text = [diff_text substringWithRange:NSMakeRange(0,\n              MIN(diff_text.length,\n              (patch_size - patch.length1 - Patch_Margin)))];\n          patch.length1 += diff_text.length;\n          start1 += diff_text.length;\n          if (diff_type == DIFF_EQUAL) {\n            patch.length2 += diff_text.length;\n            start2 += diff_text.length;\n          } else {\n            empty = NO;\n          }\n          [patch.diffs addObject:[Diff diffWithOperation:diff_type andText:diff_text]];\n          if (diff_text == ((Diff *)[bigpatch.diffs objectAtIndex:0]).text) {\n            [bigpatch.diffs removeObjectAtIndex:0];\n          } else {\n            Diff *firstDiff = [bigpatch.diffs objectAtIndex:0];\n            firstDiff.text = [firstDiff.text substringFromIndex:diff_text.length];\n          }\n        }\n      }\n      // Compute the head context for the next patch.\n      precontext = [self diff_text2:patch.diffs];\n      precontext = [precontext substringFromIndex:MAX_OF_CONST_AND_DIFF(0, precontext.length, Patch_Margin)];\n\n      NSString *postcontext = nil;\n      // Append the end context for this patch.\n      if ([self diff_text1:bigpatch.diffs].length > Patch_Margin) {\n        postcontext = [[self diff_text1:bigpatch.diffs]\n                 substringWithRange:NSMakeRange(0, Patch_Margin)];\n      } else {\n        postcontext = [self diff_text1:bigpatch.diffs];\n      }\n\n      if (postcontext.length != 0) {\n        patch.length1 += postcontext.length;\n        patch.length2 += postcontext.length;\n        if (patch.diffs.count != 0\n            && ((Diff *)[patch.diffs objectAtIndex:(patch.diffs.count - 1)]).operation\n            == DIFF_EQUAL) {\n          Diff *lastDiff = [patch.diffs lastObject];\n          lastDiff.text = [lastDiff.text stringByAppendingString:postcontext];\n        } else {\n          [patch.diffs addObject:[Diff diffWithOperation:DIFF_EQUAL andText:postcontext]];\n        }\n      }\n      if (!empty) {\n        splice(patches, ++x, 0, [NSMutableArray arrayWithObject:patch]);\n      }\n    }\n\n    [bigpatch release];\n\n  }\n}\n\n/**\n * Take a list of patches and return a textual representation.\n * @param patches NSMutableArray of Patch objects.\n * @return Text representation of patches.\n */\n- (NSString *)patch_toText:(NSMutableArray *)patches;\n{\n  NSMutableString *text = [NSMutableString string];\n  for (Patch *aPatch in patches) {\n    [text appendString:[aPatch description]];\n  }\n  return text;\n}\n\n/**\n * Parse a textual representation of patches and return a NSMutableArray of\n * Patch objects.\n * @param textline Text representation of patches.\n * @param error NSError if invalid input.\n * @return NSMutableArray of Patch objects.\n */\n- (NSMutableArray *)patch_fromText:(NSString *)textline\n                             error:(NSError **)error;\n{\n  NSMutableArray *patches = [NSMutableArray array];\n  if (textline.length == 0) {\n    return patches;\n  }\n  NSArray *text = [textline componentsSeparatedByString:@\"\\n\"];\n  NSUInteger textPointer = 0;\n  Patch *patch;\n  //NSString *patchHeader = @\"^@@ -(\\\\d+),?(\\\\d*) \\\\+(\\\\d+),?(\\\\d*) @@$\";\n  NSString *patchHeaderStart = @\"@@ -\";\n  NSString *patchHeaderMid = @\"+\";\n  NSString *patchHeaderEnd = @\"@@\";\n  NSString *optionalValueDelimiter = @\",\";\n  BOOL scanSuccess, hasOptional;\n  NSInteger scannedValue, optionalValue;\n  NSDictionary *errorDetail = nil;\n\n  unichar sign;\n  NSString *line;\n  while (textPointer < text.count) {\n    NSString *thisLine = [text objectAtIndex:textPointer];\n    NSScanner *theScanner = [NSScanner scannerWithString:thisLine];\n    patch = [[Patch new] autorelease];\n\n    scanSuccess = ([theScanner scanString:patchHeaderStart intoString:NULL]\n        && [theScanner scanInteger:&scannedValue]);\n\n    if (scanSuccess) {\n      patch.start1 = scannedValue;\n\n      hasOptional = [theScanner scanString:optionalValueDelimiter intoString:NULL];\n\n      if (hasOptional) {\n        // First set has an optional value.\n        scanSuccess = [theScanner scanInteger:&optionalValue];\n        if (scanSuccess) {\n          if (optionalValue == 0) {\n            patch.length1 = 0;\n          } else {\n            patch.start1--;\n            patch.length1 = optionalValue;\n          }\n        }\n      } else {\n        patch.start1--;\n        patch.length1 = 1;\n      }\n\n      if (scanSuccess) {\n        scanSuccess = ([theScanner scanString:patchHeaderMid intoString:NULL]\n            && [theScanner scanInteger:&scannedValue]);\n\n        if (scanSuccess) {\n          patch.start2 = scannedValue;\n\n          hasOptional = [theScanner scanString:optionalValueDelimiter intoString:NULL];\n\n          if (hasOptional) {\n            // Second set has an optional value.\n            scanSuccess = [theScanner scanInteger:&optionalValue];\n            if (scanSuccess) {\n              if (optionalValue == 0) {\n                patch.length2 = 0;\n              } else {\n                patch.start2--;\n                patch.length2 = optionalValue;\n              }\n            }\n          } else {\n            patch.start2--;\n            patch.length2 = 1;\n          }\n\n          if (scanSuccess) {\n            scanSuccess = ([theScanner scanString:patchHeaderEnd intoString:NULL]\n                && [theScanner isAtEnd] == YES);\n          }\n        }\n      }\n    }\n\n    if (!scanSuccess) {\n      if (error != NULL) {\n        errorDetail = [NSDictionary dictionaryWithObjectsAndKeys:\n            [NSString stringWithFormat:NSLocalizedString(@\"Invalid patch string: %@\", @\"Error\"),\n             [text objectAtIndex:textPointer]],\n            NSLocalizedDescriptionKey, nil];\n        *error = [NSError errorWithDomain:@\"DiffMatchPatchErrorDomain\" code:104 userInfo:errorDetail];\n      }\n      return nil;\n    }\n\n    [patches addObject:patch];\n\n    textPointer++;\n\n    while (textPointer < text.count) {\n      @try {\n        sign = [[text objectAtIndex:textPointer] characterAtIndex:0];\n      }\n      @catch (NSException *e) {\n        // Blank line?  Whatever.\n        textPointer++;\n        continue;\n      }\n      line = [[[text objectAtIndex:textPointer] substringFromIndex:1]\n              diff_stringByReplacingPercentEscapesForEncodeUriCompatibility];\n      if (sign == '-') {\n        // Deletion.\n        [patch.diffs addObject:[Diff diffWithOperation:DIFF_DELETE andText:line]];\n      } else if (sign == '+') {\n        // Insertion.\n        [patch.diffs addObject:[Diff diffWithOperation:DIFF_INSERT andText:line]];\n      } else if (sign == ' ') {\n        // Minor equality.\n        [patch.diffs addObject:[Diff diffWithOperation:DIFF_EQUAL andText:line]];\n      } else if (sign == '@') {\n        // Start of next patch.\n        break;\n      } else {\n        // WTF?\n        if (error != NULL) {\n          errorDetail = [NSDictionary dictionaryWithObjectsAndKeys:\n              [NSString stringWithFormat:NSLocalizedString(@\"Invalid patch mode '%C' in: %@\", @\"Error\"), sign, line],\n              NSLocalizedDescriptionKey, nil];\n          *error = [NSError errorWithDomain:@\"DiffMatchPatchErrorDomain\" code:104 userInfo:errorDetail];\n        }\n        return nil;\n      }\n      textPointer++;\n    }\n  }\n  return patches;\n}\n\n@end\n"
  },
  {
    "path": "objectivec/DiffMatchPatch.xcodeproj/project.pbxproj",
    "content": "// !$*UTF8*$!\n{\n\tarchiveVersion = 1;\n\tclasses = {\n\t};\n\tobjectVersion = 45;\n\tobjects = {\n\n/* Begin PBXBuildFile section */\n\t\t3D08D25412A71B9C007A5316 /* NSString+UnicharUtilities.m in Sources */ = {isa = PBXBuildFile; fileRef = 3D08D25212A71B9C007A5316 /* NSString+UnicharUtilities.m */; };\n\t\t3D5BC080128C44A700B8F5FF /* DiffMatchPatch.h in Headers */ = {isa = PBXBuildFile; fileRef = 3D5BC07E128C44A700B8F5FF /* DiffMatchPatch.h */; settings = {ATTRIBUTES = (Public, ); }; };\n\t\t3D5BC081128C44A700B8F5FF /* DiffMatchPatch.m in Sources */ = {isa = PBXBuildFile; fileRef = 3D5BC07F128C44A700B8F5FF /* DiffMatchPatch.m */; };\n\t\t3D70BCC9128EDAF80078D1A6 /* NSString+UriCompatibility.m in Sources */ = {isa = PBXBuildFile; fileRef = 3D70BCC7128EDAF80078D1A6 /* NSString+UriCompatibility.m */; };\n\t\t3D96F44A12AFC82F00C3E5C0 /* speedtest.m in Sources */ = {isa = PBXBuildFile; fileRef = 3D96F44912AFC82F00C3E5C0 /* speedtest.m */; };\n\t\t3D96F44F12AFC96A00C3E5C0 /* DiffMatchPatch.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 8DC2EF5B0486A6940098B216 /* DiffMatchPatch.framework */; };\n\t\t3DA641D1128DE21D00B33CE9 /* DiffMatchPatchCFUtilities.c in Sources */ = {isa = PBXBuildFile; fileRef = 3DA64170128DDA3400B33CE9 /* DiffMatchPatchCFUtilities.c */; };\n\t\t3DC87026129FF4B6001F602B /* NSString+JavaSubstring.m in Sources */ = {isa = PBXBuildFile; fileRef = 3DC87024129FF4B6001F602B /* NSString+JavaSubstring.m */; };\n\t\t3DEE4C73129D484D00885485 /* NSMutableDictionary+DMPExtensions.m in Sources */ = {isa = PBXBuildFile; fileRef = 3DEE4C71129D484D00885485 /* NSMutableDictionary+DMPExtensions.m */; };\n\t\t4CF04C3620323A1100945640 /* DiffMatchPatch.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 8DC2EF5B0486A6940098B216 /* DiffMatchPatch.framework */; };\n\t\t4CF04C3E20323A8200945640 /* DiffMatchPatchTest.m in Sources */ = {isa = PBXBuildFile; fileRef = 3D5BC0DE128CAAE400B8F5FF /* DiffMatchPatchTest.m */; settings = {COMPILER_FLAGS = \"-fno-objc-arc\"; }; };\n\t\t8DC2EF530486A6940098B216 /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = 089C1666FE841158C02AAC07 /* InfoPlist.strings */; };\n\t\tE2C74E4A212B1FEE00947DE0 /* DiffMatchPatch.h in Headers */ = {isa = PBXBuildFile; fileRef = 3D5BC07E128C44A700B8F5FF /* DiffMatchPatch.h */; settings = {ATTRIBUTES = (Public, ); }; };\n\t\tE2C74E4B212B200F00947DE0 /* DiffMatchPatch.m in Sources */ = {isa = PBXBuildFile; fileRef = 3D5BC07F128C44A700B8F5FF /* DiffMatchPatch.m */; };\n\t\tE2C74E4C212B200F00947DE0 /* NSString+JavaSubstring.m in Sources */ = {isa = PBXBuildFile; fileRef = 3DC87024129FF4B6001F602B /* NSString+JavaSubstring.m */; };\n\t\tE2C74E4D212B200F00947DE0 /* NSString+UnicharUtilities.m in Sources */ = {isa = PBXBuildFile; fileRef = 3D08D25212A71B9C007A5316 /* NSString+UnicharUtilities.m */; };\n\t\tE2C74E4E212B200F00947DE0 /* NSString+UriCompatibility.m in Sources */ = {isa = PBXBuildFile; fileRef = 3D70BCC7128EDAF80078D1A6 /* NSString+UriCompatibility.m */; };\n\t\tE2C74E4F212B200F00947DE0 /* NSMutableDictionary+DMPExtensions.m in Sources */ = {isa = PBXBuildFile; fileRef = 3DEE4C71129D484D00885485 /* NSMutableDictionary+DMPExtensions.m */; };\n\t\tE2C74E50212B200F00947DE0 /* DiffMatchPatchCFUtilities.c in Sources */ = {isa = PBXBuildFile; fileRef = 3DA64170128DDA3400B33CE9 /* DiffMatchPatchCFUtilities.c */; };\n\t\tE2C74E51212B201600947DE0 /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = 089C1666FE841158C02AAC07 /* InfoPlist.strings */; };\n/* End PBXBuildFile section */\n\n/* Begin PBXContainerItemProxy section */\n\t\t3D96F3B812AFC6CD00C3E5C0 /* PBXContainerItemProxy */ = {\n\t\t\tisa = PBXContainerItemProxy;\n\t\t\tcontainerPortal = 0867D690FE84028FC02AAC07 /* Project object */;\n\t\t\tproxyType = 1;\n\t\t\tremoteGlobalIDString = 8DC2EF4F0486A6940098B216;\n\t\t\tremoteInfo = DiffMatchPatch;\n\t\t};\n\t\t4CF04C3720323A1100945640 /* PBXContainerItemProxy */ = {\n\t\t\tisa = PBXContainerItemProxy;\n\t\t\tcontainerPortal = 0867D690FE84028FC02AAC07 /* Project object */;\n\t\t\tproxyType = 1;\n\t\t\tremoteGlobalIDString = 8DC2EF4F0486A6940098B216;\n\t\t\tremoteInfo = DiffMatchPatch;\n\t\t};\n/* End PBXContainerItemProxy section */\n\n/* Begin PBXFileReference section */\n\t\t089C1667FE841158C02AAC07 /* English */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.strings; name = English; path = English.lproj/InfoPlist.strings; sourceTree = \"<group>\"; };\n\t\t32DBCF5E0370ADEE00C91783 /* DiffMatchPatch_Prefix.pch */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = DiffMatchPatch_Prefix.pch; sourceTree = \"<group>\"; };\n\t\t3D08D25112A71B9C007A5316 /* NSString+UnicharUtilities.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = \"NSString+UnicharUtilities.h\"; sourceTree = \"<group>\"; };\n\t\t3D08D25212A71B9C007A5316 /* NSString+UnicharUtilities.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = \"NSString+UnicharUtilities.m\"; sourceTree = \"<group>\"; };\n\t\t3D5BC07E128C44A700B8F5FF /* DiffMatchPatch.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = DiffMatchPatch.h; sourceTree = \"<group>\"; };\n\t\t3D5BC07F128C44A700B8F5FF /* DiffMatchPatch.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = DiffMatchPatch.m; sourceTree = \"<group>\"; };\n\t\t3D5BC0B0128CA8E200B8F5FF /* DiffMatchPatchTest-Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = \"DiffMatchPatchTest-Info.plist\"; sourceTree = \"<group>\"; };\n\t\t3D5BC0DD128CAAE400B8F5FF /* DiffMatchPatchTest.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = DiffMatchPatchTest.h; sourceTree = \"<group>\"; };\n\t\t3D5BC0DE128CAAE400B8F5FF /* DiffMatchPatchTest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = DiffMatchPatchTest.m; sourceTree = \"<group>\"; };\n\t\t3D70BCC6128EDAF80078D1A6 /* NSString+UriCompatibility.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = \"NSString+UriCompatibility.h\"; sourceTree = \"<group>\"; };\n\t\t3D70BCC7128EDAF80078D1A6 /* NSString+UriCompatibility.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = \"NSString+UriCompatibility.m\"; sourceTree = \"<group>\"; };\n\t\t3D96F3A212AFC68900C3E5C0 /* speedtest */ = {isa = PBXFileReference; explicitFileType = \"compiled.mach-o.executable\"; includeInIndex = 0; path = speedtest; sourceTree = BUILT_PRODUCTS_DIR; };\n\t\t3D96F43E12AFC76600C3E5C0 /* speedtest_Prefix.pch */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = speedtest_Prefix.pch; path = ../speedtest_Prefix.pch; sourceTree = \"<group>\"; };\n\t\t3D96F44912AFC82F00C3E5C0 /* speedtest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = speedtest.m; sourceTree = \"<group>\"; };\n\t\t3DA6416F128DDA3400B33CE9 /* DiffMatchPatchCFUtilities.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = DiffMatchPatchCFUtilities.h; sourceTree = \"<group>\"; };\n\t\t3DA64170128DDA3400B33CE9 /* DiffMatchPatchCFUtilities.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = DiffMatchPatchCFUtilities.c; sourceTree = \"<group>\"; };\n\t\t3DA642BC128DEF4900B33CE9 /* MinMaxMacros.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MinMaxMacros.h; sourceTree = \"<group>\"; };\n\t\t3DC87023129FF4B6001F602B /* NSString+JavaSubstring.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = \"NSString+JavaSubstring.h\"; sourceTree = \"<group>\"; };\n\t\t3DC87024129FF4B6001F602B /* NSString+JavaSubstring.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = \"NSString+JavaSubstring.m\"; sourceTree = \"<group>\"; };\n\t\t3DEE4C70129D484D00885485 /* NSMutableDictionary+DMPExtensions.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = \"NSMutableDictionary+DMPExtensions.h\"; sourceTree = \"<group>\"; };\n\t\t3DEE4C71129D484D00885485 /* NSMutableDictionary+DMPExtensions.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = \"NSMutableDictionary+DMPExtensions.m\"; sourceTree = \"<group>\"; };\n\t\t4CF04C272032368B00945640 /* Speedtest2.txt */ = {isa = PBXFileReference; lastKnownFileType = text; path = Speedtest2.txt; sourceTree = \"<group>\"; };\n\t\t4CF04C282032368B00945640 /* Speedtest1.txt */ = {isa = PBXFileReference; lastKnownFileType = text; path = Speedtest1.txt; sourceTree = \"<group>\"; };\n\t\t4CF04C2B2032394300945640 /* XCTest.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = XCTest.framework; path = Platforms/MacOSX.platform/Developer/Library/Frameworks/XCTest.framework; sourceTree = DEVELOPER_DIR; };\n\t\t4CF04C3120323A1100945640 /* DiffMatchPatchTest.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = DiffMatchPatchTest.xctest; sourceTree = BUILT_PRODUCTS_DIR; };\n\t\t4CF04C40203240AB00945640 /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = ../../../../../System/Library/Frameworks/Foundation.framework; sourceTree = \"<group>\"; };\n\t\t8DC2EF5A0486A6940098B216 /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = \"<group>\"; };\n\t\t8DC2EF5B0486A6940098B216 /* DiffMatchPatch.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = DiffMatchPatch.framework; sourceTree = BUILT_PRODUCTS_DIR; };\n\t\tE2C74E40212B1F8E00947DE0 /* DiffMatchPatch.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = DiffMatchPatch.framework; sourceTree = BUILT_PRODUCTS_DIR; };\n/* End PBXFileReference section */\n\n/* Begin PBXFrameworksBuildPhase section */\n\t\t3D96F3A012AFC68900C3E5C0 /* Frameworks */ = {\n\t\t\tisa = PBXFrameworksBuildPhase;\n\t\t\tbuildActionMask = 2147483647;\n\t\t\tfiles = (\n\t\t\t\t3D96F44F12AFC96A00C3E5C0 /* DiffMatchPatch.framework in Frameworks */,\n\t\t\t);\n\t\t\trunOnlyForDeploymentPostprocessing = 0;\n\t\t};\n\t\t4CF04C2E20323A1100945640 /* Frameworks */ = {\n\t\t\tisa = PBXFrameworksBuildPhase;\n\t\t\tbuildActionMask = 2147483647;\n\t\t\tfiles = (\n\t\t\t\t4CF04C3620323A1100945640 /* DiffMatchPatch.framework in Frameworks */,\n\t\t\t);\n\t\t\trunOnlyForDeploymentPostprocessing = 0;\n\t\t};\n\t\t8DC2EF560486A6940098B216 /* Frameworks */ = {\n\t\t\tisa = PBXFrameworksBuildPhase;\n\t\t\tbuildActionMask = 2147483647;\n\t\t\tfiles = (\n\t\t\t);\n\t\t\trunOnlyForDeploymentPostprocessing = 0;\n\t\t};\n\t\tE2C74E3C212B1F8E00947DE0 /* Frameworks */ = {\n\t\t\tisa = PBXFrameworksBuildPhase;\n\t\t\tbuildActionMask = 2147483647;\n\t\t\tfiles = (\n\t\t\t);\n\t\t\trunOnlyForDeploymentPostprocessing = 0;\n\t\t};\n/* End PBXFrameworksBuildPhase section */\n\n/* Begin PBXGroup section */\n\t\t034768DFFF38A50411DB9C8B /* Products */ = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\t8DC2EF5B0486A6940098B216 /* DiffMatchPatch.framework */,\n\t\t\t\t3D96F3A212AFC68900C3E5C0 /* speedtest */,\n\t\t\t\t4CF04C3120323A1100945640 /* DiffMatchPatchTest.xctest */,\n\t\t\t\tE2C74E40212B1F8E00947DE0 /* DiffMatchPatch.framework */,\n\t\t\t);\n\t\t\tname = Products;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\t0867D691FE84028FC02AAC07 /* DiffMatchPatch */ = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\t08FB77AEFE84172EC02AAC07 /* Classes */,\n\t\t\t\t32C88DFF0371C24200C91783 /* Other Sources */,\n\t\t\t\t089C1665FE841158C02AAC07 /* Resources */,\n\t\t\t\t3D5BC0D9128CAA7600B8F5FF /* Tests */,\n\t\t\t\t034768DFFF38A50411DB9C8B /* Products */,\n\t\t\t\t4CF04C2A2032394300945640 /* Frameworks */,\n\t\t\t);\n\t\t\tname = DiffMatchPatch;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\t089C1665FE841158C02AAC07 /* Resources */ = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\t8DC2EF5A0486A6940098B216 /* Info.plist */,\n\t\t\t\t089C1666FE841158C02AAC07 /* InfoPlist.strings */,\n\t\t\t);\n\t\t\tname = Resources;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\t08FB77AEFE84172EC02AAC07 /* Classes */ = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\t3D5BC07E128C44A700B8F5FF /* DiffMatchPatch.h */,\n\t\t\t\t3D5BC07F128C44A700B8F5FF /* DiffMatchPatch.m */,\n\t\t\t\t3DC87023129FF4B6001F602B /* NSString+JavaSubstring.h */,\n\t\t\t\t3DC87024129FF4B6001F602B /* NSString+JavaSubstring.m */,\n\t\t\t\t3D08D25112A71B9C007A5316 /* NSString+UnicharUtilities.h */,\n\t\t\t\t3D08D25212A71B9C007A5316 /* NSString+UnicharUtilities.m */,\n\t\t\t\t3D70BCC6128EDAF80078D1A6 /* NSString+UriCompatibility.h */,\n\t\t\t\t3D70BCC7128EDAF80078D1A6 /* NSString+UriCompatibility.m */,\n\t\t\t\t3DEE4C70129D484D00885485 /* NSMutableDictionary+DMPExtensions.h */,\n\t\t\t\t3DEE4C71129D484D00885485 /* NSMutableDictionary+DMPExtensions.m */,\n\t\t\t);\n\t\t\tname = Classes;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\t32C88DFF0371C24200C91783 /* Other Sources */ = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\t32DBCF5E0370ADEE00C91783 /* DiffMatchPatch_Prefix.pch */,\n\t\t\t\t3DA642BC128DEF4900B33CE9 /* MinMaxMacros.h */,\n\t\t\t\t3DA6416F128DDA3400B33CE9 /* DiffMatchPatchCFUtilities.h */,\n\t\t\t\t3DA64170128DDA3400B33CE9 /* DiffMatchPatchCFUtilities.c */,\n\t\t\t);\n\t\t\tname = \"Other Sources\";\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\t3D5BC0D9128CAA7600B8F5FF /* Tests */ = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\t4CF04C282032368B00945640 /* Speedtest1.txt */,\n\t\t\t\t4CF04C272032368B00945640 /* Speedtest2.txt */,\n\t\t\t\t3D5BC0B0128CA8E200B8F5FF /* DiffMatchPatchTest-Info.plist */,\n\t\t\t\t3D5BC0DD128CAAE400B8F5FF /* DiffMatchPatchTest.h */,\n\t\t\t\t3D5BC0DE128CAAE400B8F5FF /* DiffMatchPatchTest.m */,\n\t\t\t\t3D96F43E12AFC76600C3E5C0 /* speedtest_Prefix.pch */,\n\t\t\t\t3D96F44912AFC82F00C3E5C0 /* speedtest.m */,\n\t\t\t);\n\t\t\tpath = Tests;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\t4CF04C2A2032394300945640 /* Frameworks */ = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\t4CF04C40203240AB00945640 /* Foundation.framework */,\n\t\t\t\t4CF04C2B2032394300945640 /* XCTest.framework */,\n\t\t\t);\n\t\t\tname = Frameworks;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n/* End PBXGroup section */\n\n/* Begin PBXHeadersBuildPhase section */\n\t\t8DC2EF500486A6940098B216 /* Headers */ = {\n\t\t\tisa = PBXHeadersBuildPhase;\n\t\t\tbuildActionMask = 2147483647;\n\t\t\tfiles = (\n\t\t\t\t3D5BC080128C44A700B8F5FF /* DiffMatchPatch.h in Headers */,\n\t\t\t);\n\t\t\trunOnlyForDeploymentPostprocessing = 0;\n\t\t};\n\t\tE2C74E3D212B1F8E00947DE0 /* Headers */ = {\n\t\t\tisa = PBXHeadersBuildPhase;\n\t\t\tbuildActionMask = 2147483647;\n\t\t\tfiles = (\n\t\t\t\tE2C74E4A212B1FEE00947DE0 /* DiffMatchPatch.h in Headers */,\n\t\t\t);\n\t\t\trunOnlyForDeploymentPostprocessing = 0;\n\t\t};\n/* End PBXHeadersBuildPhase section */\n\n/* Begin PBXNativeTarget section */\n\t\t3D96F3A112AFC68900C3E5C0 /* speedtest */ = {\n\t\t\tisa = PBXNativeTarget;\n\t\t\tbuildConfigurationList = 3D96F3B512AFC68E00C3E5C0 /* Build configuration list for PBXNativeTarget \"speedtest\" */;\n\t\t\tbuildPhases = (\n\t\t\t\t3D96F39F12AFC68900C3E5C0 /* Sources */,\n\t\t\t\t3D96F3A012AFC68900C3E5C0 /* Frameworks */,\n\t\t\t);\n\t\t\tbuildRules = (\n\t\t\t);\n\t\t\tdependencies = (\n\t\t\t\t3D96F3B912AFC6CD00C3E5C0 /* PBXTargetDependency */,\n\t\t\t);\n\t\t\tname = speedtest;\n\t\t\tproductName = speedtest;\n\t\t\tproductReference = 3D96F3A212AFC68900C3E5C0 /* speedtest */;\n\t\t\tproductType = \"com.apple.product-type.tool\";\n\t\t};\n\t\t4CF04C3020323A1100945640 /* DiffMatchPatchTest */ = {\n\t\t\tisa = PBXNativeTarget;\n\t\t\tbuildConfigurationList = 4CF04C3920323A1100945640 /* Build configuration list for PBXNativeTarget \"DiffMatchPatchTest\" */;\n\t\t\tbuildPhases = (\n\t\t\t\t4CF04C2D20323A1100945640 /* Sources */,\n\t\t\t\t4CF04C2E20323A1100945640 /* Frameworks */,\n\t\t\t\t4CF04C2F20323A1100945640 /* Resources */,\n\t\t\t);\n\t\t\tbuildRules = (\n\t\t\t);\n\t\t\tdependencies = (\n\t\t\t\t4CF04C3820323A1100945640 /* PBXTargetDependency */,\n\t\t\t);\n\t\t\tname = DiffMatchPatchTest;\n\t\t\tproductName = DiffMatchPatchTest;\n\t\t\tproductReference = 4CF04C3120323A1100945640 /* DiffMatchPatchTest.xctest */;\n\t\t\tproductType = \"com.apple.product-type.bundle.unit-test\";\n\t\t};\n\t\t8DC2EF4F0486A6940098B216 /* DiffMatchPatch */ = {\n\t\t\tisa = PBXNativeTarget;\n\t\t\tbuildConfigurationList = 1DEB91AD08733DA50010E9CD /* Build configuration list for PBXNativeTarget \"DiffMatchPatch\" */;\n\t\t\tbuildPhases = (\n\t\t\t\t8DC2EF500486A6940098B216 /* Headers */,\n\t\t\t\t8DC2EF520486A6940098B216 /* Resources */,\n\t\t\t\t8DC2EF540486A6940098B216 /* Sources */,\n\t\t\t\t8DC2EF560486A6940098B216 /* Frameworks */,\n\t\t\t);\n\t\t\tbuildRules = (\n\t\t\t);\n\t\t\tdependencies = (\n\t\t\t);\n\t\t\tname = DiffMatchPatch;\n\t\t\tproductInstallPath = \"$(HOME)/Library/Frameworks\";\n\t\t\tproductName = DiffMatchPatch;\n\t\t\tproductReference = 8DC2EF5B0486A6940098B216 /* DiffMatchPatch.framework */;\n\t\t\tproductType = \"com.apple.product-type.framework\";\n\t\t};\n\t\tE2C74E3F212B1F8E00947DE0 /* DiffMatchPatch_iOS */ = {\n\t\t\tisa = PBXNativeTarget;\n\t\t\tbuildConfigurationList = E2C74E49212B1F8E00947DE0 /* Build configuration list for PBXNativeTarget \"DiffMatchPatch_iOS\" */;\n\t\t\tbuildPhases = (\n\t\t\t\tE2C74E3B212B1F8E00947DE0 /* Sources */,\n\t\t\t\tE2C74E3C212B1F8E00947DE0 /* Frameworks */,\n\t\t\t\tE2C74E3D212B1F8E00947DE0 /* Headers */,\n\t\t\t\tE2C74E3E212B1F8E00947DE0 /* Resources */,\n\t\t\t);\n\t\t\tbuildRules = (\n\t\t\t);\n\t\t\tdependencies = (\n\t\t\t);\n\t\t\tname = DiffMatchPatch_iOS;\n\t\t\tproductName = DiffMatchPatch_iOS;\n\t\t\tproductReference = E2C74E40212B1F8E00947DE0 /* DiffMatchPatch.framework */;\n\t\t\tproductType = \"com.apple.product-type.framework\";\n\t\t};\n/* End PBXNativeTarget section */\n\n/* Begin PBXProject section */\n\t\t0867D690FE84028FC02AAC07 /* Project object */ = {\n\t\t\tisa = PBXProject;\n\t\t\tattributes = {\n\t\t\t\tLastUpgradeCheck = 0920;\n\t\t\t\tTargetAttributes = {\n\t\t\t\t\t4CF04C3020323A1100945640 = {\n\t\t\t\t\t\tCreatedOnToolsVersion = 9.2;\n\t\t\t\t\t};\n\t\t\t\t\tE2C74E3F212B1F8E00947DE0 = {\n\t\t\t\t\t\tCreatedOnToolsVersion = 9.4.1;\n\t\t\t\t\t};\n\t\t\t\t};\n\t\t\t};\n\t\t\tbuildConfigurationList = 1DEB91B108733DA50010E9CD /* Build configuration list for PBXProject \"DiffMatchPatch\" */;\n\t\t\tcompatibilityVersion = \"Xcode 3.1\";\n\t\t\tdevelopmentRegion = English;\n\t\t\thasScannedForEncodings = 1;\n\t\t\tknownRegions = (\n\t\t\t\tEnglish,\n\t\t\t\tJapanese,\n\t\t\t\tFrench,\n\t\t\t\tGerman,\n\t\t\t);\n\t\t\tmainGroup = 0867D691FE84028FC02AAC07 /* DiffMatchPatch */;\n\t\t\tproductRefGroup = 034768DFFF38A50411DB9C8B /* Products */;\n\t\t\tprojectDirPath = \"\";\n\t\t\tprojectRoot = \"\";\n\t\t\ttargets = (\n\t\t\t\t8DC2EF4F0486A6940098B216 /* DiffMatchPatch */,\n\t\t\t\tE2C74E3F212B1F8E00947DE0 /* DiffMatchPatch_iOS */,\n\t\t\t\t3D96F3A112AFC68900C3E5C0 /* speedtest */,\n\t\t\t\t4CF04C3020323A1100945640 /* DiffMatchPatchTest */,\n\t\t\t);\n\t\t};\n/* End PBXProject section */\n\n/* Begin PBXResourcesBuildPhase section */\n\t\t4CF04C2F20323A1100945640 /* Resources */ = {\n\t\t\tisa = PBXResourcesBuildPhase;\n\t\t\tbuildActionMask = 2147483647;\n\t\t\tfiles = (\n\t\t\t);\n\t\t\trunOnlyForDeploymentPostprocessing = 0;\n\t\t};\n\t\t8DC2EF520486A6940098B216 /* Resources */ = {\n\t\t\tisa = PBXResourcesBuildPhase;\n\t\t\tbuildActionMask = 2147483647;\n\t\t\tfiles = (\n\t\t\t\t8DC2EF530486A6940098B216 /* InfoPlist.strings in Resources */,\n\t\t\t);\n\t\t\trunOnlyForDeploymentPostprocessing = 0;\n\t\t};\n\t\tE2C74E3E212B1F8E00947DE0 /* Resources */ = {\n\t\t\tisa = PBXResourcesBuildPhase;\n\t\t\tbuildActionMask = 2147483647;\n\t\t\tfiles = (\n\t\t\t\tE2C74E51212B201600947DE0 /* InfoPlist.strings in Resources */,\n\t\t\t);\n\t\t\trunOnlyForDeploymentPostprocessing = 0;\n\t\t};\n/* End PBXResourcesBuildPhase section */\n\n/* Begin PBXSourcesBuildPhase section */\n\t\t3D96F39F12AFC68900C3E5C0 /* Sources */ = {\n\t\t\tisa = PBXSourcesBuildPhase;\n\t\t\tbuildActionMask = 2147483647;\n\t\t\tfiles = (\n\t\t\t\t3D96F44A12AFC82F00C3E5C0 /* speedtest.m in Sources */,\n\t\t\t);\n\t\t\trunOnlyForDeploymentPostprocessing = 0;\n\t\t};\n\t\t4CF04C2D20323A1100945640 /* Sources */ = {\n\t\t\tisa = PBXSourcesBuildPhase;\n\t\t\tbuildActionMask = 2147483647;\n\t\t\tfiles = (\n\t\t\t\t4CF04C3E20323A8200945640 /* DiffMatchPatchTest.m in Sources */,\n\t\t\t);\n\t\t\trunOnlyForDeploymentPostprocessing = 0;\n\t\t};\n\t\t8DC2EF540486A6940098B216 /* Sources */ = {\n\t\t\tisa = PBXSourcesBuildPhase;\n\t\t\tbuildActionMask = 2147483647;\n\t\t\tfiles = (\n\t\t\t\t3D5BC081128C44A700B8F5FF /* DiffMatchPatch.m in Sources */,\n\t\t\t\t3DA641D1128DE21D00B33CE9 /* DiffMatchPatchCFUtilities.c in Sources */,\n\t\t\t\t3D70BCC9128EDAF80078D1A6 /* NSString+UriCompatibility.m in Sources */,\n\t\t\t\t3DEE4C73129D484D00885485 /* NSMutableDictionary+DMPExtensions.m in Sources */,\n\t\t\t\t3DC87026129FF4B6001F602B /* NSString+JavaSubstring.m in Sources */,\n\t\t\t\t3D08D25412A71B9C007A5316 /* NSString+UnicharUtilities.m in Sources */,\n\t\t\t);\n\t\t\trunOnlyForDeploymentPostprocessing = 0;\n\t\t};\n\t\tE2C74E3B212B1F8E00947DE0 /* Sources */ = {\n\t\t\tisa = PBXSourcesBuildPhase;\n\t\t\tbuildActionMask = 2147483647;\n\t\t\tfiles = (\n\t\t\t\tE2C74E4B212B200F00947DE0 /* DiffMatchPatch.m in Sources */,\n\t\t\t\tE2C74E4D212B200F00947DE0 /* NSString+UnicharUtilities.m in Sources */,\n\t\t\t\tE2C74E4F212B200F00947DE0 /* NSMutableDictionary+DMPExtensions.m in Sources */,\n\t\t\t\tE2C74E50212B200F00947DE0 /* DiffMatchPatchCFUtilities.c in Sources */,\n\t\t\t\tE2C74E4C212B200F00947DE0 /* NSString+JavaSubstring.m in Sources */,\n\t\t\t\tE2C74E4E212B200F00947DE0 /* NSString+UriCompatibility.m in Sources */,\n\t\t\t);\n\t\t\trunOnlyForDeploymentPostprocessing = 0;\n\t\t};\n/* End PBXSourcesBuildPhase section */\n\n/* Begin PBXTargetDependency section */\n\t\t3D96F3B912AFC6CD00C3E5C0 /* PBXTargetDependency */ = {\n\t\t\tisa = PBXTargetDependency;\n\t\t\ttarget = 8DC2EF4F0486A6940098B216 /* DiffMatchPatch */;\n\t\t\ttargetProxy = 3D96F3B812AFC6CD00C3E5C0 /* PBXContainerItemProxy */;\n\t\t};\n\t\t4CF04C3820323A1100945640 /* PBXTargetDependency */ = {\n\t\t\tisa = PBXTargetDependency;\n\t\t\ttarget = 8DC2EF4F0486A6940098B216 /* DiffMatchPatch */;\n\t\t\ttargetProxy = 4CF04C3720323A1100945640 /* PBXContainerItemProxy */;\n\t\t};\n/* End PBXTargetDependency section */\n\n/* Begin PBXVariantGroup section */\n\t\t089C1666FE841158C02AAC07 /* InfoPlist.strings */ = {\n\t\t\tisa = PBXVariantGroup;\n\t\t\tchildren = (\n\t\t\t\t089C1667FE841158C02AAC07 /* English */,\n\t\t\t);\n\t\t\tname = InfoPlist.strings;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n/* End PBXVariantGroup section */\n\n/* Begin XCBuildConfiguration section */\n\t\t1DEB91AE08733DA50010E9CD /* Debug */ = {\n\t\t\tisa = XCBuildConfiguration;\n\t\t\tbuildSettings = {\n\t\t\t\tALWAYS_SEARCH_USER_PATHS = NO;\n\t\t\t\tCOPY_PHASE_STRIP = NO;\n\t\t\t\tDYLIB_COMPATIBILITY_VERSION = 1;\n\t\t\t\tDYLIB_CURRENT_VERSION = 1;\n\t\t\t\tFRAMEWORK_VERSION = A;\n\t\t\t\tGCC_DYNAMIC_NO_PIC = NO;\n\t\t\t\tGCC_ENABLE_FIX_AND_CONTINUE = YES;\n\t\t\t\tGCC_MODEL_TUNING = G5;\n\t\t\t\tGCC_OPTIMIZATION_LEVEL = 0;\n\t\t\t\tGCC_PRECOMPILE_PREFIX_HEADER = YES;\n\t\t\t\tGCC_PREFIX_HEADER = DiffMatchPatch_Prefix.pch;\n\t\t\t\tINFOPLIST_FILE = Info.plist;\n\t\t\t\tINSTALL_PATH = \"@executable_path/../Frameworks\";\n\t\t\t\tPRODUCT_BUNDLE_IDENTIFIER = \"de.geheimwerk.${PRODUCT_NAME:rfc1034Identifier}\";\n\t\t\t\tPRODUCT_NAME = DiffMatchPatch;\n\t\t\t\tWRAPPER_EXTENSION = framework;\n\t\t\t};\n\t\t\tname = Debug;\n\t\t};\n\t\t1DEB91AF08733DA50010E9CD /* Release */ = {\n\t\t\tisa = XCBuildConfiguration;\n\t\t\tbuildSettings = {\n\t\t\t\tALWAYS_SEARCH_USER_PATHS = NO;\n\t\t\t\tDEBUG_INFORMATION_FORMAT = \"dwarf-with-dsym\";\n\t\t\t\tDYLIB_COMPATIBILITY_VERSION = 1;\n\t\t\t\tDYLIB_CURRENT_VERSION = 1;\n\t\t\t\tFRAMEWORK_VERSION = A;\n\t\t\t\tGCC_MODEL_TUNING = G5;\n\t\t\t\tGCC_PRECOMPILE_PREFIX_HEADER = YES;\n\t\t\t\tGCC_PREFIX_HEADER = DiffMatchPatch_Prefix.pch;\n\t\t\t\tINFOPLIST_FILE = Info.plist;\n\t\t\t\tINSTALL_PATH = \"@executable_path/../Frameworks\";\n\t\t\t\tPRODUCT_BUNDLE_IDENTIFIER = \"de.geheimwerk.${PRODUCT_NAME:rfc1034Identifier}\";\n\t\t\t\tPRODUCT_NAME = DiffMatchPatch;\n\t\t\t\tWRAPPER_EXTENSION = framework;\n\t\t\t};\n\t\t\tname = Release;\n\t\t};\n\t\t1DEB91B208733DA50010E9CD /* Debug */ = {\n\t\t\tisa = XCBuildConfiguration;\n\t\t\tbuildSettings = {\n\t\t\t\tCLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES;\n\t\t\t\tCLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;\n\t\t\t\tCLANG_WARN_BOOL_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_COMMA = YES;\n\t\t\t\tCLANG_WARN_CONSTANT_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_EMPTY_BODY = YES;\n\t\t\t\tCLANG_WARN_ENUM_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_INFINITE_RECURSION = YES;\n\t\t\t\tCLANG_WARN_INT_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_OBJC_LITERAL_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_RANGE_LOOP_ANALYSIS = YES;\n\t\t\t\tCLANG_WARN_STRICT_PROTOTYPES = YES;\n\t\t\t\tCLANG_WARN_SUSPICIOUS_MOVE = YES;\n\t\t\t\tCLANG_WARN_UNREACHABLE_CODE = YES;\n\t\t\t\tCLANG_WARN__DUPLICATE_METHOD_MATCH = YES;\n\t\t\t\tENABLE_STRICT_OBJC_MSGSEND = YES;\n\t\t\t\tENABLE_TESTABILITY = YES;\n\t\t\t\tGCC_NO_COMMON_BLOCKS = YES;\n\t\t\t\tGCC_OPTIMIZATION_LEVEL = 0;\n\t\t\t\tGCC_WARN_UNINITIALIZED_AUTOS = NO;\n\t\t\t\tONLY_ACTIVE_ARCH = YES;\n\t\t\t\tSDKROOT = macosx;\n\t\t\t};\n\t\t\tname = Debug;\n\t\t};\n\t\t1DEB91B308733DA50010E9CD /* Release */ = {\n\t\t\tisa = XCBuildConfiguration;\n\t\t\tbuildSettings = {\n\t\t\t\tCLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES;\n\t\t\t\tCLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;\n\t\t\t\tCLANG_WARN_BOOL_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_COMMA = YES;\n\t\t\t\tCLANG_WARN_CONSTANT_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_EMPTY_BODY = YES;\n\t\t\t\tCLANG_WARN_ENUM_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_INFINITE_RECURSION = YES;\n\t\t\t\tCLANG_WARN_INT_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_OBJC_LITERAL_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_RANGE_LOOP_ANALYSIS = YES;\n\t\t\t\tCLANG_WARN_STRICT_PROTOTYPES = YES;\n\t\t\t\tCLANG_WARN_SUSPICIOUS_MOVE = YES;\n\t\t\t\tCLANG_WARN_UNREACHABLE_CODE = YES;\n\t\t\t\tCLANG_WARN__DUPLICATE_METHOD_MATCH = YES;\n\t\t\t\tENABLE_STRICT_OBJC_MSGSEND = YES;\n\t\t\t\tGCC_NO_COMMON_BLOCKS = YES;\n\t\t\t\tSDKROOT = macosx;\n\t\t\t};\n\t\t\tname = Release;\n\t\t};\n\t\t3D105A0C12DA601C002111E1 /* Debug 10.6 */ = {\n\t\t\tisa = XCBuildConfiguration;\n\t\t\tbuildSettings = {\n\t\t\t\tCLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES;\n\t\t\t\tCLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;\n\t\t\t\tCLANG_WARN_BOOL_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_COMMA = YES;\n\t\t\t\tCLANG_WARN_CONSTANT_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_EMPTY_BODY = YES;\n\t\t\t\tCLANG_WARN_ENUM_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_INFINITE_RECURSION = YES;\n\t\t\t\tCLANG_WARN_INT_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_OBJC_LITERAL_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_RANGE_LOOP_ANALYSIS = YES;\n\t\t\t\tCLANG_WARN_STRICT_PROTOTYPES = YES;\n\t\t\t\tCLANG_WARN_SUSPICIOUS_MOVE = YES;\n\t\t\t\tCLANG_WARN_UNREACHABLE_CODE = YES;\n\t\t\t\tCLANG_WARN__DUPLICATE_METHOD_MATCH = YES;\n\t\t\t\tENABLE_STRICT_OBJC_MSGSEND = YES;\n\t\t\t\tENABLE_TESTABILITY = YES;\n\t\t\t\tGCC_NO_COMMON_BLOCKS = YES;\n\t\t\t\tGCC_OPTIMIZATION_LEVEL = 0;\n\t\t\t\tGCC_WARN_UNINITIALIZED_AUTOS = NO;\n\t\t\t\tONLY_ACTIVE_ARCH = YES;\n\t\t\t\tSDKROOT = macosx;\n\t\t\t};\n\t\t\tname = \"Debug 10.6\";\n\t\t};\n\t\t3D105A0D12DA601C002111E1 /* Debug 10.6 */ = {\n\t\t\tisa = XCBuildConfiguration;\n\t\t\tbuildSettings = {\n\t\t\t\tALWAYS_SEARCH_USER_PATHS = NO;\n\t\t\t\tCOPY_PHASE_STRIP = NO;\n\t\t\t\tDYLIB_COMPATIBILITY_VERSION = 1;\n\t\t\t\tDYLIB_CURRENT_VERSION = 1;\n\t\t\t\tFRAMEWORK_VERSION = A;\n\t\t\t\tGCC_DYNAMIC_NO_PIC = NO;\n\t\t\t\tGCC_ENABLE_FIX_AND_CONTINUE = YES;\n\t\t\t\tGCC_MODEL_TUNING = G5;\n\t\t\t\tGCC_OPTIMIZATION_LEVEL = 0;\n\t\t\t\tGCC_PRECOMPILE_PREFIX_HEADER = YES;\n\t\t\t\tGCC_PREFIX_HEADER = DiffMatchPatch_Prefix.pch;\n\t\t\t\tINFOPLIST_FILE = Info.plist;\n\t\t\t\tINSTALL_PATH = \"@executable_path/../Frameworks\";\n\t\t\t\tPRODUCT_BUNDLE_IDENTIFIER = \"de.geheimwerk.${PRODUCT_NAME:rfc1034Identifier}\";\n\t\t\t\tPRODUCT_NAME = DiffMatchPatch;\n\t\t\t\tWRAPPER_EXTENSION = framework;\n\t\t\t};\n\t\t\tname = \"Debug 10.6\";\n\t\t};\n\t\t3D105A0F12DA601C002111E1 /* Debug 10.6 */ = {\n\t\t\tisa = XCBuildConfiguration;\n\t\t\tbuildSettings = {\n\t\t\t\tALWAYS_SEARCH_USER_PATHS = NO;\n\t\t\t\tCOPY_PHASE_STRIP = NO;\n\t\t\t\tGCC_DYNAMIC_NO_PIC = NO;\n\t\t\t\tGCC_ENABLE_FIX_AND_CONTINUE = YES;\n\t\t\t\tGCC_MODEL_TUNING = G5;\n\t\t\t\tGCC_OPTIMIZATION_LEVEL = 0;\n\t\t\t\tGCC_PRECOMPILE_PREFIX_HEADER = YES;\n\t\t\t\tGCC_PREFIX_HEADER = speedtest_Prefix.pch;\n\t\t\t\tINSTALL_PATH = /usr/local/bin;\n\t\t\t\tOTHER_LDFLAGS = (\n\t\t\t\t\t\"-framework\",\n\t\t\t\t\tFoundation,\n\t\t\t\t\t\"-framework\",\n\t\t\t\t\tAppKit,\n\t\t\t\t);\n\t\t\t\tPREBINDING = NO;\n\t\t\t\tPRODUCT_NAME = speedtest;\n\t\t\t};\n\t\t\tname = \"Debug 10.6\";\n\t\t};\n\t\t3D105A1012DA6024002111E1 /* Release 10.6 */ = {\n\t\t\tisa = XCBuildConfiguration;\n\t\t\tbuildSettings = {\n\t\t\t\tCLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES;\n\t\t\t\tCLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;\n\t\t\t\tCLANG_WARN_BOOL_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_COMMA = YES;\n\t\t\t\tCLANG_WARN_CONSTANT_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_EMPTY_BODY = YES;\n\t\t\t\tCLANG_WARN_ENUM_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_INFINITE_RECURSION = YES;\n\t\t\t\tCLANG_WARN_INT_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_OBJC_LITERAL_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_RANGE_LOOP_ANALYSIS = YES;\n\t\t\t\tCLANG_WARN_STRICT_PROTOTYPES = YES;\n\t\t\t\tCLANG_WARN_SUSPICIOUS_MOVE = YES;\n\t\t\t\tCLANG_WARN_UNREACHABLE_CODE = YES;\n\t\t\t\tCLANG_WARN__DUPLICATE_METHOD_MATCH = YES;\n\t\t\t\tENABLE_STRICT_OBJC_MSGSEND = YES;\n\t\t\t\tGCC_NO_COMMON_BLOCKS = YES;\n\t\t\t\tSDKROOT = macosx;\n\t\t\t};\n\t\t\tname = \"Release 10.6\";\n\t\t};\n\t\t3D105A1112DA6024002111E1 /* Release 10.6 */ = {\n\t\t\tisa = XCBuildConfiguration;\n\t\t\tbuildSettings = {\n\t\t\t\tALWAYS_SEARCH_USER_PATHS = NO;\n\t\t\t\tDEBUG_INFORMATION_FORMAT = \"dwarf-with-dsym\";\n\t\t\t\tDYLIB_COMPATIBILITY_VERSION = 1;\n\t\t\t\tDYLIB_CURRENT_VERSION = 1;\n\t\t\t\tFRAMEWORK_VERSION = A;\n\t\t\t\tGCC_MODEL_TUNING = G5;\n\t\t\t\tGCC_PRECOMPILE_PREFIX_HEADER = YES;\n\t\t\t\tGCC_PREFIX_HEADER = DiffMatchPatch_Prefix.pch;\n\t\t\t\tINFOPLIST_FILE = Info.plist;\n\t\t\t\tINSTALL_PATH = \"@executable_path/../Frameworks\";\n\t\t\t\tPRODUCT_BUNDLE_IDENTIFIER = \"de.geheimwerk.${PRODUCT_NAME:rfc1034Identifier}\";\n\t\t\t\tPRODUCT_NAME = DiffMatchPatch;\n\t\t\t\tWRAPPER_EXTENSION = framework;\n\t\t\t};\n\t\t\tname = \"Release 10.6\";\n\t\t};\n\t\t3D105A1312DA6024002111E1 /* Release 10.6 */ = {\n\t\t\tisa = XCBuildConfiguration;\n\t\t\tbuildSettings = {\n\t\t\t\tALWAYS_SEARCH_USER_PATHS = NO;\n\t\t\t\tCOPY_PHASE_STRIP = YES;\n\t\t\t\tDEBUG_INFORMATION_FORMAT = \"dwarf-with-dsym\";\n\t\t\t\tGCC_ENABLE_FIX_AND_CONTINUE = NO;\n\t\t\t\tGCC_MODEL_TUNING = G5;\n\t\t\t\tGCC_PRECOMPILE_PREFIX_HEADER = YES;\n\t\t\t\tGCC_PREFIX_HEADER = speedtest_Prefix.pch;\n\t\t\t\tINSTALL_PATH = /usr/local/bin;\n\t\t\t\tOTHER_LDFLAGS = (\n\t\t\t\t\t\"-framework\",\n\t\t\t\t\tFoundation,\n\t\t\t\t\t\"-framework\",\n\t\t\t\t\tAppKit,\n\t\t\t\t);\n\t\t\t\tPREBINDING = NO;\n\t\t\t\tPRODUCT_NAME = speedtest;\n\t\t\t\tZERO_LINK = NO;\n\t\t\t};\n\t\t\tname = \"Release 10.6\";\n\t\t};\n\t\t3D96F3A412AFC68B00C3E5C0 /* Debug */ = {\n\t\t\tisa = XCBuildConfiguration;\n\t\t\tbuildSettings = {\n\t\t\t\tALWAYS_SEARCH_USER_PATHS = NO;\n\t\t\t\tCOPY_PHASE_STRIP = NO;\n\t\t\t\tGCC_DYNAMIC_NO_PIC = NO;\n\t\t\t\tGCC_ENABLE_FIX_AND_CONTINUE = YES;\n\t\t\t\tGCC_MODEL_TUNING = G5;\n\t\t\t\tGCC_OPTIMIZATION_LEVEL = 0;\n\t\t\t\tGCC_PRECOMPILE_PREFIX_HEADER = YES;\n\t\t\t\tGCC_PREFIX_HEADER = speedtest_Prefix.pch;\n\t\t\t\tINSTALL_PATH = /usr/local/bin;\n\t\t\t\tOTHER_LDFLAGS = (\n\t\t\t\t\t\"-framework\",\n\t\t\t\t\tFoundation,\n\t\t\t\t\t\"-framework\",\n\t\t\t\t\tAppKit,\n\t\t\t\t);\n\t\t\t\tPREBINDING = NO;\n\t\t\t\tPRODUCT_NAME = speedtest;\n\t\t\t};\n\t\t\tname = Debug;\n\t\t};\n\t\t3D96F3A512AFC68B00C3E5C0 /* Release */ = {\n\t\t\tisa = XCBuildConfiguration;\n\t\t\tbuildSettings = {\n\t\t\t\tALWAYS_SEARCH_USER_PATHS = NO;\n\t\t\t\tCOPY_PHASE_STRIP = YES;\n\t\t\t\tDEBUG_INFORMATION_FORMAT = \"dwarf-with-dsym\";\n\t\t\t\tGCC_ENABLE_FIX_AND_CONTINUE = NO;\n\t\t\t\tGCC_MODEL_TUNING = G5;\n\t\t\t\tGCC_PRECOMPILE_PREFIX_HEADER = YES;\n\t\t\t\tGCC_PREFIX_HEADER = speedtest_Prefix.pch;\n\t\t\t\tINSTALL_PATH = /usr/local/bin;\n\t\t\t\tOTHER_LDFLAGS = (\n\t\t\t\t\t\"-framework\",\n\t\t\t\t\tFoundation,\n\t\t\t\t\t\"-framework\",\n\t\t\t\t\tAppKit,\n\t\t\t\t);\n\t\t\t\tPREBINDING = NO;\n\t\t\t\tPRODUCT_NAME = speedtest;\n\t\t\t\tZERO_LINK = NO;\n\t\t\t};\n\t\t\tname = Release;\n\t\t};\n\t\t4CF04C3A20323A1100945640 /* Debug */ = {\n\t\t\tisa = XCBuildConfiguration;\n\t\t\tbuildSettings = {\n\t\t\t\tALWAYS_SEARCH_USER_PATHS = NO;\n\t\t\t\tCLANG_ANALYZER_NONNULL = YES;\n\t\t\t\tCLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;\n\t\t\t\tCLANG_CXX_LANGUAGE_STANDARD = \"gnu++14\";\n\t\t\t\tCLANG_CXX_LIBRARY = \"libc++\";\n\t\t\t\tCLANG_ENABLE_MODULES = YES;\n\t\t\t\tCLANG_ENABLE_OBJC_ARC = YES;\n\t\t\t\tCLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;\n\t\t\t\tCLANG_WARN_DOCUMENTATION_COMMENTS = YES;\n\t\t\t\tCLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;\n\t\t\t\tCLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;\n\t\t\t\tCODE_SIGN_IDENTITY = \"-\";\n\t\t\t\tCODE_SIGN_STYLE = Automatic;\n\t\t\t\tCOMBINE_HIDPI_IMAGES = YES;\n\t\t\t\tCOPY_PHASE_STRIP = NO;\n\t\t\t\tDEBUG_INFORMATION_FORMAT = dwarf;\n\t\t\t\tGCC_C_LANGUAGE_STANDARD = gnu11;\n\t\t\t\tGCC_DYNAMIC_NO_PIC = NO;\n\t\t\t\tGCC_PREPROCESSOR_DEFINITIONS = (\n\t\t\t\t\t\"DEBUG=1\",\n\t\t\t\t\t\"$(inherited)\",\n\t\t\t\t);\n\t\t\t\tGCC_WARN_64_TO_32_BIT_CONVERSION = YES;\n\t\t\t\tGCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;\n\t\t\t\tGCC_WARN_UNDECLARED_SELECTOR = YES;\n\t\t\t\tGCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;\n\t\t\t\tGCC_WARN_UNUSED_FUNCTION = YES;\n\t\t\t\tGCC_WARN_UNUSED_VARIABLE = YES;\n\t\t\t\tINFOPLIST_FILE = \"Tests/DiffMatchPatchTest-Info.plist\";\n\t\t\t\tLD_RUNPATH_SEARCH_PATHS = \"$(inherited) @executable_path/../Frameworks @loader_path/../Frameworks\";\n\t\t\t\tMACOSX_DEPLOYMENT_TARGET = 10.13;\n\t\t\t\tMTL_ENABLE_DEBUG_INFO = YES;\n\t\t\t\tPRODUCT_BUNDLE_IDENTIFIER = com.google.DiffMatchPatchTest;\n\t\t\t\tPRODUCT_NAME = \"$(TARGET_NAME)\";\n\t\t\t};\n\t\t\tname = Debug;\n\t\t};\n\t\t4CF04C3B20323A1100945640 /* Debug 10.6 */ = {\n\t\t\tisa = XCBuildConfiguration;\n\t\t\tbuildSettings = {\n\t\t\t\tALWAYS_SEARCH_USER_PATHS = NO;\n\t\t\t\tCLANG_ANALYZER_NONNULL = YES;\n\t\t\t\tCLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;\n\t\t\t\tCLANG_CXX_LANGUAGE_STANDARD = \"gnu++14\";\n\t\t\t\tCLANG_CXX_LIBRARY = \"libc++\";\n\t\t\t\tCLANG_ENABLE_MODULES = YES;\n\t\t\t\tCLANG_ENABLE_OBJC_ARC = YES;\n\t\t\t\tCLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;\n\t\t\t\tCLANG_WARN_DOCUMENTATION_COMMENTS = YES;\n\t\t\t\tCLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;\n\t\t\t\tCLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;\n\t\t\t\tCODE_SIGN_IDENTITY = \"-\";\n\t\t\t\tCODE_SIGN_STYLE = Automatic;\n\t\t\t\tCOMBINE_HIDPI_IMAGES = YES;\n\t\t\t\tCOPY_PHASE_STRIP = NO;\n\t\t\t\tDEBUG_INFORMATION_FORMAT = dwarf;\n\t\t\t\tGCC_C_LANGUAGE_STANDARD = gnu11;\n\t\t\t\tGCC_DYNAMIC_NO_PIC = NO;\n\t\t\t\tGCC_PREPROCESSOR_DEFINITIONS = (\n\t\t\t\t\t\"DEBUG=1\",\n\t\t\t\t\t\"$(inherited)\",\n\t\t\t\t);\n\t\t\t\tGCC_WARN_64_TO_32_BIT_CONVERSION = YES;\n\t\t\t\tGCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;\n\t\t\t\tGCC_WARN_UNDECLARED_SELECTOR = YES;\n\t\t\t\tGCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;\n\t\t\t\tGCC_WARN_UNUSED_FUNCTION = YES;\n\t\t\t\tGCC_WARN_UNUSED_VARIABLE = YES;\n\t\t\t\tINFOPLIST_FILE = \"Tests/DiffMatchPatchTest-Info.plist\";\n\t\t\t\tLD_RUNPATH_SEARCH_PATHS = \"$(inherited) @executable_path/../Frameworks @loader_path/../Frameworks\";\n\t\t\t\tMACOSX_DEPLOYMENT_TARGET = 10.13;\n\t\t\t\tMTL_ENABLE_DEBUG_INFO = YES;\n\t\t\t\tPRODUCT_BUNDLE_IDENTIFIER = com.google.DiffMatchPatchTest;\n\t\t\t\tPRODUCT_NAME = \"$(TARGET_NAME)\";\n\t\t\t};\n\t\t\tname = \"Debug 10.6\";\n\t\t};\n\t\t4CF04C3C20323A1100945640 /* Release */ = {\n\t\t\tisa = XCBuildConfiguration;\n\t\t\tbuildSettings = {\n\t\t\t\tALWAYS_SEARCH_USER_PATHS = NO;\n\t\t\t\tCLANG_ANALYZER_NONNULL = YES;\n\t\t\t\tCLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;\n\t\t\t\tCLANG_CXX_LANGUAGE_STANDARD = \"gnu++14\";\n\t\t\t\tCLANG_CXX_LIBRARY = \"libc++\";\n\t\t\t\tCLANG_ENABLE_MODULES = YES;\n\t\t\t\tCLANG_ENABLE_OBJC_ARC = YES;\n\t\t\t\tCLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;\n\t\t\t\tCLANG_WARN_DOCUMENTATION_COMMENTS = YES;\n\t\t\t\tCLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;\n\t\t\t\tCLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;\n\t\t\t\tCODE_SIGN_IDENTITY = \"-\";\n\t\t\t\tCODE_SIGN_STYLE = Automatic;\n\t\t\t\tCOMBINE_HIDPI_IMAGES = YES;\n\t\t\t\tCOPY_PHASE_STRIP = NO;\n\t\t\t\tDEBUG_INFORMATION_FORMAT = \"dwarf-with-dsym\";\n\t\t\t\tENABLE_NS_ASSERTIONS = NO;\n\t\t\t\tGCC_C_LANGUAGE_STANDARD = gnu11;\n\t\t\t\tGCC_WARN_64_TO_32_BIT_CONVERSION = YES;\n\t\t\t\tGCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;\n\t\t\t\tGCC_WARN_UNDECLARED_SELECTOR = YES;\n\t\t\t\tGCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;\n\t\t\t\tGCC_WARN_UNUSED_FUNCTION = YES;\n\t\t\t\tGCC_WARN_UNUSED_VARIABLE = YES;\n\t\t\t\tINFOPLIST_FILE = \"Tests/DiffMatchPatchTest-Info.plist\";\n\t\t\t\tLD_RUNPATH_SEARCH_PATHS = \"$(inherited) @executable_path/../Frameworks @loader_path/../Frameworks\";\n\t\t\t\tMACOSX_DEPLOYMENT_TARGET = 10.13;\n\t\t\t\tMTL_ENABLE_DEBUG_INFO = NO;\n\t\t\t\tPRODUCT_BUNDLE_IDENTIFIER = com.google.DiffMatchPatchTest;\n\t\t\t\tPRODUCT_NAME = \"$(TARGET_NAME)\";\n\t\t\t};\n\t\t\tname = Release;\n\t\t};\n\t\t4CF04C3D20323A1100945640 /* Release 10.6 */ = {\n\t\t\tisa = XCBuildConfiguration;\n\t\t\tbuildSettings = {\n\t\t\t\tALWAYS_SEARCH_USER_PATHS = NO;\n\t\t\t\tCLANG_ANALYZER_NONNULL = YES;\n\t\t\t\tCLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;\n\t\t\t\tCLANG_CXX_LANGUAGE_STANDARD = \"gnu++14\";\n\t\t\t\tCLANG_CXX_LIBRARY = \"libc++\";\n\t\t\t\tCLANG_ENABLE_MODULES = YES;\n\t\t\t\tCLANG_ENABLE_OBJC_ARC = YES;\n\t\t\t\tCLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;\n\t\t\t\tCLANG_WARN_DOCUMENTATION_COMMENTS = YES;\n\t\t\t\tCLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;\n\t\t\t\tCLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;\n\t\t\t\tCODE_SIGN_IDENTITY = \"-\";\n\t\t\t\tCODE_SIGN_STYLE = Automatic;\n\t\t\t\tCOMBINE_HIDPI_IMAGES = YES;\n\t\t\t\tCOPY_PHASE_STRIP = NO;\n\t\t\t\tDEBUG_INFORMATION_FORMAT = \"dwarf-with-dsym\";\n\t\t\t\tENABLE_NS_ASSERTIONS = NO;\n\t\t\t\tGCC_C_LANGUAGE_STANDARD = gnu11;\n\t\t\t\tGCC_WARN_64_TO_32_BIT_CONVERSION = YES;\n\t\t\t\tGCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;\n\t\t\t\tGCC_WARN_UNDECLARED_SELECTOR = YES;\n\t\t\t\tGCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;\n\t\t\t\tGCC_WARN_UNUSED_FUNCTION = YES;\n\t\t\t\tGCC_WARN_UNUSED_VARIABLE = YES;\n\t\t\t\tINFOPLIST_FILE = \"Tests/DiffMatchPatchTest-Info.plist\";\n\t\t\t\tLD_RUNPATH_SEARCH_PATHS = \"$(inherited) @executable_path/../Frameworks @loader_path/../Frameworks\";\n\t\t\t\tMACOSX_DEPLOYMENT_TARGET = 10.13;\n\t\t\t\tMTL_ENABLE_DEBUG_INFO = NO;\n\t\t\t\tPRODUCT_BUNDLE_IDENTIFIER = com.google.DiffMatchPatchTest;\n\t\t\t\tPRODUCT_NAME = \"$(TARGET_NAME)\";\n\t\t\t};\n\t\t\tname = \"Release 10.6\";\n\t\t};\n\t\tE2C74E45212B1F8E00947DE0 /* Debug */ = {\n\t\t\tisa = XCBuildConfiguration;\n\t\t\tbuildSettings = {\n\t\t\t\tALWAYS_SEARCH_USER_PATHS = NO;\n\t\t\t\tCLANG_ANALYZER_NONNULL = YES;\n\t\t\t\tCLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;\n\t\t\t\tCLANG_CXX_LANGUAGE_STANDARD = \"gnu++14\";\n\t\t\t\tCLANG_CXX_LIBRARY = \"libc++\";\n\t\t\t\tCLANG_ENABLE_MODULES = YES;\n\t\t\t\tCLANG_ENABLE_OBJC_ARC = NO;\n\t\t\t\tCLANG_ENABLE_OBJC_WEAK = YES;\n\t\t\t\tCLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;\n\t\t\t\tCLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;\n\t\t\t\tCLANG_WARN_DOCUMENTATION_COMMENTS = YES;\n\t\t\t\tCLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;\n\t\t\t\tCLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;\n\t\t\t\tCLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;\n\t\t\t\tCODE_SIGN_IDENTITY = \"iPhone Developer\";\n\t\t\t\tCODE_SIGN_STYLE = Automatic;\n\t\t\t\tCOPY_PHASE_STRIP = NO;\n\t\t\t\tCURRENT_PROJECT_VERSION = 1;\n\t\t\t\tDEBUG_INFORMATION_FORMAT = dwarf;\n\t\t\t\tDEFINES_MODULE = YES;\n\t\t\t\tDEVELOPMENT_TEAM = KWSK6KH49N;\n\t\t\t\tDYLIB_COMPATIBILITY_VERSION = 1;\n\t\t\t\tDYLIB_CURRENT_VERSION = 1;\n\t\t\t\tDYLIB_INSTALL_NAME_BASE = \"@rpath\";\n\t\t\t\tGCC_C_LANGUAGE_STANDARD = gnu11;\n\t\t\t\tGCC_DYNAMIC_NO_PIC = NO;\n\t\t\t\tGCC_PREPROCESSOR_DEFINITIONS = (\n\t\t\t\t\t\"DEBUG=1\",\n\t\t\t\t\t\"$(inherited)\",\n\t\t\t\t);\n\t\t\t\tGCC_WARN_64_TO_32_BIT_CONVERSION = YES;\n\t\t\t\tGCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;\n\t\t\t\tGCC_WARN_UNDECLARED_SELECTOR = YES;\n\t\t\t\tGCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;\n\t\t\t\tGCC_WARN_UNUSED_FUNCTION = YES;\n\t\t\t\tGCC_WARN_UNUSED_VARIABLE = YES;\n\t\t\t\tINFOPLIST_FILE = \"$(SRCROOT)/Info.plist\";\n\t\t\t\tINSTALL_PATH = \"$(LOCAL_LIBRARY_DIR)/Frameworks\";\n\t\t\t\tIPHONEOS_DEPLOYMENT_TARGET = 10.0;\n\t\t\t\tLD_RUNPATH_SEARCH_PATHS = \"$(inherited) @executable_path/Frameworks @loader_path/Frameworks\";\n\t\t\t\tMTL_ENABLE_DEBUG_INFO = YES;\n\t\t\t\tPRODUCT_BUNDLE_IDENTIFIER = \"de.geheimwerk.DiffMatchPatch-iOS\";\n\t\t\t\tPRODUCT_NAME = DiffMatchPatch;\n\t\t\t\tSDKROOT = iphoneos;\n\t\t\t\tSKIP_INSTALL = YES;\n\t\t\t\tTARGETED_DEVICE_FAMILY = \"1,2\";\n\t\t\t\tVERSIONING_SYSTEM = \"apple-generic\";\n\t\t\t\tVERSION_INFO_PREFIX = \"\";\n\t\t\t};\n\t\t\tname = Debug;\n\t\t};\n\t\tE2C74E46212B1F8E00947DE0 /* Debug 10.6 */ = {\n\t\t\tisa = XCBuildConfiguration;\n\t\t\tbuildSettings = {\n\t\t\t\tALWAYS_SEARCH_USER_PATHS = NO;\n\t\t\t\tCLANG_ANALYZER_NONNULL = YES;\n\t\t\t\tCLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;\n\t\t\t\tCLANG_CXX_LANGUAGE_STANDARD = \"gnu++14\";\n\t\t\t\tCLANG_CXX_LIBRARY = \"libc++\";\n\t\t\t\tCLANG_ENABLE_MODULES = YES;\n\t\t\t\tCLANG_ENABLE_OBJC_ARC = NO;\n\t\t\t\tCLANG_ENABLE_OBJC_WEAK = YES;\n\t\t\t\tCLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;\n\t\t\t\tCLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;\n\t\t\t\tCLANG_WARN_DOCUMENTATION_COMMENTS = YES;\n\t\t\t\tCLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;\n\t\t\t\tCLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;\n\t\t\t\tCLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;\n\t\t\t\tCODE_SIGN_IDENTITY = \"iPhone Developer\";\n\t\t\t\tCODE_SIGN_STYLE = Automatic;\n\t\t\t\tCOPY_PHASE_STRIP = NO;\n\t\t\t\tCURRENT_PROJECT_VERSION = 1;\n\t\t\t\tDEBUG_INFORMATION_FORMAT = dwarf;\n\t\t\t\tDEFINES_MODULE = YES;\n\t\t\t\tDEVELOPMENT_TEAM = KWSK6KH49N;\n\t\t\t\tDYLIB_COMPATIBILITY_VERSION = 1;\n\t\t\t\tDYLIB_CURRENT_VERSION = 1;\n\t\t\t\tDYLIB_INSTALL_NAME_BASE = \"@rpath\";\n\t\t\t\tGCC_C_LANGUAGE_STANDARD = gnu11;\n\t\t\t\tGCC_DYNAMIC_NO_PIC = NO;\n\t\t\t\tGCC_PREPROCESSOR_DEFINITIONS = (\n\t\t\t\t\t\"DEBUG=1\",\n\t\t\t\t\t\"$(inherited)\",\n\t\t\t\t);\n\t\t\t\tGCC_WARN_64_TO_32_BIT_CONVERSION = YES;\n\t\t\t\tGCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;\n\t\t\t\tGCC_WARN_UNDECLARED_SELECTOR = YES;\n\t\t\t\tGCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;\n\t\t\t\tGCC_WARN_UNUSED_FUNCTION = YES;\n\t\t\t\tGCC_WARN_UNUSED_VARIABLE = YES;\n\t\t\t\tINFOPLIST_FILE = \"$(SRCROOT)/Info.plist\";\n\t\t\t\tINSTALL_PATH = \"$(LOCAL_LIBRARY_DIR)/Frameworks\";\n\t\t\t\tIPHONEOS_DEPLOYMENT_TARGET = 10.0;\n\t\t\t\tLD_RUNPATH_SEARCH_PATHS = \"$(inherited) @executable_path/Frameworks @loader_path/Frameworks\";\n\t\t\t\tMTL_ENABLE_DEBUG_INFO = YES;\n\t\t\t\tPRODUCT_BUNDLE_IDENTIFIER = \"de.geheimwerk.DiffMatchPatch-iOS\";\n\t\t\t\tPRODUCT_NAME = DiffMatchPatch;\n\t\t\t\tSDKROOT = iphoneos;\n\t\t\t\tSKIP_INSTALL = YES;\n\t\t\t\tTARGETED_DEVICE_FAMILY = \"1,2\";\n\t\t\t\tVERSIONING_SYSTEM = \"apple-generic\";\n\t\t\t\tVERSION_INFO_PREFIX = \"\";\n\t\t\t};\n\t\t\tname = \"Debug 10.6\";\n\t\t};\n\t\tE2C74E47212B1F8E00947DE0 /* Release */ = {\n\t\t\tisa = XCBuildConfiguration;\n\t\t\tbuildSettings = {\n\t\t\t\tALWAYS_SEARCH_USER_PATHS = NO;\n\t\t\t\tCLANG_ANALYZER_NONNULL = YES;\n\t\t\t\tCLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;\n\t\t\t\tCLANG_CXX_LANGUAGE_STANDARD = \"gnu++14\";\n\t\t\t\tCLANG_CXX_LIBRARY = \"libc++\";\n\t\t\t\tCLANG_ENABLE_MODULES = YES;\n\t\t\t\tCLANG_ENABLE_OBJC_ARC = NO;\n\t\t\t\tCLANG_ENABLE_OBJC_WEAK = YES;\n\t\t\t\tCLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;\n\t\t\t\tCLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;\n\t\t\t\tCLANG_WARN_DOCUMENTATION_COMMENTS = YES;\n\t\t\t\tCLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;\n\t\t\t\tCLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;\n\t\t\t\tCLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;\n\t\t\t\tCODE_SIGN_IDENTITY = \"iPhone Developer\";\n\t\t\t\tCODE_SIGN_STYLE = Automatic;\n\t\t\t\tCOPY_PHASE_STRIP = NO;\n\t\t\t\tCURRENT_PROJECT_VERSION = 1;\n\t\t\t\tDEBUG_INFORMATION_FORMAT = \"dwarf-with-dsym\";\n\t\t\t\tDEFINES_MODULE = YES;\n\t\t\t\tDEVELOPMENT_TEAM = KWSK6KH49N;\n\t\t\t\tDYLIB_COMPATIBILITY_VERSION = 1;\n\t\t\t\tDYLIB_CURRENT_VERSION = 1;\n\t\t\t\tDYLIB_INSTALL_NAME_BASE = \"@rpath\";\n\t\t\t\tENABLE_NS_ASSERTIONS = NO;\n\t\t\t\tGCC_C_LANGUAGE_STANDARD = gnu11;\n\t\t\t\tGCC_WARN_64_TO_32_BIT_CONVERSION = YES;\n\t\t\t\tGCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;\n\t\t\t\tGCC_WARN_UNDECLARED_SELECTOR = YES;\n\t\t\t\tGCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;\n\t\t\t\tGCC_WARN_UNUSED_FUNCTION = YES;\n\t\t\t\tGCC_WARN_UNUSED_VARIABLE = YES;\n\t\t\t\tINFOPLIST_FILE = \"$(SRCROOT)/Info.plist\";\n\t\t\t\tINSTALL_PATH = \"$(LOCAL_LIBRARY_DIR)/Frameworks\";\n\t\t\t\tIPHONEOS_DEPLOYMENT_TARGET = 10.0;\n\t\t\t\tLD_RUNPATH_SEARCH_PATHS = \"$(inherited) @executable_path/Frameworks @loader_path/Frameworks\";\n\t\t\t\tMTL_ENABLE_DEBUG_INFO = NO;\n\t\t\t\tPRODUCT_BUNDLE_IDENTIFIER = \"de.geheimwerk.DiffMatchPatch-iOS\";\n\t\t\t\tPRODUCT_NAME = DiffMatchPatch;\n\t\t\t\tSDKROOT = iphoneos;\n\t\t\t\tSKIP_INSTALL = YES;\n\t\t\t\tTARGETED_DEVICE_FAMILY = \"1,2\";\n\t\t\t\tVALIDATE_PRODUCT = YES;\n\t\t\t\tVERSIONING_SYSTEM = \"apple-generic\";\n\t\t\t\tVERSION_INFO_PREFIX = \"\";\n\t\t\t};\n\t\t\tname = Release;\n\t\t};\n\t\tE2C74E48212B1F8E00947DE0 /* Release 10.6 */ = {\n\t\t\tisa = XCBuildConfiguration;\n\t\t\tbuildSettings = {\n\t\t\t\tALWAYS_SEARCH_USER_PATHS = NO;\n\t\t\t\tCLANG_ANALYZER_NONNULL = YES;\n\t\t\t\tCLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;\n\t\t\t\tCLANG_CXX_LANGUAGE_STANDARD = \"gnu++14\";\n\t\t\t\tCLANG_CXX_LIBRARY = \"libc++\";\n\t\t\t\tCLANG_ENABLE_MODULES = YES;\n\t\t\t\tCLANG_ENABLE_OBJC_ARC = NO;\n\t\t\t\tCLANG_ENABLE_OBJC_WEAK = YES;\n\t\t\t\tCLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;\n\t\t\t\tCLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;\n\t\t\t\tCLANG_WARN_DOCUMENTATION_COMMENTS = YES;\n\t\t\t\tCLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;\n\t\t\t\tCLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;\n\t\t\t\tCLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;\n\t\t\t\tCODE_SIGN_IDENTITY = \"iPhone Developer\";\n\t\t\t\tCODE_SIGN_STYLE = Automatic;\n\t\t\t\tCOPY_PHASE_STRIP = NO;\n\t\t\t\tCURRENT_PROJECT_VERSION = 1;\n\t\t\t\tDEBUG_INFORMATION_FORMAT = \"dwarf-with-dsym\";\n\t\t\t\tDEFINES_MODULE = YES;\n\t\t\t\tDEVELOPMENT_TEAM = KWSK6KH49N;\n\t\t\t\tDYLIB_COMPATIBILITY_VERSION = 1;\n\t\t\t\tDYLIB_CURRENT_VERSION = 1;\n\t\t\t\tDYLIB_INSTALL_NAME_BASE = \"@rpath\";\n\t\t\t\tENABLE_NS_ASSERTIONS = NO;\n\t\t\t\tGCC_C_LANGUAGE_STANDARD = gnu11;\n\t\t\t\tGCC_WARN_64_TO_32_BIT_CONVERSION = YES;\n\t\t\t\tGCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;\n\t\t\t\tGCC_WARN_UNDECLARED_SELECTOR = YES;\n\t\t\t\tGCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;\n\t\t\t\tGCC_WARN_UNUSED_FUNCTION = YES;\n\t\t\t\tGCC_WARN_UNUSED_VARIABLE = YES;\n\t\t\t\tINFOPLIST_FILE = \"$(SRCROOT)/Info.plist\";\n\t\t\t\tINSTALL_PATH = \"$(LOCAL_LIBRARY_DIR)/Frameworks\";\n\t\t\t\tIPHONEOS_DEPLOYMENT_TARGET = 10.0;\n\t\t\t\tLD_RUNPATH_SEARCH_PATHS = \"$(inherited) @executable_path/Frameworks @loader_path/Frameworks\";\n\t\t\t\tMTL_ENABLE_DEBUG_INFO = NO;\n\t\t\t\tPRODUCT_BUNDLE_IDENTIFIER = \"de.geheimwerk.DiffMatchPatch-iOS\";\n\t\t\t\tPRODUCT_NAME = DiffMatchPatch;\n\t\t\t\tSDKROOT = iphoneos;\n\t\t\t\tSKIP_INSTALL = YES;\n\t\t\t\tTARGETED_DEVICE_FAMILY = \"1,2\";\n\t\t\t\tVALIDATE_PRODUCT = YES;\n\t\t\t\tVERSIONING_SYSTEM = \"apple-generic\";\n\t\t\t\tVERSION_INFO_PREFIX = \"\";\n\t\t\t};\n\t\t\tname = \"Release 10.6\";\n\t\t};\n/* End XCBuildConfiguration section */\n\n/* Begin XCConfigurationList section */\n\t\t1DEB91AD08733DA50010E9CD /* Build configuration list for PBXNativeTarget \"DiffMatchPatch\" */ = {\n\t\t\tisa = XCConfigurationList;\n\t\t\tbuildConfigurations = (\n\t\t\t\t1DEB91AE08733DA50010E9CD /* Debug */,\n\t\t\t\t3D105A0D12DA601C002111E1 /* Debug 10.6 */,\n\t\t\t\t1DEB91AF08733DA50010E9CD /* Release */,\n\t\t\t\t3D105A1112DA6024002111E1 /* Release 10.6 */,\n\t\t\t);\n\t\t\tdefaultConfigurationIsVisible = 0;\n\t\t\tdefaultConfigurationName = Release;\n\t\t};\n\t\t1DEB91B108733DA50010E9CD /* Build configuration list for PBXProject \"DiffMatchPatch\" */ = {\n\t\t\tisa = XCConfigurationList;\n\t\t\tbuildConfigurations = (\n\t\t\t\t1DEB91B208733DA50010E9CD /* Debug */,\n\t\t\t\t3D105A0C12DA601C002111E1 /* Debug 10.6 */,\n\t\t\t\t1DEB91B308733DA50010E9CD /* Release */,\n\t\t\t\t3D105A1012DA6024002111E1 /* Release 10.6 */,\n\t\t\t);\n\t\t\tdefaultConfigurationIsVisible = 0;\n\t\t\tdefaultConfigurationName = Release;\n\t\t};\n\t\t3D96F3B512AFC68E00C3E5C0 /* Build configuration list for PBXNativeTarget \"speedtest\" */ = {\n\t\t\tisa = XCConfigurationList;\n\t\t\tbuildConfigurations = (\n\t\t\t\t3D96F3A412AFC68B00C3E5C0 /* Debug */,\n\t\t\t\t3D105A0F12DA601C002111E1 /* Debug 10.6 */,\n\t\t\t\t3D96F3A512AFC68B00C3E5C0 /* Release */,\n\t\t\t\t3D105A1312DA6024002111E1 /* Release 10.6 */,\n\t\t\t);\n\t\t\tdefaultConfigurationIsVisible = 0;\n\t\t\tdefaultConfigurationName = Release;\n\t\t};\n\t\t4CF04C3920323A1100945640 /* Build configuration list for PBXNativeTarget \"DiffMatchPatchTest\" */ = {\n\t\t\tisa = XCConfigurationList;\n\t\t\tbuildConfigurations = (\n\t\t\t\t4CF04C3A20323A1100945640 /* Debug */,\n\t\t\t\t4CF04C3B20323A1100945640 /* Debug 10.6 */,\n\t\t\t\t4CF04C3C20323A1100945640 /* Release */,\n\t\t\t\t4CF04C3D20323A1100945640 /* Release 10.6 */,\n\t\t\t);\n\t\t\tdefaultConfigurationIsVisible = 0;\n\t\t\tdefaultConfigurationName = Release;\n\t\t};\n\t\tE2C74E49212B1F8E00947DE0 /* Build configuration list for PBXNativeTarget \"DiffMatchPatch_iOS\" */ = {\n\t\t\tisa = XCConfigurationList;\n\t\t\tbuildConfigurations = (\n\t\t\t\tE2C74E45212B1F8E00947DE0 /* Debug */,\n\t\t\t\tE2C74E46212B1F8E00947DE0 /* Debug 10.6 */,\n\t\t\t\tE2C74E47212B1F8E00947DE0 /* Release */,\n\t\t\t\tE2C74E48212B1F8E00947DE0 /* Release 10.6 */,\n\t\t\t);\n\t\t\tdefaultConfigurationIsVisible = 0;\n\t\t\tdefaultConfigurationName = Release;\n\t\t};\n/* End XCConfigurationList section */\n\t};\n\trootObject = 0867D690FE84028FC02AAC07 /* Project object */;\n}\n"
  },
  {
    "path": "objectivec/DiffMatchPatch.xcodeproj/project.xcworkspace/contents.xcworkspacedata",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<Workspace\n   version = \"1.0\">\n   <FileRef\n      location = \"self:\">\n   </FileRef>\n</Workspace>\n"
  },
  {
    "path": "objectivec/DiffMatchPatch.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">\n<plist version=\"1.0\">\n<dict/>\n</plist>\n"
  },
  {
    "path": "objectivec/DiffMatchPatchCFUtilities.c",
    "content": "/*\n * Diff Match and Patch\n * Copyright 2018 The diff-match-patch Authors.\n * https://github.com/google/diff-match-patch\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *   http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n *\n * Author: fraser@google.com (Neil Fraser)\n * ObjC port: jan@geheimwerk.de (Jan Weiß)\n */\n\n#include <CoreFoundation/CoreFoundation.h>\n\n#include \"DiffMatchPatchCFUtilities.h\"\n\n#include \"MinMaxMacros.h\"\n#include <regex.h>\n#include <limits.h>\n#include <assert.h>\n\nCFStringRef diff_CFStringCreateSubstring(CFStringRef text, CFIndex start_index, CFIndex length);\nCFRange diff_RightSubstringRange(CFIndex text_length, CFIndex new_length);\nCFStringRef diff_CFStringCreateRightSubstring(CFStringRef text, CFIndex text_length, CFIndex new_length);\nCFRange diff_LeftSubstringRange(CFIndex new_length);\nCFStringRef diff_CFStringCreateLeftSubstring(CFStringRef text, CFIndex new_length);\nCFStringRef diff_CFStringCreateSubstringWithStartIndex(CFStringRef text, CFIndex start_index);\nCFStringRef diff_CFStringCreateJavaSubstring(CFStringRef s, CFIndex begin, CFIndex end);\nCFStringRef diff_CFStringCreateByCombiningTwoStrings(CFStringRef best_common_part1, CFStringRef best_common_part2);\nBoolean diff_regExMatch(CFStringRef text, const regex_t *re);\n\nCFArrayRef diff_halfMatchICreate(CFStringRef longtext, CFStringRef shorttext, CFIndex i);\n\n// Utility functions\nCFStringRef diff_CFStringCreateFromUnichar(UniChar ch) {\n  CFStringRef c = CFStringCreateWithCharacters(kCFAllocatorDefault, &ch, 1);\n  CFMakeCollectable(c);\n  return c;\n}\n\nCFStringRef diff_CFStringCreateSubstring(CFStringRef text, CFIndex start_index, CFIndex length) {\n  CFRange substringRange;\n  substringRange.length = length;\n  substringRange.location = start_index;\n\n  CFStringRef substring = CFStringCreateWithSubstring(kCFAllocatorDefault, text, substringRange);\n  CFMakeCollectable(substring);\n\n  return substring;\n}\n\nCFRange diff_RightSubstringRange(CFIndex text_length, CFIndex new_length) {\n  CFRange substringRange;\n  substringRange.length = new_length;\n  substringRange.location = text_length - new_length;\n  return substringRange;\n}\n\nCFStringRef diff_CFStringCreateRightSubstring(CFStringRef text, CFIndex text_length, CFIndex new_length) {\n  return diff_CFStringCreateSubstring(text, text_length - new_length, new_length);\n}\n\nCFRange diff_LeftSubstringRange(CFIndex new_length) {\n  CFRange substringRange;\n  substringRange.length = new_length;\n  substringRange.location = 0;\n  return substringRange;\n}\n\nCFStringRef diff_CFStringCreateLeftSubstring(CFStringRef text, CFIndex new_length) {\n  return diff_CFStringCreateSubstring(text, 0, new_length);\n}\n\nCFStringRef diff_CFStringCreateSubstringWithStartIndex(CFStringRef text, CFIndex start_index) {\n  return diff_CFStringCreateSubstring(text, start_index, (CFStringGetLength(text) - start_index));\n}\n\nCFStringRef diff_CFStringCreateJavaSubstring(CFStringRef s, CFIndex begin, CFIndex end) {\n  return diff_CFStringCreateSubstring(s, begin, end - begin);\n}\n\nCFStringRef diff_CFStringCreateByCombiningTwoStrings(CFStringRef best_common_part1, CFStringRef best_common_part2) {\n  CFIndex best_common_length;\n  CFMutableStringRef best_common_mutable;\n  best_common_length = CFStringGetLength(best_common_part1) + CFStringGetLength(best_common_part2);\n  best_common_mutable = CFStringCreateMutableCopy(kCFAllocatorDefault, best_common_length, best_common_part1);\n  CFMakeCollectable(best_common_mutable);\n  CFStringAppend(best_common_mutable, best_common_part2);\n  return best_common_mutable;\n}\n\nBoolean diff_regExMatch(CFStringRef text, const regex_t *re) {\n  //TODO(jan): Using regex.h is far from optimal. Find an alternative.\n  Boolean isMatch;\n  const char *bytes;\n  char *localBuffer = NULL;\n  char *textCString = NULL;\n  // We are only interested in line endings anyway so ASCII is fine.\n  CFStringEncoding encoding = kCFStringEncodingASCII;\n\n  bytes = CFStringGetCStringPtr(text, encoding);\n\n  if (bytes == NULL) {\n    Boolean success;\n    CFIndex length;\n    CFIndex usedBufferLength;\n    CFIndex textLength = CFStringGetLength(text);\n    CFRange rangeToProcess = CFRangeMake(0, textLength);\n\n    success = (CFStringGetBytes(text, rangeToProcess, encoding, '?', false, NULL, LONG_MAX, &usedBufferLength) > 0);\n    if (success) {\n      length = usedBufferLength + 1;\n\n      localBuffer = calloc(length, sizeof(char));\n      success = (CFStringGetBytes(text, rangeToProcess, encoding, '?', false, (UInt8 *)localBuffer, length, NULL) > 0);\n\n      if (success) {\n        textCString = localBuffer;\n      }\n    }\n  } else {\n    textCString = (char *)bytes;\n  }\n\n  if (textCString != NULL) {\n    isMatch = (regexec(re, textCString, 0, NULL, 0) == 0);\n  } else {\n    isMatch = false;\n    //assert(0);\n  }\n\n  if (localBuffer != NULL) {\n    free(localBuffer);\n  }\n\n  return isMatch;\n}\n\n\n/**\n * Determine the common prefix of two strings.\n * @param text1 First string.\n * @param text2 Second string.\n * @return The number of characters common to the start of each string.\n */\nCFIndex diff_commonPrefix(CFStringRef text1, CFStringRef text2) {\n  // Performance analysis: https://neil.fraser.name/news/2007/10/09/\n  CFIndex text1_length = CFStringGetLength(text1);\n  CFIndex text2_length = CFStringGetLength(text2);\n\n  CFStringInlineBuffer text1_inlineBuffer, text2_inlineBuffer;\n  CFStringInitInlineBuffer(text1, &text1_inlineBuffer, CFRangeMake(0, text1_length));\n  CFStringInitInlineBuffer(text2, &text2_inlineBuffer, CFRangeMake(0, text2_length));\n\n  UniChar char1, char2;\n  CFIndex n = MIN(text1_length, text2_length);\n\n  for (CFIndex i = 0; i < n; i++) {\n    char1 = CFStringGetCharacterFromInlineBuffer(&text1_inlineBuffer, i);\n    char2 = CFStringGetCharacterFromInlineBuffer(&text2_inlineBuffer, i);\n\n    if (char1 != char2) {\n      return i;\n    }\n  }\n\n  return n;\n}\n\n/**\n * Determine the common suffix of two strings.\n * @param text1 First string.\n * @param text2 Second string.\n * @return The number of characters common to the end of each string.\n */\nCFIndex diff_commonSuffix(CFStringRef text1, CFStringRef text2) {\n  // Performance analysis: https://neil.fraser.name/news/2007/10/09/\n  CFIndex text1_length = CFStringGetLength(text1);\n  CFIndex text2_length = CFStringGetLength(text2);\n\n  CFStringInlineBuffer text1_inlineBuffer, text2_inlineBuffer;\n  CFStringInitInlineBuffer(text1, &text1_inlineBuffer, CFRangeMake(0, text1_length));\n  CFStringInitInlineBuffer(text2, &text2_inlineBuffer, CFRangeMake(0, text2_length));\n\n  UniChar char1, char2;\n  CFIndex n = MIN(text1_length, text2_length);\n\n  for (CFIndex i = 1; i <= n; i++) {\n    char1 = CFStringGetCharacterFromInlineBuffer(&text1_inlineBuffer, (text1_length - i));\n    char2 = CFStringGetCharacterFromInlineBuffer(&text2_inlineBuffer, (text2_length - i));\n\n    if (char1 != char2) {\n      return i - 1;\n    }\n  }\n  return n;\n}\n\n/**\n * Determine if the suffix of one CFStringRef is the prefix of another.\n * @param text1 First CFStringRef.\n * @param text2 Second CFStringRef.\n * @return The number of characters common to the end of the first\n *     CFStringRef and the start of the second CFStringRef.\n */\nCFIndex diff_commonOverlap(CFStringRef text1, CFStringRef text2) {\n  CFIndex common_overlap = 0;\n\n  // Cache the text lengths to prevent multiple calls.\n  CFIndex text1_length = CFStringGetLength(text1);\n  CFIndex text2_length = CFStringGetLength(text2);\n\n  // Eliminate the nil case.\n  if (text1_length == 0 || text2_length == 0) {\n    return 0;\n  }\n\n  // Truncate the longer CFStringRef.\n  CFStringRef text1_trunc;\n  CFStringRef text2_trunc;\n  CFIndex text1_trunc_length;\n  if (text1_length > text2_length) {\n    text1_trunc_length = text2_length;\n    text1_trunc = diff_CFStringCreateRightSubstring(text1, text1_length, text1_trunc_length);\n\n    text2_trunc = CFRetain(text2);\n  } else if (text1_length < text2_length) {\n    text1_trunc_length = text1_length;\n    text1_trunc = CFRetain(text1);\n\n    CFIndex text2_trunc_length = text1_length;\n    text2_trunc = diff_CFStringCreateLeftSubstring(text2, text2_trunc_length);\n  } else {\n    text1_trunc_length = text1_length;\n    text1_trunc = CFRetain(text1);\n\n    text2_trunc = CFRetain(text2);\n  }\n\n  CFIndex text_length = MIN(text1_length, text2_length);\n  // Quick check for the worst case.\n  if (text1_trunc == text2_trunc) {\n    common_overlap = text_length;\n  } else {\n    // Start by looking for a single character match\n    // and increase length until no match is found.\n    // Performance analysis: https://neil.fraser.name/news/2010/11/04/\n    CFIndex best = 0;\n    CFIndex length = 1;\n    while (true) {\n      CFStringRef pattern = diff_CFStringCreateRightSubstring(text1_trunc, text1_trunc_length, length);\n      CFRange foundRange = CFStringFind(text2_trunc, pattern, 0);\n      CFRelease(pattern);\n\n      CFIndex found =  foundRange.location;\n      if (found == kCFNotFound) {\n        common_overlap = best;\n        break;\n      }\n      length += found;\n\n      CFStringRef text1_sub = diff_CFStringCreateRightSubstring(text1_trunc, text1_trunc_length, length);\n      CFStringRef text2_sub = diff_CFStringCreateLeftSubstring(text2_trunc, length);\n\n      if (found == 0 || (CFStringCompare(text1_sub, text2_sub, 0) == kCFCompareEqualTo)) {\n        best = length;\n        length++;\n      }\n\n      CFRelease(text1_sub);\n      CFRelease(text2_sub);\n    }\n  }\n\n  CFRelease(text1_trunc);\n  CFRelease(text2_trunc);\n  return common_overlap;\n}\n\n/**\n * Do the two texts share a Substring which is at least half the length of\n * the longer text?\n * This speedup can produce non-minimal diffs.\n * @param text1 First CFStringRef.\n * @param text2 Second CFStringRef.\n * @param diffTimeout Time limit for diff.\n * @return Five element String array, containing the prefix of text1, the\n *     suffix of text1, the prefix of text2, the suffix of text2 and the\n *     common middle.   Or NULL if there was no match.\n */\nCFArrayRef diff_halfMatchCreate(CFStringRef text1, CFStringRef text2, const float diffTimeout) {\n  if (diffTimeout <= 0) {\n    // Don't risk returning a non-optimal diff if we have unlimited time.\n    return NULL;\n  }\n  CFStringRef longtext = CFStringGetLength(text1) > CFStringGetLength(text2) ? text1 : text2;\n  CFStringRef shorttext = CFStringGetLength(text1) > CFStringGetLength(text2) ? text2 : text1;\n  if (CFStringGetLength(longtext) < 4 || CFStringGetLength(shorttext) * 2 < CFStringGetLength(longtext)) {\n    return NULL;  // Pointless.\n  }\n\n  // First check if the second quarter is the seed for a half-match.\n  CFArrayRef hm1 = diff_halfMatchICreate(longtext, shorttext,\n                       (CFStringGetLength(longtext) + 3) / 4);\n  // Check again based on the third quarter.\n  CFArrayRef hm2 = diff_halfMatchICreate(longtext, shorttext,\n                       (CFStringGetLength(longtext) + 1) / 2);\n  CFArrayRef hm;\n  if (hm1 == NULL && hm2 == NULL) {\n    return NULL;\n  } else if (hm2 == NULL) {\n    hm = CFRetain(hm1);\n  } else if (hm1 == NULL) {\n    hm = CFRetain(hm2);\n  } else {\n    // Both matched.  Select the longest.\n    hm = CFStringGetLength(CFArrayGetValueAtIndex(hm1, 4)) > CFStringGetLength(CFArrayGetValueAtIndex(hm2, 4)) ? CFRetain(hm1) : CFRetain(hm2);\n  }\n\n  if (hm1 != NULL) {\n    CFRelease(hm1);\n  }\n  if (hm2 != NULL) {\n    CFRelease(hm2);\n  }\n\n  // A half-match was found, sort out the return data.\n  if (CFStringGetLength(text1) > CFStringGetLength(text2)) {\n    return hm;\n    //return new CFStringRef[]{hm[0], hm[1], hm[2], hm[3], hm[4]};\n  } else {\n    //    { hm[0], hm[1], hm[2], hm[3], hm[4] }\n    // => { hm[2], hm[3], hm[0], hm[1], hm[4] }\n\n    CFMutableArrayRef hm_mutable = CFArrayCreateMutableCopy(kCFAllocatorDefault, CFArrayGetCount(hm), hm);\n    CFMakeCollectable(hm_mutable);\n\n    CFRelease(hm);\n\n    CFArrayExchangeValuesAtIndices(hm_mutable, 0, 2);\n    CFArrayExchangeValuesAtIndices(hm_mutable, 1, 3);\n    return hm_mutable;\n  }\n}\n\n/**\n * Does a Substring of shorttext exist within longtext such that the\n * Substring is at least half the length of longtext?\n * @param longtext Longer CFStringRef.\n * @param shorttext Shorter CFStringRef.\n * @param i Start index of quarter length Substring within longtext.\n * @return Five element CFStringRef array, containing the prefix of longtext, the\n *     suffix of longtext, the prefix of shorttext, the suffix of shorttext\n *     and the common middle.   Or NULL if there was no match.\n */\nCFArrayRef diff_halfMatchICreate(CFStringRef longtext, CFStringRef shorttext, CFIndex i) {\n  // Start with a 1/4 length Substring at position i as a seed.\n  CFStringRef seed = diff_CFStringCreateSubstring(longtext, i, CFStringGetLength(longtext) / 4);\n  CFIndex j = -1;\n  CFStringRef best_common = CFSTR(\"\");\n  CFStringRef best_longtext_a = CFSTR(\"\"), best_longtext_b = CFSTR(\"\");\n  CFStringRef best_shorttext_a = CFSTR(\"\"), best_shorttext_b = CFSTR(\"\");\n\n  CFStringRef best_common_part1, best_common_part2;\n\n  CFStringRef longtext_substring, shorttext_substring;\n  CFIndex shorttext_length = CFStringGetLength(shorttext);\n  CFRange resultRange;\n  CFRange rangeToSearch;\n  rangeToSearch.length = shorttext_length - (j + 1);\n  rangeToSearch.location = j + 1;\n\n  while (j < CFStringGetLength(shorttext)\n       && (CFStringFindWithOptions(shorttext, seed, rangeToSearch, 0, &resultRange) == true)) {\n    j = resultRange.location;\n    rangeToSearch.length = shorttext_length - (j + 1);\n    rangeToSearch.location = j + 1;\n\n    longtext_substring = diff_CFStringCreateSubstringWithStartIndex(longtext, i);\n    shorttext_substring = diff_CFStringCreateSubstringWithStartIndex(shorttext, j);\n\n    CFIndex prefixLength = diff_commonPrefix(longtext_substring, shorttext_substring);\n\n    CFRelease(longtext_substring);\n    CFRelease(shorttext_substring);\n\n    longtext_substring = diff_CFStringCreateLeftSubstring(longtext, i);\n    shorttext_substring = diff_CFStringCreateLeftSubstring(shorttext, j);\n\n    CFIndex suffixLength = diff_commonSuffix(longtext_substring, shorttext_substring);\n\n    CFRelease(longtext_substring);\n    CFRelease(shorttext_substring);\n\n    if (CFStringGetLength(best_common) < suffixLength + prefixLength) {\n      CFRelease(best_common);\n      CFRelease(best_longtext_a);\n      CFRelease(best_longtext_b);\n      CFRelease(best_shorttext_a);\n      CFRelease(best_shorttext_b);\n\n      best_common_part1 = diff_CFStringCreateSubstring(shorttext, j - suffixLength, suffixLength);\n      best_common_part2 = diff_CFStringCreateSubstring(shorttext, j, prefixLength);\n\n      best_common = diff_CFStringCreateByCombiningTwoStrings(best_common_part1, best_common_part2);\n\n      CFRelease(best_common_part1);\n      CFRelease(best_common_part2);\n\n      best_longtext_a = diff_CFStringCreateLeftSubstring(longtext, i - suffixLength);\n      best_longtext_b = diff_CFStringCreateSubstringWithStartIndex(longtext, i + prefixLength);\n      best_shorttext_a = diff_CFStringCreateLeftSubstring(shorttext, j - suffixLength);\n      best_shorttext_b = diff_CFStringCreateSubstringWithStartIndex(shorttext, j + prefixLength);\n    }\n  }\n\n  CFRelease(seed);\n\n  CFArrayRef halfMatchIArray;\n  if (CFStringGetLength(best_common) * 2 >= CFStringGetLength(longtext)) {\n    const CFStringRef values[] = { best_longtext_a, best_longtext_b,\n                     best_shorttext_a, best_shorttext_b, best_common };\n    halfMatchIArray = CFArrayCreate(kCFAllocatorDefault, (const void **)values, (sizeof(values) / sizeof(values[0])), &kCFTypeArrayCallBacks);\n    CFMakeCollectable(halfMatchIArray);\n  } else {\n    halfMatchIArray = NULL;\n  }\n\n  CFRelease(best_common);\n  CFRelease(best_longtext_a);\n  CFRelease(best_longtext_b);\n  CFRelease(best_shorttext_a);\n  CFRelease(best_shorttext_b);\n\n  return halfMatchIArray;\n}\n\n/**\n * Split a text into a list of strings.   Reduce the texts to a CFStringRef of\n * hashes where each Unicode character represents one line.\n * @param text CFString to encode.\n * @param lineArray CFMutableArray of unique strings.\n * @param lineHash Map of strings to indices.\n * @param maxLines Maximum length for lineArray.\n * @return Encoded CFStringRef.\n */\nCFStringRef diff_linesToCharsMungeCFStringCreate(CFStringRef text, CFMutableArrayRef lineArray, CFMutableDictionaryRef lineHash, CFIndex maxLines) {\n  #define lineStart lineStartRange.location\n  #define lineEnd lineEndRange.location\n\n  CFRange lineStartRange;\n  CFRange lineEndRange;\n  lineStart = 0;\n  lineEnd = -1;\n  CFStringRef line;\n  CFMutableStringRef chars = CFStringCreateMutable(kCFAllocatorDefault, 0);\n\n  CFIndex textLength = CFStringGetLength(text);\n  CFIndex hash;\n  CFNumberRef hashNumber;\n\n  // Walk the text, pulling out a Substring for each line.\n  // CFStringCreateArrayBySeparatingStrings(kCFAllocatorDefault, text, CFSTR(\"\\n\")) would temporarily double our memory footprint.\n  // Modifying text would create many large strings.\n  while (lineEnd < textLength - 1) {\n    lineStartRange.length = textLength - lineStart;\n\n    if (CFStringFindWithOptions(text, CFSTR(\"\\n\"), lineStartRange, 0, &lineEndRange) == false) {\n      lineEnd = textLength - 1;\n    } /* else {\n      lineEnd = lineEndRange.location;\n    }*/\n\n    line = diff_CFStringCreateJavaSubstring(text, lineStart, lineEnd + 1);\n\n    if (CFDictionaryContainsKey(lineHash, line)) {\n      CFDictionaryGetValueIfPresent(lineHash, line, (const void **)&hashNumber);\n      CFNumberGetValue(hashNumber, kCFNumberCFIndexType, &hash);\n      const UniChar hashChar = (UniChar)hash;\n      CFStringAppendCharacters(chars, &hashChar, 1);\n    } else {\n      if (CFArrayGetCount(lineArray) == maxLines) {\n        // Bail out at 65535 because char 65536 == char 0.\n        line = diff_CFStringCreateJavaSubstring(text, lineStart, textLength);\n        lineEnd = textLength;\n      }\n      CFArrayAppendValue(lineArray, line);\n      hash = CFArrayGetCount(lineArray) - 1;\n      hashNumber = CFNumberCreate(kCFAllocatorDefault, kCFNumberCFIndexType, &hash);\n      CFMakeCollectable(hashNumber);\n      CFDictionaryAddValue(lineHash, line, hashNumber);\n      CFRelease(hashNumber);\n      const UniChar hashChar = (UniChar)hash;\n      CFStringAppendCharacters(chars, &hashChar, 1);\n    }\n    lineStart = lineEnd + 1;\n\n    CFRelease(line);\n  }\n  return chars;\n\n  #undef lineStart\n  #undef lineEnd\n}\n\n/**\n * Given two strings, compute a score representing whether the internal\n * boundary falls on logical boundaries.\n * Scores range from 6 (best) to 0 (worst).\n * @param one First CFStringRef.\n * @param two Second CFStringRef.\n * @return The score.\n */\nCFIndex diff_cleanupSemanticScore(CFStringRef one, CFStringRef two) {\n  static Boolean firstRun = true;\n  static CFCharacterSetRef alphaNumericSet = NULL;\n  static CFCharacterSetRef whiteSpaceSet = NULL;\n  static CFCharacterSetRef controlSet = NULL;\n  static regex_t blankLineEndRegEx;\n  static regex_t blankLineStartRegEx;\n\n  if (firstRun) {\n    // Define some regex patterns for matching boundaries.\n    alphaNumericSet = CFCharacterSetGetPredefined(kCFCharacterSetAlphaNumeric);\n    whiteSpaceSet = CFCharacterSetGetPredefined(kCFCharacterSetWhitespaceAndNewline);\n    controlSet = CFCharacterSetGetPredefined(kCFCharacterSetControl);\n    int status;\n    status = regcomp(&blankLineEndRegEx, \"\\n\\r?\\n$\", REG_EXTENDED | REG_NOSUB);\n    assert(status == 0);\n    status = regcomp(&blankLineStartRegEx, \"^\\r?\\n\\r?\\n\", REG_EXTENDED | REG_NOSUB);\n    assert(status == 0);\n    firstRun = false;\n  }\n\n  if (CFStringGetLength(one) == 0 || CFStringGetLength(two) == 0) {\n    // Edges are the best.\n    return 6;\n  }\n\n  // Each port of this function behaves slightly differently due to\n  // subtle differences in each language's definition of things like\n  // 'whitespace'.  Since this function's purpose is largely cosmetic,\n  // the choice has been made to use each language's native features\n  // rather than force total conformity.\n  UniChar char1 =\n      CFStringGetCharacterAtIndex(one, (CFStringGetLength(one) - 1));\n  UniChar char2 =\n      CFStringGetCharacterAtIndex(two, 0);\n  Boolean nonAlphaNumeric1 =\n      !CFCharacterSetIsCharacterMember(alphaNumericSet, char1);\n  Boolean nonAlphaNumeric2 =\n      !CFCharacterSetIsCharacterMember(alphaNumericSet, char2);\n  Boolean whitespace1 =\n      nonAlphaNumeric1 && CFCharacterSetIsCharacterMember(whiteSpaceSet, char1);\n  Boolean whitespace2 =\n      nonAlphaNumeric2 && CFCharacterSetIsCharacterMember(whiteSpaceSet, char2);\n  Boolean lineBreak1 =\n      whitespace1 && CFCharacterSetIsCharacterMember(controlSet, char1);\n  Boolean lineBreak2 =\n      whitespace2 && CFCharacterSetIsCharacterMember(controlSet, char2);\n  Boolean blankLine1 =\n      lineBreak1 && diff_regExMatch(one, &blankLineEndRegEx);\n  Boolean blankLine2 =\n      lineBreak2 && diff_regExMatch(two, &blankLineStartRegEx);\n\n  if (blankLine1 || blankLine2) {\n    // Five points for blank lines.\n    return 5;\n  } else if (lineBreak1 || lineBreak2) {\n    // Four points for line breaks.\n    return 4;\n  } else if (nonAlphaNumeric1 && !whitespace1 && whitespace2) {\n    // Three points for end of sentences.\n    return 3;\n  } else if (whitespace1 || whitespace2) {\n    // Two points for whitespace.\n    return 2;\n  } else if (nonAlphaNumeric1 || nonAlphaNumeric2) {\n    // One point for non-alphanumeric.\n    return 1;\n  }\n  return 0;\n}\n"
  },
  {
    "path": "objectivec/DiffMatchPatchCFUtilities.h",
    "content": "/*\n * Diff Match and Patch\n * Copyright 2018 The diff-match-patch Authors.\n * https://github.com/google/diff-match-patch\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *   http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n *\n * Author: fraser@google.com (Neil Fraser)\n * ObjC port: jan@geheimwerk.de (Jan Weiß)\n */\n\n#ifndef _DIFFMATCHPATCHCFUTILITIES_H\n#define _DIFFMATCHPATCHCFUTILITIES_H\n\nCFStringRef diff_CFStringCreateFromUnichar(UniChar ch);\nCFStringRef diff_CFStringCreateJavaSubstring(CFStringRef s, CFIndex begin, CFIndex end);\n\nCFIndex diff_commonPrefix(CFStringRef text1, CFStringRef text2);\nCFIndex diff_commonSuffix(CFStringRef text1, CFStringRef text2);\nCFIndex diff_commonOverlap(CFStringRef text1, CFStringRef text2);\nCFArrayRef diff_halfMatchCreate(CFStringRef text1, CFStringRef text2, const float diffTimeout);\nCFArrayRef diff_halfMatchICreate(CFStringRef longtext, CFStringRef shorttext, CFIndex i);\n\nCFStringRef diff_linesToCharsMungeCFStringCreate(CFStringRef text, CFMutableArrayRef lineArray, CFMutableDictionaryRef lineHash, CFIndex maxLines);\n\nCFIndex diff_cleanupSemanticScore(CFStringRef one, CFStringRef two);\n\nCF_INLINE void diff_CFStringPrepareUniCharBuffer(CFStringRef string, const UniChar **string_chars, UniChar **string_buffer, CFRange string_range) {\n  *string_chars = CFStringGetCharactersPtr(string);\n  if (*string_chars == NULL) {\n    // Fallback in case CFStringGetCharactersPtr() didn’t work.\n    *string_buffer = malloc(string_range.length * sizeof(UniChar));\n    CFStringGetCharacters(string, string_range, *string_buffer);\n    *string_chars = *string_buffer;\n  }\n}\n\n#endif //ifndef _DIFFMATCHPATCHCFUTILITIES_H\n"
  },
  {
    "path": "objectivec/DiffMatchPatch_Prefix.pch",
    "content": "//\n// Prefix header for all source files of the 'DiffMatchPatch' target in the 'DiffMatchPatch' project.\n//\n\n#ifdef __OBJC__\n    #import <Foundation/Foundation.h>\n#endif\n"
  },
  {
    "path": "objectivec/English.lproj/InfoPlist.strings",
    "content": "﻿/* Localized versions of Info.plist keys */\n\n"
  },
  {
    "path": "objectivec/Info.plist",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">\n<plist version=\"1.0\">\n<dict>\n\t<key>CFBundleDevelopmentRegion</key>\n\t<string>English</string>\n\t<key>CFBundleExecutable</key>\n\t<string>${EXECUTABLE_NAME}</string>\n\t<key>CFBundleIconFile</key>\n\t<string></string>\n\t<key>CFBundleIdentifier</key>\n\t<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>\n\t<key>CFBundleInfoDictionaryVersion</key>\n\t<string>6.0</string>\n\t<key>CFBundleName</key>\n\t<string>${PRODUCT_NAME}</string>\n\t<key>CFBundlePackageType</key>\n\t<string>FMWK</string>\n\t<key>CFBundleShortVersionString</key>\n\t<string>${MARKETING_VERSION}</string>\n\t<key>CFBundleSignature</key>\n\t<string>????</string>\n\t<key>CFBundleVersion</key>\n\t<string>${PROJECT_VERSION}</string>\n\t<key>NSPrincipalClass</key>\n\t<string></string>\n</dict>\n</plist>\n"
  },
  {
    "path": "objectivec/MinMaxMacros.h",
    "content": "/*\n * Diff Match and Patch\n * Copyright 2018 The diff-match-patch Authors.\n * https://github.com/google/diff-match-patch\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *   http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n *\n * Author: fraser@google.com (Neil Fraser)\n * ObjC port: jan@geheimwerk.de (Jan Weiß)\n */\n\n#if !defined(MIN)\n  #define MIN(A,B) \\\n    ({__typeof__(A) a = (A); \\\n      __typeof__(B) b = (B); \\\n      (a < b) ? a : b; })\n#endif\n\n#if !defined(MAX)\n  #define MAX(A,B) \\\n    ({__typeof__(A) a = (A); \\\n      __typeof__(B) b = (B); \\\n      (a > b) ? a : b; })\n#endif\n\n#if !defined(ABS)\n  #define ABS(A) \\\n    ({__typeof__(A) a = (A); \\\n      (a > 0) ? a : -a; })\n#endif\n"
  },
  {
    "path": "objectivec/NSMutableDictionary+DMPExtensions.h",
    "content": "/*\n * Diff Match and Patch\n * Copyright 2018 The diff-match-patch Authors.\n * https://github.com/google/diff-match-patch\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *   http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n *\n * Author: fraser@google.com (Neil Fraser)\n * ObjC port: jan@geheimwerk.de (Jan Weiß)\n */\n\n#import <Foundation/Foundation.h>\n\n\n@interface NSMutableDictionary (DMPExtensions)\n\n- (id)diff_objectForIntegerKey:(NSInteger)keyInteger;\n- (id)diff_objectForUnsignedIntegerKey:(NSUInteger)keyUInteger;\n- (id)diff_objectForUnicharKey:(unichar)aUnicharKey;\n\n- (NSInteger)diff_integerForKey:(id)aKey;\n- (NSUInteger)diff_unsignedIntegerForKey:(id)aKey;\n- (NSInteger)diff_integerForIntegerKey:(NSInteger)keyInteger;\n- (NSUInteger)diff_unsignedIntegerForUnicharKey:(unichar)aUnicharKey;\n\n- (BOOL)diff_containsObjectForKey:(id)aKey;\n- (BOOL)diff_containsObjectForUnicharKey:(unichar)aUnicharKey;\n\n- (void)diff_setIntegerValue:(NSInteger)anInteger forKey:(id)aKey;\n- (void)diff_setIntegerValue:(NSInteger)anInteger forIntegerKey:(NSInteger)keyInteger;\n\n- (void)diff_setUnsignedIntegerValue:(NSUInteger)anUInteger forKey:(id)aKey;\n- (void)diff_setUnsignedIntegerValue:(NSUInteger)anUInteger forUnsignedIntegerKey:(NSUInteger)keyUInteger;\n- (void)diff_setUnsignedIntegerValue:(NSUInteger)anUInteger forUnicharKey:(unichar)aUnicharKey;\n\n@end\n"
  },
  {
    "path": "objectivec/NSMutableDictionary+DMPExtensions.m",
    "content": "/*\n * Diff Match and Patch\n * Copyright 2018 The diff-match-patch Authors.\n * https://github.com/google/diff-match-patch\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *   http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n *\n * Author: fraser@google.com (Neil Fraser)\n * ObjC port: jan@geheimwerk.de (Jan Weiß)\n */\n\n#import \"NSMutableDictionary+DMPExtensions.h\"\n\n#import \"NSString+UnicharUtilities.h\"\n\n\n@implementation NSMutableDictionary (DMPExtensions)\n\n- (id)diff_objectForIntegerKey:(NSInteger)keyInteger;\n{\n  return [self objectForKey:[NSNumber numberWithInteger:keyInteger]];\n}\n\n- (id)diff_objectForUnsignedIntegerKey:(NSUInteger)keyUInteger;\n{\n  return [self objectForKey:[NSNumber numberWithUnsignedInteger:keyUInteger]];\n}\n\n- (id)diff_objectForUnicharKey:(unichar)aUnicharKey;\n{\n  return [self objectForKey:[NSString diff_stringFromUnichar:aUnicharKey]];\n}\n\n\n- (NSInteger)diff_integerForKey:(id)aKey;\n{\n  return [((NSNumber *)[self objectForKey:aKey]) integerValue];\n}\n\n- (NSUInteger)diff_unsignedIntegerForKey:(id)aKey;\n{\n  return [((NSNumber *)[self objectForKey:aKey]) unsignedIntegerValue];\n}\n\n- (NSInteger)diff_integerForIntegerKey:(NSInteger)keyInteger;\n{\n  return [((NSNumber *)[self objectForKey:[NSNumber numberWithInteger:keyInteger]]) integerValue];\n}\n\n- (NSUInteger)diff_unsignedIntegerForUnicharKey:(unichar)aUnicharKey;\n{\n  return [((NSNumber *)[self diff_objectForUnicharKey:aUnicharKey]) unsignedIntegerValue];\n}\n\n\n- (BOOL)diff_containsObjectForKey:(id)aKey;\n{\n  return ([self objectForKey:aKey] != nil);\n}\n\n- (BOOL)containsObjectForIntegerKey:(NSInteger)keyInteger;\n{\n  return ([self objectForKey:[NSNumber numberWithInteger:keyInteger]] != nil);\n}\n\n- (BOOL)diff_containsObjectForUnicharKey:(unichar)aUnicharKey;\n{\n  return ([self diff_objectForUnicharKey:aUnicharKey] != nil);\n}\n\n\n- (void)diff_setIntegerValue:(NSInteger)anInteger forKey:(id)aKey;\n{\n  [self setObject:[NSNumber numberWithInteger:anInteger] forKey:aKey];\n}\n\n- (void)diff_setIntegerValue:(NSInteger)anInteger forIntegerKey:(NSInteger)keyInteger;\n{\n  [self setObject:[NSNumber numberWithInteger:anInteger] forKey:[NSNumber numberWithInteger:keyInteger]];\n}\n\n\n- (void)diff_setUnsignedIntegerValue:(NSUInteger)anUInteger forKey:(id)aKey;\n{\n  [self setObject:[NSNumber numberWithUnsignedInteger:anUInteger] forKey:aKey];\n}\n\n- (void)diff_setUnsignedIntegerValue:(NSUInteger)anUInteger forUnsignedIntegerKey:(NSUInteger)keyUInteger;\n{\n  [self setObject:[NSNumber numberWithUnsignedInteger:anUInteger] forKey:[NSNumber numberWithUnsignedInteger:keyUInteger]];\n}\n\n- (void)diff_setUnsignedIntegerValue:(NSUInteger)anUInteger forUnicharKey:(unichar)aUnicharKey;\n{\n  [self setObject:[NSNumber numberWithUnsignedInteger:anUInteger] forKey:[NSString diff_stringFromUnichar:aUnicharKey]];\n}\n\n@end\n"
  },
  {
    "path": "objectivec/NSString+JavaSubstring.h",
    "content": "/*\n * Diff Match and Patch\n * Copyright 2018 The diff-match-patch Authors.\n * https://github.com/google/diff-match-patch\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *   http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n *\n * Author: fraser@google.com (Neil Fraser)\n * ObjC port: jan@geheimwerk.de (Jan Weiß)\n */\n\n#import <Foundation/Foundation.h>\n\n\n@interface NSString (JavaSubstring)\n\n- (NSString *)diff_javaSubstringFromStart:(NSUInteger)start toEnd:(NSUInteger)end;\n\n@end\n"
  },
  {
    "path": "objectivec/NSString+JavaSubstring.m",
    "content": "/*\n * Diff Match and Patch\n * Copyright 2018 The diff-match-patch Authors.\n * https://github.com/google/diff-match-patch\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *   http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n *\n * Author: fraser@google.com (Neil Fraser)\n * ObjC port: jan@geheimwerk.de (Jan Weiß)\n */\n\n#import \"NSString+JavaSubstring.h\"\n\n#import \"DiffMatchPatchCFUtilities.h\"\n\n@implementation NSString (JavaSubstring)\n\n- (NSString *)diff_javaSubstringFromStart:(NSUInteger)start toEnd:(NSUInteger)end;\n{\n  CFStringRef c = diff_CFStringCreateJavaSubstring((CFStringRef)self, (CFIndex)start, (CFIndex)end);\n  CFMakeCollectable(c);\n  return [(NSString *)c autorelease];\n}\n\n@end\n"
  },
  {
    "path": "objectivec/NSString+UnicharUtilities.h",
    "content": "/*\n * Diff Match and Patch\n * Copyright 2018 The diff-match-patch Authors.\n * https://github.com/google/diff-match-patch\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *   http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n *\n * Author: fraser@google.com (Neil Fraser)\n * ObjC port: jan@geheimwerk.de (Jan Weiß)\n */\n\n#import <Foundation/Foundation.h>\n\n\n@interface NSString (UnicharUtilities)\n\n+ (NSString *)diff_stringFromUnichar:(unichar)ch;\n- (NSString *)diff_substringWithCharacterAtIndex:(NSUInteger)anIndex;\n\n@end\n"
  },
  {
    "path": "objectivec/NSString+UnicharUtilities.m",
    "content": "/*\n * Diff Match and Patch\n * Copyright 2018 The diff-match-patch Authors.\n * https://github.com/google/diff-match-patch\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *   http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n *\n * Author: fraser@google.com (Neil Fraser)\n * ObjC port: jan@geheimwerk.de (Jan Weiß)\n */\n\n#import \"NSString+UnicharUtilities.h\"\n\n\n@implementation NSString (UnicharUtilities)\n\n+ (NSString *)diff_stringFromUnichar:(unichar)ch;\n{\n  CFStringRef c = CFStringCreateWithCharacters(kCFAllocatorDefault, &ch, 1);\n  CFMakeCollectable(c);\n  return [(NSString *)c autorelease];\n}\n\n- (NSString *)diff_substringWithCharacterAtIndex:(NSUInteger)anIndex;\n{\n  return [self substringWithRange:NSMakeRange(anIndex, 1)];\n}\n\n@end\n"
  },
  {
    "path": "objectivec/NSString+UriCompatibility.h",
    "content": "/*\n * Diff Match and Patch\n * Copyright 2018 The diff-match-patch Authors.\n * https://github.com/google/diff-match-patch\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *   http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n *\n * Author: fraser@google.com (Neil Fraser)\n * ObjC port: jan@geheimwerk.de (Jan Weiß)\n */\n\n#import <Foundation/Foundation.h>\n\n\n@interface NSString (UriCompatibility)\n\n- (NSString *)diff_stringByAddingPercentEscapesForEncodeUriCompatibility;\n- (NSString *)diff_stringByReplacingPercentEscapesForEncodeUriCompatibility;\n\n@end\n"
  },
  {
    "path": "objectivec/NSString+UriCompatibility.m",
    "content": "/*\n * Diff Match and Patch\n * Copyright 2018 The diff-match-patch Authors.\n * https://github.com/google/diff-match-patch\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *   http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n *\n * Author: fraser@google.com (Neil Fraser)\n * ObjC port: jan@geheimwerk.de (Jan Weiß)\n */\n\n#import \"NSString+UriCompatibility.h\"\n\n\n@implementation NSString (UriCompatibility)\n\n/**\n * Escape excluding selected chars for compatability with JavaScript's encodeURI.\n * This method produces uppercase hex.\n *\n * @return The escaped CFStringRef.\n */\n- (NSString *)diff_stringByAddingPercentEscapesForEncodeUriCompatibility {\n  NSMutableCharacterSet *allowedCharacters =\n      [NSMutableCharacterSet characterSetWithCharactersInString:@\" !~*'();/?:@&=+$,#\"];\n  [allowedCharacters formUnionWithCharacterSet:[NSCharacterSet URLQueryAllowedCharacterSet]];\n  NSString *URLString =\n      [self stringByAddingPercentEncodingWithAllowedCharacters:allowedCharacters];\n\n  return URLString;\n}\n\n/**\n * Unescape all percent escapes.\n *\n * Example: \"%3f\" -> \"?\", \"%24\" -> \"$\", etc.\n *\n * @return The unescaped NSString.\n */\n- (NSString *)diff_stringByReplacingPercentEscapesForEncodeUriCompatibility {\n  NSString *decodedString = [self stringByRemovingPercentEncoding];\n  return decodedString;\n}\n\n@end\n"
  },
  {
    "path": "objectivec/Tests/DiffMatchPatchTest-Info.plist",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">\n<plist version=\"1.0\">\n<dict>\n\t<key>CFBundleDevelopmentRegion</key>\n\t<string>$(DEVELOPMENT_LANGUAGE)</string>\n\t<key>CFBundleExecutable</key>\n\t<string>$(EXECUTABLE_NAME)</string>\n\t<key>CFBundleIdentifier</key>\n\t<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>\n\t<key>CFBundleInfoDictionaryVersion</key>\n\t<string>6.0</string>\n\t<key>CFBundleName</key>\n\t<string>$(PRODUCT_NAME)</string>\n\t<key>CFBundlePackageType</key>\n\t<string>BNDL</string>\n\t<key>CFBundleShortVersionString</key>\n\t<string>1.0</string>\n\t<key>CFBundleVersion</key>\n\t<string>1</string>\n</dict>\n</plist>\n"
  },
  {
    "path": "objectivec/Tests/DiffMatchPatchTest.h",
    "content": "/*\n * Diff Match and Patch -- Test harness\n * Copyright 2018 The diff-match-patch Authors.\n * https://github.com/google/diff-match-patch\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *   http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n *\n * Author: fraser@google.com (Neil Fraser)\n * ObjC port: jan@geheimwerk.de (Jan Weiß)\n */\n\n#import <XCTest/XCTest.h>\n\n#import \"DiffMatchPatch.h\"\n\n@interface DiffMatchPatchTest : XCTestCase\n@end\n"
  },
  {
    "path": "objectivec/Tests/DiffMatchPatchTest.m",
    "content": "/*\n * Diff Match and Patch -- Test harness\n * Copyright 2018 The diff-match-patch Authors.\n * https://github.com/google/diff-match-patch\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *   http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n *\n * Author: fraser@google.com (Neil Fraser)\n * ObjC port: jan@geheimwerk.de (Jan Weiß)\n */\n\n#import \"DiffMatchPatchTest.h\"\n\n#import \"DiffMatchPatch.h\"\n#import \"NSMutableDictionary+DMPExtensions.h\"\n\n#define stringForBOOL(A)  ([((NSNumber *)A) boolValue] ? @\"true\" : @\"false\")\n\n@interface DiffMatchPatchTest (PrivatMethods)\n- (NSArray *)diff_rebuildtexts:(NSMutableArray *)diffs;\n@end\n\n@implementation DiffMatchPatchTest\n\n- (void)test_diff_commonPrefixTest {\n  DiffMatchPatch *dmp = [DiffMatchPatch new];\n\n  // Detect any common suffix.\n  // Null case.\n  XCTAssertEqual((NSUInteger)0, [dmp diff_commonPrefixOfFirstString:@\"abc\" andSecondString:@\"xyz\"], @\"Common suffix null case failed.\");\n\n  // Non-null case.\n  XCTAssertEqual((NSUInteger)4, [dmp diff_commonPrefixOfFirstString:@\"1234abcdef\" andSecondString:@\"1234xyz\"], @\"Common suffix non-null case failed.\");\n\n  // Whole case.\n  XCTAssertEqual((NSUInteger)4, [dmp diff_commonPrefixOfFirstString:@\"1234\" andSecondString:@\"1234xyz\"], @\"Common suffix whole case failed.\");\n\n  [dmp release];\n}\n\n- (void)test_diff_commonSuffixTest {\n  DiffMatchPatch *dmp = [DiffMatchPatch new];\n\n  // Detect any common suffix.\n  // Null case.\n  XCTAssertEqual((NSUInteger)0, [dmp diff_commonSuffixOfFirstString:@\"abc\" andSecondString:@\"xyz\"], @\"Detect any common suffix. Null case.\");\n\n  // Non-null case.\n  XCTAssertEqual((NSUInteger)4, [dmp diff_commonSuffixOfFirstString:@\"abcdef1234\" andSecondString:@\"xyz1234\"], @\"Detect any common suffix. Non-null case.\");\n\n  // Whole case.\n  XCTAssertEqual((NSUInteger)4, [dmp diff_commonSuffixOfFirstString:@\"1234\" andSecondString:@\"xyz1234\"], @\"Detect any common suffix. Whole case.\");\n\n  [dmp release];\n}\n\n- (void)test_diff_commonOverlapTest {\n  DiffMatchPatch *dmp = [DiffMatchPatch new];\n\n  // Detect any suffix/prefix overlap.\n  // Null case.\n  XCTAssertEqual((NSUInteger)0, [dmp diff_commonOverlapOfFirstString:@\"\" andSecondString:@\"abcd\"], @\"Detect any suffix/prefix overlap. Null case.\");\n\n  // Whole case.\n  XCTAssertEqual((NSUInteger)3, [dmp diff_commonOverlapOfFirstString:@\"abc\" andSecondString:@\"abcd\"], @\"Detect any suffix/prefix overlap. Whole case.\");\n\n  // No overlap.\n  XCTAssertEqual((NSUInteger)0, [dmp diff_commonOverlapOfFirstString:@\"123456\" andSecondString:@\"abcd\"], @\"Detect any suffix/prefix overlap. No overlap.\");\n\n  // Overlap.\n  XCTAssertEqual((NSUInteger)3, [dmp diff_commonOverlapOfFirstString:@\"123456xxx\" andSecondString:@\"xxxabcd\"], @\"Detect any suffix/prefix overlap. Overlap.\");\n\n  // Unicode.\n  // Some overly clever languages (C#) may treat ligatures as equal to their\n  // component letters.  E.g. U+FB01 == 'fi'\n  XCTAssertEqual((NSUInteger)0, [dmp diff_commonOverlapOfFirstString:@\"fi\" andSecondString:@\"\\U0000fb01i\"], @\"Detect any suffix/prefix overlap. Unicode.\");\n\n  [dmp release];\n}\n\n- (void)test_diff_halfmatchTest {\n  DiffMatchPatch *dmp = [DiffMatchPatch new];\n  dmp.Diff_Timeout = 1;\n  NSArray *expectedResult = nil;\n\n  // No match.\n  XCTAssertNil([dmp diff_halfMatchOfFirstString:@\"1234567890\" andSecondString:@\"abcdef\"], @\"No match #1.\");\n\n  XCTAssertNil([dmp diff_halfMatchOfFirstString:@\"12345\" andSecondString:@\"23\"], @\"No match #2.\");\n\n  // Single Match.\n  expectedResult = [NSArray arrayWithObjects:@\"12\", @\"90\", @\"a\", @\"z\", @\"345678\", nil];\n  XCTAssertEqualObjects(expectedResult, [dmp diff_halfMatchOfFirstString:@\"1234567890\" andSecondString:@\"a345678z\"], @\"Single Match #1.\");\n\n  expectedResult = [NSArray arrayWithObjects:@\"a\", @\"z\", @\"12\", @\"90\", @\"345678\", nil];\n  XCTAssertEqualObjects(expectedResult, [dmp diff_halfMatchOfFirstString:@\"a345678z\" andSecondString:@\"1234567890\"], @\"Single Match #2.\");\n\n  expectedResult = [NSArray arrayWithObjects:@\"abc\", @\"z\", @\"1234\", @\"0\", @\"56789\", nil];\n  XCTAssertEqualObjects(expectedResult, [dmp diff_halfMatchOfFirstString:@\"abc56789z\" andSecondString:@\"1234567890\"], @\"Single Match #3.\");\n\n  expectedResult = [NSArray arrayWithObjects:@\"a\", @\"xyz\", @\"1\", @\"7890\", @\"23456\", nil];\n  XCTAssertEqualObjects(expectedResult, [dmp diff_halfMatchOfFirstString:@\"a23456xyz\" andSecondString:@\"1234567890\"], @\"Single Match #4.\");\n\n  // Multiple Matches.\n  expectedResult = [NSArray arrayWithObjects:@\"12123\", @\"123121\", @\"a\", @\"z\", @\"1234123451234\", nil];\n  XCTAssertEqualObjects(expectedResult, [dmp diff_halfMatchOfFirstString:@\"121231234123451234123121\" andSecondString:@\"a1234123451234z\"], @\"Multiple Matches #1.\");\n\n  expectedResult = [NSArray arrayWithObjects:@\"\", @\"-=-=-=-=-=\", @\"x\", @\"\", @\"x-=-=-=-=-=-=-=\", nil];\n  XCTAssertEqualObjects(expectedResult, [dmp diff_halfMatchOfFirstString:@\"x-=-=-=-=-=-=-=-=-=-=-=-=\" andSecondString:@\"xx-=-=-=-=-=-=-=\"], @\"Multiple Matches #2.\");\n\n  expectedResult = [NSArray arrayWithObjects:@\"-=-=-=-=-=\", @\"\", @\"\", @\"y\", @\"-=-=-=-=-=-=-=y\", nil];\n  XCTAssertEqualObjects(expectedResult, [dmp diff_halfMatchOfFirstString:@\"-=-=-=-=-=-=-=-=-=-=-=-=y\" andSecondString:@\"-=-=-=-=-=-=-=yy\"], @\"Multiple Matches #3.\");\n\n  // Non-optimal halfmatch.\n  // Optimal diff would be -q+x=H-i+e=lloHe+Hu=llo-Hew+y not -qHillo+x=HelloHe-w+Hulloy\n  expectedResult = [NSArray arrayWithObjects:@\"qHillo\", @\"w\", @\"x\", @\"Hulloy\", @\"HelloHe\", nil];\n  XCTAssertEqualObjects(expectedResult, [dmp diff_halfMatchOfFirstString:@\"qHilloHelloHew\" andSecondString:@\"xHelloHeHulloy\"], @\"Non-optimal halfmatch.\");\n\n  // Optimal no halfmatch.\n  dmp.Diff_Timeout = 0;\n  XCTAssertNil([dmp diff_halfMatchOfFirstString:@\"qHilloHelloHew\" andSecondString:@\"xHelloHeHulloy\"], @\"Optimal no halfmatch.\");\n\n  [dmp release];\n}\n\n- (void)test_diff_linesToCharsTest {\n  DiffMatchPatch *dmp = [DiffMatchPatch new];\n  NSArray *result;\n\n  // Convert lines down to characters.\n  NSMutableArray *tmpVector = [NSMutableArray array];  // Array of NSString objects.\n  [tmpVector addObject:@\"\"];\n  [tmpVector addObject:@\"alpha\\n\"];\n  [tmpVector addObject:@\"beta\\n\"];\n  result = [dmp diff_linesToCharsForFirstString:@\"alpha\\nbeta\\nalpha\\n\" andSecondString:@\"beta\\nalpha\\nbeta\\n\"];\n  XCTAssertEqualObjects(@\"\\001\\002\\001\", [result objectAtIndex:0], @\"Shared lines #1.\");\n  XCTAssertEqualObjects(@\"\\002\\001\\002\", [result objectAtIndex:1], @\"Shared lines #2.\");\n  XCTAssertEqualObjects(tmpVector, (NSArray *)[result objectAtIndex:2], @\"Shared lines #3.\");\n\n  [tmpVector removeAllObjects];\n  [tmpVector addObject:@\"\"];\n  [tmpVector addObject:@\"alpha\\r\\n\"];\n  [tmpVector addObject:@\"beta\\r\\n\"];\n  [tmpVector addObject:@\"\\r\\n\"];\n  result = [dmp diff_linesToCharsForFirstString:@\"\" andSecondString:@\"alpha\\r\\nbeta\\r\\n\\r\\n\\r\\n\"];\n  XCTAssertEqualObjects(@\"\", [result objectAtIndex:0], @\"Empty string and blank lines #1.\");\n  XCTAssertEqualObjects(@\"\\001\\002\\003\\003\", [result objectAtIndex:1], @\"Empty string and blank lines #2.\");\n  XCTAssertEqualObjects(tmpVector, (NSArray *)[result objectAtIndex:2], @\"Empty string and blank lines #3.\");\n\n  [tmpVector removeAllObjects];\n  [tmpVector addObject:@\"\"];\n  [tmpVector addObject:@\"a\"];\n  [tmpVector addObject:@\"b\"];\n  result = [dmp diff_linesToCharsForFirstString:@\"a\" andSecondString:@\"b\"];\n  XCTAssertEqualObjects(@\"\\001\", [result objectAtIndex:0], @\"No linebreaks #1.\");\n  XCTAssertEqualObjects(@\"\\002\", [result objectAtIndex:1], @\"No linebreaks #2.\");\n  XCTAssertEqualObjects(tmpVector, (NSArray *)[result objectAtIndex:2], @\"No linebreaks #3.\");\n\n  // More than 256 to reveal any 8-bit limitations.\n  unichar n = 300;\n  [tmpVector removeAllObjects];\n  NSMutableString *lines = [NSMutableString string];\n  NSMutableString *chars = [NSMutableString string];\n  NSString *currentLine;\n  for (unichar i = 1; i < n + 1; i++) {\n    currentLine = [NSString stringWithFormat:@\"%d\\n\", (int)i];\n    [tmpVector addObject:currentLine];\n    [lines appendString:currentLine];\n    [chars appendString:[NSString stringWithFormat:@\"%C\", i]];\n  }\n  XCTAssertEqual((NSUInteger)n, tmpVector.count, @\"More than 256 #1.\");\n  XCTAssertEqual((NSUInteger)n, chars.length, @\"More than 256 #2.\");\n  [tmpVector insertObject:@\"\" atIndex:0];\n  result = [dmp diff_linesToCharsForFirstString:lines andSecondString:@\"\"];\n  XCTAssertEqualObjects(chars, [result objectAtIndex:0], @\"More than 256 #3.\");\n  XCTAssertEqualObjects(@\"\", [result objectAtIndex:1], @\"More than 256 #4.\");\n  XCTAssertEqualObjects(tmpVector, (NSArray *)[result objectAtIndex:2], @\"More than 256 #5.\");\n\n  [dmp release];\n}\n\n- (void)test_diff_charsToLinesTest {\n  DiffMatchPatch *dmp = [DiffMatchPatch new];\n\n  // Convert chars up to lines.\n  NSArray *diffs = [NSArray arrayWithObjects:\n      [Diff diffWithOperation:DIFF_EQUAL andText:@\"\\001\\002\\001\"],\n      [Diff diffWithOperation:DIFF_INSERT andText:@\"\\002\\001\\002\"], nil];\n  NSMutableArray *tmpVector = [NSMutableArray array]; // Array of NSString objects.\n  [tmpVector addObject:@\"\"];\n  [tmpVector addObject:@\"alpha\\n\"];\n  [tmpVector addObject:@\"beta\\n\"];\n  [dmp diff_chars:diffs toLines:tmpVector];\n  NSArray *expectedResult = [NSArray arrayWithObjects:\n      [Diff diffWithOperation:DIFF_EQUAL andText:@\"alpha\\nbeta\\nalpha\\n\"],\n      [Diff diffWithOperation:DIFF_INSERT andText:@\"beta\\nalpha\\nbeta\\n\"], nil];\n  XCTAssertEqualObjects(expectedResult, diffs, @\"Shared lines.\");\n\n  // More than 256 to reveal any 8-bit limitations.\n  unichar n = 300;\n  [tmpVector removeAllObjects];\n  NSMutableString *lines = [NSMutableString string];\n  NSMutableString *chars = [NSMutableString string];\n  NSString *currentLine;\n  for (unichar i = 1; i < n + 1; i++) {\n    currentLine = [NSString stringWithFormat:@\"%d\\n\", (int)i];\n    [tmpVector addObject:currentLine];\n    [lines appendString:currentLine];\n    [chars appendString:[NSString stringWithFormat:@\"%C\", i]];\n  }\n  XCTAssertEqual((NSUInteger)n, tmpVector.count, @\"More than 256 #1.\");\n  XCTAssertEqual((NSUInteger)n, chars.length, @\"More than 256 #2.\");\n  [tmpVector insertObject:@\"\" atIndex:0];\n  diffs = [NSArray arrayWithObject:[Diff diffWithOperation:DIFF_DELETE andText:chars]];\n  [dmp diff_chars:diffs toLines:tmpVector];\n  XCTAssertEqualObjects([NSArray arrayWithObject:[Diff diffWithOperation:DIFF_DELETE andText:lines]], diffs, @\"More than 256 #3.\");\n\n  // More than 65536 to verify any 16-bit limitation.\n  lines = [NSMutableString string];\n  for (int i = 1; i < 66000; i++) {\n    currentLine = [NSString stringWithFormat:@\"%d\\n\", i];\n    [lines appendString:currentLine];\n  }\n  NSArray *result;\n  result = [dmp diff_linesToCharsForFirstString:lines andSecondString:@\"\"];\n  diffs = [NSArray arrayWithObject:[Diff diffWithOperation:DIFF_INSERT andText:result[0]]];\n  [dmp diff_chars:diffs toLines:result[2]];\n  Diff *myDiff = diffs.firstObject;\n  XCTAssertEqualObjects(lines, myDiff.text, @\"More than 65536.\");\n\n  [dmp release];\n}\n\n- (void)test_diff_cleanupMergeTest {\n  DiffMatchPatch *dmp = [DiffMatchPatch new];\n  NSMutableArray *expectedResult = nil;\n\n  // Cleanup a messy diff.\n  // Null case.\n  NSMutableArray *diffs = [NSMutableArray array];\n  [dmp diff_cleanupMerge:diffs];\n  XCTAssertEqualObjects([NSMutableArray array], diffs, @\"Null case.\");\n\n  // No change case.\n  diffs = [NSMutableArray arrayWithObjects:[Diff diffWithOperation:DIFF_EQUAL andText:@\"a\"], [Diff diffWithOperation:DIFF_DELETE andText:@\"b\"], [Diff diffWithOperation:DIFF_INSERT andText:@\"c\"], nil];\n  [dmp diff_cleanupMerge:diffs];\n  expectedResult = [NSMutableArray arrayWithObjects:[Diff diffWithOperation:DIFF_EQUAL andText:@\"a\"], [Diff diffWithOperation:DIFF_DELETE andText:@\"b\"], [Diff diffWithOperation:DIFF_INSERT andText:@\"c\"], nil];\n  XCTAssertEqualObjects(expectedResult, diffs, @\"No change case.\");\n\n  // Merge equalities.\n  diffs = [NSMutableArray arrayWithObjects:[Diff diffWithOperation:DIFF_EQUAL andText:@\"a\"], [Diff diffWithOperation:DIFF_EQUAL andText:@\"b\"], [Diff diffWithOperation:DIFF_EQUAL andText:@\"c\"], nil];\n  [dmp diff_cleanupMerge:diffs];\n  expectedResult = [NSMutableArray arrayWithObjects:[Diff diffWithOperation:DIFF_EQUAL andText:@\"abc\"], nil];\n  XCTAssertEqualObjects(expectedResult, diffs, @\"Merge equalities.\");\n\n  // Merge deletions.\n  diffs = [NSMutableArray arrayWithObjects:[Diff diffWithOperation:DIFF_DELETE andText:@\"a\"], [Diff diffWithOperation:DIFF_DELETE andText:@\"b\"], [Diff diffWithOperation:DIFF_DELETE andText:@\"c\"], nil];\n  [dmp diff_cleanupMerge:diffs];\n  expectedResult = [NSMutableArray arrayWithObjects:[Diff diffWithOperation:DIFF_DELETE andText:@\"abc\"], nil];\n  XCTAssertEqualObjects(expectedResult, diffs, @\"Merge deletions.\");\n\n  // Merge insertions.\n  diffs = [NSMutableArray arrayWithObjects:[Diff diffWithOperation:DIFF_INSERT andText:@\"a\"], [Diff diffWithOperation:DIFF_INSERT andText:@\"b\"], [Diff diffWithOperation:DIFF_INSERT andText:@\"c\"], nil];\n  [dmp diff_cleanupMerge:diffs];\n  expectedResult = [NSMutableArray arrayWithObjects:[Diff diffWithOperation:DIFF_INSERT andText:@\"abc\"], nil];\n  XCTAssertEqualObjects(expectedResult, diffs, @\"Merge insertions.\");\n\n  // Merge interweave.\n  diffs = [NSMutableArray arrayWithObjects:[Diff diffWithOperation:DIFF_DELETE andText:@\"a\"], [Diff diffWithOperation:DIFF_INSERT andText:@\"b\"], [Diff diffWithOperation:DIFF_DELETE andText:@\"c\"], [Diff diffWithOperation:DIFF_INSERT andText:@\"d\"], [Diff diffWithOperation:DIFF_EQUAL andText:@\"e\"], [Diff diffWithOperation:DIFF_EQUAL andText:@\"f\"], nil];\n  [dmp diff_cleanupMerge:diffs];\n  expectedResult = [NSMutableArray arrayWithObjects:[Diff diffWithOperation:DIFF_DELETE andText:@\"ac\"], [Diff diffWithOperation:DIFF_INSERT andText:@\"bd\"], [Diff diffWithOperation:DIFF_EQUAL andText:@\"ef\"], nil];\n  XCTAssertEqualObjects(expectedResult, diffs, @\"Merge interweave.\");\n\n  // Prefix and suffix detection.\n  diffs = [NSMutableArray arrayWithObjects:[Diff diffWithOperation:DIFF_DELETE andText:@\"a\"], [Diff diffWithOperation:DIFF_INSERT andText:@\"abc\"], [Diff diffWithOperation:DIFF_DELETE andText:@\"dc\"], nil];\n  [dmp diff_cleanupMerge:diffs];\n  expectedResult = [NSMutableArray arrayWithObjects:[Diff diffWithOperation:DIFF_EQUAL andText:@\"a\"], [Diff diffWithOperation:DIFF_DELETE andText:@\"d\"], [Diff diffWithOperation:DIFF_INSERT andText:@\"b\"], [Diff diffWithOperation:DIFF_EQUAL andText:@\"c\"], nil];\n  XCTAssertEqualObjects(expectedResult, diffs, @\"Prefix and suffix detection.\");\n\n  // Prefix and suffix detection with equalities.\n  diffs = [NSMutableArray arrayWithObjects:[Diff diffWithOperation:DIFF_EQUAL andText:@\"x\"], [Diff diffWithOperation:DIFF_DELETE andText:@\"a\"], [Diff diffWithOperation:DIFF_INSERT andText:@\"abc\"], [Diff diffWithOperation:DIFF_DELETE andText:@\"dc\"], [Diff diffWithOperation:DIFF_EQUAL andText:@\"y\"], nil];\n  [dmp diff_cleanupMerge:diffs];\n  expectedResult = [NSMutableArray arrayWithObjects:[Diff diffWithOperation:DIFF_EQUAL andText:@\"xa\"], [Diff diffWithOperation:DIFF_DELETE andText:@\"d\"], [Diff diffWithOperation:DIFF_INSERT andText:@\"b\"], [Diff diffWithOperation:DIFF_EQUAL andText:@\"cy\"], nil];\n  XCTAssertEqualObjects(expectedResult, diffs, @\"Prefix and suffix detection with equalities.\");\n\n  // Slide edit left.\n  diffs = [NSMutableArray arrayWithObjects:[Diff diffWithOperation:DIFF_EQUAL andText:@\"a\"], [Diff diffWithOperation:DIFF_INSERT andText:@\"ba\"], [Diff diffWithOperation:DIFF_EQUAL andText:@\"c\"], nil];\n  [dmp diff_cleanupMerge:diffs];\n  expectedResult = [NSMutableArray arrayWithObjects:[Diff diffWithOperation:DIFF_INSERT andText:@\"ab\"], [Diff diffWithOperation:DIFF_EQUAL andText:@\"ac\"], nil];\n  XCTAssertEqualObjects(expectedResult, diffs, @\"Slide edit left.\");\n\n  // Slide edit right.\n  diffs = [NSMutableArray arrayWithObjects:[Diff diffWithOperation:DIFF_EQUAL andText:@\"c\"], [Diff diffWithOperation:DIFF_INSERT andText:@\"ab\"], [Diff diffWithOperation:DIFF_EQUAL andText:@\"a\"], nil];\n  [dmp diff_cleanupMerge:diffs];\n  expectedResult = [NSMutableArray arrayWithObjects:[Diff diffWithOperation:DIFF_EQUAL andText:@\"ca\"], [Diff diffWithOperation:DIFF_INSERT andText:@\"ba\"], nil];\n  XCTAssertEqualObjects(expectedResult, diffs, @\"Slide edit right.\");\n\n  // Slide edit left recursive.\n  diffs = [NSMutableArray arrayWithObjects:[Diff diffWithOperation:DIFF_EQUAL andText:@\"a\"], [Diff diffWithOperation:DIFF_DELETE andText:@\"b\"], [Diff diffWithOperation:DIFF_EQUAL andText:@\"c\"], [Diff diffWithOperation:DIFF_DELETE andText:@\"ac\"], [Diff diffWithOperation:DIFF_EQUAL andText:@\"x\"], nil];\n  [dmp diff_cleanupMerge:diffs];\n  expectedResult = [NSMutableArray arrayWithObjects:[Diff diffWithOperation:DIFF_DELETE andText:@\"abc\"], [Diff diffWithOperation:DIFF_EQUAL andText:@\"acx\"], nil];\n  XCTAssertEqualObjects(expectedResult, diffs, @\"Slide edit left recursive.\");\n\n  // Slide edit right recursive.\n  diffs = [NSMutableArray arrayWithObjects:[Diff diffWithOperation:DIFF_EQUAL andText:@\"x\"], [Diff diffWithOperation:DIFF_DELETE andText:@\"ca\"], [Diff diffWithOperation:DIFF_EQUAL andText:@\"c\"], [Diff diffWithOperation:DIFF_DELETE andText:@\"b\"], [Diff diffWithOperation:DIFF_EQUAL andText:@\"a\"], nil];\n  [dmp diff_cleanupMerge:diffs];\n  expectedResult = [NSMutableArray arrayWithObjects:[Diff diffWithOperation:DIFF_EQUAL andText:@\"xca\"], [Diff diffWithOperation:DIFF_DELETE andText:@\"cba\"], nil];\n  XCTAssertEqualObjects(expectedResult, diffs, @\"Slide edit right recursive.\");\n\n  // Empty merge.\n  diffs = [NSMutableArray arrayWithObjects:[Diff diffWithOperation:DIFF_DELETE andText:@\"b\"], [Diff diffWithOperation:DIFF_INSERT andText:@\"ab\"], [Diff diffWithOperation:DIFF_EQUAL andText:@\"c\"], nil];\n  [dmp diff_cleanupMerge:diffs];\n  expectedResult = [NSMutableArray arrayWithObjects:[Diff diffWithOperation:DIFF_INSERT andText:@\"a\"], [Diff diffWithOperation:DIFF_EQUAL andText:@\"bc\"], nil];\n  XCTAssertEqualObjects(expectedResult, diffs, @\"Empty merge.\");\n\n  // Empty equality.\n  diffs = [NSMutableArray arrayWithObjects:[Diff diffWithOperation:DIFF_EQUAL andText:@\"\"], [Diff diffWithOperation:DIFF_INSERT andText:@\"a\"], [Diff diffWithOperation:DIFF_EQUAL andText:@\"b\"], nil];\n  [dmp diff_cleanupMerge:diffs];\n  expectedResult = [NSMutableArray arrayWithObjects:[Diff diffWithOperation:DIFF_INSERT andText:@\"a\"], [Diff diffWithOperation:DIFF_EQUAL andText:@\"b\"], nil];\n  XCTAssertEqualObjects(expectedResult, diffs, @\"Empty equality.\");\n\n  [dmp release];\n}\n\n- (void)test_diff_cleanupSemanticLosslessTest {\n  DiffMatchPatch *dmp = [DiffMatchPatch new];\n  NSMutableArray *expectedResult = nil;\n\n  // Slide diffs to match logical boundaries.\n  // Null case.\n  NSMutableArray *diffs = [NSMutableArray array];\n  [dmp diff_cleanupSemanticLossless:diffs];\n  XCTAssertEqualObjects([NSMutableArray array], diffs, @\"Null case.\");\n\n  // Blank lines.\n  diffs = [NSMutableArray arrayWithObjects:\n      [Diff diffWithOperation:DIFF_EQUAL andText:@\"AAA\\r\\n\\r\\nBBB\"],\n      [Diff diffWithOperation:DIFF_INSERT andText:@\"\\r\\nDDD\\r\\n\\r\\nBBB\"],\n      [Diff diffWithOperation:DIFF_EQUAL andText:@\"\\r\\nEEE\"], nil];\n  [dmp diff_cleanupSemanticLossless:diffs];\n  expectedResult = [NSMutableArray arrayWithObjects:\n      [Diff diffWithOperation:DIFF_EQUAL andText:@\"AAA\\r\\n\\r\\n\"],\n      [Diff diffWithOperation:DIFF_INSERT andText:@\"BBB\\r\\nDDD\\r\\n\\r\\n\"],\n      [Diff diffWithOperation:DIFF_EQUAL andText:@\"BBB\\r\\nEEE\"], nil];\n  XCTAssertEqualObjects(expectedResult, diffs, @\"Blank lines.\");\n\n  // Line boundaries.\n  diffs = [NSMutableArray arrayWithObjects:\n      [Diff diffWithOperation:DIFF_EQUAL andText:@\"AAA\\r\\nBBB\"],\n      [Diff diffWithOperation:DIFF_INSERT andText:@\" DDD\\r\\nBBB\"],\n      [Diff diffWithOperation:DIFF_EQUAL andText:@\" EEE\"], nil];\n  [dmp diff_cleanupSemanticLossless:diffs];\n  expectedResult = [NSMutableArray arrayWithObjects:\n      [Diff diffWithOperation:DIFF_EQUAL andText:@\"AAA\\r\\n\"],\n      [Diff diffWithOperation:DIFF_INSERT andText:@\"BBB DDD\\r\\n\"],\n      [Diff diffWithOperation:DIFF_EQUAL andText:@\"BBB EEE\"], nil];\n  XCTAssertEqualObjects(expectedResult, diffs, @\"Line boundaries.\");\n\n  // Word boundaries.\n  diffs = [NSMutableArray arrayWithObjects:\n      [Diff diffWithOperation:DIFF_EQUAL andText:@\"The c\"],\n      [Diff diffWithOperation:DIFF_INSERT andText:@\"ow and the c\"],\n      [Diff diffWithOperation:DIFF_EQUAL andText:@\"at.\"], nil];\n  [dmp diff_cleanupSemanticLossless:diffs];\n  expectedResult = [NSMutableArray arrayWithObjects:\n      [Diff diffWithOperation:DIFF_EQUAL andText:@\"The \"],\n      [Diff diffWithOperation:DIFF_INSERT andText:@\"cow and the \"],\n      [Diff diffWithOperation:DIFF_EQUAL andText:@\"cat.\"], nil];\n  XCTAssertEqualObjects(expectedResult, diffs, @\"Word boundaries.\");\n\n  // Alphanumeric boundaries.\n  diffs = [NSMutableArray arrayWithObjects:\n      [Diff diffWithOperation:DIFF_EQUAL andText:@\"The-c\"],\n      [Diff diffWithOperation:DIFF_INSERT andText:@\"ow-and-the-c\"],\n      [Diff diffWithOperation:DIFF_EQUAL andText:@\"at.\"], nil];\n  [dmp diff_cleanupSemanticLossless:diffs];\n  expectedResult = [NSMutableArray arrayWithObjects:\n      [Diff diffWithOperation:DIFF_EQUAL andText:@\"The-\"],\n      [Diff diffWithOperation:DIFF_INSERT andText:@\"cow-and-the-\"],\n      [Diff diffWithOperation:DIFF_EQUAL andText:@\"cat.\"], nil];\n  XCTAssertEqualObjects(expectedResult, diffs, @\"Alphanumeric boundaries.\");\n\n  // Hitting the start.\n  diffs = [NSMutableArray arrayWithObjects:\n      [Diff diffWithOperation:DIFF_EQUAL andText:@\"a\"],\n      [Diff diffWithOperation:DIFF_DELETE andText:@\"a\"],\n      [Diff diffWithOperation:DIFF_EQUAL andText:@\"ax\"], nil];\n  [dmp diff_cleanupSemanticLossless:diffs];\n  expectedResult = [NSMutableArray arrayWithObjects:\n      [Diff diffWithOperation:DIFF_DELETE andText:@\"a\"],\n      [Diff diffWithOperation:DIFF_EQUAL andText:@\"aax\"], nil];\n  XCTAssertEqualObjects(expectedResult, diffs, @\"Hitting the start.\");\n\n  // Hitting the end.\n  diffs = [NSMutableArray arrayWithObjects:\n      [Diff diffWithOperation:DIFF_EQUAL andText:@\"xa\"],\n      [Diff diffWithOperation:DIFF_DELETE andText:@\"a\"],\n      [Diff diffWithOperation:DIFF_EQUAL andText:@\"a\"], nil];\n  [dmp diff_cleanupSemanticLossless:diffs];\n  expectedResult = [NSMutableArray arrayWithObjects:\n      [Diff diffWithOperation:DIFF_EQUAL andText:@\"xaa\"],\n      [Diff diffWithOperation:DIFF_DELETE andText:@\"a\"], nil];\n  XCTAssertEqualObjects(expectedResult, diffs, @\"Hitting the end.\");\n\n  // Alphanumeric boundaries.\n  diffs = [NSMutableArray arrayWithObjects:\n      [Diff diffWithOperation:DIFF_EQUAL andText:@\"The xxx. The \"],\n      [Diff diffWithOperation:DIFF_INSERT andText:@\"zzz. The \"],\n      [Diff diffWithOperation:DIFF_EQUAL andText:@\"yyy.\"], nil];\n  [dmp diff_cleanupSemanticLossless:diffs];\n  expectedResult = [NSMutableArray arrayWithObjects:\n      [Diff diffWithOperation:DIFF_EQUAL andText:@\"The xxx.\"],\n      [Diff diffWithOperation:DIFF_INSERT andText:@\" The zzz.\"],\n      [Diff diffWithOperation:DIFF_EQUAL andText:@\" The yyy.\"], nil];\n  XCTAssertEqualObjects(expectedResult, diffs, @\"Sentence boundaries.\");\n\n  [dmp release];\n}\n\n- (void)test_diff_cleanupSemanticTest {\n  DiffMatchPatch *dmp = [DiffMatchPatch new];\n  NSMutableArray *expectedResult = nil;\n\n  // Cleanup semantically trivial equalities.\n  // Null case.\n  NSMutableArray *diffs = [NSMutableArray array];\n  [dmp diff_cleanupSemantic:diffs];\n  XCTAssertEqualObjects([NSMutableArray array], diffs, @\"Null case.\");\n\n  // No elimination #1.\n  diffs = [NSMutableArray arrayWithObjects:\n      [Diff diffWithOperation:DIFF_DELETE andText:@\"ab\"],\n      [Diff diffWithOperation:DIFF_INSERT andText:@\"cd\"],\n      [Diff diffWithOperation:DIFF_EQUAL andText:@\"12\"],\n      [Diff diffWithOperation:DIFF_DELETE andText:@\"e\"], nil];\n  [dmp diff_cleanupSemantic:diffs];\n  expectedResult = [NSMutableArray arrayWithObjects:\n      [Diff diffWithOperation:DIFF_DELETE andText:@\"ab\"],\n      [Diff diffWithOperation:DIFF_INSERT andText:@\"cd\"],\n      [Diff diffWithOperation:DIFF_EQUAL andText:@\"12\"],\n      [Diff diffWithOperation:DIFF_DELETE andText:@\"e\"], nil];\n  XCTAssertEqualObjects(expectedResult, diffs, @\"No elimination #1.\");\n\n  // No elimination #2.\n  diffs = [NSMutableArray arrayWithObjects:\n      [Diff diffWithOperation:DIFF_DELETE andText:@\"abc\"],\n      [Diff diffWithOperation:DIFF_INSERT andText:@\"ABC\"],\n      [Diff diffWithOperation:DIFF_EQUAL andText:@\"1234\"],\n      [Diff diffWithOperation:DIFF_DELETE andText:@\"wxyz\"], nil];\n  [dmp diff_cleanupSemantic:diffs];\n  expectedResult = [NSMutableArray arrayWithObjects:\n      [Diff diffWithOperation:DIFF_DELETE andText:@\"abc\"],\n      [Diff diffWithOperation:DIFF_INSERT andText:@\"ABC\"],\n      [Diff diffWithOperation:DIFF_EQUAL andText:@\"1234\"],\n      [Diff diffWithOperation:DIFF_DELETE andText:@\"wxyz\"], nil];\n  XCTAssertEqualObjects(expectedResult, diffs, @\"No elimination #2.\");\n\n  // Simple elimination.\n  diffs = [NSMutableArray arrayWithObjects:\n      [Diff diffWithOperation:DIFF_DELETE andText:@\"a\"],\n      [Diff diffWithOperation:DIFF_EQUAL andText:@\"b\"],\n      [Diff diffWithOperation:DIFF_DELETE andText:@\"c\"], nil];\n  [dmp diff_cleanupSemantic:diffs];\n  expectedResult = [NSMutableArray arrayWithObjects:\n      [Diff diffWithOperation:DIFF_DELETE andText:@\"abc\"],\n      [Diff diffWithOperation:DIFF_INSERT andText:@\"b\"], nil];\n  XCTAssertEqualObjects(expectedResult, diffs, @\"Simple elimination.\");\n\n  // Backpass elimination.\n  diffs = [NSMutableArray arrayWithObjects:\n      [Diff diffWithOperation:DIFF_DELETE andText:@\"ab\"],\n      [Diff diffWithOperation:DIFF_EQUAL andText:@\"cd\"],\n      [Diff diffWithOperation:DIFF_DELETE andText:@\"e\"],\n      [Diff diffWithOperation:DIFF_EQUAL andText:@\"f\"],\n      [Diff diffWithOperation:DIFF_INSERT andText:@\"g\"], nil];\n  [dmp diff_cleanupSemantic:diffs];\n  expectedResult = [NSMutableArray arrayWithObjects:\n      [Diff diffWithOperation:DIFF_DELETE andText:@\"abcdef\"],\n      [Diff diffWithOperation:DIFF_INSERT andText:@\"cdfg\"], nil];\n  XCTAssertEqualObjects(expectedResult, diffs, @\"Backpass elimination.\");\n\n  // Multiple eliminations.\n  diffs = [NSMutableArray arrayWithObjects:\n      [Diff diffWithOperation:DIFF_INSERT andText:@\"1\"],\n      [Diff diffWithOperation:DIFF_EQUAL andText:@\"A\"],\n      [Diff diffWithOperation:DIFF_DELETE andText:@\"B\"],\n      [Diff diffWithOperation:DIFF_INSERT andText:@\"2\"],\n      [Diff diffWithOperation:DIFF_EQUAL andText:@\"_\"],\n      [Diff diffWithOperation:DIFF_INSERT andText:@\"1\"],\n      [Diff diffWithOperation:DIFF_EQUAL andText:@\"A\"],\n      [Diff diffWithOperation:DIFF_DELETE andText:@\"B\"],\n      [Diff diffWithOperation:DIFF_INSERT andText:@\"2\"], nil];\n  [dmp diff_cleanupSemantic:diffs];\n  expectedResult = [NSMutableArray arrayWithObjects:\n      [Diff diffWithOperation:DIFF_DELETE andText:@\"AB_AB\"],\n      [Diff diffWithOperation:DIFF_INSERT andText:@\"1A2_1A2\"], nil];\n  XCTAssertEqualObjects(expectedResult, diffs, @\"Multiple eliminations.\");\n\n  // Word boundaries.\n  diffs = [NSMutableArray arrayWithObjects:\n      [Diff diffWithOperation:DIFF_EQUAL andText:@\"The c\"],\n      [Diff diffWithOperation:DIFF_DELETE andText:@\"ow and the c\"],\n      [Diff diffWithOperation:DIFF_EQUAL andText:@\"at.\"], nil];\n  [dmp diff_cleanupSemantic:diffs];\n  expectedResult = [NSMutableArray arrayWithObjects:\n      [Diff diffWithOperation:DIFF_EQUAL andText:@\"The \"],\n      [Diff diffWithOperation:DIFF_DELETE andText:@\"cow and the \"],\n      [Diff diffWithOperation:DIFF_EQUAL andText:@\"cat.\"], nil];\n  XCTAssertEqualObjects(expectedResult, diffs, @\"Word boundaries.\");\n\n  // No overlap elimination.\n  diffs = [NSMutableArray arrayWithObjects:\n      [Diff diffWithOperation:DIFF_DELETE andText:@\"abcxx\"],\n      [Diff diffWithOperation:DIFF_INSERT andText:@\"xxdef\"], nil];\n  [dmp diff_cleanupSemantic:diffs];\n  expectedResult = [NSMutableArray arrayWithObjects:\n      [Diff diffWithOperation:DIFF_DELETE andText:@\"abcxx\"],\n      [Diff diffWithOperation:DIFF_INSERT andText:@\"xxdef\"], nil];\n  XCTAssertEqualObjects(expectedResult, diffs, @\"No overlap elimination.\");\n\n  // Overlap elimination.\n  diffs = [NSMutableArray arrayWithObjects:\n      [Diff diffWithOperation:DIFF_DELETE andText:@\"abcxxx\"],\n      [Diff diffWithOperation:DIFF_INSERT andText:@\"xxxdef\"], nil];\n  [dmp diff_cleanupSemantic:diffs];\n  expectedResult = [NSMutableArray arrayWithObjects:\n      [Diff diffWithOperation:DIFF_DELETE andText:@\"abc\"],\n      [Diff diffWithOperation:DIFF_EQUAL andText:@\"xxx\"],\n      [Diff diffWithOperation:DIFF_INSERT andText:@\"def\"], nil];\n  XCTAssertEqualObjects(expectedResult, diffs, @\"Overlap elimination.\");\n\n  // Reverse overlap elimination.\n  diffs = [NSMutableArray arrayWithObjects:\n      [Diff diffWithOperation:DIFF_DELETE andText:@\"xxxabc\"],\n      [Diff diffWithOperation:DIFF_INSERT andText:@\"defxxx\"], nil];\n  [dmp diff_cleanupSemantic:diffs];\n  expectedResult = [NSMutableArray arrayWithObjects:\n      [Diff diffWithOperation:DIFF_INSERT andText:@\"def\"],\n      [Diff diffWithOperation:DIFF_EQUAL andText:@\"xxx\"],\n      [Diff diffWithOperation:DIFF_DELETE andText:@\"abc\"], nil];\n  XCTAssertEqualObjects(expectedResult, diffs, @\"Reverse overlap elimination.\");\n\n  // Two overlap eliminations.\n  diffs = [NSMutableArray arrayWithObjects:\n      [Diff diffWithOperation:DIFF_DELETE andText:@\"abcd1212\"],\n      [Diff diffWithOperation:DIFF_INSERT andText:@\"1212efghi\"],\n      [Diff diffWithOperation:DIFF_EQUAL andText:@\"----\"],\n      [Diff diffWithOperation:DIFF_DELETE andText:@\"A3\"],\n      [Diff diffWithOperation:DIFF_INSERT andText:@\"3BC\"], nil];\n  [dmp diff_cleanupSemantic:diffs];\n  expectedResult = [NSMutableArray arrayWithObjects:\n      [Diff diffWithOperation:DIFF_DELETE andText:@\"abcd\"],\n      [Diff diffWithOperation:DIFF_EQUAL andText:@\"1212\"],\n      [Diff diffWithOperation:DIFF_INSERT andText:@\"efghi\"],\n      [Diff diffWithOperation:DIFF_EQUAL andText:@\"----\"],\n      [Diff diffWithOperation:DIFF_DELETE andText:@\"A\"],\n      [Diff diffWithOperation:DIFF_EQUAL andText:@\"3\"],\n      [Diff diffWithOperation:DIFF_INSERT andText:@\"BC\"], nil];\n  XCTAssertEqualObjects(expectedResult, diffs, @\"Two overlap eliminations.\");\n\n  [dmp release];\n}\n\n- (void)test_diff_cleanupEfficiencyTest {\n  DiffMatchPatch *dmp = [DiffMatchPatch new];\n  NSMutableArray *expectedResult = nil;\n\n  // Cleanup operationally trivial equalities.\n  dmp.Diff_EditCost = 4;\n  // Null case.\n  NSMutableArray *diffs = [NSMutableArray array];\n  [dmp diff_cleanupEfficiency:diffs];\n  XCTAssertEqualObjects([NSMutableArray array], diffs, @\"Null case.\");\n\n  // No elimination.\n  diffs = [NSMutableArray arrayWithObjects:\n      [Diff diffWithOperation:DIFF_DELETE andText:@\"ab\"],\n      [Diff diffWithOperation:DIFF_INSERT andText:@\"12\"],\n      [Diff diffWithOperation:DIFF_EQUAL andText:@\"wxyz\"],\n      [Diff diffWithOperation:DIFF_DELETE andText:@\"cd\"],\n      [Diff diffWithOperation:DIFF_INSERT andText:@\"34\"], nil];\n  [dmp diff_cleanupEfficiency:diffs];\n  expectedResult = [NSMutableArray arrayWithObjects:\n      [Diff diffWithOperation:DIFF_DELETE andText:@\"ab\"],\n      [Diff diffWithOperation:DIFF_INSERT andText:@\"12\"],\n      [Diff diffWithOperation:DIFF_EQUAL andText:@\"wxyz\"],\n      [Diff diffWithOperation:DIFF_DELETE andText:@\"cd\"],\n      [Diff diffWithOperation:DIFF_INSERT andText:@\"34\"], nil];\n  XCTAssertEqualObjects(expectedResult, diffs, @\"No elimination.\");\n\n  // Four-edit elimination.\n  diffs = [NSMutableArray arrayWithObjects:\n      [Diff diffWithOperation:DIFF_DELETE andText:@\"ab\"],\n      [Diff diffWithOperation:DIFF_INSERT andText:@\"12\"],\n      [Diff diffWithOperation:DIFF_EQUAL andText:@\"xyz\"],\n      [Diff diffWithOperation:DIFF_DELETE andText:@\"cd\"],\n      [Diff diffWithOperation:DIFF_INSERT andText:@\"34\"], nil];\n  [dmp diff_cleanupEfficiency:diffs];\n  expectedResult = [NSMutableArray arrayWithObjects:\n      [Diff diffWithOperation:DIFF_DELETE andText:@\"abxyzcd\"],\n      [Diff diffWithOperation:DIFF_INSERT andText:@\"12xyz34\"], nil];\n  XCTAssertEqualObjects(expectedResult, diffs, @\"Four-edit elimination.\");\n\n  // Three-edit elimination.\n  diffs = [NSMutableArray arrayWithObjects:\n      [Diff diffWithOperation:DIFF_INSERT andText:@\"12\"],\n      [Diff diffWithOperation:DIFF_EQUAL andText:@\"x\"],\n      [Diff diffWithOperation:DIFF_DELETE andText:@\"cd\"],\n      [Diff diffWithOperation:DIFF_INSERT andText:@\"34\"], nil];\n  [dmp diff_cleanupEfficiency:diffs];\n  expectedResult = [NSMutableArray arrayWithObjects:\n      [Diff diffWithOperation:DIFF_DELETE andText:@\"xcd\"],\n      [Diff diffWithOperation:DIFF_INSERT andText:@\"12x34\"], nil];\n  XCTAssertEqualObjects(expectedResult, diffs, @\"Three-edit elimination.\");\n\n  // Backpass elimination.\n  diffs = [NSMutableArray arrayWithObjects:\n      [Diff diffWithOperation:DIFF_DELETE andText:@\"ab\"],\n      [Diff diffWithOperation:DIFF_INSERT andText:@\"12\"],\n      [Diff diffWithOperation:DIFF_EQUAL andText:@\"xy\"],\n      [Diff diffWithOperation:DIFF_INSERT andText:@\"34\"],\n      [Diff diffWithOperation:DIFF_EQUAL andText:@\"z\"],\n      [Diff diffWithOperation:DIFF_DELETE andText:@\"cd\"],\n      [Diff diffWithOperation:DIFF_INSERT andText:@\"56\"], nil];\n  [dmp diff_cleanupEfficiency:diffs];\n  expectedResult = [NSMutableArray arrayWithObjects:\n      [Diff diffWithOperation:DIFF_DELETE andText:@\"abxyzcd\"],\n      [Diff diffWithOperation:DIFF_INSERT andText:@\"12xy34z56\"], nil];\n  XCTAssertEqualObjects(expectedResult, diffs, @\"Backpass elimination.\");\n\n  // High cost elimination.\n  dmp.Diff_EditCost = 5;\n  diffs = [NSMutableArray arrayWithObjects:\n      [Diff diffWithOperation:DIFF_DELETE andText:@\"ab\"],\n      [Diff diffWithOperation:DIFF_INSERT andText:@\"12\"],\n      [Diff diffWithOperation:DIFF_EQUAL andText:@\"wxyz\"],\n      [Diff diffWithOperation:DIFF_DELETE andText:@\"cd\"],\n      [Diff diffWithOperation:DIFF_INSERT andText:@\"34\"], nil];\n  [dmp diff_cleanupEfficiency:diffs];\n  expectedResult = [NSMutableArray arrayWithObjects:\n      [Diff diffWithOperation:DIFF_DELETE andText:@\"abwxyzcd\"],\n      [Diff diffWithOperation:DIFF_INSERT andText:@\"12wxyz34\"], nil];\n  XCTAssertEqualObjects(expectedResult, diffs, @\"High cost elimination.\");\n  dmp.Diff_EditCost = 4;\n\n  [dmp release];\n}\n\n- (void)test_diff_prettyHtmlTest {\n  DiffMatchPatch *dmp = [DiffMatchPatch new];\n\n  // Pretty print.\n  NSMutableArray *diffs = [NSMutableArray arrayWithObjects:\n      [Diff diffWithOperation:DIFF_EQUAL andText:@\"a\\n\"],\n      [Diff diffWithOperation:DIFF_DELETE andText:@\"<B>b</B>\"],\n      [Diff diffWithOperation:DIFF_INSERT andText:@\"c&d\"], nil];\n  NSString *expectedResult = @\"<span>a&para;<br></span><del style=\\\"background:#ffe6e6;\\\">&lt;B&gt;b&lt;/B&gt;</del><ins style=\\\"background:#e6ffe6;\\\">c&amp;d</ins>\";\n  XCTAssertEqualObjects(expectedResult, [dmp diff_prettyHtml:diffs], @\"Pretty print.\");\n\n  [dmp release];\n}\n\n- (void)test_diff_textTest {\n  DiffMatchPatch *dmp = [DiffMatchPatch new];\n\n  // Compute the source and destination texts.\n  NSMutableArray *diffs = [NSMutableArray arrayWithObjects:\n      [Diff diffWithOperation:DIFF_EQUAL andText:@\"jump\"],\n      [Diff diffWithOperation:DIFF_DELETE andText:@\"s\"],\n      [Diff diffWithOperation:DIFF_INSERT andText:@\"ed\"],\n      [Diff diffWithOperation:DIFF_EQUAL andText:@\" over \"],\n      [Diff diffWithOperation:DIFF_DELETE andText:@\"the\"],\n      [Diff diffWithOperation:DIFF_INSERT andText:@\"a\"],\n      [Diff diffWithOperation:DIFF_EQUAL andText:@\" lazy\"], nil];\n  XCTAssertEqualObjects(@\"jumps over the lazy\", [dmp diff_text1:diffs], @\"Compute the source and destination texts #1\");\n\n  XCTAssertEqualObjects(@\"jumped over a lazy\", [dmp diff_text2:diffs], @\"Compute the source and destination texts #2\");\n\n  [dmp release];\n}\n\n- (void)test_diff_deltaTest {\n  DiffMatchPatch *dmp = [DiffMatchPatch new];\n  NSMutableArray *expectedResult = nil;\n  NSError *error = nil;\n\n  // Convert a diff into delta string.\n  NSMutableArray *diffs = [NSMutableArray arrayWithObjects:\n      [Diff diffWithOperation:DIFF_EQUAL andText:@\"jump\"],\n      [Diff diffWithOperation:DIFF_DELETE andText:@\"s\"],\n      [Diff diffWithOperation:DIFF_INSERT andText:@\"ed\"],\n      [Diff diffWithOperation:DIFF_EQUAL andText:@\" over \"],\n      [Diff diffWithOperation:DIFF_DELETE andText:@\"the\"],\n      [Diff diffWithOperation:DIFF_INSERT andText:@\"a\"],\n      [Diff diffWithOperation:DIFF_EQUAL andText:@\" lazy\"],\n      [Diff diffWithOperation:DIFF_INSERT andText:@\"old dog\"], nil];\n  NSString *text1 = [dmp diff_text1:diffs];\n  XCTAssertEqualObjects(@\"jumps over the lazy\", text1, @\"Convert a diff into delta string 1.\");\n\n  NSString *delta = [dmp diff_toDelta:diffs];\n  XCTAssertEqualObjects(@\"=4\\t-1\\t+ed\\t=6\\t-3\\t+a\\t=5\\t+old dog\", delta, @\"Convert a diff into delta string 2.\");\n\n  // Convert delta string into a diff.\n  XCTAssertEqualObjects(diffs, [dmp diff_fromDeltaWithText:text1 andDelta:delta error:NULL], @\"Convert delta string into a diff.\");\n\n  // Generates error (19 < 20).\n  diffs = [dmp diff_fromDeltaWithText:[text1 stringByAppendingString:@\"x\"] andDelta:delta error:&error];\n  if (diffs != nil || error == nil) {\n    XCTFail(@\"diff_fromDelta: Too long.\");\n  }\n  error = nil;\n\n  // Generates error (19 > 18).\n  diffs = [dmp diff_fromDeltaWithText:[text1 substringFromIndex:1] andDelta:delta error:&error];\n  if (diffs != nil || error == nil) {\n    XCTFail(@\"diff_fromDelta: Too short.\");\n  }\n  error = nil;\n\n  // Generates error (%c3%xy invalid Unicode).\n  diffs = [dmp diff_fromDeltaWithText:@\"\" andDelta:@\"+%c3%xy\" error:&error];\n  if (diffs != nil || error == nil) {\n    XCTFail(@\"diff_fromDelta: Invalid character.\");\n  }\n  error = nil;\n\n  // Test deltas with special characters.\n  unichar zero = (unichar)0;\n  unichar one = (unichar)1;\n  unichar two = (unichar)2;\n  diffs = [NSMutableArray arrayWithObjects:\n      [Diff diffWithOperation:DIFF_EQUAL andText:[NSString stringWithFormat:@\"\\U00000680 %C \\t %%\", zero]],\n      [Diff diffWithOperation:DIFF_DELETE andText:[NSString stringWithFormat:@\"\\U00000681 %C \\n ^\", one]],\n      [Diff diffWithOperation:DIFF_INSERT andText:[NSString stringWithFormat:@\"\\U00000682 %C \\\\ |\", two]], nil];\n  text1 = [dmp diff_text1:diffs];\n  NSString *expectedString = [NSString stringWithFormat:@\"\\U00000680 %C \\t %%\\U00000681 %C \\n ^\", zero, one];\n  XCTAssertEqualObjects(expectedString, text1, @\"Test deltas with special characters.\");\n\n  delta = [dmp diff_toDelta:diffs];\n  // Upper case, because to CFURLCreateStringByAddingPercentEscapes() uses upper.\n  XCTAssertEqualObjects(@\"=7\\t-7\\t+%DA%82 %02 %5C %7C\", delta, @\"diff_toDelta: Unicode 1.\");\n\n  XCTAssertEqualObjects(diffs, [dmp diff_fromDeltaWithText:text1 andDelta:delta error:NULL], @\"diff_fromDelta: Unicode 2.\");\n\n  // Verify pool of unchanged characters.\n  diffs = [NSMutableArray arrayWithObject:\n       [Diff diffWithOperation:DIFF_INSERT andText:@\"A-Z a-z 0-9 - _ . ! ~ * ' ( ) ; / ? : @ & = + $ , # \"]];\n  NSString *text2 = [dmp diff_text2:diffs];\n  XCTAssertEqualObjects(@\"A-Z a-z 0-9 - _ . ! ~ * ' ( ) ; / ? : @ & = + $ , # \", text2, @\"diff_text2: Unchanged characters 1.\");\n\n  delta = [dmp diff_toDelta:diffs];\n  XCTAssertEqualObjects(@\"+A-Z a-z 0-9 - _ . ! ~ * ' ( ) ; / ? : @ & = + $ , # \", delta, @\"diff_toDelta: Unchanged characters 2.\");\n\n  // Convert delta string into a diff.\n  expectedResult = [dmp diff_fromDeltaWithText:@\"\" andDelta:delta error:NULL];\n  XCTAssertEqualObjects(diffs, expectedResult, @\"diff_fromDelta: Unchanged characters. Convert delta string into a diff.\");\n\n  // 160 kb string.\n  NSString *a = @\"abcdefghij\";\n  NSMutableString *aMutable = [NSMutableString stringWithString:a];\n  for (int i = 0; i < 14; i++) {\n    [aMutable appendString:aMutable];\n  }\n  a = aMutable;\n  diffs = [NSMutableArray arrayWithObject:\n           [Diff diffWithOperation:DIFF_INSERT andText:a]];\n  delta = [dmp diff_toDelta:diffs];\n  XCTAssertEqualObjects([@\"+\" stringByAppendingString:a], delta, @\"diff_toDelta: 160kb string.\");\n  \n  // Convert delta string into a diff.\n  expectedResult = [dmp diff_fromDeltaWithText:@\"\" andDelta:delta error:NULL];\n  XCTAssertEqualObjects(diffs, expectedResult, @\"diff_fromDelta: 160kb string. Convert delta string into a diff.\");\n\n  [dmp release];\n}\n\n- (void)test_diff_xIndexTest {\n  DiffMatchPatch *dmp = [DiffMatchPatch new];\n\n  // Translate a location in text1 to text2.\n  NSMutableArray *diffs = [NSMutableArray arrayWithObjects:\n      [Diff diffWithOperation:DIFF_DELETE andText:@\"a\"],\n      [Diff diffWithOperation:DIFF_INSERT andText:@\"1234\"],\n      [Diff diffWithOperation:DIFF_EQUAL andText:@\"xyz\"], nil] /* Diff */;\n  XCTAssertEqual((NSUInteger)5, [dmp diff_xIndexIn:diffs location:2], @\"diff_xIndex: Translation on equality. Translate a location in text1 to text2.\");\n\n  diffs = [NSMutableArray arrayWithObjects:\n      [Diff diffWithOperation:DIFF_EQUAL andText:@\"a\"],\n      [Diff diffWithOperation:DIFF_DELETE andText:@\"1234\"],\n      [Diff diffWithOperation:DIFF_EQUAL andText:@\"xyz\"], nil] /* Diff */;\n  XCTAssertEqual((NSUInteger)1, [dmp diff_xIndexIn:diffs location:3], @\"diff_xIndex: Translation on deletion.\");\n\n  [dmp release];\n}\n\n- (void)test_diff_levenshteinTest {\n  DiffMatchPatch *dmp = [DiffMatchPatch new];\n\n  NSMutableArray *diffs = [NSMutableArray arrayWithObjects:\n      [Diff diffWithOperation:DIFF_DELETE andText:@\"abc\"],\n      [Diff diffWithOperation:DIFF_INSERT andText:@\"1234\"],\n      [Diff diffWithOperation:DIFF_EQUAL andText:@\"xyz\"], nil] /* Diff */;\n  XCTAssertEqual((NSUInteger)4, [dmp diff_levenshtein:diffs], @\"diff_levenshtein: Levenshtein with trailing equality.\");\n\n  diffs = [NSMutableArray arrayWithObjects:\n      [Diff diffWithOperation:DIFF_EQUAL andText:@\"xyz\"],\n      [Diff diffWithOperation:DIFF_DELETE andText:@\"abc\"],\n      [Diff diffWithOperation:DIFF_INSERT andText:@\"1234\"], nil] /* Diff */;\n  XCTAssertEqual((NSUInteger)4, [dmp diff_levenshtein:diffs], @\"diff_levenshtein: Levenshtein with leading equality.\");\n\n  diffs = [NSMutableArray arrayWithObjects:\n      [Diff diffWithOperation:DIFF_DELETE andText:@\"abc\"],\n      [Diff diffWithOperation:DIFF_EQUAL andText:@\"xyz\"],\n      [Diff diffWithOperation:DIFF_INSERT andText:@\"1234\"], nil] /* Diff */;\n  XCTAssertEqual((NSUInteger)7, [dmp diff_levenshtein:diffs], @\"diff_levenshtein: Levenshtein with middle equality.\");\n\n  [dmp release];\n}\n\n- (void)diff_bisectTest;\n{\n  DiffMatchPatch *dmp = [DiffMatchPatch new];\n\n  // Normal.\n  NSString *a = @\"cat\";\n  NSString *b = @\"map\";\n  // Since the resulting diff hasn't been normalized, it would be ok if\n  // the insertion and deletion pairs are swapped.\n  // If the order changes, tweak this test as required.\n  NSMutableArray *diffs = [NSMutableArray arrayWithObjects:[Diff diffWithOperation:DIFF_DELETE andText:@\"c\"], [Diff diffWithOperation:DIFF_INSERT andText:@\"m\"], [Diff diffWithOperation:DIFF_EQUAL andText:@\"a\"], [Diff diffWithOperation:DIFF_DELETE andText:@\"t\"], [Diff diffWithOperation:DIFF_INSERT andText:@\"p\"], nil];\n  XCTAssertEqualObjects(diffs, [dmp diff_bisectOfOldString:a andNewString:b deadline:[[NSDate distantFuture] timeIntervalSinceReferenceDate]], @\"Bisect test.\");\n\n  // Timeout.\n  diffs = [NSMutableArray arrayWithObjects:[Diff diffWithOperation:DIFF_DELETE andText:@\"cat\"], [Diff diffWithOperation:DIFF_INSERT andText:@\"map\"], nil];\n  XCTAssertEqualObjects(diffs, [dmp diff_bisectOfOldString:a andNewString:b deadline:[[NSDate distantPast] timeIntervalSinceReferenceDate]], @\"Bisect timeout.\");\n\n  [dmp release];\n}\n\n- (void)test_diff_mainTest {\n  DiffMatchPatch *dmp = [DiffMatchPatch new];\n\n  // Perform a trivial diff.\n  NSMutableArray *diffs = [NSMutableArray array];\n  XCTAssertEqualObjects(diffs, [dmp diff_mainOfOldString:@\"\" andNewString:@\"\" checkLines:NO], @\"diff_main: Null case.\");\n\n  diffs = [NSMutableArray arrayWithObjects:[Diff diffWithOperation:DIFF_EQUAL andText:@\"abc\"], nil];\n  XCTAssertEqualObjects(diffs, [dmp diff_mainOfOldString:@\"abc\" andNewString:@\"abc\" checkLines:NO], @\"diff_main: Equality.\");\n\n  diffs = [NSMutableArray arrayWithObjects:[Diff diffWithOperation:DIFF_EQUAL andText:@\"ab\"], [Diff diffWithOperation:DIFF_INSERT andText:@\"123\"], [Diff diffWithOperation:DIFF_EQUAL andText:@\"c\"], nil];\n  XCTAssertEqualObjects(diffs, [dmp diff_mainOfOldString:@\"abc\" andNewString:@\"ab123c\" checkLines:NO], @\"diff_main: Simple insertion.\");\n\n  diffs = [NSMutableArray arrayWithObjects:[Diff diffWithOperation:DIFF_EQUAL andText:@\"a\"], [Diff diffWithOperation:DIFF_DELETE andText:@\"123\"], [Diff diffWithOperation:DIFF_EQUAL andText:@\"bc\"], nil];\n  XCTAssertEqualObjects(diffs, [dmp diff_mainOfOldString:@\"a123bc\" andNewString:@\"abc\" checkLines:NO], @\"diff_main: Simple deletion.\");\n\n  diffs = [NSMutableArray arrayWithObjects:[Diff diffWithOperation:DIFF_EQUAL andText:@\"a\"], [Diff diffWithOperation:DIFF_INSERT andText:@\"123\"], [Diff diffWithOperation:DIFF_EQUAL andText:@\"b\"], [Diff diffWithOperation:DIFF_INSERT andText:@\"456\"], [Diff diffWithOperation:DIFF_EQUAL andText:@\"c\"], nil];\n  XCTAssertEqualObjects(diffs, [dmp diff_mainOfOldString:@\"abc\" andNewString:@\"a123b456c\" checkLines:NO], @\"diff_main: Two insertions.\");\n\n  diffs = [NSMutableArray arrayWithObjects:[Diff diffWithOperation:DIFF_EQUAL andText:@\"a\"], [Diff diffWithOperation:DIFF_DELETE andText:@\"123\"], [Diff diffWithOperation:DIFF_EQUAL andText:@\"b\"], [Diff diffWithOperation:DIFF_DELETE andText:@\"456\"], [Diff diffWithOperation:DIFF_EQUAL andText:@\"c\"], nil];\n  XCTAssertEqualObjects(diffs, [dmp diff_mainOfOldString:@\"a123b456c\" andNewString:@\"abc\" checkLines:NO], @\"diff_main: Two deletions.\");\n\n  // Perform a real diff.\n  // Switch off the timeout.\n  dmp.Diff_Timeout = 0;\n  diffs = [NSMutableArray arrayWithObjects:[Diff diffWithOperation:DIFF_DELETE andText:@\"a\"], [Diff diffWithOperation:DIFF_INSERT andText:@\"b\"], nil];\n  XCTAssertEqualObjects(diffs, [dmp diff_mainOfOldString:@\"a\" andNewString:@\"b\" checkLines:NO], @\"diff_main: Simple case #1.\");\n\n  diffs = [NSMutableArray arrayWithObjects:[Diff diffWithOperation:DIFF_DELETE andText:@\"Apple\"], [Diff diffWithOperation:DIFF_INSERT andText:@\"Banana\"], [Diff diffWithOperation:DIFF_EQUAL andText:@\"s are a\"], [Diff diffWithOperation:DIFF_INSERT andText:@\"lso\"], [Diff diffWithOperation:DIFF_EQUAL andText:@\" fruit.\"], nil];\n  XCTAssertEqualObjects(diffs, [dmp diff_mainOfOldString:@\"Apples are a fruit.\" andNewString:@\"Bananas are also fruit.\" checkLines:NO], @\"diff_main: Simple case #2.\");\n\n  diffs = [NSMutableArray arrayWithObjects:[Diff diffWithOperation:DIFF_DELETE andText:@\"a\"], [Diff diffWithOperation:DIFF_INSERT andText:@\"\\U00000680\"], [Diff diffWithOperation:DIFF_EQUAL andText:@\"x\"], [Diff diffWithOperation:DIFF_DELETE andText:@\"\\t\"], [Diff diffWithOperation:DIFF_INSERT andText:[NSString stringWithFormat:@\"%C\", 0]], nil];\n  NSString *aString = [NSString stringWithFormat:@\"\\U00000680x%C\", 0];\n  XCTAssertEqualObjects(diffs, [dmp diff_mainOfOldString:@\"ax\\t\" andNewString:aString checkLines:NO], @\"diff_main: Simple case #3.\");\n\n  diffs = [NSMutableArray arrayWithObjects:[Diff diffWithOperation:DIFF_DELETE andText:@\"1\"], [Diff diffWithOperation:DIFF_EQUAL andText:@\"a\"], [Diff diffWithOperation:DIFF_DELETE andText:@\"y\"], [Diff diffWithOperation:DIFF_EQUAL andText:@\"b\"], [Diff diffWithOperation:DIFF_DELETE andText:@\"2\"], [Diff diffWithOperation:DIFF_INSERT andText:@\"xab\"], nil];\n  XCTAssertEqualObjects(diffs, [dmp diff_mainOfOldString:@\"1ayb2\" andNewString:@\"abxab\" checkLines:NO], @\"diff_main: Overlap #1.\");\n\n  diffs = [NSMutableArray arrayWithObjects:[Diff diffWithOperation:DIFF_INSERT andText:@\"xaxcx\"], [Diff diffWithOperation:DIFF_EQUAL andText:@\"abc\"], [Diff diffWithOperation:DIFF_DELETE andText:@\"y\"], nil];\n  XCTAssertEqualObjects(diffs, [dmp diff_mainOfOldString:@\"abcy\" andNewString:@\"xaxcxabc\" checkLines:NO], @\"diff_main: Overlap #2.\");\n\n  diffs = [NSMutableArray arrayWithObjects:[Diff diffWithOperation:DIFF_DELETE andText:@\"ABCD\"], [Diff diffWithOperation:DIFF_EQUAL andText:@\"a\"], [Diff diffWithOperation:DIFF_DELETE andText:@\"=\"], [Diff diffWithOperation:DIFF_INSERT andText:@\"-\"], [Diff diffWithOperation:DIFF_EQUAL andText:@\"bcd\"], [Diff diffWithOperation:DIFF_DELETE andText:@\"=\"], [Diff diffWithOperation:DIFF_INSERT andText:@\"-\"], [Diff diffWithOperation:DIFF_EQUAL andText:@\"efghijklmnopqrs\"], [Diff diffWithOperation:DIFF_DELETE andText:@\"EFGHIJKLMNOefg\"], nil];\n  XCTAssertEqualObjects(diffs, [dmp diff_mainOfOldString:@\"ABCDa=bcd=efghijklmnopqrsEFGHIJKLMNOefg\" andNewString:@\"a-bcd-efghijklmnopqrs\" checkLines:NO], @\"diff_main: Overlap #3.\");\n\n  diffs = [NSMutableArray arrayWithObjects:[Diff diffWithOperation:DIFF_INSERT andText:@\" \"], [Diff diffWithOperation:DIFF_EQUAL andText:@\"a\"], [Diff diffWithOperation:DIFF_INSERT andText:@\"nd\"], [Diff diffWithOperation:DIFF_EQUAL andText:@\" [[Pennsylvania]]\"], [Diff diffWithOperation:DIFF_DELETE andText:@\" and [[New\"], nil];\n  XCTAssertEqualObjects(diffs, [dmp diff_mainOfOldString:@\"a [[Pennsylvania]] and [[New\" andNewString:@\" and [[Pennsylvania]]\" checkLines:NO], @\"diff_main: Large equality.\");\n\n  dmp.Diff_Timeout = 0.1f;  // 100ms\n  NSString *a = @\"`Twas brillig, and the slithy toves\\nDid gyre and gimble in the wabe:\\nAll mimsy were the borogoves,\\nAnd the mome raths outgrabe.\\n\";\n  NSString *b = @\"I am the very model of a modern major general,\\nI've information vegetable, animal, and mineral,\\nI know the kings of England, and I quote the fights historical,\\nFrom Marathon to Waterloo, in order categorical.\\n\";\n  NSMutableString *aMutable = [NSMutableString stringWithString:a];\n  NSMutableString *bMutable = [NSMutableString stringWithString:b];\n  // Increase the text lengths by 1024 times to ensure a timeout.\n  for (int i = 0; i < 10; i++) {\n    [aMutable appendString:aMutable];\n    [bMutable appendString:bMutable];\n  }\n  a = aMutable;\n  b = bMutable;\n  NSTimeInterval startTime = [NSDate timeIntervalSinceReferenceDate];\n  [dmp diff_mainOfOldString:a andNewString:b];\n  NSTimeInterval endTime = [NSDate timeIntervalSinceReferenceDate];\n  // Test that we took at least the timeout period.\n  XCTAssertTrue((dmp.Diff_Timeout <= (endTime - startTime)), @\"Test that we took at least the timeout period.\");\n   // Test that we didn't take forever (be forgiving).\n   // Theoretically this test could fail very occasionally if the\n   // OS task swaps or locks up for a second at the wrong moment.\n   // This will fail when running this as PPC code thru Rosetta on Intel.\n  XCTAssertTrue(((dmp.Diff_Timeout * 2) > (endTime - startTime)), @\"Test that we didn't take forever (be forgiving).\");\n  dmp.Diff_Timeout = 0;\n\n  // Test the linemode speedup.\n  // Must be long to pass the 200 character cutoff.\n  a = @\"1234567890\\n1234567890\\n1234567890\\n1234567890\\n1234567890\\n1234567890\\n1234567890\\n1234567890\\n1234567890\\n1234567890\\n1234567890\\n1234567890\\n1234567890\\n\";\n  b = @\"abcdefghij\\nabcdefghij\\nabcdefghij\\nabcdefghij\\nabcdefghij\\nabcdefghij\\nabcdefghij\\nabcdefghij\\nabcdefghij\\nabcdefghij\\nabcdefghij\\nabcdefghij\\nabcdefghij\\n\";\n  XCTAssertEqualObjects([dmp diff_mainOfOldString:a andNewString:b checkLines:YES], [dmp diff_mainOfOldString:a andNewString:b checkLines:NO], @\"diff_main: Simple line-mode.\");\n\n  a = @\"1234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890\";\n  b = @\"abcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghij\";\n  XCTAssertEqualObjects([dmp diff_mainOfOldString:a andNewString:b checkLines:YES], [dmp diff_mainOfOldString:a andNewString:b checkLines:NO], @\"diff_main: Single line-mode.\");\n\n  a = @\"1234567890\\n1234567890\\n1234567890\\n1234567890\\n1234567890\\n1234567890\\n1234567890\\n1234567890\\n1234567890\\n1234567890\\n1234567890\\n1234567890\\n1234567890\\n\";\n  b = @\"abcdefghij\\n1234567890\\n1234567890\\n1234567890\\nabcdefghij\\n1234567890\\n1234567890\\n1234567890\\nabcdefghij\\n1234567890\\n1234567890\\n1234567890\\nabcdefghij\\n\";\n  NSArray *texts_linemode = [self diff_rebuildtexts:[dmp diff_mainOfOldString:a andNewString:b checkLines:YES]];\n  NSArray *texts_textmode = [self diff_rebuildtexts:[dmp diff_mainOfOldString:a andNewString:b checkLines:NO]];\n  XCTAssertEqualObjects(texts_textmode, texts_linemode, @\"diff_main: Overlap line-mode.\");\n\n  // CHANGEME: Test null inputs\n\n  [dmp release];\n}\n\n\n#pragma mark Match Test Functions\n//  MATCH TEST FUNCTIONS\n\n\n- (void)test_match_alphabetTest {\n  DiffMatchPatch *dmp = [DiffMatchPatch new];\n\n  // Initialise the bitmasks for Bitap.\n  NSMutableDictionary *bitmask = [NSMutableDictionary dictionary];\n\n  [bitmask diff_setUnsignedIntegerValue:4 forUnicharKey:'a'];\n  [bitmask diff_setUnsignedIntegerValue:2 forUnicharKey:'b'];\n  [bitmask diff_setUnsignedIntegerValue:1 forUnicharKey:'c'];\n  XCTAssertEqualObjects(bitmask, [dmp match_alphabet:@\"abc\"], @\"match_alphabet: Unique.\");\n\n  [bitmask removeAllObjects];\n  [bitmask diff_setUnsignedIntegerValue:37 forUnicharKey:'a'];\n  [bitmask diff_setUnsignedIntegerValue:18 forUnicharKey:'b'];\n  [bitmask diff_setUnsignedIntegerValue:8 forUnicharKey:'c'];\n  XCTAssertEqualObjects(bitmask, [dmp match_alphabet:@\"abcaba\"], @\"match_alphabet: Duplicates.\");\n\n  [dmp release];\n}\n\n- (void)test_match_bitapTest {\n  DiffMatchPatch *dmp = [DiffMatchPatch new];\n\n  // Bitap algorithm.\n  dmp.Match_Distance = 100;\n  dmp.Match_Threshold = 0.5f;\n  XCTAssertEqual((NSUInteger)5, [dmp match_bitapOfText:@\"abcdefghijk\" andPattern:@\"fgh\" near:5], @\"match_bitap: Exact match #1.\");\n\n  XCTAssertEqual((NSUInteger)5, [dmp match_bitapOfText:@\"abcdefghijk\" andPattern:@\"fgh\" near:0], @\"match_bitap: Exact match #2.\");\n\n  XCTAssertEqual((NSUInteger)4, [dmp match_bitapOfText:@\"abcdefghijk\" andPattern:@\"efxhi\" near:0], @\"match_bitap: Fuzzy match #1.\");\n\n  XCTAssertEqual((NSUInteger)2, [dmp match_bitapOfText:@\"abcdefghijk\" andPattern:@\"cdefxyhijk\" near:5], @\"match_bitap: Fuzzy match #2.\");\n\n  XCTAssertEqual((NSUInteger)NSNotFound, [dmp match_bitapOfText:@\"abcdefghijk\" andPattern:@\"bxy\" near:1], @\"match_bitap: Fuzzy match #3.\");\n\n  XCTAssertEqual((NSUInteger)2, [dmp match_bitapOfText:@\"123456789xx0\" andPattern:@\"3456789x0\" near:2], @\"match_bitap: Overflow.\");\n\n  XCTAssertEqual((NSUInteger)0, [dmp match_bitapOfText:@\"abcdef\" andPattern:@\"xxabc\" near:4], @\"match_bitap: Before start match.\");\n\n  XCTAssertEqual((NSUInteger)3, [dmp match_bitapOfText:@\"abcdef\" andPattern:@\"defyy\" near:4], @\"match_bitap: Beyond end match.\");\n\n  XCTAssertEqual((NSUInteger)0, [dmp match_bitapOfText:@\"abcdef\" andPattern:@\"xabcdefy\" near:0], @\"match_bitap: Oversized pattern.\");\n\n  dmp.Match_Threshold = 0.4f;\n  XCTAssertEqual((NSUInteger)4, [dmp match_bitapOfText:@\"abcdefghijk\" andPattern:@\"efxyhi\" near:1], @\"match_bitap: Threshold #1.\");\n\n  dmp.Match_Threshold = 0.3f;\n  XCTAssertEqual((NSUInteger)NSNotFound, [dmp match_bitapOfText:@\"abcdefghijk\" andPattern:@\"efxyhi\" near:1], @\"match_bitap: Threshold #2.\");\n\n  dmp.Match_Threshold = 0.0f;\n  XCTAssertEqual((NSUInteger)1, [dmp match_bitapOfText:@\"abcdefghijk\" andPattern:@\"bcdef\" near:1], @\"match_bitap: Threshold #3.\");\n\n  dmp.Match_Threshold = 0.5f;\n  XCTAssertEqual((NSUInteger)0, [dmp match_bitapOfText:@\"abcdexyzabcde\" andPattern:@\"abccde\" near:3], @\"match_bitap: Multiple select #1.\");\n\n  XCTAssertEqual((NSUInteger)8, [dmp match_bitapOfText:@\"abcdexyzabcde\" andPattern:@\"abccde\" near:5], @\"match_bitap: Multiple select #2.\");\n\n  dmp.Match_Distance = 10;  // Strict location.\n  XCTAssertEqual((NSUInteger)NSNotFound, [dmp match_bitapOfText:@\"abcdefghijklmnopqrstuvwxyz\" andPattern:@\"abcdefg\" near:24], @\"match_bitap: Distance test #1.\");\n\n  XCTAssertEqual((NSUInteger)0, [dmp match_bitapOfText:@\"abcdefghijklmnopqrstuvwxyz\" andPattern:@\"abcdxxefg\" near:1], @\"match_bitap: Distance test #2.\");\n\n  dmp.Match_Distance = 1000;  // Loose location.\n  XCTAssertEqual((NSUInteger)0, [dmp match_bitapOfText:@\"abcdefghijklmnopqrstuvwxyz\" andPattern:@\"abcdefg\" near:24], @\"match_bitap: Distance test #3.\");\n\n  [dmp release];\n}\n\n- (void)test_match_mainTest {\n  DiffMatchPatch *dmp = [DiffMatchPatch new];\n\n  // Full match.\n  XCTAssertEqual((NSUInteger)0, [dmp match_mainForText:@\"abcdef\" pattern:@\"abcdef\" near:1000], @\"match_main: Equality.\");\n\n  XCTAssertEqual((NSUInteger)NSNotFound, [dmp match_mainForText:@\"\" pattern:@\"abcdef\" near:1], @\"match_main: Null text.\");\n\n  XCTAssertEqual((NSUInteger)3, [dmp match_mainForText:@\"abcdef\" pattern:@\"\" near:3], @\"match_main: Null pattern.\");\n\n  XCTAssertEqual((NSUInteger)3, [dmp match_mainForText:@\"abcdef\" pattern:@\"de\" near:3], @\"match_main: Exact match.\");\n\n  XCTAssertEqual((NSUInteger)3, [dmp match_mainForText:@\"abcdef\" pattern:@\"defy\" near:4], @\"match_main: Beyond end match.\");\n\n  XCTAssertEqual((NSUInteger)0, [dmp match_mainForText:@\"abcdef\" pattern:@\"abcdefy\" near:0], @\"match_main: Oversized pattern.\");\n\n  dmp.Match_Threshold = 0.7f;\n  XCTAssertEqual((NSUInteger)4, [dmp match_mainForText:@\"I am the very model of a modern major general.\" pattern:@\" that berry \" near:5], @\"match_main: Complex match.\");\n  dmp.Match_Threshold = 0.5f;\n\n  // CHANGEME: Test null inputs\n\n  [dmp release];\n}\n\n\n#pragma mark Patch Test Functions\n//  PATCH TEST FUNCTIONS\n\n\n- (void)test_patch_patchObjTest {\n  // Patch Object.\n  Patch *p = [[Patch new] autorelease];\n  p.start1 = 20;\n  p.start2 = 21;\n  p.length1 = 18;\n  p.length2 = 17;\n  p.diffs = [NSMutableArray arrayWithObjects:\n      [Diff diffWithOperation:DIFF_EQUAL andText:@\"jump\"],\n      [Diff diffWithOperation:DIFF_DELETE andText:@\"s\"],\n      [Diff diffWithOperation:DIFF_INSERT andText:@\"ed\"],\n      [Diff diffWithOperation:DIFF_EQUAL andText:@\" over \"],\n      [Diff diffWithOperation:DIFF_DELETE andText:@\"the\"],\n      [Diff diffWithOperation:DIFF_INSERT andText:@\"a\"],\n      [Diff diffWithOperation:DIFF_EQUAL andText:@\"\\nlaz\"], nil];\n  NSString *strp = @\"@@ -21,18 +22,17 @@\\n jump\\n-s\\n+ed\\n  over \\n-the\\n+a\\n %0Alaz\\n\";\n  XCTAssertEqualObjects(strp, [p description], @\"Patch: description.\");\n}\n\n- (void)test_patch_fromTextTest {\n  DiffMatchPatch *dmp = [DiffMatchPatch new];\n\n  XCTAssertTrue(((NSMutableArray *)[dmp patch_fromText:@\"\" error:NULL]).count == 0, @\"patch_fromText: #0.\");\n\n  NSString *strp = @\"@@ -21,18 +22,17 @@\\n jump\\n-s\\n+ed\\n  over \\n-the\\n+a\\n %0Alaz\\n\";\n  XCTAssertEqualObjects(strp, [[[dmp patch_fromText:strp error:NULL] objectAtIndex:0] description], @\"patch_fromText: #1.\");\n\n  XCTAssertEqualObjects(@\"@@ -1 +1 @@\\n-a\\n+b\\n\", [[[dmp patch_fromText:@\"@@ -1 +1 @@\\n-a\\n+b\\n\" error:NULL] objectAtIndex:0] description], @\"patch_fromText: #2.\");\n\n  XCTAssertEqualObjects(@\"@@ -1,3 +0,0 @@\\n-abc\\n\", [[[dmp patch_fromText:@\"@@ -1,3 +0,0 @@\\n-abc\\n\" error:NULL] objectAtIndex:0] description], @\"patch_fromText: #3.\");\n\n  XCTAssertEqualObjects(@\"@@ -0,0 +1,3 @@\\n+abc\\n\", [[[dmp patch_fromText:@\"@@ -0,0 +1,3 @@\\n+abc\\n\" error:NULL] objectAtIndex:0] description], @\"patch_fromText: #4.\");\n\n  // Generates error.\n  NSError *error = nil;\n  NSMutableArray *patches = [dmp patch_fromText:@\"Bad\\nPatch\\n\" error:&error];\n  if (patches != nil || error == nil) {\n    // Error expected.\n    XCTFail(@\"patch_fromText: #5.\");\n  }\n  error = nil;\n\n  [dmp release];\n}\n\n- (void)test_patch_toTextTest {\n  DiffMatchPatch *dmp = [DiffMatchPatch new];\n\n  NSString *strp = @\"@@ -21,18 +22,17 @@\\n jump\\n-s\\n+ed\\n  over \\n-the\\n+a\\n  laz\\n\";\n  NSMutableArray *patches;\n  patches = [dmp patch_fromText:strp error:NULL];\n  XCTAssertEqualObjects(strp, [dmp patch_toText:patches], @\"toText Test #1\");\n\n  strp = @\"@@ -1,9 +1,9 @@\\n-f\\n+F\\n oo+fooba\\n@@ -7,9 +7,9 @@\\n obar\\n-,\\n+.\\n  tes\\n\";\n  patches = [dmp patch_fromText:strp error:NULL];\n  XCTAssertEqualObjects(strp, [dmp patch_toText:patches], @\"toText Test #2\");\n\n  [dmp release];\n}\n\n- (void)test_patch_addContextTest {\n  DiffMatchPatch *dmp = [DiffMatchPatch new];\n\n  dmp.Patch_Margin = 4;\n  Patch *p;\n  p = [[dmp patch_fromText:@\"@@ -21,4 +21,10 @@\\n-jump\\n+somersault\\n\" error:NULL] objectAtIndex:0];\n  [dmp patch_addContextToPatch:p sourceText:@\"The quick brown fox jumps over the lazy dog.\"];\n  XCTAssertEqualObjects(@\"@@ -17,12 +17,18 @@\\n fox \\n-jump\\n+somersault\\n s ov\\n\", [p description], @\"patch_addContext: Simple case.\");\n\n  p = [[dmp patch_fromText:@\"@@ -21,4 +21,10 @@\\n-jump\\n+somersault\\n\" error:NULL] objectAtIndex:0];\n  [dmp patch_addContextToPatch:p sourceText:@\"The quick brown fox jumps.\"];\n  XCTAssertEqualObjects(@\"@@ -17,10 +17,16 @@\\n fox \\n-jump\\n+somersault\\n s.\\n\", [p description], @\"patch_addContext: Not enough trailing context.\");\n\n  p = [[dmp patch_fromText:@\"@@ -3 +3,2 @@\\n-e\\n+at\\n\" error:NULL] objectAtIndex:0];\n  [dmp patch_addContextToPatch:p sourceText:@\"The quick brown fox jumps.\"];\n  XCTAssertEqualObjects(@\"@@ -1,7 +1,8 @@\\n Th\\n-e\\n+at\\n  qui\\n\", [p description], @\"patch_addContext: Not enough leading context.\");\n\n  p = [[dmp patch_fromText:@\"@@ -3 +3,2 @@\\n-e\\n+at\\n\" error:NULL] objectAtIndex:0];\n  [dmp patch_addContextToPatch:p sourceText:@\"The quick brown fox jumps.  The quick brown fox crashes.\"];\n  XCTAssertEqualObjects(@\"@@ -1,27 +1,28 @@\\n Th\\n-e\\n+at\\n  quick brown fox jumps. \\n\", [p description], @\"patch_addContext: Ambiguity.\");\n\n  [dmp release];\n}\n\n- (void)test_patch_makeTest {\n  DiffMatchPatch *dmp = [DiffMatchPatch new];\n\n  NSMutableArray *patches;\n  patches = [dmp patch_makeFromOldString:@\"\" andNewString:@\"\"];\n  XCTAssertEqualObjects(@\"\", [dmp patch_toText:patches], @\"patch_make: Null case.\");\n\n  NSString *text1 = @\"The quick brown fox jumps over the lazy dog.\";\n  NSString *text2 = @\"That quick brown fox jumped over a lazy dog.\";\n  NSString *expectedPatch = @\"@@ -1,8 +1,7 @@\\n Th\\n-at\\n+e\\n  qui\\n@@ -21,17 +21,18 @@\\n jump\\n-ed\\n+s\\n  over \\n-a\\n+the\\n  laz\\n\";\n  // The second patch must be @\"-21,17 +21,18\", not @\"-22,17 +21,18\" due to rolling context.\n  patches = [dmp patch_makeFromOldString:text2 andNewString:text1];\n  XCTAssertEqualObjects(expectedPatch, [dmp patch_toText:patches], @\"patch_make: Text2+Text1 inputs.\");\n\n  expectedPatch = @\"@@ -1,11 +1,12 @@\\n Th\\n-e\\n+at\\n  quick b\\n@@ -22,18 +22,17 @@\\n jump\\n-s\\n+ed\\n  over \\n-the\\n+a\\n  laz\\n\";\n  patches = [dmp patch_makeFromOldString:text1 andNewString:text2];\n  XCTAssertEqualObjects(expectedPatch, [dmp patch_toText:patches], @\"patch_make: Text1+Text2 inputs.\");\n\n  NSMutableArray *diffs = [dmp diff_mainOfOldString:text1 andNewString:text2 checkLines:NO];\n  patches = [dmp patch_makeFromDiffs:diffs];\n  XCTAssertEqualObjects(expectedPatch, [dmp patch_toText:patches], @\"patch_make: Diff input.\");\n\n  patches = [dmp patch_makeFromOldString:text1 andDiffs:diffs];\n  XCTAssertEqualObjects(expectedPatch, [dmp patch_toText:patches], @\"patch_make: Text1+Diff inputs.\");\n\n  patches = [dmp patch_makeFromOldString:text1 newString:text2 diffs:diffs];\n  XCTAssertEqualObjects(expectedPatch, [dmp patch_toText:patches], @\"patch_make: Text1+Text2+Diff inputs (deprecated).\");\n\n  patches = [dmp patch_makeFromOldString:@\"`1234567890-=[]\\\\;',./\" andNewString:@\"~!@#$%^&*()_+{}|:\\\"<>?\"];\n  XCTAssertEqualObjects(@\"@@ -1,21 +1,21 @@\\n-%601234567890-=%5B%5D%5C;',./\\n+~!@#$%25%5E&*()_+%7B%7D%7C:%22%3C%3E?\\n\",\n      [dmp patch_toText:patches],\n      @\"patch_toText: Character encoding.\");\n\n  diffs = [NSMutableArray arrayWithObjects:\n      [Diff diffWithOperation:DIFF_DELETE andText:@\"`1234567890-=[]\\\\;',./\"],\n      [Diff diffWithOperation:DIFF_INSERT andText:@\"~!@#$%^&*()_+{}|:\\\"<>?\"], nil];\n  XCTAssertEqualObjects(diffs,\n      ((Patch *)[[dmp patch_fromText:@\"@@ -1,21 +1,21 @@\\n-%601234567890-=%5B%5D%5C;',./\\n+~!@#$%25%5E&*()_+%7B%7D%7C:%22%3C%3E?\\n\" error:NULL] objectAtIndex:0]).diffs,\n      @\"patch_fromText: Character decoding.\");\n\n  NSMutableString *text1Mutable = [NSMutableString string];\n  for (int x = 0; x < 100; x++) {\n    [text1Mutable appendString:@\"abcdef\"];\n  }\n  text1 = text1Mutable;\n  text2 = [text1 stringByAppendingString:@\"123\"];\n  // CHANGEME: Why does this implementation produce a different, more brief patch?\n  //expectedPatch = @\"@@ -573,28 +573,31 @@\\n cdefabcdefabcdefabcdefabcdef\\n+123\\n\";\n  expectedPatch = @\"@@ -597,4 +597,7 @@\\n cdef\\n+123\\n\";\n  patches = [dmp patch_makeFromOldString:text1 andNewString:text2];\n  XCTAssertEqualObjects(expectedPatch, [dmp patch_toText:patches], @\"patch_make: Long string with repeats.\");\n\n  // CHANGEME: Test null inputs\n\n  [dmp release];\n}\n\n\n- (void)test_patch_splitMaxTest {\n  // Assumes that Match_MaxBits is 32.\n  DiffMatchPatch *dmp = [DiffMatchPatch new];\n  NSMutableArray *patches;\n\n  patches = [dmp patch_makeFromOldString:@\"abcdefghijklmnopqrstuvwxyz01234567890\" andNewString:@\"XabXcdXefXghXijXklXmnXopXqrXstXuvXwxXyzX01X23X45X67X89X0\"];\n  [dmp patch_splitMax:patches];\n  XCTAssertEqualObjects(@\"@@ -1,32 +1,46 @@\\n+X\\n ab\\n+X\\n cd\\n+X\\n ef\\n+X\\n gh\\n+X\\n ij\\n+X\\n kl\\n+X\\n mn\\n+X\\n op\\n+X\\n qr\\n+X\\n st\\n+X\\n uv\\n+X\\n wx\\n+X\\n yz\\n+X\\n 012345\\n@@ -25,13 +39,18 @@\\n zX01\\n+X\\n 23\\n+X\\n 45\\n+X\\n 67\\n+X\\n 89\\n+X\\n 0\\n\", [dmp patch_toText:patches], @\"Assumes that Match_MaxBits is 32 #1\");\n\n  patches = [dmp patch_makeFromOldString:@\"abcdef1234567890123456789012345678901234567890123456789012345678901234567890uvwxyz\" andNewString:@\"abcdefuvwxyz\"];\n  NSString *oldToText = [dmp patch_toText:patches];\n  [dmp patch_splitMax:patches];\n  XCTAssertEqualObjects(oldToText, [dmp patch_toText:patches], @\"Assumes that Match_MaxBits is 32 #2\");\n\n  patches = [dmp patch_makeFromOldString:@\"1234567890123456789012345678901234567890123456789012345678901234567890\" andNewString:@\"abc\"];\n  [dmp patch_splitMax:patches];\n  XCTAssertEqualObjects(@\"@@ -1,32 +1,4 @@\\n-1234567890123456789012345678\\n 9012\\n@@ -29,32 +1,4 @@\\n-9012345678901234567890123456\\n 7890\\n@@ -57,14 +1,3 @@\\n-78901234567890\\n+abc\\n\", [dmp patch_toText:patches], @\"Assumes that Match_MaxBits is 32 #3\");\n\n  patches = [dmp patch_makeFromOldString:@\"abcdefghij , h : 0 , t : 1 abcdefghij , h : 0 , t : 1 abcdefghij , h : 0 , t : 1\" andNewString:@\"abcdefghij , h : 1 , t : 1 abcdefghij , h : 1 , t : 1 abcdefghij , h : 0 , t : 1\"];\n  [dmp patch_splitMax:patches];\n  XCTAssertEqualObjects(@\"@@ -2,32 +2,32 @@\\n bcdefghij , h : \\n-0\\n+1\\n  , t : 1 abcdef\\n@@ -29,32 +29,32 @@\\n bcdefghij , h : \\n-0\\n+1\\n  , t : 1 abcdef\\n\", [dmp patch_toText:patches], @\"Assumes that Match_MaxBits is 32 #4\");\n\n  [dmp release];\n}\n\n- (void)test_patch_addPaddingTest {\n  DiffMatchPatch *dmp = [DiffMatchPatch new];\n\n  NSMutableArray *patches;\n  patches = [dmp patch_makeFromOldString:@\"\" andNewString:@\"test\"];\n  XCTAssertEqualObjects(@\"@@ -0,0 +1,4 @@\\n+test\\n\",\n      [dmp patch_toText:patches],\n      @\"patch_addPadding: Both edges full.\");\n  [dmp patch_addPadding:patches];\n  XCTAssertEqualObjects(@\"@@ -1,8 +1,12 @@\\n %01%02%03%04\\n+test\\n %01%02%03%04\\n\",\n      [dmp patch_toText:patches],\n      @\"patch_addPadding: Both edges full.\");\n\n  patches = [dmp patch_makeFromOldString:@\"XY\" andNewString:@\"XtestY\"];\n  XCTAssertEqualObjects(@\"@@ -1,2 +1,6 @@\\n X\\n+test\\n Y\\n\",\n      [dmp patch_toText:patches],\n      @\"patch_addPadding: Both edges partial.\");\n  [dmp patch_addPadding:patches];\n  XCTAssertEqualObjects(@\"@@ -2,8 +2,12 @@\\n %02%03%04X\\n+test\\n Y%01%02%03\\n\",\n      [dmp patch_toText:patches],\n      @\"patch_addPadding: Both edges partial.\");\n\n  patches = [dmp patch_makeFromOldString:@\"XXXXYYYY\" andNewString:@\"XXXXtestYYYY\"];\n  XCTAssertEqualObjects(@\"@@ -1,8 +1,12 @@\\n XXXX\\n+test\\n YYYY\\n\",\n      [dmp patch_toText:patches],\n      @\"patch_addPadding: Both edges none.\");\n  [dmp patch_addPadding:patches];\n  XCTAssertEqualObjects(@\"@@ -5,8 +5,12 @@\\n XXXX\\n+test\\n YYYY\\n\",\n      [dmp patch_toText:patches],\n      @\"patch_addPadding: Both edges none.\");\n\n  [dmp release];\n}\n\n- (void)test_patch_applyTest {\n  DiffMatchPatch *dmp = [DiffMatchPatch new];\n\n  dmp.Match_Distance = 1000;\n  dmp.Match_Threshold = 0.5f;\n  dmp.Patch_DeleteThreshold = 0.5f;\n  NSMutableArray *patches;\n  patches = [dmp patch_makeFromOldString:@\"\" andNewString:@\"\"];\n  NSArray *results = [dmp patch_apply:patches toString:@\"Hello world.\"];\n  NSMutableArray *boolArray = [results objectAtIndex:1];\n  NSString *resultStr = [NSString stringWithFormat:@\"%@\\t%lu\", [results objectAtIndex:0], (unsigned long)boolArray.count];\n  XCTAssertEqualObjects(@\"Hello world.\\t0\", resultStr, @\"patch_apply: Null case.\");\n\n  patches = [dmp patch_makeFromOldString:@\"The quick brown fox jumps over the lazy dog.\" andNewString:@\"That quick brown fox jumped over a lazy dog.\"];\n  results = [dmp patch_apply:patches toString:@\"The quick brown fox jumps over the lazy dog.\"];\n  boolArray = [results objectAtIndex:1];\n  resultStr = [NSString stringWithFormat:@\"%@\\t%@\\t%@\", [results objectAtIndex:0], stringForBOOL([boolArray objectAtIndex:0]), stringForBOOL([boolArray objectAtIndex:1])];\n  XCTAssertEqualObjects(@\"That quick brown fox jumped over a lazy dog.\\ttrue\\ttrue\", resultStr, @\"patch_apply: Exact match.\");\n\n  results = [dmp patch_apply:patches toString:@\"The quick red rabbit jumps over the tired tiger.\"];\n  boolArray = [results objectAtIndex:1];\n  resultStr = [NSString stringWithFormat:@\"%@\\t%@\\t%@\", [results objectAtIndex:0], stringForBOOL([boolArray objectAtIndex:0]), stringForBOOL([boolArray objectAtIndex:1])];\n  XCTAssertEqualObjects(@\"That quick red rabbit jumped over a tired tiger.\\ttrue\\ttrue\", resultStr, @\"patch_apply: Partial match.\");\n\n  results = [dmp patch_apply:patches toString:@\"I am the very model of a modern major general.\"];\n  boolArray = [results objectAtIndex:1];\n  resultStr = [NSString stringWithFormat:@\"%@\\t%@\\t%@\", [results objectAtIndex:0], stringForBOOL([boolArray objectAtIndex:0]), stringForBOOL([boolArray objectAtIndex:1])];\n  XCTAssertEqualObjects(@\"I am the very model of a modern major general.\\tfalse\\tfalse\", resultStr, @\"patch_apply: Failed match.\");\n\n  patches = [dmp patch_makeFromOldString:@\"x1234567890123456789012345678901234567890123456789012345678901234567890y\" andNewString:@\"xabcy\"];\n  results = [dmp patch_apply:patches toString:@\"x123456789012345678901234567890-----++++++++++-----123456789012345678901234567890y\"];\n  boolArray = [results objectAtIndex:1];\n  resultStr = [NSString stringWithFormat:@\"%@\\t%@\\t%@\", [results objectAtIndex:0], stringForBOOL([boolArray objectAtIndex:0]), stringForBOOL([boolArray objectAtIndex:1])];\n  XCTAssertEqualObjects(@\"xabcy\\ttrue\\ttrue\", resultStr, @\"patch_apply: Big delete, small change.\");\n\n  patches = [dmp patch_makeFromOldString:@\"x1234567890123456789012345678901234567890123456789012345678901234567890y\" andNewString:@\"xabcy\"];\n  results = [dmp patch_apply:patches toString:@\"x12345678901234567890---------------++++++++++---------------12345678901234567890y\"];\n  boolArray = [results objectAtIndex:1];\n  resultStr = [NSString stringWithFormat:@\"%@\\t%@\\t%@\", [results objectAtIndex:0], stringForBOOL([boolArray objectAtIndex:0]), stringForBOOL([boolArray objectAtIndex:1])];\n  XCTAssertEqualObjects(@\"xabc12345678901234567890---------------++++++++++---------------12345678901234567890y\\tfalse\\ttrue\", resultStr, @\"patch_apply: Big delete, big change 1.\");\n\n  dmp.Patch_DeleteThreshold = 0.6f;\n  patches = [dmp patch_makeFromOldString:@\"x1234567890123456789012345678901234567890123456789012345678901234567890y\" andNewString:@\"xabcy\"];\n  results = [dmp patch_apply:patches toString:@\"x12345678901234567890---------------++++++++++---------------12345678901234567890y\"];\n  boolArray = [results objectAtIndex:1];\n  resultStr = [NSString stringWithFormat:@\"%@\\t%@\\t%@\", [results objectAtIndex:0], stringForBOOL([boolArray objectAtIndex:0]), stringForBOOL([boolArray objectAtIndex:1])];\n  XCTAssertEqualObjects(@\"xabcy\\ttrue\\ttrue\", resultStr, @\"patch_apply: Big delete, big change 2.\");\n  dmp.Patch_DeleteThreshold = 0.5f;\n\n  dmp.Match_Threshold = 0.0f;\n  dmp.Match_Distance = 0;\n  patches = [dmp patch_makeFromOldString:@\"abcdefghijklmnopqrstuvwxyz--------------------1234567890\" andNewString:@\"abcXXXXXXXXXXdefghijklmnopqrstuvwxyz--------------------1234567YYYYYYYYYY890\"];\n  results = [dmp patch_apply:patches toString:@\"ABCDEFGHIJKLMNOPQRSTUVWXYZ--------------------1234567890\"];\n  boolArray = [results objectAtIndex:1];\n  resultStr = [NSString stringWithFormat:@\"%@\\t%@\\t%@\", [results objectAtIndex:0], stringForBOOL([boolArray objectAtIndex:0]), stringForBOOL([boolArray objectAtIndex:1])];\n  XCTAssertEqualObjects(@\"ABCDEFGHIJKLMNOPQRSTUVWXYZ--------------------1234567YYYYYYYYYY890\\tfalse\\ttrue\", resultStr, @\"patch_apply: Compensate for failed patch.\");\n  dmp.Match_Threshold = 0.5f;\n  dmp.Match_Distance = 1000;\n\n  patches = [dmp patch_makeFromOldString:@\"\" andNewString:@\"test\"];\n  NSString *patchStr = [dmp patch_toText:patches];\n  [dmp patch_apply:patches toString:@\"\"];\n  XCTAssertEqualObjects(patchStr, [dmp patch_toText:patches], @\"patch_apply: No side effects.\");\n\n  patches = [dmp patch_makeFromOldString:@\"The quick brown fox jumps over the lazy dog.\" andNewString:@\"Woof\"];\n  patchStr = [dmp patch_toText:patches];\n  [dmp patch_apply:patches toString:@\"The quick brown fox jumps over the lazy dog.\"];\n  XCTAssertEqualObjects(patchStr, [dmp patch_toText:patches], @\"patch_apply: No side effects with major delete.\");\n\n  patches = [dmp patch_makeFromOldString:@\"\" andNewString:@\"test\"];\n  results = [dmp patch_apply:patches toString:@\"\"];\n  boolArray = [results objectAtIndex:1];\n  resultStr = [NSString stringWithFormat:@\"%@\\t%@\", [results objectAtIndex:0], stringForBOOL([boolArray objectAtIndex:0])];\n  XCTAssertEqualObjects(@\"test\\ttrue\", resultStr, @\"patch_apply: Edge exact match.\");\n\n  patches = [dmp patch_makeFromOldString:@\"XY\" andNewString:@\"XtestY\"];\n  results = [dmp patch_apply:patches toString:@\"XY\"];\n  boolArray = [results objectAtIndex:1];\n  resultStr = [NSString stringWithFormat:@\"%@\\t%@\", [results objectAtIndex:0], stringForBOOL([boolArray objectAtIndex:0])];\n  XCTAssertEqualObjects(@\"XtestY\\ttrue\", resultStr, @\"patch_apply: Near edge exact match.\");\n\n  patches = [dmp patch_makeFromOldString:@\"y\" andNewString:@\"y123\"];\n  results = [dmp patch_apply:patches toString:@\"x\"];\n  boolArray = [results objectAtIndex:1];\n  resultStr = [NSString stringWithFormat:@\"%@\\t%@\", [results objectAtIndex:0], stringForBOOL([boolArray objectAtIndex:0])];\n  XCTAssertEqualObjects(@\"x123\\ttrue\", resultStr, @\"patch_apply: Edge partial match.\");\n\n  [dmp release];\n}\n\n\n#pragma mark Test Utility Functions\n//  TEST UTILITY FUNCTIONS\n\n\n- (NSArray *)diff_rebuildtexts:(NSMutableArray *)diffs;\n{\n  NSArray *text = [NSMutableArray arrayWithObjects:[NSMutableString string], [NSMutableString string], nil];\n  for (Diff *myDiff in diffs) {\n    if (myDiff.operation != DIFF_INSERT) {\n      [[text objectAtIndex:0] appendString:myDiff.text];\n    }\n    if (myDiff.operation != DIFF_DELETE) {\n      [[text objectAtIndex:1] appendString:myDiff.text];\n    }\n  }\n  return text;\n}\n\n@end\n"
  },
  {
    "path": "objectivec/Tests/Speedtest1.txt",
    "content": "This is a '''list of newspapers published by [[Journal Register Company]]'''.\n\nThe company owns daily and weekly newspapers, other print media properties and newspaper-affiliated local Websites in the [[U.S.]] states of [[Connecticut]], [[Michigan]], [[New York]], [[Ohio]] and [[Pennsylvania]], organized in six geographic \"clusters\":<ref>[http://www.journalregister.com/newspapers.html Journal Register Company: Our Newspapers], accessed February 10, 2008.</ref>\n\n== Capital-Saratoga ==\nThree dailies, associated weeklies and [[pennysaver]]s in greater [[Albany, New York]]; also [http://www.capitalcentral.com capitalcentral.com] and [http://www.jobsinnewyork.com JobsInNewYork.com].\n\n* ''The Oneida Daily Dispatch'' {{WS|oneidadispatch.com}} of [[Oneida, New York]]\n* ''[[The Record (Troy)|The Record]]'' {{WS|troyrecord.com}} of [[Troy, New York]]\n* ''[[The Saratogian]]'' {{WS|saratogian.com}} of [[Saratoga Springs, New York]]\n* Weeklies:\n** ''Community News'' {{WS|cnweekly.com}} weekly of [[Clifton Park, New York]]\n** ''Rome Observer'' of [[Rome, New York]]\n** ''Life & Times of Utica'' of [[Utica, New York]]\n\n== Connecticut ==\nFive dailies, associated weeklies and [[pennysaver]]s in the state of [[Connecticut]]; also [http://www.ctcentral.com CTcentral.com], [http://www.ctcarsandtrucks.com CTCarsAndTrucks.com] and [http://www.jobsinct.com JobsInCT.com].\n\n* ''The Middletown Press'' {{WS|middletownpress.com}} of [[Middletown, Connecticut|Middletown]]\n* ''[[New Haven Register]]'' {{WS|newhavenregister.com}} of [[New Haven, Connecticut|New Haven]]\n* ''The Register Citizen'' {{WS|registercitizen.com}} of [[Torrington, Connecticut|Torrington]]\n\n* [[New Haven Register#Competitors|Elm City Newspapers]] {{WS|ctcentral.com}}\n** ''The Advertiser'' of [[East Haven, Connecticut|East Haven]]\n** ''Hamden Chronicle'' of [[Hamden, Connecticut|Hamden]]\n** ''Milford Weekly'' of [[Milford, Connecticut|Milford]]\n** ''The Orange Bulletin'' of [[Orange, Connecticut|Orange]]\n** ''The Post'' of [[North Haven, Connecticut|North Haven]]\n** ''Shelton Weekly'' of [[Shelton, Connecticut|Shelton]]\n** ''The Stratford Bard'' of [[Stratford, Connecticut|Stratford]]\n** ''Wallingford Voice'' of [[Wallingford, Connecticut|Wallingford]]\n** ''West Haven News'' of [[West Haven, Connecticut|West Haven]]\n* Housatonic Publications \n** ''The New Milford Times'' {{WS|newmilfordtimes.com}} of [[New Milford, Connecticut|New Milford]]\n** ''The Brookfield Journal'' of [[Brookfield, Connecticut|Brookfield]]\n** ''The Kent Good Times Dispatch'' of [[Kent, Connecticut|Kent]]\n** ''The Bethel Beacon'' of [[Bethel, Connecticut|Bethel]]\n** ''The Litchfield Enquirer'' of [[Litchfield, Connecticut|Litchfield]]\n** ''Litchfield County Times'' of [[Litchfield, Connecticut|Litchfield]]\n* Imprint Newspapers {{WS|imprintnewspapers.com}}\n** ''West Hartford News'' of [[West Hartford, Connecticut|West Hartford]]\n** ''Windsor Journal'' of [[Windsor, Connecticut|Windsor]]\n** ''Windsor Locks Journal'' of [[Windsor Locks, Connecticut|Windsor Locks]]\n** ''Avon Post'' of [[Avon, Connecticut|Avon]]\n** ''Farmington Post'' of [[Farmington, Connecticut|Farmington]]\n** ''Simsbury Post'' of [[Simsbury, Connecticut|Simsbury]]\n** ''Tri-Town Post'' of [[Burlington, Connecticut|Burlington]], [[Canton, Connecticut|Canton]] and [[Harwinton, Connecticut|Harwinton]]\n* Minuteman Publications\n** ''[[Fairfield Minuteman]]'' of [[Fairfield, Connecticut|Fairfield]]\n** ''The Westport Minuteman'' {{WS|westportminuteman.com}} of [[Westport, Connecticut|Westport]]\n* Shoreline Newspapers weeklies:\n** ''Branford Review'' of [[Branford, Connecticut|Branford]]\n** ''Clinton Recorder'' of [[Clinton, Connecticut|Clinton]]\n** ''The Dolphin'' of [[Naval Submarine Base New London]] in [[New London, Connecticut|New London]]\n** ''Main Street News'' {{WS|ctmainstreetnews.com}} of [[Essex, Connecticut|Essex]]\n** ''Pictorial Gazette'' of [[Old Saybrook, Connecticut|Old Saybrook]]\n** ''Regional Express'' of [[Colchester, Connecticut|Colchester]]\n** ''Regional Standard'' of [[Colchester, Connecticut|Colchester]]\n** ''Shoreline Times'' {{WS|shorelinetimes.com}} of [[Guilford, Connecticut|Guilford]]\n** ''Shore View East'' of [[Madison, Connecticut|Madison]]\n** ''Shore View West'' of [[Guilford, Connecticut|Guilford]]\n* Other weeklies:\n** ''Registro'' {{WS|registroct.com}} of [[New Haven, Connecticut|New Haven]]\n** ''Thomaston Express'' {{WS|thomastownexpress.com}} of [[Thomaston, Connecticut|Thomaston]]\n** ''Foothills Traders'' {{WS|foothillstrader.com}} of Torrington, Bristol, Canton\n\n== Michigan ==\nFour dailies, associated weeklies and [[pennysaver]]s in the state of [[Michigan]]; also [http://www.micentralhomes.com MIcentralhomes.com] and [http://www.micentralautos.com MIcentralautos.com]\n* ''[[Oakland Press]]'' {{WS|theoaklandpress.com}} of [[Oakland, Michigan|Oakland]]\n* ''Daily Tribune'' {{WS|dailytribune.com}} of [[Royal Oak, Michigan|Royal Oak]]\n* ''Macomb Daily'' {{WS|macombdaily.com}} of [[Mt. Clemens, Michigan|Mt. Clemens]]\n* ''[[Morning Sun]]'' {{WS|themorningsun.com}} of  [[Mount Pleasant, Michigan|Mount Pleasant]]\n* Heritage Newspapers {{WS|heritage.com}}\n** ''Belleville View''\n** ''Ile Camera''\n** ''Monroe Guardian''\n** ''Ypsilanti Courier''\n** ''News-Herald''\n** ''Press & Guide''\n** ''Chelsea Standard & Dexter Leader''\n** ''Manchester Enterprise''\n** ''Milan News-Leader''\n** ''Saline Reporter''\n* Independent Newspapers {{WS|sourcenewspapers.com}}\n** ''Advisor''\n** ''Source''\n* Morning Star {{WS|morningstarpublishing.com}}\n** ''Alma Reminder''\n** ''Alpena Star''\n** ''Antrim County News''\n** ''Carson City Reminder''\n** ''The Leader & Kalkaskian''\n** ''Ogemaw/Oscoda County Star''\n** ''Petoskey/Charlevoix Star''\n** ''Presque Isle Star''\n** ''Preview Community Weekly''\n** ''Roscommon County Star''\n** ''St. Johns Reminder''\n** ''Straits Area Star''\n** ''The (Edmore) Advertiser'' \n* Voice Newspapers {{WS|voicenews.com}}\n** ''Armada Times''\n** ''Bay Voice''\n** ''Blue Water Voice''\n** ''Downriver Voice''\n** ''Macomb Township Voice''\n** ''North Macomb Voice''\n** ''Weekend Voice''\n** ''Suburban Lifestyles'' {{WS|suburbanlifestyles.com}}\n\n== Mid-Hudson ==\nOne daily, associated magazines in the [[Hudson River Valley]] of [[New York]]; also [http://www.midhudsoncentral.com MidHudsonCentral.com] and [http://www.jobsinnewyork.com JobsInNewYork.com].\n\n* ''[[Daily Freeman]]'' {{WS|dailyfreeman.com}} of [[Kingston, New York]]\n\n== Ohio ==\nTwo dailies, associated magazines and three shared Websites, all in the state of [[Ohio]]: [http://www.allaroundcleveland.com AllAroundCleveland.com], [http://www.allaroundclevelandcars.com AllAroundClevelandCars.com] and [http://www.allaroundclevelandjobs.com AllAroundClevelandJobs.com].\n\n* ''[[The News-Herald (Ohio)|The News-Herald]]'' {{WS|news-herald.com}} of [[Willoughby, Ohio|Willoughby]]\n* ''[[The Morning Journal]]'' {{WS|morningjournal.com}} of [[Lorain, Ohio|Lorain]]\n\n== Philadelphia area ==\nSeven dailies and associated weeklies and magazines in [[Pennsylvania]] and [[New Jersey]], and associated Websites: [http://www.allaroundphilly.com AllAroundPhilly.com], [http://www.jobsinnj.com JobsInNJ.com], [http://www.jobsinpa.com JobsInPA.com], and [http://www.phillycarsearch.com PhillyCarSearch.com].\n\n* ''The Daily Local'' {{WS|dailylocal.com}} of [[West Chester, Pennsylvania|West Chester]]\n* ''[[Delaware County Daily and Sunday Times]] {{WS|delcotimes.com}} of Primos\n* ''[[The Mercury (Pennsylvania)|The Mercury]]'' {{WS|pottstownmercury.com}} of [[Pottstown, Pennsylvania|Pottstown]]\n* ''The Phoenix'' {{WS|phoenixvillenews.com}} of [[Phoenixville, Pennsylvania|Phoenixville]]\n* ''[[The Reporter (Lansdale)|The Reporter]]'' {{WS|thereporteronline.com}} of [[Lansdale, Pennsylvania|Lansdale]]\n* ''The Times Herald'' {{WS|timesherald.com}} of [[Norristown, Pennsylvania|Norristown]]\n* ''[[The Trentonian]]'' {{WS|trentonian.com}} of [[Trenton, New Jersey]]\n\n* Weeklies\n** ''El Latino Expreso'' of [[Trenton, New Jersey]]\n** ''La Voz'' of [[Norristown, Pennsylvania]]\n** ''The Village News'' of [[Downingtown, Pennsylvania]]\n** ''The Times Record'' of [[Kennett Square, Pennsylvania]]\n** ''The Tri-County Record'' {{WS|tricountyrecord.com}} of [[Morgantown, Pennsylvania]]\n** ''News of Delaware County'' {{WS|newsofdelawarecounty.com}}of [[Havertown, Pennsylvania]]\n** ''Main Line Times'' {{WS|mainlinetimes.com}}of [[Ardmore, Pennsylvania]]\n** ''Penny Pincher'' of [[Pottstown, Pennsylvania]]\n** ''Town Talk'' {{WS|towntalknews.com}} of [[Ridley, Pennsylvania]]\n* Chesapeake Publishing {{WS|pa8newsgroup.com}} \n** ''Solanco Sun Ledger'' of [[Quarryville, Pennsylvania]]\n** ''Columbia Ledger'' of [[Columbia, Pennsylvania]]\n** ''Coatesville Ledger'' of [[Downingtown, Pennsylvania]]\n** ''Parkesburg Post Ledger'' of [[Quarryville, Pennsylvania]]\n** ''Downingtown Ledger'' of [[Downingtown, Pennsylvania]]\n** ''The Kennett Paper'' of [[Kennett Square, Pennsylvania]]\n** ''Avon Grove Sun'' of [[West Grove, Pennsylvania]]\n** ''Oxford Tribune'' of [[Oxford, Pennsylvania]]\n** ''Elizabethtown Chronicle'' of [[Elizabethtown, Pennsylvania]]\n** ''Donegal Ledger'' of [[Donegal, Pennsylvania]]\n** ''Chadds Ford Post'' of [[Chadds Ford, Pennsylvania]]\n** ''The Central Record'' of [[Medford, New Jersey]]\n** ''Maple Shade Progress'' of [[Maple Shade, New Jersey]]\n* Intercounty Newspapers {{WS|buckslocalnews.com}} \n** ''The Review'' of Roxborough, Pennsylvania\n** ''The Recorder'' of [[Conshohocken, Pennsylvania]]\n** ''The Leader'' of [[Mount Airy, Pennsylvania|Mount Airy]] and West Oak Lake, Pennsylvania\n** ''The Pennington Post'' of [[Pennington, New Jersey]]\n** ''The Bristol Pilot'' of [[Bristol, Pennsylvania]]\n** ''Yardley News'' of [[Yardley, Pennsylvania]]\n** ''New Hope Gazette'' of [[New Hope, Pennsylvania]]\n** ''Doylestown Patriot'' of [[Doylestown, Pennsylvania]]\n** ''Newtown Advance'' of [[Newtown, Pennsylvania]]\n** ''The Plain Dealer'' of [[Williamstown, New Jersey]]\n** ''News Report'' of [[Sewell, New Jersey]]\n** ''Record Breeze'' of [[Berlin, New Jersey]]\n** ''Newsweekly'' of [[Moorestown, New Jersey]]\n** ''Haddon Herald'' of [[Haddonfield, New Jersey]]\n** ''New Egypt Press'' of [[New Egypt, New Jersey]]\n** ''Community News'' of [[Pemberton, New Jersey]]\n** ''Plymouth Meeting Journal'' of [[Plymouth Meeting, Pennsylvania]]\n** ''Lafayette Hill Journal'' of [[Lafayette Hill, Pennsylvania]]\n* Montgomery Newspapers {{WS|montgomerynews.com}} \n** ''Ambler Gazette'' of [[Ambler, Pennsylvania]]\n** ''Central Bucks Life'' of [[Bucks County, Pennsylvania]]\n** ''The Colonial'' of [[Plymouth Meeting, Pennsylvania]]\n** ''Glenside News'' of [[Glenside, Pennsylvania]]\n** ''The Globe'' of [[Lower Moreland Township, Pennsylvania]]\n** ''Main Line Life'' of [[Ardmore, Pennsylvania]]\n** ''Montgomery Life'' of [[Fort Washington, Pennsylvania]]\n** ''North Penn Life'' of [[Lansdale, Pennsylvania]]\n** ''Perkasie News Herald'' of [[Perkasie, Pennsylvania]]\n** ''Public Spirit'' of [[Hatboro, Pennsylvania]]\n** ''Souderton Independent'' of [[Souderton, Pennsylvania]]\n** ''Springfield Sun'' of [[Springfield, Pennsylvania]]\n** ''Spring-Ford Reporter'' of [[Royersford, Pennsylvania]]\n** ''Times Chronicle'' of [[Jenkintown, Pennsylvania]]\n** ''Valley Item'' of [[Perkiomenville, Pennsylvania]]\n** ''Willow Grove Guide'' of [[Willow Grove, Pennsylvania]]\n* News Gleaner Publications (closed December 2008) {{WS|newsgleaner.com}} \n** ''Life Newspapers'' of [[Philadelphia, Pennsylvania]]\n* Suburban Publications\n** ''The Suburban & Wayne Times'' {{WS|waynesuburban.com}} of [[Wayne, Pennsylvania]]\n** ''The Suburban Advertiser'' of [[Exton, Pennsylvania]]\n** ''The King of Prussia Courier'' of [[King of Prussia, Pennsylvania]]\n* Press Newspapers {{WS|countypressonline.com}} \n** ''County Press'' of [[Newtown Square, Pennsylvania]]\n** ''Garnet Valley Press'' of [[Glen Mills, Pennsylvania]]\n** ''Haverford Press'' of [[Newtown Square, Pennsylvania]] (closed January 2009)\n** ''Hometown Press'' of [[Glen Mills, Pennsylvania]] (closed January 2009)\n** ''Media Press'' of [[Newtown Square, Pennsylvania]] (closed January 2009)\n** ''Springfield Press'' of [[Springfield, Pennsylvania]]\n* Berks-Mont Newspapers {{WS|berksmontnews.com}} \n** ''The Boyertown Area Times'' of [[Boyertown, Pennsylvania]]\n** ''The Kutztown Area Patriot'' of [[Kutztown, Pennsylvania]]\n** ''The Hamburg Area Item'' of [[Hamburg, Pennsylvania]]\n** ''The Southern Berks News'' of [[Exeter Township, Berks County, Pennsylvania]]\n** ''The Free Press'' of [[Quakertown, Pennsylvania]]\n** ''The Saucon News'' of [[Quakertown, Pennsylvania]]\n** ''Westside Weekly'' of [[Reading, Pennsylvania]]\n\n* Magazines\n** ''Bucks Co. Town & Country Living''\n** ''Chester Co. Town & Country Living''\n** ''Montomgery Co. Town & Country Living''\n** ''Garden State Town & Country Living''\n** ''Montgomery Homes''\n** ''Philadelphia Golfer''\n** ''Parents Express''\n** ''Art Matters''\n\n{{JRC}}\n\n==References==\n<references />\n\n[[Category:Journal Register publications|*]]\n"
  },
  {
    "path": "objectivec/Tests/Speedtest2.txt",
    "content": "This is a '''list of newspapers published by [[Journal Register Company]]'''.\n\nThe company owns daily and weekly newspapers, other print media properties and newspaper-affiliated local Websites in the [[U.S.]] states of [[Connecticut]], [[Michigan]], [[New York]], [[Ohio]], [[Pennsylvania]] and [[New Jersey]], organized in six geographic \"clusters\":<ref>[http://www.journalregister.com/publications.html Journal Register Company: Our Publications], accessed April 21, 2010.</ref>\n\n== Capital-Saratoga ==\nThree dailies, associated weeklies and [[pennysaver]]s in greater [[Albany, New York]]; also [http://www.capitalcentral.com capitalcentral.com] and [http://www.jobsinnewyork.com JobsInNewYork.com].\n\n* ''The Oneida Daily Dispatch'' {{WS|oneidadispatch.com}} of [[Oneida, New York]]\n* ''[[The Record (Troy)|The Record]]'' {{WS|troyrecord.com}} of [[Troy, New York]]\n* ''[[The Saratogian]]'' {{WS|saratogian.com}} of [[Saratoga Springs, New York]]\n* Weeklies:\n** ''Community News'' {{WS|cnweekly.com}} weekly of [[Clifton Park, New York]]\n** ''Rome Observer'' {{WS|romeobserver.com}} of [[Rome, New York]]\n** ''WG Life '' {{WS|saratogian.com/wglife/}} of [[Wilton, New York]]\n** ''Ballston Spa Life '' {{WS|saratogian.com/bspalife}} of [[Ballston Spa, New York]]\n** ''Greenbush Life'' {{WS|troyrecord.com/greenbush}} of [[Troy, New York]]\n** ''Latham Life'' {{WS|troyrecord.com/latham}} of [[Latham, New York]]\n** ''River Life'' {{WS|troyrecord.com/river}} of [[Troy, New York]]\n\n== Connecticut ==\nThree dailies, associated weeklies and [[pennysaver]]s in the state of [[Connecticut]]; also [http://www.ctcentral.com CTcentral.com], [http://www.ctcarsandtrucks.com CTCarsAndTrucks.com] and [http://www.jobsinct.com JobsInCT.com].\n\n* ''The Middletown Press'' {{WS|middletownpress.com}} of [[Middletown, Connecticut|Middletown]]\n* ''[[New Haven Register]]'' {{WS|newhavenregister.com}} of [[New Haven, Connecticut|New Haven]]\n* ''The Register Citizen'' {{WS|registercitizen.com}} of [[Torrington, Connecticut|Torrington]]\n\n* Housatonic Publications \n** ''The Housatonic Times'' {{WS|housatonictimes.com}} of [[New Milford, Connecticut|New Milford]]\n** ''Litchfield County Times'' {{WS|countytimes.com}} of [[Litchfield, Connecticut|Litchfield]]\n\n* Minuteman Publications\n** ''[[Fairfield Minuteman]]'' {{WS|fairfieldminuteman.com}}of [[Fairfield, Connecticut|Fairfield]]\n** ''The Westport Minuteman'' {{WS|westportminuteman.com}} of [[Westport, Connecticut|Westport]]\n\n* Shoreline Newspapers \n** ''The Dolphin'' {{WS|dolphin-news.com}} of [[Naval Submarine Base New London]] in [[New London, Connecticut|New London]]\n** ''Shoreline Times'' {{WS|shorelinetimes.com}} of [[Guilford, Connecticut|Guilford]]\n\n* Foothills Media Group {{WS|foothillsmediagroup.com}}\n** ''Thomaston Express'' {{WS|thomastonexpress.com}} of [[Thomaston, Connecticut|Thomaston]]\n** ''Good News About Torrington'' {{WS|goodnewsabouttorrington.com}} of [[Torrington, Connecticut|Torrington]]\n** ''Granby News'' {{WS|foothillsmediagroup.com/granby}} of [[Granby, Connecticut|Granby]]\n** ''Canton News'' {{WS|foothillsmediagroup.com/canton}} of [[Canton, Connecticut|Canton]]\n** ''Avon News'' {{WS|foothillsmediagroup.com/avon}} of [[Avon, Connecticut|Avon]]\n** ''Simsbury News'' {{WS|foothillsmediagroup.com/simsbury}} of [[Simsbury, Connecticut|Simsbury]]\n** ''Litchfield News'' {{WS|foothillsmediagroup.com/litchfield}} of [[Litchfield, Connecticut|Litchfield]]\n** ''Foothills Trader'' {{WS|foothillstrader.com}} of Torrington, Bristol, Canton\n\n* Other weeklies\n** ''The Milford-Orange Bulletin'' {{WS|ctbulletin.com}} of [[Orange, Connecticut|Orange]]\n** ''The Post-Chronicle'' {{WS|ctpostchronicle.com}} of [[North Haven, Connecticut|North Haven]]\n** ''West Hartford News'' {{WS|westhartfordnews.com}} of [[West Hartford, Connecticut|West Hartford]]\n\n* Magazines\n** ''The Connecticut Bride'' {{WS|connecticutmag.com}}\n** ''Connecticut Magazine'' {{WS|theconnecticutbride.com}}\n** ''Passport Magazine'' {{WS|passport-mag.com}}\n\n== Michigan ==\nFour dailies, associated weeklies and [[pennysaver]]s in the state of [[Michigan]]; also [http://www.micentralhomes.com MIcentralhomes.com] and [http://www.micentralautos.com MIcentralautos.com]\n* ''[[Oakland Press]]'' {{WS|theoaklandpress.com}} of [[Oakland, Michigan|Oakland]]\n* ''Daily Tribune'' {{WS|dailytribune.com}} of [[Royal Oak, Michigan|Royal Oak]]\n* ''Macomb Daily'' {{WS|macombdaily.com}} of [[Mt. Clemens, Michigan|Mt. Clemens]]\n* ''[[Morning Sun]]'' {{WS|themorningsun.com}} of  [[Mount Pleasant, Michigan|Mount Pleasant]]\n\n* Heritage Newspapers {{WS|heritage.com}}\n** ''Belleville View'' {{WS|bellevilleview.com}}\n** ''Ile Camera'' {{WS|thenewsherald.com/ile_camera}}\n** ''Monroe Guardian''  {{WS|monreguardian.com}}\n** ''Ypsilanti Courier'' {{WS|ypsilanticourier.com}}\n** ''News-Herald'' {{WS|thenewsherald.com}}\n** ''Press & Guide'' {{WS|pressandguide.com}}\n** ''Chelsea Standard & Dexter Leader'' {{WS|chelseastandard.com}}\n** ''Manchester Enterprise'' {{WS|manchesterguardian.com}}\n** ''Milan News-Leader'' {{WS|milannews.com}}\n** ''Saline Reporter'' {{WS|salinereporter.com}}\n* Independent Newspapers \n** ''Advisor'' {{WS|sourcenewspapers.com}}\n** ''Source'' {{WS|sourcenewspapers.com}}\n* Morning Star {{WS|morningstarpublishing.com}}\n** ''The Leader & Kalkaskian'' {{WS|leaderandkalkaskian.com}}\n** ''Grand Traverse Insider'' {{WS|grandtraverseinsider.com}}\n** ''Alma Reminder''\n** ''Alpena Star''\n** ''Ogemaw/Oscoda County Star''\n** ''Presque Isle Star''\n** ''St. Johns Reminder''\n\n* Voice Newspapers {{WS|voicenews.com}}\n** ''Armada Times''\n** ''Bay Voice''\n** ''Blue Water Voice''\n** ''Downriver Voice''\n** ''Macomb Township Voice''\n** ''North Macomb Voice''\n** ''Weekend Voice''\n\n== Mid-Hudson ==\nOne daily, associated magazines in the [[Hudson River Valley]] of [[New York]]; also [http://www.midhudsoncentral.com MidHudsonCentral.com] and [http://www.jobsinnewyork.com JobsInNewYork.com].\n\n* ''[[Daily Freeman]]'' {{WS|dailyfreeman.com}} of [[Kingston, New York]]\n* ''Las Noticias'' {{WS|lasnoticiasny.com}} of [[Kingston, New York]]\n\n== Ohio ==\nTwo dailies, associated magazines and three shared Websites, all in the state of [[Ohio]]: [http://www.allaroundcleveland.com AllAroundCleveland.com], [http://www.allaroundclevelandcars.com AllAroundClevelandCars.com] and [http://www.allaroundclevelandjobs.com AllAroundClevelandJobs.com].\n\n* ''[[The News-Herald (Ohio)|The News-Herald]]'' {{WS|news-herald.com}} of [[Willoughby, Ohio|Willoughby]]\n* ''[[The Morning Journal]]'' {{WS|morningjournal.com}} of [[Lorain, Ohio|Lorain]]\n* ''El Latino Expreso'' {{WS|lorainlatino.com}} of [[Lorain, Ohio|Lorain]]\n\n== Philadelphia area ==\nSeven dailies and associated weeklies and magazines in [[Pennsylvania]] and [[New Jersey]], and associated Websites: [http://www.allaroundphilly.com AllAroundPhilly.com], [http://www.jobsinnj.com JobsInNJ.com], [http://www.jobsinpa.com JobsInPA.com], and [http://www.phillycarsearch.com PhillyCarSearch.com].\n\n* ''[[The Daily Local News]]'' {{WS|dailylocal.com}} of [[West Chester, Pennsylvania|West Chester]]\n* ''[[Delaware County Daily and Sunday Times]] {{WS|delcotimes.com}} of Primos [[Upper Darby Township, Pennsylvania]]\n* ''[[The Mercury (Pennsylvania)|The Mercury]]'' {{WS|pottstownmercury.com}} of [[Pottstown, Pennsylvania|Pottstown]]\n* ''[[The Reporter (Lansdale)|The Reporter]]'' {{WS|thereporteronline.com}} of [[Lansdale, Pennsylvania|Lansdale]]\n* ''The Times Herald'' {{WS|timesherald.com}} of [[Norristown, Pennsylvania|Norristown]]\n* ''[[The Trentonian]]'' {{WS|trentonian.com}} of [[Trenton, New Jersey]]\n\n* Weeklies\n* ''The Phoenix'' {{WS|phoenixvillenews.com}} of [[Phoenixville, Pennsylvania]]\n** ''El Latino Expreso'' {{WS|njexpreso.com}} of [[Trenton, New Jersey]]\n** ''La Voz'' {{WS|lavozpa.com}} of [[Norristown, Pennsylvania]]\n** ''The Tri County Record'' {{WS|tricountyrecord.com}} of [[Morgantown, Pennsylvania]]\n** ''Penny Pincher'' {{WS|pennypincherpa.com}}of [[Pottstown, Pennsylvania]]\n\n* Chesapeake Publishing  {{WS|southernchestercountyweeklies.com}}\n** ''The Kennett Paper'' {{WS|kennettpaper.com}} of [[Kennett Square, Pennsylvania]]\n** ''Avon Grove Sun'' {{WS|avongrovesun.com}} of [[West Grove, Pennsylvania]]\n** ''The Central Record'' {{WS|medfordcentralrecord.com}} of [[Medford, New Jersey]]\n** ''Maple Shade Progress'' {{WS|mapleshadeprogress.com}} of [[Maple Shade, New Jersey]]\n\n* Intercounty Newspapers {{WS|buckslocalnews.com}} {{WS|southjerseylocalnews.com}} \n** ''The Pennington Post'' {{WS|penningtonpost.com}} of [[Pennington, New Jersey]]\n** ''The Bristol Pilot'' {{WS|bristolpilot.com}} of [[Bristol, Pennsylvania]]\n** ''Yardley News'' {{WS|yardleynews.com}} of [[Yardley, Pennsylvania]]\n** ''Advance of Bucks County'' {{WS|advanceofbucks.com}} of [[Newtown, Pennsylvania]]\n** ''Record Breeze'' {{WS|recordbreeze.com}} of [[Berlin, New Jersey]]\n** ''Community News'' {{WS|sjcommunitynews.com}} of [[Pemberton, New Jersey]]\n\n* Montgomery Newspapers {{WS|montgomerynews.com}} \n** ''Ambler Gazette'' {{WS|amblergazette.com}} of [[Ambler, Pennsylvania]]\n** ''The Colonial'' {{WS|colonialnews.com}} of [[Plymouth Meeting, Pennsylvania]]\n** ''Glenside News'' {{WS|glensidenews.com}} of [[Glenside, Pennsylvania]]\n** ''The Globe'' {{WS|globenewspaper.com}} of [[Lower Moreland Township, Pennsylvania]]\n** ''Montgomery Life'' {{WS|montgomerylife.com}} of [[Fort Washington, Pennsylvania]]\n** ''North Penn Life'' {{WS|northpennlife.com}} of [[Lansdale, Pennsylvania]]\n** ''Perkasie News Herald'' {{WS|perkasienewsherald.com}} of [[Perkasie, Pennsylvania]]\n** ''Public Spirit'' {{WS|thepublicspirit.com}} of [[Hatboro, Pennsylvania]]\n** ''Souderton Independent'' {{WS|soudertonindependent.com}} of [[Souderton, Pennsylvania]]\n** ''Springfield Sun'' {{WS|springfieldsun.com}} of [[Springfield, Pennsylvania]]\n** ''Spring-Ford Reporter'' {{WS|springfordreporter.com}} of [[Royersford, Pennsylvania]]\n** ''Times Chronicle'' {{WS|thetimeschronicle.com}} of [[Jenkintown, Pennsylvania]]\n** ''Valley Item'' {{WS|valleyitem.com}} of [[Perkiomenville, Pennsylvania]]\n** ''Willow Grove Guide'' {{WS|willowgroveguide.com}} of [[Willow Grove, Pennsylvania]]\n** ''The Review'' {{WS|roxreview.com}} of [[Roxborough, Philadelphia, Pennsylvania]]\n\n* Main Line Media News {{WS|mainlinemedianews.com}}\n** ''Main Line Times'' {{WS|mainlinetimes.com}} of [[Ardmore, Pennsylvania]]\n** ''Main Line Life'' {{WS|mainlinelife.com}} of [[Ardmore, Pennsylvania]]\n** ''The King of Prussia Courier'' {{WS|kingofprussiacourier.com}} of [[King of Prussia, Pennsylvania]]\n\n* Delaware County News Network {{WS|delconewsnetwork.com}} \n** ''News of Delaware County'' {{WS|newsofdelawarecounty.com}} of [[Havertown, Pennsylvania]]\n** ''County Press'' {{WS|countypressonline.com}} of [[Newtown Square, Pennsylvania]]\n** ''Garnet Valley Press'' {{WS|countypressonline.com}} of [[Glen Mills, Pennsylvania]]\n** ''Springfield Press'' {{WS|countypressonline.com}} of [[Springfield, Pennsylvania]]\n** ''Town Talk'' {{WS|towntalknews.com}} of [[Ridley, Pennsylvania]]\n\n* Berks-Mont Newspapers {{WS|berksmontnews.com}} \n** ''The Boyertown Area Times'' {{WS|berksmontnews.com/boyertown_area_times}} of [[Boyertown, Pennsylvania]]\n** ''The Kutztown Area Patriot'' {{WS|berksmontnews.com/kutztown_area_patriot}} of [[Kutztown, Pennsylvania]]\n** ''The Hamburg Area Item'' {{WS|berksmontnews.com/hamburg_area_item}} of [[Hamburg, Pennsylvania]]\n** ''The Southern Berks News'' {{WS|berksmontnews.com/southern_berks_news}} of [[Exeter Township, Berks County, Pennsylvania]]\n** ''Community Connection'' {{WS|berksmontnews.com/community_connection}} of [[Boyertown, Pennsylvania]]\n\n* Magazines\n** ''Bucks Co. Town & Country Living'' {{WS|buckscountymagazine.com}} \n** ''Parents Express'' {{WS|parents-express.com}} \n** ''Real Men, Rednecks'' {{WS|realmenredneck.com}} \n\n{{JRC}}\n\n==References==\n<references />\n\n[[Category:Journal Register publications|*]]\n"
  },
  {
    "path": "objectivec/Tests/speedtest.m",
    "content": "/*\n * Diff Match and Patch -- Test harness\n * Copyright 2018 The diff-match-patch Authors.\n * https://github.com/google/diff-match-patch\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *   http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n *\n * Author: fraser@google.com (Neil Fraser)\n * ObjC port: jan@geheimwerk.de (Jan Weiß)\n */\n\n#import <Foundation/Foundation.h>\n\n#import <DiffMatchPatch/DiffMatchPatch.h>\n\nint main (int argc, const char * argv[]) {\n  NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];\n\n  NSString *directory = @\"\";\n  if (argc >= 1) {\n     directory = [NSString stringWithCString:argv[1] encoding:NSUTF8StringEncoding];\n  }\n\n  NSString *filePath1 =\n      [directory stringByAppendingPathComponent:@\"Tests/Speedtest1.txt\"];\n  NSString *text1 = [NSString stringWithContentsOfFile:filePath1\n                        encoding:NSUTF8StringEncoding\n                           error:NULL];\n\n  NSString *filePath2 =\n      [directory stringByAppendingPathComponent:@\"Tests/Speedtest2.txt\"];\n  NSString *text2 = [NSString stringWithContentsOfFile:filePath2\n                        encoding:NSUTF8StringEncoding\n                           error:NULL];\n\n  DiffMatchPatch *dmp = [DiffMatchPatch new];\n  dmp.Diff_Timeout = 0;\n\n  NSTimeInterval start = [NSDate timeIntervalSinceReferenceDate];\n  [dmp diff_mainOfOldString:text1 andNewString:text2];\n  NSTimeInterval duration = [NSDate timeIntervalSinceReferenceDate] - start;\n\n  [dmp release];\n\n  NSLog(@\"Elapsed time: %.4lf\", (double)duration);\n\n  [pool drain];\n  return 0;\n}\n"
  },
  {
    "path": "objectivec/speedtest_Prefix.pch",
    "content": "//\n// Prefix header for all source files of the 'speedtest' target in the 'DiffMatchPatch' project.\n//\n\n#ifdef __OBJC__\n  #import <Foundation/Foundation.h>\n#endif\n"
  },
  {
    "path": "python2/__init__.py",
    "content": "from .diff_match_patch import diff_match_patch, patch_obj\n\n"
  },
  {
    "path": "python2/diff_match_patch.py",
    "content": "#!/usr/bin/python2.4\n\nfrom __future__ import division\n\n\"\"\"Diff Match and Patch\nCopyright 2018 The diff-match-patch Authors.\nhttps://github.com/google/diff-match-patch\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n  http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n\"\"\"\n\n\"\"\"Functions for diff, match and patch.\n\nComputes the difference between two texts to create a patch.\nApplies the patch onto another text, allowing for errors.\n\"\"\"\n\n__author__ = 'fraser@google.com (Neil Fraser)'\n\nimport re\nimport sys\nimport time\nimport urllib\n\n\nclass diff_match_patch:\n  \"\"\"Class containing the diff, match and patch methods.\n\n  Also contains the behaviour settings.\n  \"\"\"\n\n  def __init__(self):\n    \"\"\"Inits a diff_match_patch object with default settings.\n    Redefine these in your program to override the defaults.\n    \"\"\"\n\n    # Number of seconds to map a diff before giving up (0 for infinity).\n    self.Diff_Timeout = 1.0\n    # Cost of an empty edit operation in terms of edit characters.\n    self.Diff_EditCost = 4\n    # At what point is no match declared (0.0 = perfection, 1.0 = very loose).\n    self.Match_Threshold = 0.5\n    # How far to search for a match (0 = exact location, 1000+ = broad match).\n    # A match this many characters away from the expected location will add\n    # 1.0 to the score (0.0 is a perfect match).\n    self.Match_Distance = 1000\n    # When deleting a large block of text (over ~64 characters), how close do\n    # the contents have to be to match the expected contents. (0.0 = perfection,\n    # 1.0 = very loose).  Note that Match_Threshold controls how closely the\n    # end points of a delete need to match.\n    self.Patch_DeleteThreshold = 0.5\n    # Chunk size for context length.\n    self.Patch_Margin = 4\n\n    # The number of bits in an int.\n    # Python has no maximum, thus to disable patch splitting set to 0.\n    # However to avoid long patches in certain pathological cases, use 32.\n    # Multiple short patches (using native ints) are much faster than long ones.\n    self.Match_MaxBits = 32\n\n  #  DIFF FUNCTIONS\n\n  # The data structure representing a diff is an array of tuples:\n  # [(DIFF_DELETE, \"Hello\"), (DIFF_INSERT, \"Goodbye\"), (DIFF_EQUAL, \" world.\")]\n  # which means: delete \"Hello\", add \"Goodbye\" and keep \" world.\"\n  DIFF_DELETE = -1\n  DIFF_INSERT = 1\n  DIFF_EQUAL = 0\n\n  def diff_main(self, text1, text2, checklines=True, deadline=None):\n    \"\"\"Find the differences between two texts.  Simplifies the problem by\n      stripping any common prefix or suffix off the texts before diffing.\n\n    Args:\n      text1: Old string to be diffed.\n      text2: New string to be diffed.\n      checklines: Optional speedup flag.  If present and false, then don't run\n        a line-level diff first to identify the changed areas.\n        Defaults to true, which does a faster, slightly less optimal diff.\n      deadline: Optional time when the diff should be complete by.  Used\n        internally for recursive calls.  Users should set DiffTimeout instead.\n\n    Returns:\n      Array of changes.\n    \"\"\"\n    # Set a deadline by which time the diff must be complete.\n    if deadline == None:\n      # Unlike in most languages, Python counts time in seconds.\n      if self.Diff_Timeout <= 0:\n        deadline = sys.maxint\n      else:\n        deadline = time.time() + self.Diff_Timeout\n\n    # Check for null inputs.\n    if text1 == None or text2 == None:\n      raise ValueError(\"Null inputs. (diff_main)\")\n\n    # Check for equality (speedup).\n    if text1 == text2:\n      if text1:\n        return [(self.DIFF_EQUAL, text1)]\n      return []\n\n    # Trim off common prefix (speedup).\n    commonlength = self.diff_commonPrefix(text1, text2)\n    commonprefix = text1[:commonlength]\n    text1 = text1[commonlength:]\n    text2 = text2[commonlength:]\n\n    # Trim off common suffix (speedup).\n    commonlength = self.diff_commonSuffix(text1, text2)\n    if commonlength == 0:\n      commonsuffix = ''\n    else:\n      commonsuffix = text1[-commonlength:]\n      text1 = text1[:-commonlength]\n      text2 = text2[:-commonlength]\n\n    # Compute the diff on the middle block.\n    diffs = self.diff_compute(text1, text2, checklines, deadline)\n\n    # Restore the prefix and suffix.\n    if commonprefix:\n      diffs[:0] = [(self.DIFF_EQUAL, commonprefix)]\n    if commonsuffix:\n      diffs.append((self.DIFF_EQUAL, commonsuffix))\n    self.diff_cleanupMerge(diffs)\n    return diffs\n\n  def diff_compute(self, text1, text2, checklines, deadline):\n    \"\"\"Find the differences between two texts.  Assumes that the texts do not\n      have any common prefix or suffix.\n\n    Args:\n      text1: Old string to be diffed.\n      text2: New string to be diffed.\n      checklines: Speedup flag.  If false, then don't run a line-level diff\n        first to identify the changed areas.\n        If true, then run a faster, slightly less optimal diff.\n      deadline: Time when the diff should be complete by.\n\n    Returns:\n      Array of changes.\n    \"\"\"\n    if not text1:\n      # Just add some text (speedup).\n      return [(self.DIFF_INSERT, text2)]\n\n    if not text2:\n      # Just delete some text (speedup).\n      return [(self.DIFF_DELETE, text1)]\n\n    if len(text1) > len(text2):\n      (longtext, shorttext) = (text1, text2)\n    else:\n      (shorttext, longtext) = (text1, text2)\n    i = longtext.find(shorttext)\n    if i != -1:\n      # Shorter text is inside the longer text (speedup).\n      diffs = [(self.DIFF_INSERT, longtext[:i]), (self.DIFF_EQUAL, shorttext),\n               (self.DIFF_INSERT, longtext[i + len(shorttext):])]\n      # Swap insertions for deletions if diff is reversed.\n      if len(text1) > len(text2):\n        diffs[0] = (self.DIFF_DELETE, diffs[0][1])\n        diffs[2] = (self.DIFF_DELETE, diffs[2][1])\n      return diffs\n\n    if len(shorttext) == 1:\n      # Single character string.\n      # After the previous speedup, the character can't be an equality.\n      return [(self.DIFF_DELETE, text1), (self.DIFF_INSERT, text2)]\n\n    # Check to see if the problem can be split in two.\n    hm = self.diff_halfMatch(text1, text2)\n    if hm:\n      # A half-match was found, sort out the return data.\n      (text1_a, text1_b, text2_a, text2_b, mid_common) = hm\n      # Send both pairs off for separate processing.\n      diffs_a = self.diff_main(text1_a, text2_a, checklines, deadline)\n      diffs_b = self.diff_main(text1_b, text2_b, checklines, deadline)\n      # Merge the results.\n      return diffs_a + [(self.DIFF_EQUAL, mid_common)] + diffs_b\n\n    if checklines and len(text1) > 100 and len(text2) > 100:\n      return self.diff_lineMode(text1, text2, deadline)\n\n    return self.diff_bisect(text1, text2, deadline)\n\n  def diff_lineMode(self, text1, text2, deadline):\n    \"\"\"Do a quick line-level diff on both strings, then rediff the parts for\n      greater accuracy.\n      This speedup can produce non-minimal diffs.\n\n    Args:\n      text1: Old string to be diffed.\n      text2: New string to be diffed.\n      deadline: Time when the diff should be complete by.\n\n    Returns:\n      Array of changes.\n    \"\"\"\n\n    # Scan the text on a line-by-line basis first.\n    (text1, text2, linearray) = self.diff_linesToChars(text1, text2)\n\n    diffs = self.diff_main(text1, text2, False, deadline)\n\n    # Convert the diff back to original text.\n    self.diff_charsToLines(diffs, linearray)\n    # Eliminate freak matches (e.g. blank lines)\n    self.diff_cleanupSemantic(diffs)\n\n    # Rediff any replacement blocks, this time character-by-character.\n    # Add a dummy entry at the end.\n    diffs.append((self.DIFF_EQUAL, ''))\n    pointer = 0\n    count_delete = 0\n    count_insert = 0\n    text_delete = ''\n    text_insert = ''\n    while pointer < len(diffs):\n      if diffs[pointer][0] == self.DIFF_INSERT:\n        count_insert += 1\n        text_insert += diffs[pointer][1]\n      elif diffs[pointer][0] == self.DIFF_DELETE:\n        count_delete += 1\n        text_delete += diffs[pointer][1]\n      elif diffs[pointer][0] == self.DIFF_EQUAL:\n        # Upon reaching an equality, check for prior redundancies.\n        if count_delete >= 1 and count_insert >= 1:\n          # Delete the offending records and add the merged ones.\n          subDiff = self.diff_main(text_delete, text_insert, False, deadline)\n          diffs[pointer - count_delete - count_insert : pointer] = subDiff\n          pointer = pointer - count_delete - count_insert + len(subDiff)\n        count_insert = 0\n        count_delete = 0\n        text_delete = ''\n        text_insert = ''\n\n      pointer += 1\n\n    diffs.pop()  # Remove the dummy entry at the end.\n\n    return diffs\n\n  def diff_bisect(self, text1, text2, deadline):\n    \"\"\"Find the 'middle snake' of a diff, split the problem in two\n      and return the recursively constructed diff.\n      See Myers 1986 paper: An O(ND) Difference Algorithm and Its Variations.\n\n    Args:\n      text1: Old string to be diffed.\n      text2: New string to be diffed.\n      deadline: Time at which to bail if not yet complete.\n\n    Returns:\n      Array of diff tuples.\n    \"\"\"\n\n    # Cache the text lengths to prevent multiple calls.\n    text1_length = len(text1)\n    text2_length = len(text2)\n    max_d = (text1_length + text2_length + 1) // 2\n    v_offset = max_d\n    v_length = 2 * max_d\n    v1 = [-1] * v_length\n    v1[v_offset + 1] = 0\n    v2 = v1[:]\n    delta = text1_length - text2_length\n    # If the total number of characters is odd, then the front path will\n    # collide with the reverse path.\n    front = (delta % 2 != 0)\n    # Offsets for start and end of k loop.\n    # Prevents mapping of space beyond the grid.\n    k1start = 0\n    k1end = 0\n    k2start = 0\n    k2end = 0\n    for d in xrange(max_d):\n      # Bail out if deadline is reached.\n      if time.time() > deadline:\n        break\n\n      # Walk the front path one step.\n      for k1 in xrange(-d + k1start, d + 1 - k1end, 2):\n        k1_offset = v_offset + k1\n        if k1 == -d or (k1 != d and\n            v1[k1_offset - 1] < v1[k1_offset + 1]):\n          x1 = v1[k1_offset + 1]\n        else:\n          x1 = v1[k1_offset - 1] + 1\n        y1 = x1 - k1\n        while (x1 < text1_length and y1 < text2_length and\n               text1[x1] == text2[y1]):\n          x1 += 1\n          y1 += 1\n        v1[k1_offset] = x1\n        if x1 > text1_length:\n          # Ran off the right of the graph.\n          k1end += 2\n        elif y1 > text2_length:\n          # Ran off the bottom of the graph.\n          k1start += 2\n        elif front:\n          k2_offset = v_offset + delta - k1\n          if k2_offset >= 0 and k2_offset < v_length and v2[k2_offset] != -1:\n            # Mirror x2 onto top-left coordinate system.\n            x2 = text1_length - v2[k2_offset]\n            if x1 >= x2:\n              # Overlap detected.\n              return self.diff_bisectSplit(text1, text2, x1, y1, deadline)\n\n      # Walk the reverse path one step.\n      for k2 in xrange(-d + k2start, d + 1 - k2end, 2):\n        k2_offset = v_offset + k2\n        if k2 == -d or (k2 != d and\n            v2[k2_offset - 1] < v2[k2_offset + 1]):\n          x2 = v2[k2_offset + 1]\n        else:\n          x2 = v2[k2_offset - 1] + 1\n        y2 = x2 - k2\n        while (x2 < text1_length and y2 < text2_length and\n               text1[-x2 - 1] == text2[-y2 - 1]):\n          x2 += 1\n          y2 += 1\n        v2[k2_offset] = x2\n        if x2 > text1_length:\n          # Ran off the left of the graph.\n          k2end += 2\n        elif y2 > text2_length:\n          # Ran off the top of the graph.\n          k2start += 2\n        elif not front:\n          k1_offset = v_offset + delta - k2\n          if k1_offset >= 0 and k1_offset < v_length and v1[k1_offset] != -1:\n            x1 = v1[k1_offset]\n            y1 = v_offset + x1 - k1_offset\n            # Mirror x2 onto top-left coordinate system.\n            x2 = text1_length - x2\n            if x1 >= x2:\n              # Overlap detected.\n              return self.diff_bisectSplit(text1, text2, x1, y1, deadline)\n\n    # Diff took too long and hit the deadline or\n    # number of diffs equals number of characters, no commonality at all.\n    return [(self.DIFF_DELETE, text1), (self.DIFF_INSERT, text2)]\n\n  def diff_bisectSplit(self, text1, text2, x, y, deadline):\n    \"\"\"Given the location of the 'middle snake', split the diff in two parts\n    and recurse.\n\n    Args:\n      text1: Old string to be diffed.\n      text2: New string to be diffed.\n      x: Index of split point in text1.\n      y: Index of split point in text2.\n      deadline: Time at which to bail if not yet complete.\n\n    Returns:\n      Array of diff tuples.\n    \"\"\"\n    text1a = text1[:x]\n    text2a = text2[:y]\n    text1b = text1[x:]\n    text2b = text2[y:]\n\n    # Compute both diffs serially.\n    diffs = self.diff_main(text1a, text2a, False, deadline)\n    diffsb = self.diff_main(text1b, text2b, False, deadline)\n\n    return diffs + diffsb\n\n  def diff_linesToChars(self, text1, text2):\n    \"\"\"Split two texts into an array of strings.  Reduce the texts to a string\n    of hashes where each Unicode character represents one line.\n\n    Args:\n      text1: First string.\n      text2: Second string.\n\n    Returns:\n      Three element tuple, containing the encoded text1, the encoded text2 and\n      the array of unique strings.  The zeroth element of the array of unique\n      strings is intentionally blank.\n    \"\"\"\n    lineArray = []  # e.g. lineArray[4] == \"Hello\\n\"\n    lineHash = {}   # e.g. lineHash[\"Hello\\n\"] == 4\n\n    # \"\\x00\" is a valid character, but various debuggers don't like it.\n    # So we'll insert a junk entry to avoid generating a null character.\n    lineArray.append('')\n\n    def diff_linesToCharsMunge(text):\n      \"\"\"Split a text into an array of strings.  Reduce the texts to a string\n      of hashes where each Unicode character represents one line.\n      Modifies linearray and linehash through being a closure.\n\n      Args:\n        text: String to encode.\n\n      Returns:\n        Encoded string.\n      \"\"\"\n      chars = []\n      # Walk the text, pulling out a substring for each line.\n      # text.split('\\n') would would temporarily double our memory footprint.\n      # Modifying text would create many large strings to garbage collect.\n      lineStart = 0\n      lineEnd = -1\n      while lineEnd < len(text) - 1:\n        lineEnd = text.find('\\n', lineStart)\n        if lineEnd == -1:\n          lineEnd = len(text) - 1\n        line = text[lineStart:lineEnd + 1]\n\n        if line in lineHash:\n          chars.append(unichr(lineHash[line]))\n        else:\n          if len(lineArray) == maxLines:\n            # Bail out at 65535 because unichr(65536) throws.\n            line = text[lineStart:]\n            lineEnd = len(text)\n          lineArray.append(line)\n          lineHash[line] = len(lineArray) - 1\n          chars.append(unichr(len(lineArray) - 1))\n        lineStart = lineEnd + 1\n      return \"\".join(chars)\n\n    # Allocate 2/3rds of the space for text1, the rest for text2.\n    maxLines = 40000\n    chars1 = diff_linesToCharsMunge(text1)\n    maxLines = 65535\n    chars2 = diff_linesToCharsMunge(text2)\n    return (chars1, chars2, lineArray)\n\n  def diff_charsToLines(self, diffs, lineArray):\n    \"\"\"Rehydrate the text in a diff from a string of line hashes to real lines\n    of text.\n\n    Args:\n      diffs: Array of diff tuples.\n      lineArray: Array of unique strings.\n    \"\"\"\n    for i in xrange(len(diffs)):\n      text = []\n      for char in diffs[i][1]:\n        text.append(lineArray[ord(char)])\n      diffs[i] = (diffs[i][0], \"\".join(text))\n\n  def diff_commonPrefix(self, text1, text2):\n    \"\"\"Determine the common prefix of two strings.\n\n    Args:\n      text1: First string.\n      text2: Second string.\n\n    Returns:\n      The number of characters common to the start of each string.\n    \"\"\"\n    # Quick check for common null cases.\n    if not text1 or not text2 or text1[0] != text2[0]:\n      return 0\n    # Binary search.\n    # Performance analysis: https://neil.fraser.name/news/2007/10/09/\n    pointermin = 0\n    pointermax = min(len(text1), len(text2))\n    pointermid = pointermax\n    pointerstart = 0\n    while pointermin < pointermid:\n      if text1[pointerstart:pointermid] == text2[pointerstart:pointermid]:\n        pointermin = pointermid\n        pointerstart = pointermin\n      else:\n        pointermax = pointermid\n      pointermid = (pointermax - pointermin) // 2 + pointermin\n    return pointermid\n\n  def diff_commonSuffix(self, text1, text2):\n    \"\"\"Determine the common suffix of two strings.\n\n    Args:\n      text1: First string.\n      text2: Second string.\n\n    Returns:\n      The number of characters common to the end of each string.\n    \"\"\"\n    # Quick check for common null cases.\n    if not text1 or not text2 or text1[-1] != text2[-1]:\n      return 0\n    # Binary search.\n    # Performance analysis: https://neil.fraser.name/news/2007/10/09/\n    pointermin = 0\n    pointermax = min(len(text1), len(text2))\n    pointermid = pointermax\n    pointerend = 0\n    while pointermin < pointermid:\n      if (text1[-pointermid:len(text1) - pointerend] ==\n          text2[-pointermid:len(text2) - pointerend]):\n        pointermin = pointermid\n        pointerend = pointermin\n      else:\n        pointermax = pointermid\n      pointermid = (pointermax - pointermin) // 2 + pointermin\n    return pointermid\n\n  def diff_commonOverlap(self, text1, text2):\n    \"\"\"Determine if the suffix of one string is the prefix of another.\n\n    Args:\n      text1 First string.\n      text2 Second string.\n\n    Returns:\n      The number of characters common to the end of the first\n      string and the start of the second string.\n    \"\"\"\n    # Cache the text lengths to prevent multiple calls.\n    text1_length = len(text1)\n    text2_length = len(text2)\n    # Eliminate the null case.\n    if text1_length == 0 or text2_length == 0:\n      return 0\n    # Truncate the longer string.\n    if text1_length > text2_length:\n      text1 = text1[-text2_length:]\n    elif text1_length < text2_length:\n      text2 = text2[:text1_length]\n    text_length = min(text1_length, text2_length)\n    # Quick check for the worst case.\n    if text1 == text2:\n      return text_length\n\n    # Start by looking for a single character match\n    # and increase length until no match is found.\n    # Performance analysis: https://neil.fraser.name/news/2010/11/04/\n    best = 0\n    length = 1\n    while True:\n      pattern = text1[-length:]\n      found = text2.find(pattern)\n      if found == -1:\n        return best\n      length += found\n      if found == 0 or text1[-length:] == text2[:length]:\n        best = length\n        length += 1\n\n  def diff_halfMatch(self, text1, text2):\n    \"\"\"Do the two texts share a substring which is at least half the length of\n    the longer text?\n    This speedup can produce non-minimal diffs.\n\n    Args:\n      text1: First string.\n      text2: Second string.\n\n    Returns:\n      Five element Array, containing the prefix of text1, the suffix of text1,\n      the prefix of text2, the suffix of text2 and the common middle.  Or None\n      if there was no match.\n    \"\"\"\n    if self.Diff_Timeout <= 0:\n      # Don't risk returning a non-optimal diff if we have unlimited time.\n      return None\n    if len(text1) > len(text2):\n      (longtext, shorttext) = (text1, text2)\n    else:\n      (shorttext, longtext) = (text1, text2)\n    if len(longtext) < 4 or len(shorttext) * 2 < len(longtext):\n      return None  # Pointless.\n\n    def diff_halfMatchI(longtext, shorttext, i):\n      \"\"\"Does a substring of shorttext exist within longtext such that the\n      substring is at least half the length of longtext?\n      Closure, but does not reference any external variables.\n\n      Args:\n        longtext: Longer string.\n        shorttext: Shorter string.\n        i: Start index of quarter length substring within longtext.\n\n      Returns:\n        Five element Array, containing the prefix of longtext, the suffix of\n        longtext, the prefix of shorttext, the suffix of shorttext and the\n        common middle.  Or None if there was no match.\n      \"\"\"\n      seed = longtext[i:i + len(longtext) // 4]\n      best_common = ''\n      j = shorttext.find(seed)\n      while j != -1:\n        prefixLength = self.diff_commonPrefix(longtext[i:], shorttext[j:])\n        suffixLength = self.diff_commonSuffix(longtext[:i], shorttext[:j])\n        if len(best_common) < suffixLength + prefixLength:\n          best_common = (shorttext[j - suffixLength:j] +\n              shorttext[j:j + prefixLength])\n          best_longtext_a = longtext[:i - suffixLength]\n          best_longtext_b = longtext[i + prefixLength:]\n          best_shorttext_a = shorttext[:j - suffixLength]\n          best_shorttext_b = shorttext[j + prefixLength:]\n        j = shorttext.find(seed, j + 1)\n\n      if len(best_common) * 2 >= len(longtext):\n        return (best_longtext_a, best_longtext_b,\n                best_shorttext_a, best_shorttext_b, best_common)\n      else:\n        return None\n\n    # First check if the second quarter is the seed for a half-match.\n    hm1 = diff_halfMatchI(longtext, shorttext, (len(longtext) + 3) // 4)\n    # Check again based on the third quarter.\n    hm2 = diff_halfMatchI(longtext, shorttext, (len(longtext) + 1) // 2)\n    if not hm1 and not hm2:\n      return None\n    elif not hm2:\n      hm = hm1\n    elif not hm1:\n      hm = hm2\n    else:\n      # Both matched.  Select the longest.\n      if len(hm1[4]) > len(hm2[4]):\n        hm = hm1\n      else:\n        hm = hm2\n\n    # A half-match was found, sort out the return data.\n    if len(text1) > len(text2):\n      (text1_a, text1_b, text2_a, text2_b, mid_common) = hm\n    else:\n      (text2_a, text2_b, text1_a, text1_b, mid_common) = hm\n    return (text1_a, text1_b, text2_a, text2_b, mid_common)\n\n  def diff_cleanupSemantic(self, diffs):\n    \"\"\"Reduce the number of edits by eliminating semantically trivial\n    equalities.\n\n    Args:\n      diffs: Array of diff tuples.\n    \"\"\"\n    changes = False\n    equalities = []  # Stack of indices where equalities are found.\n    lastEquality = None  # Always equal to diffs[equalities[-1]][1]\n    pointer = 0  # Index of current position.\n    # Number of chars that changed prior to the equality.\n    length_insertions1, length_deletions1 = 0, 0\n    # Number of chars that changed after the equality.\n    length_insertions2, length_deletions2 = 0, 0\n    while pointer < len(diffs):\n      if diffs[pointer][0] == self.DIFF_EQUAL:  # Equality found.\n        equalities.append(pointer)\n        length_insertions1, length_insertions2 = length_insertions2, 0\n        length_deletions1, length_deletions2 = length_deletions2, 0\n        lastEquality = diffs[pointer][1]\n      else:  # An insertion or deletion.\n        if diffs[pointer][0] == self.DIFF_INSERT:\n          length_insertions2 += len(diffs[pointer][1])\n        else:\n          length_deletions2 += len(diffs[pointer][1])\n        # Eliminate an equality that is smaller or equal to the edits on both\n        # sides of it.\n        if (lastEquality and (len(lastEquality) <=\n            max(length_insertions1, length_deletions1)) and\n            (len(lastEquality) <= max(length_insertions2, length_deletions2))):\n          # Duplicate record.\n          diffs.insert(equalities[-1], (self.DIFF_DELETE, lastEquality))\n          # Change second copy to insert.\n          diffs[equalities[-1] + 1] = (self.DIFF_INSERT,\n              diffs[equalities[-1] + 1][1])\n          # Throw away the equality we just deleted.\n          equalities.pop()\n          # Throw away the previous equality (it needs to be reevaluated).\n          if len(equalities):\n            equalities.pop()\n          if len(equalities):\n            pointer = equalities[-1]\n          else:\n            pointer = -1\n          # Reset the counters.\n          length_insertions1, length_deletions1 = 0, 0\n          length_insertions2, length_deletions2 = 0, 0\n          lastEquality = None\n          changes = True\n      pointer += 1\n\n    # Normalize the diff.\n    if changes:\n      self.diff_cleanupMerge(diffs)\n    self.diff_cleanupSemanticLossless(diffs)\n\n    # Find any overlaps between deletions and insertions.\n    # e.g: <del>abcxxx</del><ins>xxxdef</ins>\n    #   -> <del>abc</del>xxx<ins>def</ins>\n    # e.g: <del>xxxabc</del><ins>defxxx</ins>\n    #   -> <ins>def</ins>xxx<del>abc</del>\n    # Only extract an overlap if it is as big as the edit ahead or behind it.\n    pointer = 1\n    while pointer < len(diffs):\n      if (diffs[pointer - 1][0] == self.DIFF_DELETE and\n          diffs[pointer][0] == self.DIFF_INSERT):\n        deletion = diffs[pointer - 1][1]\n        insertion = diffs[pointer][1]\n        overlap_length1 = self.diff_commonOverlap(deletion, insertion)\n        overlap_length2 = self.diff_commonOverlap(insertion, deletion)\n        if overlap_length1 >= overlap_length2:\n          if (overlap_length1 >= len(deletion) / 2.0 or\n              overlap_length1 >= len(insertion) / 2.0):\n            # Overlap found.  Insert an equality and trim the surrounding edits.\n            diffs.insert(pointer, (self.DIFF_EQUAL,\n                                   insertion[:overlap_length1]))\n            diffs[pointer - 1] = (self.DIFF_DELETE,\n                                  deletion[:len(deletion) - overlap_length1])\n            diffs[pointer + 1] = (self.DIFF_INSERT,\n                                  insertion[overlap_length1:])\n            pointer += 1\n        else:\n          if (overlap_length2 >= len(deletion) / 2.0 or\n              overlap_length2 >= len(insertion) / 2.0):\n            # Reverse overlap found.\n            # Insert an equality and swap and trim the surrounding edits.\n            diffs.insert(pointer, (self.DIFF_EQUAL, deletion[:overlap_length2]))\n            diffs[pointer - 1] = (self.DIFF_INSERT,\n                                  insertion[:len(insertion) - overlap_length2])\n            diffs[pointer + 1] = (self.DIFF_DELETE, deletion[overlap_length2:])\n            pointer += 1\n        pointer += 1\n      pointer += 1\n\n  def diff_cleanupSemanticLossless(self, diffs):\n    \"\"\"Look for single edits surrounded on both sides by equalities\n    which can be shifted sideways to align the edit to a word boundary.\n    e.g: The c<ins>at c</ins>ame. -> The <ins>cat </ins>came.\n\n    Args:\n      diffs: Array of diff tuples.\n    \"\"\"\n\n    def diff_cleanupSemanticScore(one, two):\n      \"\"\"Given two strings, compute a score representing whether the\n      internal boundary falls on logical boundaries.\n      Scores range from 6 (best) to 0 (worst).\n      Closure, but does not reference any external variables.\n\n      Args:\n        one: First string.\n        two: Second string.\n\n      Returns:\n        The score.\n      \"\"\"\n      if not one or not two:\n        # Edges are the best.\n        return 6\n\n      # Each port of this function behaves slightly differently due to\n      # subtle differences in each language's definition of things like\n      # 'whitespace'.  Since this function's purpose is largely cosmetic,\n      # the choice has been made to use each language's native features\n      # rather than force total conformity.\n      char1 = one[-1]\n      char2 = two[0]\n      nonAlphaNumeric1 = not char1.isalnum()\n      nonAlphaNumeric2 = not char2.isalnum()\n      whitespace1 = nonAlphaNumeric1 and char1.isspace()\n      whitespace2 = nonAlphaNumeric2 and char2.isspace()\n      lineBreak1 = whitespace1 and (char1 == \"\\r\" or char1 == \"\\n\")\n      lineBreak2 = whitespace2 and (char2 == \"\\r\" or char2 == \"\\n\")\n      blankLine1 = lineBreak1 and self.BLANKLINEEND.search(one)\n      blankLine2 = lineBreak2 and self.BLANKLINESTART.match(two)\n\n      if blankLine1 or blankLine2:\n        # Five points for blank lines.\n        return 5\n      elif lineBreak1 or lineBreak2:\n        # Four points for line breaks.\n        return 4\n      elif nonAlphaNumeric1 and not whitespace1 and whitespace2:\n        # Three points for end of sentences.\n        return 3\n      elif whitespace1 or whitespace2:\n        # Two points for whitespace.\n        return 2\n      elif nonAlphaNumeric1 or nonAlphaNumeric2:\n        # One point for non-alphanumeric.\n        return 1\n      return 0\n\n    pointer = 1\n    # Intentionally ignore the first and last element (don't need checking).\n    while pointer < len(diffs) - 1:\n      if (diffs[pointer - 1][0] == self.DIFF_EQUAL and\n          diffs[pointer + 1][0] == self.DIFF_EQUAL):\n        # This is a single edit surrounded by equalities.\n        equality1 = diffs[pointer - 1][1]\n        edit = diffs[pointer][1]\n        equality2 = diffs[pointer + 1][1]\n\n        # First, shift the edit as far left as possible.\n        commonOffset = self.diff_commonSuffix(equality1, edit)\n        if commonOffset:\n          commonString = edit[-commonOffset:]\n          equality1 = equality1[:-commonOffset]\n          edit = commonString + edit[:-commonOffset]\n          equality2 = commonString + equality2\n\n        # Second, step character by character right, looking for the best fit.\n        bestEquality1 = equality1\n        bestEdit = edit\n        bestEquality2 = equality2\n        bestScore = (diff_cleanupSemanticScore(equality1, edit) +\n            diff_cleanupSemanticScore(edit, equality2))\n        while edit and equality2 and edit[0] == equality2[0]:\n          equality1 += edit[0]\n          edit = edit[1:] + equality2[0]\n          equality2 = equality2[1:]\n          score = (diff_cleanupSemanticScore(equality1, edit) +\n              diff_cleanupSemanticScore(edit, equality2))\n          # The >= encourages trailing rather than leading whitespace on edits.\n          if score >= bestScore:\n            bestScore = score\n            bestEquality1 = equality1\n            bestEdit = edit\n            bestEquality2 = equality2\n\n        if diffs[pointer - 1][1] != bestEquality1:\n          # We have an improvement, save it back to the diff.\n          if bestEquality1:\n            diffs[pointer - 1] = (diffs[pointer - 1][0], bestEquality1)\n          else:\n            del diffs[pointer - 1]\n            pointer -= 1\n          diffs[pointer] = (diffs[pointer][0], bestEdit)\n          if bestEquality2:\n            diffs[pointer + 1] = (diffs[pointer + 1][0], bestEquality2)\n          else:\n            del diffs[pointer + 1]\n            pointer -= 1\n      pointer += 1\n\n  # Define some regex patterns for matching boundaries.\n  BLANKLINEEND = re.compile(r\"\\n\\r?\\n$\")\n  BLANKLINESTART = re.compile(r\"^\\r?\\n\\r?\\n\")\n\n  def diff_cleanupEfficiency(self, diffs):\n    \"\"\"Reduce the number of edits by eliminating operationally trivial\n    equalities.\n\n    Args:\n      diffs: Array of diff tuples.\n    \"\"\"\n    changes = False\n    equalities = []  # Stack of indices where equalities are found.\n    lastEquality = None  # Always equal to diffs[equalities[-1]][1]\n    pointer = 0  # Index of current position.\n    pre_ins = False  # Is there an insertion operation before the last equality.\n    pre_del = False  # Is there a deletion operation before the last equality.\n    post_ins = False  # Is there an insertion operation after the last equality.\n    post_del = False  # Is there a deletion operation after the last equality.\n    while pointer < len(diffs):\n      if diffs[pointer][0] == self.DIFF_EQUAL:  # Equality found.\n        if (len(diffs[pointer][1]) < self.Diff_EditCost and\n            (post_ins or post_del)):\n          # Candidate found.\n          equalities.append(pointer)\n          pre_ins = post_ins\n          pre_del = post_del\n          lastEquality = diffs[pointer][1]\n        else:\n          # Not a candidate, and can never become one.\n          equalities = []\n          lastEquality = None\n\n        post_ins = post_del = False\n      else:  # An insertion or deletion.\n        if diffs[pointer][0] == self.DIFF_DELETE:\n          post_del = True\n        else:\n          post_ins = True\n\n        # Five types to be split:\n        # <ins>A</ins><del>B</del>XY<ins>C</ins><del>D</del>\n        # <ins>A</ins>X<ins>C</ins><del>D</del>\n        # <ins>A</ins><del>B</del>X<ins>C</ins>\n        # <ins>A</del>X<ins>C</ins><del>D</del>\n        # <ins>A</ins><del>B</del>X<del>C</del>\n\n        if lastEquality and ((pre_ins and pre_del and post_ins and post_del) or\n                             ((len(lastEquality) < self.Diff_EditCost / 2) and\n                              (pre_ins + pre_del + post_ins + post_del) == 3)):\n          # Duplicate record.\n          diffs.insert(equalities[-1], (self.DIFF_DELETE, lastEquality))\n          # Change second copy to insert.\n          diffs[equalities[-1] + 1] = (self.DIFF_INSERT,\n              diffs[equalities[-1] + 1][1])\n          equalities.pop()  # Throw away the equality we just deleted.\n          lastEquality = None\n          if pre_ins and pre_del:\n            # No changes made which could affect previous entry, keep going.\n            post_ins = post_del = True\n            equalities = []\n          else:\n            if len(equalities):\n              equalities.pop()  # Throw away the previous equality.\n            if len(equalities):\n              pointer = equalities[-1]\n            else:\n              pointer = -1\n            post_ins = post_del = False\n          changes = True\n      pointer += 1\n\n    if changes:\n      self.diff_cleanupMerge(diffs)\n\n  def diff_cleanupMerge(self, diffs):\n    \"\"\"Reorder and merge like edit sections.  Merge equalities.\n    Any edit section can move as long as it doesn't cross an equality.\n\n    Args:\n      diffs: Array of diff tuples.\n    \"\"\"\n    diffs.append((self.DIFF_EQUAL, ''))  # Add a dummy entry at the end.\n    pointer = 0\n    count_delete = 0\n    count_insert = 0\n    text_delete = ''\n    text_insert = ''\n    while pointer < len(diffs):\n      if diffs[pointer][0] == self.DIFF_INSERT:\n        count_insert += 1\n        text_insert += diffs[pointer][1]\n        pointer += 1\n      elif diffs[pointer][0] == self.DIFF_DELETE:\n        count_delete += 1\n        text_delete += diffs[pointer][1]\n        pointer += 1\n      elif diffs[pointer][0] == self.DIFF_EQUAL:\n        # Upon reaching an equality, check for prior redundancies.\n        if count_delete + count_insert > 1:\n          if count_delete != 0 and count_insert != 0:\n            # Factor out any common prefixies.\n            commonlength = self.diff_commonPrefix(text_insert, text_delete)\n            if commonlength != 0:\n              x = pointer - count_delete - count_insert - 1\n              if x >= 0 and diffs[x][0] == self.DIFF_EQUAL:\n                diffs[x] = (diffs[x][0], diffs[x][1] +\n                            text_insert[:commonlength])\n              else:\n                diffs.insert(0, (self.DIFF_EQUAL, text_insert[:commonlength]))\n                pointer += 1\n              text_insert = text_insert[commonlength:]\n              text_delete = text_delete[commonlength:]\n            # Factor out any common suffixies.\n            commonlength = self.diff_commonSuffix(text_insert, text_delete)\n            if commonlength != 0:\n              diffs[pointer] = (diffs[pointer][0], text_insert[-commonlength:] +\n                  diffs[pointer][1])\n              text_insert = text_insert[:-commonlength]\n              text_delete = text_delete[:-commonlength]\n          # Delete the offending records and add the merged ones.\n          new_ops = []\n          if len(text_delete) != 0:\n            new_ops.append((self.DIFF_DELETE, text_delete))\n          if len(text_insert) != 0:\n            new_ops.append((self.DIFF_INSERT, text_insert))\n          pointer -= count_delete + count_insert\n          diffs[pointer : pointer + count_delete + count_insert] = new_ops\n          pointer += len(new_ops) + 1\n        elif pointer != 0 and diffs[pointer - 1][0] == self.DIFF_EQUAL:\n          # Merge this equality with the previous one.\n          diffs[pointer - 1] = (diffs[pointer - 1][0],\n                                diffs[pointer - 1][1] + diffs[pointer][1])\n          del diffs[pointer]\n        else:\n          pointer += 1\n\n        count_insert = 0\n        count_delete = 0\n        text_delete = ''\n        text_insert = ''\n\n    if diffs[-1][1] == '':\n      diffs.pop()  # Remove the dummy entry at the end.\n\n    # Second pass: look for single edits surrounded on both sides by equalities\n    # which can be shifted sideways to eliminate an equality.\n    # e.g: A<ins>BA</ins>C -> <ins>AB</ins>AC\n    changes = False\n    pointer = 1\n    # Intentionally ignore the first and last element (don't need checking).\n    while pointer < len(diffs) - 1:\n      if (diffs[pointer - 1][0] == self.DIFF_EQUAL and\n          diffs[pointer + 1][0] == self.DIFF_EQUAL):\n        # This is a single edit surrounded by equalities.\n        if diffs[pointer][1].endswith(diffs[pointer - 1][1]):\n          # Shift the edit over the previous equality.\n          if diffs[pointer - 1][1] != \"\":\n            diffs[pointer] = (diffs[pointer][0],\n                diffs[pointer - 1][1] +\n                diffs[pointer][1][:-len(diffs[pointer - 1][1])])\n            diffs[pointer + 1] = (diffs[pointer + 1][0],\n                                  diffs[pointer - 1][1] + diffs[pointer + 1][1])\n          del diffs[pointer - 1]\n          changes = True\n        elif diffs[pointer][1].startswith(diffs[pointer + 1][1]):\n          # Shift the edit over the next equality.\n          diffs[pointer - 1] = (diffs[pointer - 1][0],\n                                diffs[pointer - 1][1] + diffs[pointer + 1][1])\n          diffs[pointer] = (diffs[pointer][0],\n              diffs[pointer][1][len(diffs[pointer + 1][1]):] +\n              diffs[pointer + 1][1])\n          del diffs[pointer + 1]\n          changes = True\n      pointer += 1\n\n    # If shifts were made, the diff needs reordering and another shift sweep.\n    if changes:\n      self.diff_cleanupMerge(diffs)\n\n  def diff_xIndex(self, diffs, loc):\n    \"\"\"loc is a location in text1, compute and return the equivalent location\n    in text2.  e.g. \"The cat\" vs \"The big cat\", 1->1, 5->8\n\n    Args:\n      diffs: Array of diff tuples.\n      loc: Location within text1.\n\n    Returns:\n      Location within text2.\n    \"\"\"\n    chars1 = 0\n    chars2 = 0\n    last_chars1 = 0\n    last_chars2 = 0\n    for x in xrange(len(diffs)):\n      (op, text) = diffs[x]\n      if op != self.DIFF_INSERT:  # Equality or deletion.\n        chars1 += len(text)\n      if op != self.DIFF_DELETE:  # Equality or insertion.\n        chars2 += len(text)\n      if chars1 > loc:  # Overshot the location.\n        break\n      last_chars1 = chars1\n      last_chars2 = chars2\n\n    if len(diffs) != x and diffs[x][0] == self.DIFF_DELETE:\n      # The location was deleted.\n      return last_chars2\n    # Add the remaining len(character).\n    return last_chars2 + (loc - last_chars1)\n\n  def diff_prettyHtml(self, diffs):\n    \"\"\"Convert a diff array into a pretty HTML report.\n\n    Args:\n      diffs: Array of diff tuples.\n\n    Returns:\n      HTML representation.\n    \"\"\"\n    html = []\n    for (op, data) in diffs:\n      text = (data.replace(\"&\", \"&amp;\").replace(\"<\", \"&lt;\")\n                 .replace(\">\", \"&gt;\").replace(\"\\n\", \"&para;<br>\"))\n      if op == self.DIFF_INSERT:\n        html.append(\"<ins style=\\\"background:#e6ffe6;\\\">%s</ins>\" % text)\n      elif op == self.DIFF_DELETE:\n        html.append(\"<del style=\\\"background:#ffe6e6;\\\">%s</del>\" % text)\n      elif op == self.DIFF_EQUAL:\n        html.append(\"<span>%s</span>\" % text)\n    return \"\".join(html)\n\n  def diff_text1(self, diffs):\n    \"\"\"Compute and return the source text (all equalities and deletions).\n\n    Args:\n      diffs: Array of diff tuples.\n\n    Returns:\n      Source text.\n    \"\"\"\n    text = []\n    for (op, data) in diffs:\n      if op != self.DIFF_INSERT:\n        text.append(data)\n    return \"\".join(text)\n\n  def diff_text2(self, diffs):\n    \"\"\"Compute and return the destination text (all equalities and insertions).\n\n    Args:\n      diffs: Array of diff tuples.\n\n    Returns:\n      Destination text.\n    \"\"\"\n    text = []\n    for (op, data) in diffs:\n      if op != self.DIFF_DELETE:\n        text.append(data)\n    return \"\".join(text)\n\n  def diff_levenshtein(self, diffs):\n    \"\"\"Compute the Levenshtein distance; the number of inserted, deleted or\n    substituted characters.\n\n    Args:\n      diffs: Array of diff tuples.\n\n    Returns:\n      Number of changes.\n    \"\"\"\n    levenshtein = 0\n    insertions = 0\n    deletions = 0\n    for (op, data) in diffs:\n      if op == self.DIFF_INSERT:\n        insertions += len(data)\n      elif op == self.DIFF_DELETE:\n        deletions += len(data)\n      elif op == self.DIFF_EQUAL:\n        # A deletion and an insertion is one substitution.\n        levenshtein += max(insertions, deletions)\n        insertions = 0\n        deletions = 0\n    levenshtein += max(insertions, deletions)\n    return levenshtein\n\n  def diff_toDelta(self, diffs):\n    \"\"\"Crush the diff into an encoded string which describes the operations\n    required to transform text1 into text2.\n    E.g. =3\\t-2\\t+ing  -> Keep 3 chars, delete 2 chars, insert 'ing'.\n    Operations are tab-separated.  Inserted text is escaped using %xx notation.\n\n    Args:\n      diffs: Array of diff tuples.\n\n    Returns:\n      Delta text.\n    \"\"\"\n    text = []\n    for (op, data) in diffs:\n      if op == self.DIFF_INSERT:\n        # High ascii will raise UnicodeDecodeError.  Use Unicode instead.\n        data = data.encode(\"utf-8\")\n        text.append(\"+\" + urllib.quote(data, \"!~*'();/?:@&=+$,# \"))\n      elif op == self.DIFF_DELETE:\n        text.append(\"-%d\" % len(data))\n      elif op == self.DIFF_EQUAL:\n        text.append(\"=%d\" % len(data))\n    return \"\\t\".join(text)\n\n  def diff_fromDelta(self, text1, delta):\n    \"\"\"Given the original text1, and an encoded string which describes the\n    operations required to transform text1 into text2, compute the full diff.\n\n    Args:\n      text1: Source string for the diff.\n      delta: Delta text.\n\n    Returns:\n      Array of diff tuples.\n\n    Raises:\n      ValueError: If invalid input.\n    \"\"\"\n    if type(delta) == unicode:\n      # Deltas should be composed of a subset of ascii chars, Unicode not\n      # required.  If this encode raises UnicodeEncodeError, delta is invalid.\n      delta = delta.encode(\"ascii\")\n    diffs = []\n    pointer = 0  # Cursor in text1\n    tokens = delta.split(\"\\t\")\n    for token in tokens:\n      if token == \"\":\n        # Blank tokens are ok (from a trailing \\t).\n        continue\n      # Each token begins with a one character parameter which specifies the\n      # operation of this token (delete, insert, equality).\n      param = token[1:]\n      if token[0] == \"+\":\n        param = urllib.unquote(param).decode(\"utf-8\")\n        diffs.append((self.DIFF_INSERT, param))\n      elif token[0] == \"-\" or token[0] == \"=\":\n        try:\n          n = int(param)\n        except ValueError:\n          raise ValueError(\"Invalid number in diff_fromDelta: \" + param)\n        if n < 0:\n          raise ValueError(\"Negative number in diff_fromDelta: \" + param)\n        text = text1[pointer : pointer + n]\n        pointer += n\n        if token[0] == \"=\":\n          diffs.append((self.DIFF_EQUAL, text))\n        else:\n          diffs.append((self.DIFF_DELETE, text))\n      else:\n        # Anything else is an error.\n        raise ValueError(\"Invalid diff operation in diff_fromDelta: \" +\n            token[0])\n    if pointer != len(text1):\n      raise ValueError(\n          \"Delta length (%d) does not equal source text length (%d).\" %\n         (pointer, len(text1)))\n    return diffs\n\n  #  MATCH FUNCTIONS\n\n  def match_main(self, text, pattern, loc):\n    \"\"\"Locate the best instance of 'pattern' in 'text' near 'loc'.\n\n    Args:\n      text: The text to search.\n      pattern: The pattern to search for.\n      loc: The location to search around.\n\n    Returns:\n      Best match index or -1.\n    \"\"\"\n    # Check for null inputs.\n    if text == None or pattern == None:\n      raise ValueError(\"Null inputs. (match_main)\")\n\n    loc = max(0, min(loc, len(text)))\n    if text == pattern:\n      # Shortcut (potentially not guaranteed by the algorithm)\n      return 0\n    elif not text:\n      # Nothing to match.\n      return -1\n    elif text[loc:loc + len(pattern)] == pattern:\n      # Perfect match at the perfect spot!  (Includes case of null pattern)\n      return loc\n    else:\n      # Do a fuzzy compare.\n      match = self.match_bitap(text, pattern, loc)\n      return match\n\n  def match_bitap(self, text, pattern, loc):\n    \"\"\"Locate the best instance of 'pattern' in 'text' near 'loc' using the\n    Bitap algorithm.\n\n    Args:\n      text: The text to search.\n      pattern: The pattern to search for.\n      loc: The location to search around.\n\n    Returns:\n      Best match index or -1.\n    \"\"\"\n    # Python doesn't have a maxint limit, so ignore this check.\n    #if self.Match_MaxBits != 0 and len(pattern) > self.Match_MaxBits:\n    #  raise ValueError(\"Pattern too long for this application.\")\n\n    # Initialise the alphabet.\n    s = self.match_alphabet(pattern)\n\n    def match_bitapScore(e, x):\n      \"\"\"Compute and return the score for a match with e errors and x location.\n      Accesses loc and pattern through being a closure.\n\n      Args:\n        e: Number of errors in match.\n        x: Location of match.\n\n      Returns:\n        Overall score for match (0.0 = good, 1.0 = bad).\n      \"\"\"\n      accuracy = float(e) / len(pattern)\n      proximity = abs(loc - x)\n      if not self.Match_Distance:\n        # Dodge divide by zero error.\n        return proximity and 1.0 or accuracy\n      return accuracy + (proximity / float(self.Match_Distance))\n\n    # Highest score beyond which we give up.\n    score_threshold = self.Match_Threshold\n    # Is there a nearby exact match? (speedup)\n    best_loc = text.find(pattern, loc)\n    if best_loc != -1:\n      score_threshold = min(match_bitapScore(0, best_loc), score_threshold)\n      # What about in the other direction? (speedup)\n      best_loc = text.rfind(pattern, loc + len(pattern))\n      if best_loc != -1:\n        score_threshold = min(match_bitapScore(0, best_loc), score_threshold)\n\n    # Initialise the bit arrays.\n    matchmask = 1 << (len(pattern) - 1)\n    best_loc = -1\n\n    bin_max = len(pattern) + len(text)\n    # Empty initialization added to appease pychecker.\n    last_rd = None\n    for d in xrange(len(pattern)):\n      # Scan for the best match each iteration allows for one more error.\n      # Run a binary search to determine how far from 'loc' we can stray at\n      # this error level.\n      bin_min = 0\n      bin_mid = bin_max\n      while bin_min < bin_mid:\n        if match_bitapScore(d, loc + bin_mid) <= score_threshold:\n          bin_min = bin_mid\n        else:\n          bin_max = bin_mid\n        bin_mid = (bin_max - bin_min) // 2 + bin_min\n\n      # Use the result from this iteration as the maximum for the next.\n      bin_max = bin_mid\n      start = max(1, loc - bin_mid + 1)\n      finish = min(loc + bin_mid, len(text)) + len(pattern)\n\n      rd = [0] * (finish + 2)\n      rd[finish + 1] = (1 << d) - 1\n      for j in xrange(finish, start - 1, -1):\n        if len(text) <= j - 1:\n          # Out of range.\n          charMatch = 0\n        else:\n          charMatch = s.get(text[j - 1], 0)\n        if d == 0:  # First pass: exact match.\n          rd[j] = ((rd[j + 1] << 1) | 1) & charMatch\n        else:  # Subsequent passes: fuzzy match.\n          rd[j] = (((rd[j + 1] << 1) | 1) & charMatch) | (\n              ((last_rd[j + 1] | last_rd[j]) << 1) | 1) | last_rd[j + 1]\n        if rd[j] & matchmask:\n          score = match_bitapScore(d, j - 1)\n          # This match will almost certainly be better than any existing match.\n          # But check anyway.\n          if score <= score_threshold:\n            # Told you so.\n            score_threshold = score\n            best_loc = j - 1\n            if best_loc > loc:\n              # When passing loc, don't exceed our current distance from loc.\n              start = max(1, 2 * loc - best_loc)\n            else:\n              # Already passed loc, downhill from here on in.\n              break\n      # No hope for a (better) match at greater error levels.\n      if match_bitapScore(d + 1, loc) > score_threshold:\n        break\n      last_rd = rd\n    return best_loc\n\n  def match_alphabet(self, pattern):\n    \"\"\"Initialise the alphabet for the Bitap algorithm.\n\n    Args:\n      pattern: The text to encode.\n\n    Returns:\n      Hash of character locations.\n    \"\"\"\n    s = {}\n    for char in pattern:\n      s[char] = 0\n    for i in xrange(len(pattern)):\n      s[pattern[i]] |= 1 << (len(pattern) - i - 1)\n    return s\n\n  #  PATCH FUNCTIONS\n\n  def patch_addContext(self, patch, text):\n    \"\"\"Increase the context until it is unique,\n    but don't let the pattern expand beyond Match_MaxBits.\n\n    Args:\n      patch: The patch to grow.\n      text: Source text.\n    \"\"\"\n    if len(text) == 0:\n      return\n    pattern = text[patch.start2 : patch.start2 + patch.length1]\n    padding = 0\n\n    # Look for the first and last matches of pattern in text.  If two different\n    # matches are found, increase the pattern length.\n    while (text.find(pattern) != text.rfind(pattern) and (self.Match_MaxBits ==\n        0 or len(pattern) < self.Match_MaxBits - self.Patch_Margin -\n        self.Patch_Margin)):\n      padding += self.Patch_Margin\n      pattern = text[max(0, patch.start2 - padding) :\n                     patch.start2 + patch.length1 + padding]\n    # Add one chunk for good luck.\n    padding += self.Patch_Margin\n\n    # Add the prefix.\n    prefix = text[max(0, patch.start2 - padding) : patch.start2]\n    if prefix:\n      patch.diffs[:0] = [(self.DIFF_EQUAL, prefix)]\n    # Add the suffix.\n    suffix = text[patch.start2 + patch.length1 :\n                  patch.start2 + patch.length1 + padding]\n    if suffix:\n      patch.diffs.append((self.DIFF_EQUAL, suffix))\n\n    # Roll back the start points.\n    patch.start1 -= len(prefix)\n    patch.start2 -= len(prefix)\n    # Extend lengths.\n    patch.length1 += len(prefix) + len(suffix)\n    patch.length2 += len(prefix) + len(suffix)\n\n  def patch_make(self, a, b=None, c=None):\n    \"\"\"Compute a list of patches to turn text1 into text2.\n    Use diffs if provided, otherwise compute it ourselves.\n    There are four ways to call this function, depending on what data is\n    available to the caller:\n    Method 1:\n    a = text1, b = text2\n    Method 2:\n    a = diffs\n    Method 3 (optimal):\n    a = text1, b = diffs\n    Method 4 (deprecated, use method 3):\n    a = text1, b = text2, c = diffs\n\n    Args:\n      a: text1 (methods 1,3,4) or Array of diff tuples for text1 to\n          text2 (method 2).\n      b: text2 (methods 1,4) or Array of diff tuples for text1 to\n          text2 (method 3) or undefined (method 2).\n      c: Array of diff tuples for text1 to text2 (method 4) or\n          undefined (methods 1,2,3).\n\n    Returns:\n      Array of Patch objects.\n    \"\"\"\n    text1 = None\n    diffs = None\n    # Note that texts may arrive as 'str' or 'unicode'.\n    if isinstance(a, basestring) and isinstance(b, basestring) and c is None:\n      # Method 1: text1, text2\n      # Compute diffs from text1 and text2.\n      text1 = a\n      diffs = self.diff_main(text1, b, True)\n      if len(diffs) > 2:\n        self.diff_cleanupSemantic(diffs)\n        self.diff_cleanupEfficiency(diffs)\n    elif isinstance(a, list) and b is None and c is None:\n      # Method 2: diffs\n      # Compute text1 from diffs.\n      diffs = a\n      text1 = self.diff_text1(diffs)\n    elif isinstance(a, basestring) and isinstance(b, list) and c is None:\n      # Method 3: text1, diffs\n      text1 = a\n      diffs = b\n    elif (isinstance(a, basestring) and isinstance(b, basestring) and\n          isinstance(c, list)):\n      # Method 4: text1, text2, diffs\n      # text2 is not used.\n      text1 = a\n      diffs = c\n    else:\n      raise ValueError(\"Unknown call format to patch_make.\")\n\n    if not diffs:\n      return []  # Get rid of the None case.\n    patches = []\n    patch = patch_obj()\n    char_count1 = 0  # Number of characters into the text1 string.\n    char_count2 = 0  # Number of characters into the text2 string.\n    prepatch_text = text1  # Recreate the patches to determine context info.\n    postpatch_text = text1\n    for x in xrange(len(diffs)):\n      (diff_type, diff_text) = diffs[x]\n      if len(patch.diffs) == 0 and diff_type != self.DIFF_EQUAL:\n        # A new patch starts here.\n        patch.start1 = char_count1\n        patch.start2 = char_count2\n      if diff_type == self.DIFF_INSERT:\n        # Insertion\n        patch.diffs.append(diffs[x])\n        patch.length2 += len(diff_text)\n        postpatch_text = (postpatch_text[:char_count2] + diff_text +\n                          postpatch_text[char_count2:])\n      elif diff_type == self.DIFF_DELETE:\n        # Deletion.\n        patch.length1 += len(diff_text)\n        patch.diffs.append(diffs[x])\n        postpatch_text = (postpatch_text[:char_count2] +\n                          postpatch_text[char_count2 + len(diff_text):])\n      elif (diff_type == self.DIFF_EQUAL and\n            len(diff_text) <= 2 * self.Patch_Margin and\n            len(patch.diffs) != 0 and len(diffs) != x + 1):\n        # Small equality inside a patch.\n        patch.diffs.append(diffs[x])\n        patch.length1 += len(diff_text)\n        patch.length2 += len(diff_text)\n\n      if (diff_type == self.DIFF_EQUAL and\n          len(diff_text) >= 2 * self.Patch_Margin):\n        # Time for a new patch.\n        if len(patch.diffs) != 0:\n          self.patch_addContext(patch, prepatch_text)\n          patches.append(patch)\n          patch = patch_obj()\n          # Unlike Unidiff, our patch lists have a rolling context.\n          # https://github.com/google/diff-match-patch/wiki/Unidiff\n          # Update prepatch text & pos to reflect the application of the\n          # just completed patch.\n          prepatch_text = postpatch_text\n          char_count1 = char_count2\n\n      # Update the current character count.\n      if diff_type != self.DIFF_INSERT:\n        char_count1 += len(diff_text)\n      if diff_type != self.DIFF_DELETE:\n        char_count2 += len(diff_text)\n\n    # Pick up the leftover patch if not empty.\n    if len(patch.diffs) != 0:\n      self.patch_addContext(patch, prepatch_text)\n      patches.append(patch)\n    return patches\n\n  def patch_deepCopy(self, patches):\n    \"\"\"Given an array of patches, return another array that is identical.\n\n    Args:\n      patches: Array of Patch objects.\n\n    Returns:\n      Array of Patch objects.\n    \"\"\"\n    patchesCopy = []\n    for patch in patches:\n      patchCopy = patch_obj()\n      # No need to deep copy the tuples since they are immutable.\n      patchCopy.diffs = patch.diffs[:]\n      patchCopy.start1 = patch.start1\n      patchCopy.start2 = patch.start2\n      patchCopy.length1 = patch.length1\n      patchCopy.length2 = patch.length2\n      patchesCopy.append(patchCopy)\n    return patchesCopy\n\n  def patch_apply(self, patches, text):\n    \"\"\"Merge a set of patches onto the text.  Return a patched text, as well\n    as a list of true/false values indicating which patches were applied.\n\n    Args:\n      patches: Array of Patch objects.\n      text: Old text.\n\n    Returns:\n      Two element Array, containing the new text and an array of boolean values.\n    \"\"\"\n    if not patches:\n      return (text, [])\n\n    # Deep copy the patches so that no changes are made to originals.\n    patches = self.patch_deepCopy(patches)\n\n    nullPadding = self.patch_addPadding(patches)\n    text = nullPadding + text + nullPadding\n    self.patch_splitMax(patches)\n\n    # delta keeps track of the offset between the expected and actual location\n    # of the previous patch.  If there are patches expected at positions 10 and\n    # 20, but the first patch was found at 12, delta is 2 and the second patch\n    # has an effective expected position of 22.\n    delta = 0\n    results = []\n    for patch in patches:\n      expected_loc = patch.start2 + delta\n      text1 = self.diff_text1(patch.diffs)\n      end_loc = -1\n      if len(text1) > self.Match_MaxBits:\n        # patch_splitMax will only provide an oversized pattern in the case of\n        # a monster delete.\n        start_loc = self.match_main(text, text1[:self.Match_MaxBits],\n                                    expected_loc)\n        if start_loc != -1:\n          end_loc = self.match_main(text, text1[-self.Match_MaxBits:],\n              expected_loc + len(text1) - self.Match_MaxBits)\n          if end_loc == -1 or start_loc >= end_loc:\n            # Can't find valid trailing context.  Drop this patch.\n            start_loc = -1\n      else:\n        start_loc = self.match_main(text, text1, expected_loc)\n      if start_loc == -1:\n        # No match found.  :(\n        results.append(False)\n        # Subtract the delta for this failed patch from subsequent patches.\n        delta -= patch.length2 - patch.length1\n      else:\n        # Found a match.  :)\n        results.append(True)\n        delta = start_loc - expected_loc\n        if end_loc == -1:\n          text2 = text[start_loc : start_loc + len(text1)]\n        else:\n          text2 = text[start_loc : end_loc + self.Match_MaxBits]\n        if text1 == text2:\n          # Perfect match, just shove the replacement text in.\n          text = (text[:start_loc] + self.diff_text2(patch.diffs) +\n                      text[start_loc + len(text1):])\n        else:\n          # Imperfect match.\n          # Run a diff to get a framework of equivalent indices.\n          diffs = self.diff_main(text1, text2, False)\n          if (len(text1) > self.Match_MaxBits and\n              self.diff_levenshtein(diffs) / float(len(text1)) >\n              self.Patch_DeleteThreshold):\n            # The end points match, but the content is unacceptably bad.\n            results[-1] = False\n          else:\n            self.diff_cleanupSemanticLossless(diffs)\n            index1 = 0\n            for (op, data) in patch.diffs:\n              if op != self.DIFF_EQUAL:\n                index2 = self.diff_xIndex(diffs, index1)\n              if op == self.DIFF_INSERT:  # Insertion\n                text = text[:start_loc + index2] + data + text[start_loc +\n                                                               index2:]\n              elif op == self.DIFF_DELETE:  # Deletion\n                text = text[:start_loc + index2] + text[start_loc +\n                    self.diff_xIndex(diffs, index1 + len(data)):]\n              if op != self.DIFF_DELETE:\n                index1 += len(data)\n    # Strip the padding off.\n    text = text[len(nullPadding):-len(nullPadding)]\n    return (text, results)\n\n  def patch_addPadding(self, patches):\n    \"\"\"Add some padding on text start and end so that edges can match\n    something.  Intended to be called only from within patch_apply.\n\n    Args:\n      patches: Array of Patch objects.\n\n    Returns:\n      The padding string added to each side.\n    \"\"\"\n    paddingLength = self.Patch_Margin\n    nullPadding = \"\"\n    for x in xrange(1, paddingLength + 1):\n      nullPadding += chr(x)\n\n    # Bump all the patches forward.\n    for patch in patches:\n      patch.start1 += paddingLength\n      patch.start2 += paddingLength\n\n    # Add some padding on start of first diff.\n    patch = patches[0]\n    diffs = patch.diffs\n    if not diffs or diffs[0][0] != self.DIFF_EQUAL:\n      # Add nullPadding equality.\n      diffs.insert(0, (self.DIFF_EQUAL, nullPadding))\n      patch.start1 -= paddingLength  # Should be 0.\n      patch.start2 -= paddingLength  # Should be 0.\n      patch.length1 += paddingLength\n      patch.length2 += paddingLength\n    elif paddingLength > len(diffs[0][1]):\n      # Grow first equality.\n      extraLength = paddingLength - len(diffs[0][1])\n      newText = nullPadding[len(diffs[0][1]):] + diffs[0][1]\n      diffs[0] = (diffs[0][0], newText)\n      patch.start1 -= extraLength\n      patch.start2 -= extraLength\n      patch.length1 += extraLength\n      patch.length2 += extraLength\n\n    # Add some padding on end of last diff.\n    patch = patches[-1]\n    diffs = patch.diffs\n    if not diffs or diffs[-1][0] != self.DIFF_EQUAL:\n      # Add nullPadding equality.\n      diffs.append((self.DIFF_EQUAL, nullPadding))\n      patch.length1 += paddingLength\n      patch.length2 += paddingLength\n    elif paddingLength > len(diffs[-1][1]):\n      # Grow last equality.\n      extraLength = paddingLength - len(diffs[-1][1])\n      newText = diffs[-1][1] + nullPadding[:extraLength]\n      diffs[-1] = (diffs[-1][0], newText)\n      patch.length1 += extraLength\n      patch.length2 += extraLength\n\n    return nullPadding\n\n  def patch_splitMax(self, patches):\n    \"\"\"Look through the patches and break up any which are longer than the\n    maximum limit of the match algorithm.\n    Intended to be called only from within patch_apply.\n\n    Args:\n      patches: Array of Patch objects.\n    \"\"\"\n    patch_size = self.Match_MaxBits\n    if patch_size == 0:\n      # Python has the option of not splitting strings due to its ability\n      # to handle integers of arbitrary precision.\n      return\n    for x in xrange(len(patches)):\n      if patches[x].length1 <= patch_size:\n        continue\n      bigpatch = patches[x]\n      # Remove the big old patch.\n      del patches[x]\n      x -= 1\n      start1 = bigpatch.start1\n      start2 = bigpatch.start2\n      precontext = ''\n      while len(bigpatch.diffs) != 0:\n        # Create one of several smaller patches.\n        patch = patch_obj()\n        empty = True\n        patch.start1 = start1 - len(precontext)\n        patch.start2 = start2 - len(precontext)\n        if precontext:\n          patch.length1 = patch.length2 = len(precontext)\n          patch.diffs.append((self.DIFF_EQUAL, precontext))\n\n        while (len(bigpatch.diffs) != 0 and\n               patch.length1 < patch_size - self.Patch_Margin):\n          (diff_type, diff_text) = bigpatch.diffs[0]\n          if diff_type == self.DIFF_INSERT:\n            # Insertions are harmless.\n            patch.length2 += len(diff_text)\n            start2 += len(diff_text)\n            patch.diffs.append(bigpatch.diffs.pop(0))\n            empty = False\n          elif (diff_type == self.DIFF_DELETE and len(patch.diffs) == 1 and\n              patch.diffs[0][0] == self.DIFF_EQUAL and\n              len(diff_text) > 2 * patch_size):\n            # This is a large deletion.  Let it pass in one chunk.\n            patch.length1 += len(diff_text)\n            start1 += len(diff_text)\n            empty = False\n            patch.diffs.append((diff_type, diff_text))\n            del bigpatch.diffs[0]\n          else:\n            # Deletion or equality.  Only take as much as we can stomach.\n            diff_text = diff_text[:patch_size - patch.length1 -\n                                  self.Patch_Margin]\n            patch.length1 += len(diff_text)\n            start1 += len(diff_text)\n            if diff_type == self.DIFF_EQUAL:\n              patch.length2 += len(diff_text)\n              start2 += len(diff_text)\n            else:\n              empty = False\n\n            patch.diffs.append((diff_type, diff_text))\n            if diff_text == bigpatch.diffs[0][1]:\n              del bigpatch.diffs[0]\n            else:\n              bigpatch.diffs[0] = (bigpatch.diffs[0][0],\n                                   bigpatch.diffs[0][1][len(diff_text):])\n\n        # Compute the head context for the next patch.\n        precontext = self.diff_text2(patch.diffs)\n        precontext = precontext[-self.Patch_Margin:]\n        # Append the end context for this patch.\n        postcontext = self.diff_text1(bigpatch.diffs)[:self.Patch_Margin]\n        if postcontext:\n          patch.length1 += len(postcontext)\n          patch.length2 += len(postcontext)\n          if len(patch.diffs) != 0 and patch.diffs[-1][0] == self.DIFF_EQUAL:\n            patch.diffs[-1] = (self.DIFF_EQUAL, patch.diffs[-1][1] +\n                               postcontext)\n          else:\n            patch.diffs.append((self.DIFF_EQUAL, postcontext))\n\n        if not empty:\n          x += 1\n          patches.insert(x, patch)\n\n  def patch_toText(self, patches):\n    \"\"\"Take a list of patches and return a textual representation.\n\n    Args:\n      patches: Array of Patch objects.\n\n    Returns:\n      Text representation of patches.\n    \"\"\"\n    text = []\n    for patch in patches:\n      text.append(str(patch))\n    return \"\".join(text)\n\n  def patch_fromText(self, textline):\n    \"\"\"Parse a textual representation of patches and return a list of patch\n    objects.\n\n    Args:\n      textline: Text representation of patches.\n\n    Returns:\n      Array of Patch objects.\n\n    Raises:\n      ValueError: If invalid input.\n    \"\"\"\n    if type(textline) == unicode:\n      # Patches should be composed of a subset of ascii chars, Unicode not\n      # required.  If this encode raises UnicodeEncodeError, patch is invalid.\n      textline = textline.encode(\"ascii\")\n    patches = []\n    if not textline:\n      return patches\n    text = textline.split('\\n')\n    while len(text) != 0:\n      m = re.match(\"^@@ -(\\d+),?(\\d*) \\+(\\d+),?(\\d*) @@$\", text[0])\n      if not m:\n        raise ValueError(\"Invalid patch string: \" + text[0])\n      patch = patch_obj()\n      patches.append(patch)\n      patch.start1 = int(m.group(1))\n      if m.group(2) == '':\n        patch.start1 -= 1\n        patch.length1 = 1\n      elif m.group(2) == '0':\n        patch.length1 = 0\n      else:\n        patch.start1 -= 1\n        patch.length1 = int(m.group(2))\n\n      patch.start2 = int(m.group(3))\n      if m.group(4) == '':\n        patch.start2 -= 1\n        patch.length2 = 1\n      elif m.group(4) == '0':\n        patch.length2 = 0\n      else:\n        patch.start2 -= 1\n        patch.length2 = int(m.group(4))\n\n      del text[0]\n\n      while len(text) != 0:\n        if text[0]:\n          sign = text[0][0]\n        else:\n          sign = ''\n        line = urllib.unquote(text[0][1:])\n        line = line.decode(\"utf-8\")\n        if sign == '+':\n          # Insertion.\n          patch.diffs.append((self.DIFF_INSERT, line))\n        elif sign == '-':\n          # Deletion.\n          patch.diffs.append((self.DIFF_DELETE, line))\n        elif sign == ' ':\n          # Minor equality.\n          patch.diffs.append((self.DIFF_EQUAL, line))\n        elif sign == '@':\n          # Start of next patch.\n          break\n        elif sign == '':\n          # Blank line?  Whatever.\n          pass\n        else:\n          # WTF?\n          raise ValueError(\"Invalid patch mode: '%s'\\n%s\" % (sign, line))\n        del text[0]\n    return patches\n\n\nclass patch_obj:\n  \"\"\"Class representing one patch operation.\n  \"\"\"\n\n  def __init__(self):\n    \"\"\"Initializes with an empty list of diffs.\n    \"\"\"\n    self.diffs = []\n    self.start1 = None\n    self.start2 = None\n    self.length1 = 0\n    self.length2 = 0\n\n  def __str__(self):\n    \"\"\"Emulate GNU diff's format.\n    Header: @@ -382,8 +481,9 @@\n    Indices are printed as 1-based, not 0-based.\n\n    Returns:\n      The GNU diff string.\n    \"\"\"\n    if self.length1 == 0:\n      coords1 = str(self.start1) + \",0\"\n    elif self.length1 == 1:\n      coords1 = str(self.start1 + 1)\n    else:\n      coords1 = str(self.start1 + 1) + \",\" + str(self.length1)\n    if self.length2 == 0:\n      coords2 = str(self.start2) + \",0\"\n    elif self.length2 == 1:\n      coords2 = str(self.start2 + 1)\n    else:\n      coords2 = str(self.start2 + 1) + \",\" + str(self.length2)\n    text = [\"@@ -\", coords1, \" +\", coords2, \" @@\\n\"]\n    # Escape the body of the patch with %xx notation.\n    for (op, data) in self.diffs:\n      if op == diff_match_patch.DIFF_INSERT:\n        text.append(\"+\")\n      elif op == diff_match_patch.DIFF_DELETE:\n        text.append(\"-\")\n      elif op == diff_match_patch.DIFF_EQUAL:\n        text.append(\" \")\n      # High ascii will raise UnicodeDecodeError.  Use Unicode instead.\n      data = data.encode(\"utf-8\")\n      text.append(urllib.quote(data, \"!~*'();/?:@&=+$,# \") + \"\\n\")\n    return \"\".join(text)\n"
  },
  {
    "path": "python2/tests/diff_match_patch_test.py",
    "content": "#!/usr/bin/python2.4\n\n\"\"\"Diff Match and Patch -- Test harness\nCopyright 2018 The diff-match-patch Authors.\nhttps://github.com/google/diff-match-patch\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n  http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n\"\"\"\n\nimport os\nimport sys\nimport time\nimport unittest\nparentPath = os.path.abspath(\"..\")\nif parentPath not in sys.path:\n    sys.path.insert(0, parentPath)\nimport diff_match_patch as dmp_module\n# Force a module reload.  Allows one to edit the DMP module and rerun the tests\n# without leaving the Python interpreter.\nreload(dmp_module)\n\nclass DiffMatchPatchTest(unittest.TestCase):\n\n  def setUp(self):\n    \"Test harness for dmp_module.\"\n    self.dmp = dmp_module.diff_match_patch()\n\n  def diff_rebuildtexts(self, diffs):\n    # Construct the two texts which made up the diff originally.\n    text1 = \"\"\n    text2 = \"\"\n    for x in range(0, len(diffs)):\n      if diffs[x][0] != dmp_module.diff_match_patch.DIFF_INSERT:\n        text1 += diffs[x][1]\n      if diffs[x][0] != dmp_module.diff_match_patch.DIFF_DELETE:\n        text2 += diffs[x][1]\n    return (text1, text2)\n\n\nclass DiffTest(DiffMatchPatchTest):\n  \"\"\"DIFF TEST FUNCTIONS\"\"\"\n\n  def testDiffCommonPrefix(self):\n    # Detect any common prefix.\n    # Null case.\n    self.assertEquals(0, self.dmp.diff_commonPrefix(\"abc\", \"xyz\"))\n\n    # Non-null case.\n    self.assertEquals(4, self.dmp.diff_commonPrefix(\"1234abcdef\", \"1234xyz\"))\n\n    # Whole case.\n    self.assertEquals(4, self.dmp.diff_commonPrefix(\"1234\", \"1234xyz\"))\n\n  def testDiffCommonSuffix(self):\n    # Detect any common suffix.\n    # Null case.\n    self.assertEquals(0, self.dmp.diff_commonSuffix(\"abc\", \"xyz\"))\n\n    # Non-null case.\n    self.assertEquals(4, self.dmp.diff_commonSuffix(\"abcdef1234\", \"xyz1234\"))\n\n    # Whole case.\n    self.assertEquals(4, self.dmp.diff_commonSuffix(\"1234\", \"xyz1234\"))\n\n  def testDiffCommonOverlap(self):\n    # Null case.\n    self.assertEquals(0, self.dmp.diff_commonOverlap(\"\", \"abcd\"))\n\n    # Whole case.\n    self.assertEquals(3, self.dmp.diff_commonOverlap(\"abc\", \"abcd\"))\n\n    # No overlap.\n    self.assertEquals(0, self.dmp.diff_commonOverlap(\"123456\", \"abcd\"))\n\n    # Overlap.\n    self.assertEquals(3, self.dmp.diff_commonOverlap(\"123456xxx\", \"xxxabcd\"))\n\n    # Unicode.\n    # Some overly clever languages (C#) may treat ligatures as equal to their\n    # component letters.  E.g. U+FB01 == 'fi'\n    self.assertEquals(0, self.dmp.diff_commonOverlap(\"fi\", u\"\\ufb01i\"))\n\n  def testDiffHalfMatch(self):\n    # Detect a halfmatch.\n    self.dmp.Diff_Timeout = 1\n    # No match.\n    self.assertEquals(None, self.dmp.diff_halfMatch(\"1234567890\", \"abcdef\"))\n\n    self.assertEquals(None, self.dmp.diff_halfMatch(\"12345\", \"23\"))\n\n    # Single Match.\n    self.assertEquals((\"12\", \"90\", \"a\", \"z\", \"345678\"), self.dmp.diff_halfMatch(\"1234567890\", \"a345678z\"))\n\n    self.assertEquals((\"a\", \"z\", \"12\", \"90\", \"345678\"), self.dmp.diff_halfMatch(\"a345678z\", \"1234567890\"))\n\n    self.assertEquals((\"abc\", \"z\", \"1234\", \"0\", \"56789\"), self.dmp.diff_halfMatch(\"abc56789z\", \"1234567890\"))\n\n    self.assertEquals((\"a\", \"xyz\", \"1\", \"7890\", \"23456\"), self.dmp.diff_halfMatch(\"a23456xyz\", \"1234567890\"))\n\n    # Multiple Matches.\n    self.assertEquals((\"12123\", \"123121\", \"a\", \"z\", \"1234123451234\"), self.dmp.diff_halfMatch(\"121231234123451234123121\", \"a1234123451234z\"))\n\n    self.assertEquals((\"\", \"-=-=-=-=-=\", \"x\", \"\", \"x-=-=-=-=-=-=-=\"), self.dmp.diff_halfMatch(\"x-=-=-=-=-=-=-=-=-=-=-=-=\", \"xx-=-=-=-=-=-=-=\"))\n\n    self.assertEquals((\"-=-=-=-=-=\", \"\", \"\", \"y\", \"-=-=-=-=-=-=-=y\"), self.dmp.diff_halfMatch(\"-=-=-=-=-=-=-=-=-=-=-=-=y\", \"-=-=-=-=-=-=-=yy\"))\n\n    # Non-optimal halfmatch.\n    # Optimal diff would be -q+x=H-i+e=lloHe+Hu=llo-Hew+y not -qHillo+x=HelloHe-w+Hulloy\n    self.assertEquals((\"qHillo\", \"w\", \"x\", \"Hulloy\", \"HelloHe\"), self.dmp.diff_halfMatch(\"qHilloHelloHew\", \"xHelloHeHulloy\"))\n\n    # Optimal no halfmatch.\n    self.dmp.Diff_Timeout = 0\n    self.assertEquals(None, self.dmp.diff_halfMatch(\"qHilloHelloHew\", \"xHelloHeHulloy\"))\n\n  def testDiffLinesToChars(self):\n    # Convert lines down to characters.\n    self.assertEquals((\"\\x01\\x02\\x01\", \"\\x02\\x01\\x02\", [\"\", \"alpha\\n\", \"beta\\n\"]), self.dmp.diff_linesToChars(\"alpha\\nbeta\\nalpha\\n\", \"beta\\nalpha\\nbeta\\n\"))\n\n    self.assertEquals((\"\", \"\\x01\\x02\\x03\\x03\", [\"\", \"alpha\\r\\n\", \"beta\\r\\n\", \"\\r\\n\"]), self.dmp.diff_linesToChars(\"\", \"alpha\\r\\nbeta\\r\\n\\r\\n\\r\\n\"))\n\n    self.assertEquals((\"\\x01\", \"\\x02\", [\"\", \"a\", \"b\"]), self.dmp.diff_linesToChars(\"a\", \"b\"))\n\n    # More than 256 to reveal any 8-bit limitations.\n    n = 300\n    lineList = []\n    charList = []\n    for i in range(1, n + 1):\n      lineList.append(str(i) + \"\\n\")\n      charList.append(unichr(i))\n    self.assertEquals(n, len(lineList))\n    lines = \"\".join(lineList)\n    chars = \"\".join(charList)\n    self.assertEquals(n, len(chars))\n    lineList.insert(0, \"\")\n    self.assertEquals((chars, \"\", lineList), self.dmp.diff_linesToChars(lines, \"\"))\n\n  def testDiffCharsToLines(self):\n    # Convert chars up to lines.\n    diffs = [(self.dmp.DIFF_EQUAL, \"\\x01\\x02\\x01\"), (self.dmp.DIFF_INSERT, \"\\x02\\x01\\x02\")]\n    self.dmp.diff_charsToLines(diffs, [\"\", \"alpha\\n\", \"beta\\n\"])\n    self.assertEquals([(self.dmp.DIFF_EQUAL, \"alpha\\nbeta\\nalpha\\n\"), (self.dmp.DIFF_INSERT, \"beta\\nalpha\\nbeta\\n\")], diffs)\n\n    # More than 256 to reveal any 8-bit limitations.\n    n = 300\n    lineList = []\n    charList = []\n    for i in range(1, n + 1):\n      lineList.append(str(i) + \"\\n\")\n      charList.append(unichr(i))\n    self.assertEquals(n, len(lineList))\n    lines = \"\".join(lineList)\n    chars = \"\".join(charList)\n    self.assertEquals(n, len(chars))\n    lineList.insert(0, \"\")\n    diffs = [(self.dmp.DIFF_DELETE, chars)]\n    self.dmp.diff_charsToLines(diffs, lineList)\n    self.assertEquals([(self.dmp.DIFF_DELETE, lines)], diffs)\n\n    # More than 65536 to verify any 16-bit limitation.\n    lineList = []\n    for i in range(1, 66000 + 1):\n      lineList.append(str(i) + \"\\n\")\n    chars = \"\".join(lineList)\n    results = self.dmp.diff_linesToChars(chars, \"\")\n    diffs = [(self.dmp.DIFF_INSERT, results[0])]\n    self.dmp.diff_charsToLines(diffs, results[2])\n    self.assertEquals(chars, diffs[0][1])\n\n  def testDiffCleanupMerge(self):\n    # Cleanup a messy diff.\n    # Null case.\n    diffs = []\n    self.dmp.diff_cleanupMerge(diffs)\n    self.assertEquals([], diffs)\n\n    # No change case.\n    diffs = [(self.dmp.DIFF_EQUAL, \"a\"), (self.dmp.DIFF_DELETE, \"b\"), (self.dmp.DIFF_INSERT, \"c\")]\n    self.dmp.diff_cleanupMerge(diffs)\n    self.assertEquals([(self.dmp.DIFF_EQUAL, \"a\"), (self.dmp.DIFF_DELETE, \"b\"), (self.dmp.DIFF_INSERT, \"c\")], diffs)\n\n    # Merge equalities.\n    diffs = [(self.dmp.DIFF_EQUAL, \"a\"), (self.dmp.DIFF_EQUAL, \"b\"), (self.dmp.DIFF_EQUAL, \"c\")]\n    self.dmp.diff_cleanupMerge(diffs)\n    self.assertEquals([(self.dmp.DIFF_EQUAL, \"abc\")], diffs)\n\n    # Merge deletions.\n    diffs = [(self.dmp.DIFF_DELETE, \"a\"), (self.dmp.DIFF_DELETE, \"b\"), (self.dmp.DIFF_DELETE, \"c\")]\n    self.dmp.diff_cleanupMerge(diffs)\n    self.assertEquals([(self.dmp.DIFF_DELETE, \"abc\")], diffs)\n\n    # Merge insertions.\n    diffs = [(self.dmp.DIFF_INSERT, \"a\"), (self.dmp.DIFF_INSERT, \"b\"), (self.dmp.DIFF_INSERT, \"c\")]\n    self.dmp.diff_cleanupMerge(diffs)\n    self.assertEquals([(self.dmp.DIFF_INSERT, \"abc\")], diffs)\n\n    # Merge interweave.\n    diffs = [(self.dmp.DIFF_DELETE, \"a\"), (self.dmp.DIFF_INSERT, \"b\"), (self.dmp.DIFF_DELETE, \"c\"), (self.dmp.DIFF_INSERT, \"d\"), (self.dmp.DIFF_EQUAL, \"e\"), (self.dmp.DIFF_EQUAL, \"f\")]\n    self.dmp.diff_cleanupMerge(diffs)\n    self.assertEquals([(self.dmp.DIFF_DELETE, \"ac\"), (self.dmp.DIFF_INSERT, \"bd\"), (self.dmp.DIFF_EQUAL, \"ef\")], diffs)\n\n    # Prefix and suffix detection.\n    diffs = [(self.dmp.DIFF_DELETE, \"a\"), (self.dmp.DIFF_INSERT, \"abc\"), (self.dmp.DIFF_DELETE, \"dc\")]\n    self.dmp.diff_cleanupMerge(diffs)\n    self.assertEquals([(self.dmp.DIFF_EQUAL, \"a\"), (self.dmp.DIFF_DELETE, \"d\"), (self.dmp.DIFF_INSERT, \"b\"), (self.dmp.DIFF_EQUAL, \"c\")], diffs)\n\n    # Prefix and suffix detection with equalities.\n    diffs = [(self.dmp.DIFF_EQUAL, \"x\"), (self.dmp.DIFF_DELETE, \"a\"), (self.dmp.DIFF_INSERT, \"abc\"), (self.dmp.DIFF_DELETE, \"dc\"), (self.dmp.DIFF_EQUAL, \"y\")]\n    self.dmp.diff_cleanupMerge(diffs)\n    self.assertEquals([(self.dmp.DIFF_EQUAL, \"xa\"), (self.dmp.DIFF_DELETE, \"d\"), (self.dmp.DIFF_INSERT, \"b\"), (self.dmp.DIFF_EQUAL, \"cy\")], diffs)\n\n    # Slide edit left.\n    diffs = [(self.dmp.DIFF_EQUAL, \"a\"), (self.dmp.DIFF_INSERT, \"ba\"), (self.dmp.DIFF_EQUAL, \"c\")]\n    self.dmp.diff_cleanupMerge(diffs)\n    self.assertEquals([(self.dmp.DIFF_INSERT, \"ab\"), (self.dmp.DIFF_EQUAL, \"ac\")], diffs)\n\n    # Slide edit right.\n    diffs = [(self.dmp.DIFF_EQUAL, \"c\"), (self.dmp.DIFF_INSERT, \"ab\"), (self.dmp.DIFF_EQUAL, \"a\")]\n    self.dmp.diff_cleanupMerge(diffs)\n    self.assertEquals([(self.dmp.DIFF_EQUAL, \"ca\"), (self.dmp.DIFF_INSERT, \"ba\")], diffs)\n\n    # Slide edit left recursive.\n    diffs = [(self.dmp.DIFF_EQUAL, \"a\"), (self.dmp.DIFF_DELETE, \"b\"), (self.dmp.DIFF_EQUAL, \"c\"), (self.dmp.DIFF_DELETE, \"ac\"), (self.dmp.DIFF_EQUAL, \"x\")]\n    self.dmp.diff_cleanupMerge(diffs)\n    self.assertEquals([(self.dmp.DIFF_DELETE, \"abc\"), (self.dmp.DIFF_EQUAL, \"acx\")], diffs)\n\n    # Slide edit right recursive.\n    diffs = [(self.dmp.DIFF_EQUAL, \"x\"), (self.dmp.DIFF_DELETE, \"ca\"), (self.dmp.DIFF_EQUAL, \"c\"), (self.dmp.DIFF_DELETE, \"b\"), (self.dmp.DIFF_EQUAL, \"a\")]\n    self.dmp.diff_cleanupMerge(diffs)\n    self.assertEquals([(self.dmp.DIFF_EQUAL, \"xca\"), (self.dmp.DIFF_DELETE, \"cba\")], diffs)\n\n    # Empty merge.\n    diffs = [(self.dmp.DIFF_DELETE, \"b\"), (self.dmp.DIFF_INSERT, \"ab\"), (self.dmp.DIFF_EQUAL, \"c\")]\n    self.dmp.diff_cleanupMerge(diffs)\n    self.assertEquals([(self.dmp.DIFF_INSERT, \"a\"), (self.dmp.DIFF_EQUAL, \"bc\")], diffs)\n\n    # Empty equality.\n    diffs = [(self.dmp.DIFF_EQUAL, \"\"), (self.dmp.DIFF_INSERT, \"a\"), (self.dmp.DIFF_EQUAL, \"b\")]\n    self.dmp.diff_cleanupMerge(diffs)\n    self.assertEquals([(self.dmp.DIFF_INSERT, \"a\"), (self.dmp.DIFF_EQUAL, \"b\")], diffs)\n\n  def testDiffCleanupSemanticLossless(self):\n    # Slide diffs to match logical boundaries.\n    # Null case.\n    diffs = []\n    self.dmp.diff_cleanupSemanticLossless(diffs)\n    self.assertEquals([], diffs)\n\n    # Blank lines.\n    diffs = [(self.dmp.DIFF_EQUAL, \"AAA\\r\\n\\r\\nBBB\"), (self.dmp.DIFF_INSERT, \"\\r\\nDDD\\r\\n\\r\\nBBB\"), (self.dmp.DIFF_EQUAL, \"\\r\\nEEE\")]\n    self.dmp.diff_cleanupSemanticLossless(diffs)\n    self.assertEquals([(self.dmp.DIFF_EQUAL, \"AAA\\r\\n\\r\\n\"), (self.dmp.DIFF_INSERT, \"BBB\\r\\nDDD\\r\\n\\r\\n\"), (self.dmp.DIFF_EQUAL, \"BBB\\r\\nEEE\")], diffs)\n\n    # Line boundaries.\n    diffs = [(self.dmp.DIFF_EQUAL, \"AAA\\r\\nBBB\"), (self.dmp.DIFF_INSERT, \" DDD\\r\\nBBB\"), (self.dmp.DIFF_EQUAL, \" EEE\")]\n    self.dmp.diff_cleanupSemanticLossless(diffs)\n    self.assertEquals([(self.dmp.DIFF_EQUAL, \"AAA\\r\\n\"), (self.dmp.DIFF_INSERT, \"BBB DDD\\r\\n\"), (self.dmp.DIFF_EQUAL, \"BBB EEE\")], diffs)\n\n    # Word boundaries.\n    diffs = [(self.dmp.DIFF_EQUAL, \"The c\"), (self.dmp.DIFF_INSERT, \"ow and the c\"), (self.dmp.DIFF_EQUAL, \"at.\")]\n    self.dmp.diff_cleanupSemanticLossless(diffs)\n    self.assertEquals([(self.dmp.DIFF_EQUAL, \"The \"), (self.dmp.DIFF_INSERT, \"cow and the \"), (self.dmp.DIFF_EQUAL, \"cat.\")], diffs)\n\n    # Alphanumeric boundaries.\n    diffs = [(self.dmp.DIFF_EQUAL, \"The-c\"), (self.dmp.DIFF_INSERT, \"ow-and-the-c\"), (self.dmp.DIFF_EQUAL, \"at.\")]\n    self.dmp.diff_cleanupSemanticLossless(diffs)\n    self.assertEquals([(self.dmp.DIFF_EQUAL, \"The-\"), (self.dmp.DIFF_INSERT, \"cow-and-the-\"), (self.dmp.DIFF_EQUAL, \"cat.\")], diffs)\n\n    # Hitting the start.\n    diffs = [(self.dmp.DIFF_EQUAL, \"a\"), (self.dmp.DIFF_DELETE, \"a\"), (self.dmp.DIFF_EQUAL, \"ax\")]\n    self.dmp.diff_cleanupSemanticLossless(diffs)\n    self.assertEquals([(self.dmp.DIFF_DELETE, \"a\"), (self.dmp.DIFF_EQUAL, \"aax\")], diffs)\n\n    # Hitting the end.\n    diffs = [(self.dmp.DIFF_EQUAL, \"xa\"), (self.dmp.DIFF_DELETE, \"a\"), (self.dmp.DIFF_EQUAL, \"a\")]\n    self.dmp.diff_cleanupSemanticLossless(diffs)\n    self.assertEquals([(self.dmp.DIFF_EQUAL, \"xaa\"), (self.dmp.DIFF_DELETE, \"a\")], diffs)\n\n    # Sentence boundaries.\n    diffs = [(self.dmp.DIFF_EQUAL, \"The xxx. The \"), (self.dmp.DIFF_INSERT, \"zzz. The \"), (self.dmp.DIFF_EQUAL, \"yyy.\")]\n    self.dmp.diff_cleanupSemanticLossless(diffs)\n    self.assertEquals([(self.dmp.DIFF_EQUAL, \"The xxx.\"), (self.dmp.DIFF_INSERT, \" The zzz.\"), (self.dmp.DIFF_EQUAL, \" The yyy.\")], diffs)\n\n  def testDiffCleanupSemantic(self):\n    # Cleanup semantically trivial equalities.\n    # Null case.\n    diffs = []\n    self.dmp.diff_cleanupSemantic(diffs)\n    self.assertEquals([], diffs)\n\n    # No elimination #1.\n    diffs = [(self.dmp.DIFF_DELETE, \"ab\"), (self.dmp.DIFF_INSERT, \"cd\"), (self.dmp.DIFF_EQUAL, \"12\"), (self.dmp.DIFF_DELETE, \"e\")]\n    self.dmp.diff_cleanupSemantic(diffs)\n    self.assertEquals([(self.dmp.DIFF_DELETE, \"ab\"), (self.dmp.DIFF_INSERT, \"cd\"), (self.dmp.DIFF_EQUAL, \"12\"), (self.dmp.DIFF_DELETE, \"e\")], diffs)\n\n    # No elimination #2.\n    diffs = [(self.dmp.DIFF_DELETE, \"abc\"), (self.dmp.DIFF_INSERT, \"ABC\"), (self.dmp.DIFF_EQUAL, \"1234\"), (self.dmp.DIFF_DELETE, \"wxyz\")]\n    self.dmp.diff_cleanupSemantic(diffs)\n    self.assertEquals([(self.dmp.DIFF_DELETE, \"abc\"), (self.dmp.DIFF_INSERT, \"ABC\"), (self.dmp.DIFF_EQUAL, \"1234\"), (self.dmp.DIFF_DELETE, \"wxyz\")], diffs)\n\n    # Simple elimination.\n    diffs = [(self.dmp.DIFF_DELETE, \"a\"), (self.dmp.DIFF_EQUAL, \"b\"), (self.dmp.DIFF_DELETE, \"c\")]\n    self.dmp.diff_cleanupSemantic(diffs)\n    self.assertEquals([(self.dmp.DIFF_DELETE, \"abc\"), (self.dmp.DIFF_INSERT, \"b\")], diffs)\n\n    # Backpass elimination.\n    diffs = [(self.dmp.DIFF_DELETE, \"ab\"), (self.dmp.DIFF_EQUAL, \"cd\"), (self.dmp.DIFF_DELETE, \"e\"), (self.dmp.DIFF_EQUAL, \"f\"), (self.dmp.DIFF_INSERT, \"g\")]\n    self.dmp.diff_cleanupSemantic(diffs)\n    self.assertEquals([(self.dmp.DIFF_DELETE, \"abcdef\"), (self.dmp.DIFF_INSERT, \"cdfg\")], diffs)\n\n    # Multiple eliminations.\n    diffs = [(self.dmp.DIFF_INSERT, \"1\"), (self.dmp.DIFF_EQUAL, \"A\"), (self.dmp.DIFF_DELETE, \"B\"), (self.dmp.DIFF_INSERT, \"2\"), (self.dmp.DIFF_EQUAL, \"_\"), (self.dmp.DIFF_INSERT, \"1\"), (self.dmp.DIFF_EQUAL, \"A\"), (self.dmp.DIFF_DELETE, \"B\"), (self.dmp.DIFF_INSERT, \"2\")]\n    self.dmp.diff_cleanupSemantic(diffs)\n    self.assertEquals([(self.dmp.DIFF_DELETE, \"AB_AB\"), (self.dmp.DIFF_INSERT, \"1A2_1A2\")], diffs)\n\n    # Word boundaries.\n    diffs = [(self.dmp.DIFF_EQUAL, \"The c\"), (self.dmp.DIFF_DELETE, \"ow and the c\"), (self.dmp.DIFF_EQUAL, \"at.\")]\n    self.dmp.diff_cleanupSemantic(diffs)\n    self.assertEquals([(self.dmp.DIFF_EQUAL, \"The \"), (self.dmp.DIFF_DELETE, \"cow and the \"), (self.dmp.DIFF_EQUAL, \"cat.\")], diffs)\n\n    # No overlap elimination.\n    diffs = [(self.dmp.DIFF_DELETE, \"abcxx\"), (self.dmp.DIFF_INSERT, \"xxdef\")]\n    self.dmp.diff_cleanupSemantic(diffs)\n    self.assertEquals([(self.dmp.DIFF_DELETE, \"abcxx\"), (self.dmp.DIFF_INSERT, \"xxdef\")], diffs)\n\n    # Overlap elimination.\n    diffs = [(self.dmp.DIFF_DELETE, \"abcxxx\"), (self.dmp.DIFF_INSERT, \"xxxdef\")]\n    self.dmp.diff_cleanupSemantic(diffs)\n    self.assertEquals([(self.dmp.DIFF_DELETE, \"abc\"), (self.dmp.DIFF_EQUAL, \"xxx\"), (self.dmp.DIFF_INSERT, \"def\")], diffs)\n\n    # Reverse overlap elimination.\n    diffs = [(self.dmp.DIFF_DELETE, \"xxxabc\"), (self.dmp.DIFF_INSERT, \"defxxx\")]\n    self.dmp.diff_cleanupSemantic(diffs)\n    self.assertEquals([(self.dmp.DIFF_INSERT, \"def\"), (self.dmp.DIFF_EQUAL, \"xxx\"), (self.dmp.DIFF_DELETE, \"abc\")], diffs)\n\n    # Two overlap eliminations.\n    diffs = [(self.dmp.DIFF_DELETE, \"abcd1212\"), (self.dmp.DIFF_INSERT, \"1212efghi\"), (self.dmp.DIFF_EQUAL, \"----\"), (self.dmp.DIFF_DELETE, \"A3\"), (self.dmp.DIFF_INSERT, \"3BC\")]\n    self.dmp.diff_cleanupSemantic(diffs)\n    self.assertEquals([(self.dmp.DIFF_DELETE, \"abcd\"), (self.dmp.DIFF_EQUAL, \"1212\"), (self.dmp.DIFF_INSERT, \"efghi\"), (self.dmp.DIFF_EQUAL, \"----\"), (self.dmp.DIFF_DELETE, \"A\"), (self.dmp.DIFF_EQUAL, \"3\"), (self.dmp.DIFF_INSERT, \"BC\")], diffs)\n\n  def testDiffCleanupEfficiency(self):\n    # Cleanup operationally trivial equalities.\n    self.dmp.Diff_EditCost = 4\n    # Null case.\n    diffs = []\n    self.dmp.diff_cleanupEfficiency(diffs)\n    self.assertEquals([], diffs)\n\n    # No elimination.\n    diffs = [(self.dmp.DIFF_DELETE, \"ab\"), (self.dmp.DIFF_INSERT, \"12\"), (self.dmp.DIFF_EQUAL, \"wxyz\"), (self.dmp.DIFF_DELETE, \"cd\"), (self.dmp.DIFF_INSERT, \"34\")]\n    self.dmp.diff_cleanupEfficiency(diffs)\n    self.assertEquals([(self.dmp.DIFF_DELETE, \"ab\"), (self.dmp.DIFF_INSERT, \"12\"), (self.dmp.DIFF_EQUAL, \"wxyz\"), (self.dmp.DIFF_DELETE, \"cd\"), (self.dmp.DIFF_INSERT, \"34\")], diffs)\n\n    # Four-edit elimination.\n    diffs = [(self.dmp.DIFF_DELETE, \"ab\"), (self.dmp.DIFF_INSERT, \"12\"), (self.dmp.DIFF_EQUAL, \"xyz\"), (self.dmp.DIFF_DELETE, \"cd\"), (self.dmp.DIFF_INSERT, \"34\")]\n    self.dmp.diff_cleanupEfficiency(diffs)\n    self.assertEquals([(self.dmp.DIFF_DELETE, \"abxyzcd\"), (self.dmp.DIFF_INSERT, \"12xyz34\")], diffs)\n\n    # Three-edit elimination.\n    diffs = [(self.dmp.DIFF_INSERT, \"12\"), (self.dmp.DIFF_EQUAL, \"x\"), (self.dmp.DIFF_DELETE, \"cd\"), (self.dmp.DIFF_INSERT, \"34\")]\n    self.dmp.diff_cleanupEfficiency(diffs)\n    self.assertEquals([(self.dmp.DIFF_DELETE, \"xcd\"), (self.dmp.DIFF_INSERT, \"12x34\")], diffs)\n\n    # Backpass elimination.\n    diffs = [(self.dmp.DIFF_DELETE, \"ab\"), (self.dmp.DIFF_INSERT, \"12\"), (self.dmp.DIFF_EQUAL, \"xy\"), (self.dmp.DIFF_INSERT, \"34\"), (self.dmp.DIFF_EQUAL, \"z\"), (self.dmp.DIFF_DELETE, \"cd\"), (self.dmp.DIFF_INSERT, \"56\")]\n    self.dmp.diff_cleanupEfficiency(diffs)\n    self.assertEquals([(self.dmp.DIFF_DELETE, \"abxyzcd\"), (self.dmp.DIFF_INSERT, \"12xy34z56\")], diffs)\n\n    # High cost elimination.\n    self.dmp.Diff_EditCost = 5\n    diffs = [(self.dmp.DIFF_DELETE, \"ab\"), (self.dmp.DIFF_INSERT, \"12\"), (self.dmp.DIFF_EQUAL, \"wxyz\"), (self.dmp.DIFF_DELETE, \"cd\"), (self.dmp.DIFF_INSERT, \"34\")]\n    self.dmp.diff_cleanupEfficiency(diffs)\n    self.assertEquals([(self.dmp.DIFF_DELETE, \"abwxyzcd\"), (self.dmp.DIFF_INSERT, \"12wxyz34\")], diffs)\n    self.dmp.Diff_EditCost = 4\n\n  def testDiffPrettyHtml(self):\n    # Pretty print.\n    diffs = [(self.dmp.DIFF_EQUAL, \"a\\n\"), (self.dmp.DIFF_DELETE, \"<B>b</B>\"), (self.dmp.DIFF_INSERT, \"c&d\")]\n    self.assertEquals(\"<span>a&para;<br></span><del style=\\\"background:#ffe6e6;\\\">&lt;B&gt;b&lt;/B&gt;</del><ins style=\\\"background:#e6ffe6;\\\">c&amp;d</ins>\", self.dmp.diff_prettyHtml(diffs))\n\n  def testDiffText(self):\n    # Compute the source and destination texts.\n    diffs = [(self.dmp.DIFF_EQUAL, \"jump\"), (self.dmp.DIFF_DELETE, \"s\"), (self.dmp.DIFF_INSERT, \"ed\"), (self.dmp.DIFF_EQUAL, \" over \"), (self.dmp.DIFF_DELETE, \"the\"), (self.dmp.DIFF_INSERT, \"a\"), (self.dmp.DIFF_EQUAL, \" lazy\")]\n    self.assertEquals(\"jumps over the lazy\", self.dmp.diff_text1(diffs))\n\n    self.assertEquals(\"jumped over a lazy\", self.dmp.diff_text2(diffs))\n\n  def testDiffDelta(self):\n    # Convert a diff into delta string.\n    diffs = [(self.dmp.DIFF_EQUAL, \"jump\"), (self.dmp.DIFF_DELETE, \"s\"), (self.dmp.DIFF_INSERT, \"ed\"), (self.dmp.DIFF_EQUAL, \" over \"), (self.dmp.DIFF_DELETE, \"the\"), (self.dmp.DIFF_INSERT, \"a\"), (self.dmp.DIFF_EQUAL, \" lazy\"), (self.dmp.DIFF_INSERT, \"old dog\")]\n    text1 = self.dmp.diff_text1(diffs)\n    self.assertEquals(\"jumps over the lazy\", text1)\n\n    delta = self.dmp.diff_toDelta(diffs)\n    self.assertEquals(\"=4\\t-1\\t+ed\\t=6\\t-3\\t+a\\t=5\\t+old dog\", delta)\n\n    # Convert delta string into a diff.\n    self.assertEquals(diffs, self.dmp.diff_fromDelta(text1, delta))\n\n    # Generates error (19 != 20).\n    try:\n      self.dmp.diff_fromDelta(text1 + \"x\", delta)\n      self.assertFalse(True)\n    except ValueError:\n      # Exception expected.\n      pass\n\n    # Generates error (19 != 18).\n    try:\n      self.dmp.diff_fromDelta(text1[1:], delta)\n      self.assertFalse(True)\n    except ValueError:\n      # Exception expected.\n      pass\n\n    # Generates error (%c3%xy invalid Unicode).\n    try:\n      self.dmp.diff_fromDelta(\"\", \"+%c3xy\")\n      self.assertFalse(True)\n    except ValueError:\n      # Exception expected.\n      pass\n\n    # Test deltas with special characters.\n    diffs = [(self.dmp.DIFF_EQUAL, u\"\\u0680 \\x00 \\t %\"), (self.dmp.DIFF_DELETE, u\"\\u0681 \\x01 \\n ^\"), (self.dmp.DIFF_INSERT, u\"\\u0682 \\x02 \\\\ |\")]\n    text1 = self.dmp.diff_text1(diffs)\n    self.assertEquals(u\"\\u0680 \\x00 \\t %\\u0681 \\x01 \\n ^\", text1)\n\n    delta = self.dmp.diff_toDelta(diffs)\n    self.assertEquals(\"=7\\t-7\\t+%DA%82 %02 %5C %7C\", delta)\n\n    # Convert delta string into a diff.\n    self.assertEquals(diffs, self.dmp.diff_fromDelta(text1, delta))\n\n    # Verify pool of unchanged characters.\n    diffs = [(self.dmp.DIFF_INSERT, \"A-Z a-z 0-9 - _ . ! ~ * ' ( ) ; / ? : @ & = + $ , # \")]\n    text2 = self.dmp.diff_text2(diffs)\n    self.assertEquals(\"A-Z a-z 0-9 - _ . ! ~ * \\' ( ) ; / ? : @ & = + $ , # \", text2)\n\n    delta = self.dmp.diff_toDelta(diffs)\n    self.assertEquals(\"+A-Z a-z 0-9 - _ . ! ~ * \\' ( ) ; / ? : @ & = + $ , # \", delta)\n\n    # Convert delta string into a diff.\n    self.assertEquals(diffs, self.dmp.diff_fromDelta(\"\", delta))\n\n    # 160 kb string.\n    a = \"abcdefghij\"\n    for i in range(14):\n      a += a\n    diffs = [(self.dmp.DIFF_INSERT, a)]\n    delta = self.dmp.diff_toDelta(diffs);\n    self.assertEquals('+' + a, delta);\n\n    # Convert delta string into a diff.\n    self.assertEquals(diffs, self.dmp.diff_fromDelta(\"\", delta));\n\n  def testDiffXIndex(self):\n    # Translate a location in text1 to text2.\n    self.assertEquals(5, self.dmp.diff_xIndex([(self.dmp.DIFF_DELETE, \"a\"), (self.dmp.DIFF_INSERT, \"1234\"), (self.dmp.DIFF_EQUAL, \"xyz\")], 2))\n\n    # Translation on deletion.\n    self.assertEquals(1, self.dmp.diff_xIndex([(self.dmp.DIFF_EQUAL, \"a\"), (self.dmp.DIFF_DELETE, \"1234\"), (self.dmp.DIFF_EQUAL, \"xyz\")], 3))\n\n  def testDiffLevenshtein(self):\n    # Levenshtein with trailing equality.\n    self.assertEquals(4, self.dmp.diff_levenshtein([(self.dmp.DIFF_DELETE, \"abc\"), (self.dmp.DIFF_INSERT, \"1234\"), (self.dmp.DIFF_EQUAL, \"xyz\")]))\n    # Levenshtein with leading equality.\n    self.assertEquals(4, self.dmp.diff_levenshtein([(self.dmp.DIFF_EQUAL, \"xyz\"), (self.dmp.DIFF_DELETE, \"abc\"), (self.dmp.DIFF_INSERT, \"1234\")]))\n    # Levenshtein with middle equality.\n    self.assertEquals(7, self.dmp.diff_levenshtein([(self.dmp.DIFF_DELETE, \"abc\"), (self.dmp.DIFF_EQUAL, \"xyz\"), (self.dmp.DIFF_INSERT, \"1234\")]))\n\n  def testDiffBisect(self):\n    # Normal.\n    a = \"cat\"\n    b = \"map\"\n    # Since the resulting diff hasn't been normalized, it would be ok if\n    # the insertion and deletion pairs are swapped.\n    # If the order changes, tweak this test as required.\n    self.assertEquals([(self.dmp.DIFF_DELETE, \"c\"), (self.dmp.DIFF_INSERT, \"m\"), (self.dmp.DIFF_EQUAL, \"a\"), (self.dmp.DIFF_DELETE, \"t\"), (self.dmp.DIFF_INSERT, \"p\")], self.dmp.diff_bisect(a, b, sys.maxint))\n\n    # Timeout.\n    self.assertEquals([(self.dmp.DIFF_DELETE, \"cat\"), (self.dmp.DIFF_INSERT, \"map\")], self.dmp.diff_bisect(a, b, 0))\n\n  def testDiffMain(self):\n    # Perform a trivial diff.\n    # Null case.\n    self.assertEquals([], self.dmp.diff_main(\"\", \"\", False))\n\n    # Equality.\n    self.assertEquals([(self.dmp.DIFF_EQUAL, \"abc\")], self.dmp.diff_main(\"abc\", \"abc\", False))\n\n    # Simple insertion.\n    self.assertEquals([(self.dmp.DIFF_EQUAL, \"ab\"), (self.dmp.DIFF_INSERT, \"123\"), (self.dmp.DIFF_EQUAL, \"c\")], self.dmp.diff_main(\"abc\", \"ab123c\", False))\n\n    # Simple deletion.\n    self.assertEquals([(self.dmp.DIFF_EQUAL, \"a\"), (self.dmp.DIFF_DELETE, \"123\"), (self.dmp.DIFF_EQUAL, \"bc\")], self.dmp.diff_main(\"a123bc\", \"abc\", False))\n\n    # Two insertions.\n    self.assertEquals([(self.dmp.DIFF_EQUAL, \"a\"), (self.dmp.DIFF_INSERT, \"123\"), (self.dmp.DIFF_EQUAL, \"b\"), (self.dmp.DIFF_INSERT, \"456\"), (self.dmp.DIFF_EQUAL, \"c\")], self.dmp.diff_main(\"abc\", \"a123b456c\", False))\n\n    # Two deletions.\n    self.assertEquals([(self.dmp.DIFF_EQUAL, \"a\"), (self.dmp.DIFF_DELETE, \"123\"), (self.dmp.DIFF_EQUAL, \"b\"), (self.dmp.DIFF_DELETE, \"456\"), (self.dmp.DIFF_EQUAL, \"c\")], self.dmp.diff_main(\"a123b456c\", \"abc\", False))\n\n    # Perform a real diff.\n    # Switch off the timeout.\n    self.dmp.Diff_Timeout = 0\n    # Simple cases.\n    self.assertEquals([(self.dmp.DIFF_DELETE, \"a\"), (self.dmp.DIFF_INSERT, \"b\")], self.dmp.diff_main(\"a\", \"b\", False))\n\n    self.assertEquals([(self.dmp.DIFF_DELETE, \"Apple\"), (self.dmp.DIFF_INSERT, \"Banana\"), (self.dmp.DIFF_EQUAL, \"s are a\"), (self.dmp.DIFF_INSERT, \"lso\"), (self.dmp.DIFF_EQUAL, \" fruit.\")], self.dmp.diff_main(\"Apples are a fruit.\", \"Bananas are also fruit.\", False))\n\n    self.assertEquals([(self.dmp.DIFF_DELETE, \"a\"), (self.dmp.DIFF_INSERT, u\"\\u0680\"), (self.dmp.DIFF_EQUAL, \"x\"), (self.dmp.DIFF_DELETE, \"\\t\"), (self.dmp.DIFF_INSERT, \"\\x00\")], self.dmp.diff_main(\"ax\\t\", u\"\\u0680x\\x00\", False))\n\n    # Overlaps.\n    self.assertEquals([(self.dmp.DIFF_DELETE, \"1\"), (self.dmp.DIFF_EQUAL, \"a\"), (self.dmp.DIFF_DELETE, \"y\"), (self.dmp.DIFF_EQUAL, \"b\"), (self.dmp.DIFF_DELETE, \"2\"), (self.dmp.DIFF_INSERT, \"xab\")], self.dmp.diff_main(\"1ayb2\", \"abxab\", False))\n\n    self.assertEquals([(self.dmp.DIFF_INSERT, \"xaxcx\"), (self.dmp.DIFF_EQUAL, \"abc\"), (self.dmp.DIFF_DELETE, \"y\")], self.dmp.diff_main(\"abcy\", \"xaxcxabc\", False))\n\n    self.assertEquals([(self.dmp.DIFF_DELETE, \"ABCD\"), (self.dmp.DIFF_EQUAL, \"a\"), (self.dmp.DIFF_DELETE, \"=\"), (self.dmp.DIFF_INSERT, \"-\"), (self.dmp.DIFF_EQUAL, \"bcd\"), (self.dmp.DIFF_DELETE, \"=\"), (self.dmp.DIFF_INSERT, \"-\"), (self.dmp.DIFF_EQUAL, \"efghijklmnopqrs\"), (self.dmp.DIFF_DELETE, \"EFGHIJKLMNOefg\")], self.dmp.diff_main(\"ABCDa=bcd=efghijklmnopqrsEFGHIJKLMNOefg\", \"a-bcd-efghijklmnopqrs\", False))\n\n    # Large equality.\n    self.assertEquals([(self.dmp.DIFF_INSERT, \" \"), (self.dmp.DIFF_EQUAL,\"a\"), (self.dmp.DIFF_INSERT,\"nd\"), (self.dmp.DIFF_EQUAL,\" [[Pennsylvania]]\"), (self.dmp.DIFF_DELETE,\" and [[New\")], self.dmp.diff_main(\"a [[Pennsylvania]] and [[New\", \" and [[Pennsylvania]]\", False))\n\n    # Timeout.\n    self.dmp.Diff_Timeout = 0.1  # 100ms\n    a = \"`Twas brillig, and the slithy toves\\nDid gyre and gimble in the wabe:\\nAll mimsy were the borogoves,\\nAnd the mome raths outgrabe.\\n\"\n    b = \"I am the very model of a modern major general,\\nI've information vegetable, animal, and mineral,\\nI know the kings of England, and I quote the fights historical,\\nFrom Marathon to Waterloo, in order categorical.\\n\"\n    # Increase the text lengths by 1024 times to ensure a timeout.\n    for i in range(10):\n      a += a\n      b += b\n    startTime = time.time()\n    self.dmp.diff_main(a, b)\n    endTime = time.time()\n    # Test that we took at least the timeout period.\n    self.assertTrue(self.dmp.Diff_Timeout <= endTime - startTime)\n    # Test that we didn't take forever (be forgiving).\n    # Theoretically this test could fail very occasionally if the\n    # OS task swaps or locks up for a second at the wrong moment.\n    self.assertTrue(self.dmp.Diff_Timeout * 2 > endTime - startTime)\n    self.dmp.Diff_Timeout = 0\n\n    # Test the linemode speedup.\n    # Must be long to pass the 100 char cutoff.\n    # Simple line-mode.\n    a = \"1234567890\\n\" * 13\n    b = \"abcdefghij\\n\" * 13\n    self.assertEquals(self.dmp.diff_main(a, b, False), self.dmp.diff_main(a, b, True))\n\n    # Single line-mode.\n    a = \"1234567890\" * 13\n    b = \"abcdefghij\" * 13\n    self.assertEquals(self.dmp.diff_main(a, b, False), self.dmp.diff_main(a, b, True))\n\n    # Overlap line-mode.\n    a = \"1234567890\\n\" * 13\n    b = \"abcdefghij\\n1234567890\\n1234567890\\n1234567890\\nabcdefghij\\n1234567890\\n1234567890\\n1234567890\\nabcdefghij\\n1234567890\\n1234567890\\n1234567890\\nabcdefghij\\n\"\n    texts_linemode = self.diff_rebuildtexts(self.dmp.diff_main(a, b, True))\n    texts_textmode = self.diff_rebuildtexts(self.dmp.diff_main(a, b, False))\n    self.assertEquals(texts_textmode, texts_linemode)\n\n    # Test null inputs.\n    try:\n      self.dmp.diff_main(None, None)\n      self.assertFalse(True)\n    except ValueError:\n      # Exception expected.\n      pass\n\n\nclass MatchTest(DiffMatchPatchTest):\n  \"\"\"MATCH TEST FUNCTIONS\"\"\"\n\n  def testMatchAlphabet(self):\n    # Initialise the bitmasks for Bitap.\n    self.assertEquals({\"a\":4, \"b\":2, \"c\":1}, self.dmp.match_alphabet(\"abc\"))\n\n    self.assertEquals({\"a\":37, \"b\":18, \"c\":8}, self.dmp.match_alphabet(\"abcaba\"))\n\n  def testMatchBitap(self):\n    self.dmp.Match_Distance = 100\n    self.dmp.Match_Threshold = 0.5\n    # Exact matches.\n    self.assertEquals(5, self.dmp.match_bitap(\"abcdefghijk\", \"fgh\", 5))\n\n    self.assertEquals(5, self.dmp.match_bitap(\"abcdefghijk\", \"fgh\", 0))\n\n    # Fuzzy matches.\n    self.assertEquals(4, self.dmp.match_bitap(\"abcdefghijk\", \"efxhi\", 0))\n\n    self.assertEquals(2, self.dmp.match_bitap(\"abcdefghijk\", \"cdefxyhijk\", 5))\n\n    self.assertEquals(-1, self.dmp.match_bitap(\"abcdefghijk\", \"bxy\", 1))\n\n    # Overflow.\n    self.assertEquals(2, self.dmp.match_bitap(\"123456789xx0\", \"3456789x0\", 2))\n\n    self.assertEquals(0, self.dmp.match_bitap(\"abcdef\", \"xxabc\", 4))\n\n    self.assertEquals(3, self.dmp.match_bitap(\"abcdef\", \"defyy\", 4))\n\n    self.assertEquals(0, self.dmp.match_bitap(\"abcdef\", \"xabcdefy\", 0))\n\n    # Threshold test.\n    self.dmp.Match_Threshold = 0.4\n    self.assertEquals(4, self.dmp.match_bitap(\"abcdefghijk\", \"efxyhi\", 1))\n\n    self.dmp.Match_Threshold = 0.3\n    self.assertEquals(-1, self.dmp.match_bitap(\"abcdefghijk\", \"efxyhi\", 1))\n\n    self.dmp.Match_Threshold = 0.0\n    self.assertEquals(1, self.dmp.match_bitap(\"abcdefghijk\", \"bcdef\", 1))\n    self.dmp.Match_Threshold = 0.5\n\n    # Multiple select.\n    self.assertEquals(0, self.dmp.match_bitap(\"abcdexyzabcde\", \"abccde\", 3))\n\n    self.assertEquals(8, self.dmp.match_bitap(\"abcdexyzabcde\", \"abccde\", 5))\n\n    # Distance test.\n    self.dmp.Match_Distance = 10  # Strict location.\n    self.assertEquals(-1, self.dmp.match_bitap(\"abcdefghijklmnopqrstuvwxyz\", \"abcdefg\", 24))\n\n    self.assertEquals(0, self.dmp.match_bitap(\"abcdefghijklmnopqrstuvwxyz\", \"abcdxxefg\", 1))\n\n    self.dmp.Match_Distance = 1000  # Loose location.\n    self.assertEquals(0, self.dmp.match_bitap(\"abcdefghijklmnopqrstuvwxyz\", \"abcdefg\", 24))\n\n\n  def testMatchMain(self):\n    # Full match.\n    # Shortcut matches.\n    self.assertEquals(0, self.dmp.match_main(\"abcdef\", \"abcdef\", 1000))\n\n    self.assertEquals(-1, self.dmp.match_main(\"\", \"abcdef\", 1))\n\n    self.assertEquals(3, self.dmp.match_main(\"abcdef\", \"\", 3))\n\n    self.assertEquals(3, self.dmp.match_main(\"abcdef\", \"de\", 3))\n\n    self.assertEquals(3, self.dmp.match_main(\"abcdef\", \"defy\", 4))\n\n    self.assertEquals(0, self.dmp.match_main(\"abcdef\", \"abcdefy\", 0))\n\n    # Complex match.\n    self.dmp.Match_Threshold = 0.7\n    self.assertEquals(4, self.dmp.match_main(\"I am the very model of a modern major general.\", \" that berry \", 5))\n    self.dmp.Match_Threshold = 0.5\n\n    # Test null inputs.\n    try:\n      self.dmp.match_main(None, None, 0)\n      self.assertFalse(True)\n    except ValueError:\n      # Exception expected.\n      pass\n\n\nclass PatchTest(DiffMatchPatchTest):\n  \"\"\"PATCH TEST FUNCTIONS\"\"\"\n\n  def testPatchObj(self):\n    # Patch Object.\n    p = dmp_module.patch_obj()\n    p.start1 = 20\n    p.start2 = 21\n    p.length1 = 18\n    p.length2 = 17\n    p.diffs = [(self.dmp.DIFF_EQUAL, \"jump\"), (self.dmp.DIFF_DELETE, \"s\"), (self.dmp.DIFF_INSERT, \"ed\"), (self.dmp.DIFF_EQUAL, \" over \"), (self.dmp.DIFF_DELETE, \"the\"), (self.dmp.DIFF_INSERT, \"a\"), (self.dmp.DIFF_EQUAL, \"\\nlaz\")]\n    strp = str(p)\n    self.assertEquals(\"@@ -21,18 +22,17 @@\\n jump\\n-s\\n+ed\\n  over \\n-the\\n+a\\n %0Alaz\\n\", strp)\n\n  def testPatchFromText(self):\n    self.assertEquals([], self.dmp.patch_fromText(\"\"))\n\n    strp = \"@@ -21,18 +22,17 @@\\n jump\\n-s\\n+ed\\n  over \\n-the\\n+a\\n %0Alaz\\n\"\n    self.assertEquals(strp, str(self.dmp.patch_fromText(strp)[0]))\n\n    self.assertEquals(\"@@ -1 +1 @@\\n-a\\n+b\\n\", str(self.dmp.patch_fromText(\"@@ -1 +1 @@\\n-a\\n+b\\n\")[0]))\n\n    self.assertEquals(\"@@ -1,3 +0,0 @@\\n-abc\\n\", str(self.dmp.patch_fromText(\"@@ -1,3 +0,0 @@\\n-abc\\n\")[0]))\n\n    self.assertEquals(\"@@ -0,0 +1,3 @@\\n+abc\\n\", str(self.dmp.patch_fromText(\"@@ -0,0 +1,3 @@\\n+abc\\n\")[0]))\n\n    # Generates error.\n    try:\n      self.dmp.patch_fromText(\"Bad\\nPatch\\n\")\n      self.assertFalse(True)\n    except ValueError:\n      # Exception expected.\n      pass\n\n  def testPatchToText(self):\n    strp = \"@@ -21,18 +22,17 @@\\n jump\\n-s\\n+ed\\n  over \\n-the\\n+a\\n  laz\\n\"\n    p = self.dmp.patch_fromText(strp)\n    self.assertEquals(strp, self.dmp.patch_toText(p))\n\n    strp = \"@@ -1,9 +1,9 @@\\n-f\\n+F\\n oo+fooba\\n@@ -7,9 +7,9 @@\\n obar\\n-,\\n+.\\n tes\\n\"\n    p = self.dmp.patch_fromText(strp)\n    self.assertEquals(strp, self.dmp.patch_toText(p))\n\n  def testPatchAddContext(self):\n    self.dmp.Patch_Margin = 4\n    p = self.dmp.patch_fromText(\"@@ -21,4 +21,10 @@\\n-jump\\n+somersault\\n\")[0]\n    self.dmp.patch_addContext(p, \"The quick brown fox jumps over the lazy dog.\")\n    self.assertEquals(\"@@ -17,12 +17,18 @@\\n fox \\n-jump\\n+somersault\\n s ov\\n\", str(p))\n\n    # Same, but not enough trailing context.\n    p = self.dmp.patch_fromText(\"@@ -21,4 +21,10 @@\\n-jump\\n+somersault\\n\")[0]\n    self.dmp.patch_addContext(p, \"The quick brown fox jumps.\")\n    self.assertEquals(\"@@ -17,10 +17,16 @@\\n fox \\n-jump\\n+somersault\\n s.\\n\", str(p))\n\n    # Same, but not enough leading context.\n    p = self.dmp.patch_fromText(\"@@ -3 +3,2 @@\\n-e\\n+at\\n\")[0]\n    self.dmp.patch_addContext(p, \"The quick brown fox jumps.\")\n    self.assertEquals(\"@@ -1,7 +1,8 @@\\n Th\\n-e\\n+at\\n  qui\\n\", str(p))\n\n    # Same, but with ambiguity.\n    p = self.dmp.patch_fromText(\"@@ -3 +3,2 @@\\n-e\\n+at\\n\")[0]\n    self.dmp.patch_addContext(p, \"The quick brown fox jumps.  The quick brown fox crashes.\")\n    self.assertEquals(\"@@ -1,27 +1,28 @@\\n Th\\n-e\\n+at\\n  quick brown fox jumps. \\n\", str(p))\n\n  def testPatchMake(self):\n    # Null case.\n    patches = self.dmp.patch_make(\"\", \"\")\n    self.assertEquals(\"\", self.dmp.patch_toText(patches))\n\n    text1 = \"The quick brown fox jumps over the lazy dog.\"\n    text2 = \"That quick brown fox jumped over a lazy dog.\"\n    # Text2+Text1 inputs.\n    expectedPatch = \"@@ -1,8 +1,7 @@\\n Th\\n-at\\n+e\\n  qui\\n@@ -21,17 +21,18 @@\\n jump\\n-ed\\n+s\\n  over \\n-a\\n+the\\n  laz\\n\"\n    # The second patch must be \"-21,17 +21,18\", not \"-22,17 +21,18\" due to rolling context.\n    patches = self.dmp.patch_make(text2, text1)\n    self.assertEquals(expectedPatch, self.dmp.patch_toText(patches))\n\n    # Text1+Text2 inputs.\n    expectedPatch = \"@@ -1,11 +1,12 @@\\n Th\\n-e\\n+at\\n  quick b\\n@@ -22,18 +22,17 @@\\n jump\\n-s\\n+ed\\n  over \\n-the\\n+a\\n  laz\\n\"\n    patches = self.dmp.patch_make(text1, text2)\n    self.assertEquals(expectedPatch, self.dmp.patch_toText(patches))\n\n    # Diff input.\n    diffs = self.dmp.diff_main(text1, text2, False)\n    patches = self.dmp.patch_make(diffs)\n    self.assertEquals(expectedPatch, self.dmp.patch_toText(patches))\n\n    # Text1+Diff inputs.\n    patches = self.dmp.patch_make(text1, diffs)\n    self.assertEquals(expectedPatch, self.dmp.patch_toText(patches))\n\n    # Text1+Text2+Diff inputs (deprecated).\n    patches = self.dmp.patch_make(text1, text2, diffs)\n    self.assertEquals(expectedPatch, self.dmp.patch_toText(patches))\n\n    # Character encoding.\n    patches = self.dmp.patch_make(\"`1234567890-=[]\\\\;',./\", \"~!@#$%^&*()_+{}|:\\\"<>?\")\n    self.assertEquals(\"@@ -1,21 +1,21 @@\\n-%601234567890-=%5B%5D%5C;',./\\n+~!@#$%25%5E&*()_+%7B%7D%7C:%22%3C%3E?\\n\", self.dmp.patch_toText(patches))\n\n    # Character decoding.\n    diffs = [(self.dmp.DIFF_DELETE, \"`1234567890-=[]\\\\;',./\"), (self.dmp.DIFF_INSERT, \"~!@#$%^&*()_+{}|:\\\"<>?\")]\n    self.assertEquals(diffs, self.dmp.patch_fromText(\"@@ -1,21 +1,21 @@\\n-%601234567890-=%5B%5D%5C;',./\\n+~!@#$%25%5E&*()_+%7B%7D%7C:%22%3C%3E?\\n\")[0].diffs)\n\n    # Long string with repeats.\n    text1 = \"\"\n    for x in range(100):\n      text1 += \"abcdef\"\n    text2 = text1 + \"123\"\n    expectedPatch = \"@@ -573,28 +573,31 @@\\n cdefabcdefabcdefabcdefabcdef\\n+123\\n\"\n    patches = self.dmp.patch_make(text1, text2)\n    self.assertEquals(expectedPatch, self.dmp.patch_toText(patches))\n\n    # Test null inputs.\n    try:\n      self.dmp.patch_make(None, None)\n      self.assertFalse(True)\n    except ValueError:\n      # Exception expected.\n      pass\n\n  def testPatchSplitMax(self):\n    # Assumes that Match_MaxBits is 32.\n    patches = self.dmp.patch_make(\"abcdefghijklmnopqrstuvwxyz01234567890\", \"XabXcdXefXghXijXklXmnXopXqrXstXuvXwxXyzX01X23X45X67X89X0\")\n    self.dmp.patch_splitMax(patches)\n    self.assertEquals(\"@@ -1,32 +1,46 @@\\n+X\\n ab\\n+X\\n cd\\n+X\\n ef\\n+X\\n gh\\n+X\\n ij\\n+X\\n kl\\n+X\\n mn\\n+X\\n op\\n+X\\n qr\\n+X\\n st\\n+X\\n uv\\n+X\\n wx\\n+X\\n yz\\n+X\\n 012345\\n@@ -25,13 +39,18 @@\\n zX01\\n+X\\n 23\\n+X\\n 45\\n+X\\n 67\\n+X\\n 89\\n+X\\n 0\\n\", self.dmp.patch_toText(patches))\n\n    patches = self.dmp.patch_make(\"abcdef1234567890123456789012345678901234567890123456789012345678901234567890uvwxyz\", \"abcdefuvwxyz\")\n    oldToText = self.dmp.patch_toText(patches)\n    self.dmp.patch_splitMax(patches)\n    self.assertEquals(oldToText, self.dmp.patch_toText(patches))\n\n    patches = self.dmp.patch_make(\"1234567890123456789012345678901234567890123456789012345678901234567890\", \"abc\")\n    self.dmp.patch_splitMax(patches)\n    self.assertEquals(\"@@ -1,32 +1,4 @@\\n-1234567890123456789012345678\\n 9012\\n@@ -29,32 +1,4 @@\\n-9012345678901234567890123456\\n 7890\\n@@ -57,14 +1,3 @@\\n-78901234567890\\n+abc\\n\", self.dmp.patch_toText(patches))\n\n    patches = self.dmp.patch_make(\"abcdefghij , h : 0 , t : 1 abcdefghij , h : 0 , t : 1 abcdefghij , h : 0 , t : 1\", \"abcdefghij , h : 1 , t : 1 abcdefghij , h : 1 , t : 1 abcdefghij , h : 0 , t : 1\")\n    self.dmp.patch_splitMax(patches)\n    self.assertEquals(\"@@ -2,32 +2,32 @@\\n bcdefghij , h : \\n-0\\n+1\\n  , t : 1 abcdef\\n@@ -29,32 +29,32 @@\\n bcdefghij , h : \\n-0\\n+1\\n  , t : 1 abcdef\\n\", self.dmp.patch_toText(patches))\n\n  def testPatchAddPadding(self):\n    # Both edges full.\n    patches = self.dmp.patch_make(\"\", \"test\")\n    self.assertEquals(\"@@ -0,0 +1,4 @@\\n+test\\n\", self.dmp.patch_toText(patches))\n    self.dmp.patch_addPadding(patches)\n    self.assertEquals(\"@@ -1,8 +1,12 @@\\n %01%02%03%04\\n+test\\n %01%02%03%04\\n\", self.dmp.patch_toText(patches))\n\n    # Both edges partial.\n    patches = self.dmp.patch_make(\"XY\", \"XtestY\")\n    self.assertEquals(\"@@ -1,2 +1,6 @@\\n X\\n+test\\n Y\\n\", self.dmp.patch_toText(patches))\n    self.dmp.patch_addPadding(patches)\n    self.assertEquals(\"@@ -2,8 +2,12 @@\\n %02%03%04X\\n+test\\n Y%01%02%03\\n\", self.dmp.patch_toText(patches))\n\n    # Both edges none.\n    patches = self.dmp.patch_make(\"XXXXYYYY\", \"XXXXtestYYYY\")\n    self.assertEquals(\"@@ -1,8 +1,12 @@\\n XXXX\\n+test\\n YYYY\\n\", self.dmp.patch_toText(patches))\n    self.dmp.patch_addPadding(patches)\n    self.assertEquals(\"@@ -5,8 +5,12 @@\\n XXXX\\n+test\\n YYYY\\n\", self.dmp.patch_toText(patches))\n\n  def testPatchApply(self):\n    self.dmp.Match_Distance = 1000\n    self.dmp.Match_Threshold = 0.5\n    self.dmp.Patch_DeleteThreshold = 0.5\n    # Null case.\n    patches = self.dmp.patch_make(\"\", \"\")\n    results = self.dmp.patch_apply(patches, \"Hello world.\")\n    self.assertEquals((\"Hello world.\", []), results)\n\n    # Exact match.\n    patches = self.dmp.patch_make(\"The quick brown fox jumps over the lazy dog.\", \"That quick brown fox jumped over a lazy dog.\")\n    results = self.dmp.patch_apply(patches, \"The quick brown fox jumps over the lazy dog.\")\n    self.assertEquals((\"That quick brown fox jumped over a lazy dog.\", [True, True]), results)\n\n    # Partial match.\n    results = self.dmp.patch_apply(patches, \"The quick red rabbit jumps over the tired tiger.\")\n    self.assertEquals((\"That quick red rabbit jumped over a tired tiger.\", [True, True]), results)\n\n    # Failed match.\n    results = self.dmp.patch_apply(patches, \"I am the very model of a modern major general.\")\n    self.assertEquals((\"I am the very model of a modern major general.\", [False, False]), results)\n\n    # Big delete, small change.\n    patches = self.dmp.patch_make(\"x1234567890123456789012345678901234567890123456789012345678901234567890y\", \"xabcy\")\n    results = self.dmp.patch_apply(patches, \"x123456789012345678901234567890-----++++++++++-----123456789012345678901234567890y\")\n    self.assertEquals((\"xabcy\", [True, True]), results)\n\n    # Big delete, big change 1.\n    patches = self.dmp.patch_make(\"x1234567890123456789012345678901234567890123456789012345678901234567890y\", \"xabcy\")\n    results = self.dmp.patch_apply(patches, \"x12345678901234567890---------------++++++++++---------------12345678901234567890y\")\n    self.assertEquals((\"xabc12345678901234567890---------------++++++++++---------------12345678901234567890y\", [False, True]), results)\n\n    # Big delete, big change 2.\n    self.dmp.Patch_DeleteThreshold = 0.6\n    patches = self.dmp.patch_make(\"x1234567890123456789012345678901234567890123456789012345678901234567890y\", \"xabcy\")\n    results = self.dmp.patch_apply(patches, \"x12345678901234567890---------------++++++++++---------------12345678901234567890y\")\n    self.assertEquals((\"xabcy\", [True, True]), results)\n    self.dmp.Patch_DeleteThreshold = 0.5\n\n    # Compensate for failed patch.\n    self.dmp.Match_Threshold = 0.0\n    self.dmp.Match_Distance = 0\n    patches = self.dmp.patch_make(\"abcdefghijklmnopqrstuvwxyz--------------------1234567890\", \"abcXXXXXXXXXXdefghijklmnopqrstuvwxyz--------------------1234567YYYYYYYYYY890\")\n    results = self.dmp.patch_apply(patches, \"ABCDEFGHIJKLMNOPQRSTUVWXYZ--------------------1234567890\")\n    self.assertEquals((\"ABCDEFGHIJKLMNOPQRSTUVWXYZ--------------------1234567YYYYYYYYYY890\", [False, True]), results)\n    self.dmp.Match_Threshold = 0.5\n    self.dmp.Match_Distance = 1000\n\n    # No side effects.\n    patches = self.dmp.patch_make(\"\", \"test\")\n    patchstr = self.dmp.patch_toText(patches)\n    results = self.dmp.patch_apply(patches, \"\")\n    self.assertEquals(patchstr, self.dmp.patch_toText(patches))\n\n    # No side effects with major delete.\n    patches = self.dmp.patch_make(\"The quick brown fox jumps over the lazy dog.\", \"Woof\")\n    patchstr = self.dmp.patch_toText(patches)\n    self.dmp.patch_apply(patches, \"The quick brown fox jumps over the lazy dog.\")\n    self.assertEquals(patchstr, self.dmp.patch_toText(patches))\n\n    # Edge exact match.\n    patches = self.dmp.patch_make(\"\", \"test\")\n    self.dmp.patch_apply(patches, \"\")\n    self.assertEquals((\"test\", [True]), results)\n\n    # Near edge exact match.\n    patches = self.dmp.patch_make(\"XY\", \"XtestY\")\n    results = self.dmp.patch_apply(patches, \"XY\")\n    self.assertEquals((\"XtestY\", [True]), results)\n\n    # Edge partial match.\n    patches = self.dmp.patch_make(\"y\", \"y123\")\n    results = self.dmp.patch_apply(patches, \"x\")\n    self.assertEquals((\"x123\", [True]), results)\n\n\nif __name__ == \"__main__\":\n  unittest.main()\n"
  },
  {
    "path": "python2/tests/speedtest.py",
    "content": "#!/usr/bin/python2.4\n\n\"\"\"Diff Speed Test\nCopyright 2018 The diff-match-patch Authors.\nhttps://github.com/google/diff-match-patch\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n  http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n\"\"\"\n\nimport gc\nimport os\nimport sys\nimport time\nparentPath = os.path.abspath(\"..\")\nif parentPath not in sys.path:\n    sys.path.insert(0, parentPath)\nimport diff_match_patch as dmp_module\n# Force a module reload.  Allows one to edit the DMP module and rerun the test\n# without leaving the Python interpreter.\nreload(dmp_module)\n\ndef main():\n  text1 = open(\"speedtest1.txt\").read()\n  text2 = open(\"speedtest2.txt\").read()\n\n  dmp = dmp_module.diff_match_patch()\n  dmp.Diff_Timeout = 0.0\n\n  # Execute one reverse diff as a warmup.\n  dmp.diff_main(text2, text1, False)\n  gc.collect()\n\n  start_time = time.time()\n  dmp.diff_main(text1, text2, False)\n  end_time = time.time()\n  print \"Elapsed time: %f\" % (end_time - start_time)\n\nif __name__ == \"__main__\":\n  main()\n"
  },
  {
    "path": "python2/tests/speedtest1.txt",
    "content": "This is a '''list of newspapers published by [[Journal Register Company]]'''.\n\nThe company owns daily and weekly newspapers, other print media properties and newspaper-affiliated local Websites in the [[U.S.]] states of [[Connecticut]], [[Michigan]], [[New York]], [[Ohio]] and [[Pennsylvania]], organized in six geographic \"clusters\":<ref>[http://www.journalregister.com/newspapers.html Journal Register Company: Our Newspapers], accessed February 10, 2008.</ref>\n\n== Capital-Saratoga ==\nThree dailies, associated weeklies and [[pennysaver]]s in greater [[Albany, New York]]; also [http://www.capitalcentral.com capitalcentral.com] and [http://www.jobsinnewyork.com JobsInNewYork.com].\n\n* ''The Oneida Daily Dispatch'' {{WS|oneidadispatch.com}} of [[Oneida, New York]]\n* ''[[The Record (Troy)|The Record]]'' {{WS|troyrecord.com}} of [[Troy, New York]]\n* ''[[The Saratogian]]'' {{WS|saratogian.com}} of [[Saratoga Springs, New York]]\n* Weeklies:\n** ''Community News'' {{WS|cnweekly.com}} weekly of [[Clifton Park, New York]]\n** ''Rome Observer'' of [[Rome, New York]]\n** ''Life & Times of Utica'' of [[Utica, New York]]\n\n== Connecticut ==\nFive dailies, associated weeklies and [[pennysaver]]s in the state of [[Connecticut]]; also [http://www.ctcentral.com CTcentral.com], [http://www.ctcarsandtrucks.com CTCarsAndTrucks.com] and [http://www.jobsinct.com JobsInCT.com].\n\n* ''The Middletown Press'' {{WS|middletownpress.com}} of [[Middletown, Connecticut|Middletown]]\n* ''[[New Haven Register]]'' {{WS|newhavenregister.com}} of [[New Haven, Connecticut|New Haven]]\n* ''The Register Citizen'' {{WS|registercitizen.com}} of [[Torrington, Connecticut|Torrington]]\n\n* [[New Haven Register#Competitors|Elm City Newspapers]] {{WS|ctcentral.com}}\n** ''The Advertiser'' of [[East Haven, Connecticut|East Haven]]\n** ''Hamden Chronicle'' of [[Hamden, Connecticut|Hamden]]\n** ''Milford Weekly'' of [[Milford, Connecticut|Milford]]\n** ''The Orange Bulletin'' of [[Orange, Connecticut|Orange]]\n** ''The Post'' of [[North Haven, Connecticut|North Haven]]\n** ''Shelton Weekly'' of [[Shelton, Connecticut|Shelton]]\n** ''The Stratford Bard'' of [[Stratford, Connecticut|Stratford]]\n** ''Wallingford Voice'' of [[Wallingford, Connecticut|Wallingford]]\n** ''West Haven News'' of [[West Haven, Connecticut|West Haven]]\n* Housatonic Publications \n** ''The New Milford Times'' {{WS|newmilfordtimes.com}} of [[New Milford, Connecticut|New Milford]]\n** ''The Brookfield Journal'' of [[Brookfield, Connecticut|Brookfield]]\n** ''The Kent Good Times Dispatch'' of [[Kent, Connecticut|Kent]]\n** ''The Bethel Beacon'' of [[Bethel, Connecticut|Bethel]]\n** ''The Litchfield Enquirer'' of [[Litchfield, Connecticut|Litchfield]]\n** ''Litchfield County Times'' of [[Litchfield, Connecticut|Litchfield]]\n* Imprint Newspapers {{WS|imprintnewspapers.com}}\n** ''West Hartford News'' of [[West Hartford, Connecticut|West Hartford]]\n** ''Windsor Journal'' of [[Windsor, Connecticut|Windsor]]\n** ''Windsor Locks Journal'' of [[Windsor Locks, Connecticut|Windsor Locks]]\n** ''Avon Post'' of [[Avon, Connecticut|Avon]]\n** ''Farmington Post'' of [[Farmington, Connecticut|Farmington]]\n** ''Simsbury Post'' of [[Simsbury, Connecticut|Simsbury]]\n** ''Tri-Town Post'' of [[Burlington, Connecticut|Burlington]], [[Canton, Connecticut|Canton]] and [[Harwinton, Connecticut|Harwinton]]\n* Minuteman Publications\n** ''[[Fairfield Minuteman]]'' of [[Fairfield, Connecticut|Fairfield]]\n** ''The Westport Minuteman'' {{WS|westportminuteman.com}} of [[Westport, Connecticut|Westport]]\n* Shoreline Newspapers weeklies:\n** ''Branford Review'' of [[Branford, Connecticut|Branford]]\n** ''Clinton Recorder'' of [[Clinton, Connecticut|Clinton]]\n** ''The Dolphin'' of [[Naval Submarine Base New London]] in [[New London, Connecticut|New London]]\n** ''Main Street News'' {{WS|ctmainstreetnews.com}} of [[Essex, Connecticut|Essex]]\n** ''Pictorial Gazette'' of [[Old Saybrook, Connecticut|Old Saybrook]]\n** ''Regional Express'' of [[Colchester, Connecticut|Colchester]]\n** ''Regional Standard'' of [[Colchester, Connecticut|Colchester]]\n** ''Shoreline Times'' {{WS|shorelinetimes.com}} of [[Guilford, Connecticut|Guilford]]\n** ''Shore View East'' of [[Madison, Connecticut|Madison]]\n** ''Shore View West'' of [[Guilford, Connecticut|Guilford]]\n* Other weeklies:\n** ''Registro'' {{WS|registroct.com}} of [[New Haven, Connecticut|New Haven]]\n** ''Thomaston Express'' {{WS|thomastownexpress.com}} of [[Thomaston, Connecticut|Thomaston]]\n** ''Foothills Traders'' {{WS|foothillstrader.com}} of Torrington, Bristol, Canton\n\n== Michigan ==\nFour dailies, associated weeklies and [[pennysaver]]s in the state of [[Michigan]]; also [http://www.micentralhomes.com MIcentralhomes.com] and [http://www.micentralautos.com MIcentralautos.com]\n* ''[[Oakland Press]]'' {{WS|theoaklandpress.com}} of [[Oakland, Michigan|Oakland]]\n* ''Daily Tribune'' {{WS|dailytribune.com}} of [[Royal Oak, Michigan|Royal Oak]]\n* ''Macomb Daily'' {{WS|macombdaily.com}} of [[Mt. Clemens, Michigan|Mt. Clemens]]\n* ''[[Morning Sun]]'' {{WS|themorningsun.com}} of  [[Mount Pleasant, Michigan|Mount Pleasant]]\n* Heritage Newspapers {{WS|heritage.com}}\n** ''Belleville View''\n** ''Ile Camera''\n** ''Monroe Guardian''\n** ''Ypsilanti Courier''\n** ''News-Herald''\n** ''Press & Guide''\n** ''Chelsea Standard & Dexter Leader''\n** ''Manchester Enterprise''\n** ''Milan News-Leader''\n** ''Saline Reporter''\n* Independent Newspapers {{WS|sourcenewspapers.com}}\n** ''Advisor''\n** ''Source''\n* Morning Star {{WS|morningstarpublishing.com}}\n** ''Alma Reminder''\n** ''Alpena Star''\n** ''Antrim County News''\n** ''Carson City Reminder''\n** ''The Leader & Kalkaskian''\n** ''Ogemaw/Oscoda County Star''\n** ''Petoskey/Charlevoix Star''\n** ''Presque Isle Star''\n** ''Preview Community Weekly''\n** ''Roscommon County Star''\n** ''St. Johns Reminder''\n** ''Straits Area Star''\n** ''The (Edmore) Advertiser'' \n* Voice Newspapers {{WS|voicenews.com}}\n** ''Armada Times''\n** ''Bay Voice''\n** ''Blue Water Voice''\n** ''Downriver Voice''\n** ''Macomb Township Voice''\n** ''North Macomb Voice''\n** ''Weekend Voice''\n** ''Suburban Lifestyles'' {{WS|suburbanlifestyles.com}}\n\n== Mid-Hudson ==\nOne daily, associated magazines in the [[Hudson River Valley]] of [[New York]]; also [http://www.midhudsoncentral.com MidHudsonCentral.com] and [http://www.jobsinnewyork.com JobsInNewYork.com].\n\n* ''[[Daily Freeman]]'' {{WS|dailyfreeman.com}} of [[Kingston, New York]]\n\n== Ohio ==\nTwo dailies, associated magazines and three shared Websites, all in the state of [[Ohio]]: [http://www.allaroundcleveland.com AllAroundCleveland.com], [http://www.allaroundclevelandcars.com AllAroundClevelandCars.com] and [http://www.allaroundclevelandjobs.com AllAroundClevelandJobs.com].\n\n* ''[[The News-Herald (Ohio)|The News-Herald]]'' {{WS|news-herald.com}} of [[Willoughby, Ohio|Willoughby]]\n* ''[[The Morning Journal]]'' {{WS|morningjournal.com}} of [[Lorain, Ohio|Lorain]]\n\n== Philadelphia area ==\nSeven dailies and associated weeklies and magazines in [[Pennsylvania]] and [[New Jersey]], and associated Websites: [http://www.allaroundphilly.com AllAroundPhilly.com], [http://www.jobsinnj.com JobsInNJ.com], [http://www.jobsinpa.com JobsInPA.com], and [http://www.phillycarsearch.com PhillyCarSearch.com].\n\n* ''The Daily Local'' {{WS|dailylocal.com}} of [[West Chester, Pennsylvania|West Chester]]\n* ''[[Delaware County Daily and Sunday Times]] {{WS|delcotimes.com}} of Primos\n* ''[[The Mercury (Pennsylvania)|The Mercury]]'' {{WS|pottstownmercury.com}} of [[Pottstown, Pennsylvania|Pottstown]]\n* ''The Phoenix'' {{WS|phoenixvillenews.com}} of [[Phoenixville, Pennsylvania|Phoenixville]]\n* ''[[The Reporter (Lansdale)|The Reporter]]'' {{WS|thereporteronline.com}} of [[Lansdale, Pennsylvania|Lansdale]]\n* ''The Times Herald'' {{WS|timesherald.com}} of [[Norristown, Pennsylvania|Norristown]]\n* ''[[The Trentonian]]'' {{WS|trentonian.com}} of [[Trenton, New Jersey]]\n\n* Weeklies\n** ''El Latino Expreso'' of [[Trenton, New Jersey]]\n** ''La Voz'' of [[Norristown, Pennsylvania]]\n** ''The Village News'' of [[Downingtown, Pennsylvania]]\n** ''The Times Record'' of [[Kennett Square, Pennsylvania]]\n** ''The Tri-County Record'' {{WS|tricountyrecord.com}} of [[Morgantown, Pennsylvania]]\n** ''News of Delaware County'' {{WS|newsofdelawarecounty.com}}of [[Havertown, Pennsylvania]]\n** ''Main Line Times'' {{WS|mainlinetimes.com}}of [[Ardmore, Pennsylvania]]\n** ''Penny Pincher'' of [[Pottstown, Pennsylvania]]\n** ''Town Talk'' {{WS|towntalknews.com}} of [[Ridley, Pennsylvania]]\n* Chesapeake Publishing {{WS|pa8newsgroup.com}} \n** ''Solanco Sun Ledger'' of [[Quarryville, Pennsylvania]]\n** ''Columbia Ledger'' of [[Columbia, Pennsylvania]]\n** ''Coatesville Ledger'' of [[Downingtown, Pennsylvania]]\n** ''Parkesburg Post Ledger'' of [[Quarryville, Pennsylvania]]\n** ''Downingtown Ledger'' of [[Downingtown, Pennsylvania]]\n** ''The Kennett Paper'' of [[Kennett Square, Pennsylvania]]\n** ''Avon Grove Sun'' of [[West Grove, Pennsylvania]]\n** ''Oxford Tribune'' of [[Oxford, Pennsylvania]]\n** ''Elizabethtown Chronicle'' of [[Elizabethtown, Pennsylvania]]\n** ''Donegal Ledger'' of [[Donegal, Pennsylvania]]\n** ''Chadds Ford Post'' of [[Chadds Ford, Pennsylvania]]\n** ''The Central Record'' of [[Medford, New Jersey]]\n** ''Maple Shade Progress'' of [[Maple Shade, New Jersey]]\n* Intercounty Newspapers {{WS|buckslocalnews.com}} \n** ''The Review'' of Roxborough, Pennsylvania\n** ''The Recorder'' of [[Conshohocken, Pennsylvania]]\n** ''The Leader'' of [[Mount Airy, Pennsylvania|Mount Airy]] and West Oak Lake, Pennsylvania\n** ''The Pennington Post'' of [[Pennington, New Jersey]]\n** ''The Bristol Pilot'' of [[Bristol, Pennsylvania]]\n** ''Yardley News'' of [[Yardley, Pennsylvania]]\n** ''New Hope Gazette'' of [[New Hope, Pennsylvania]]\n** ''Doylestown Patriot'' of [[Doylestown, Pennsylvania]]\n** ''Newtown Advance'' of [[Newtown, Pennsylvania]]\n** ''The Plain Dealer'' of [[Williamstown, New Jersey]]\n** ''News Report'' of [[Sewell, New Jersey]]\n** ''Record Breeze'' of [[Berlin, New Jersey]]\n** ''Newsweekly'' of [[Moorestown, New Jersey]]\n** ''Haddon Herald'' of [[Haddonfield, New Jersey]]\n** ''New Egypt Press'' of [[New Egypt, New Jersey]]\n** ''Community News'' of [[Pemberton, New Jersey]]\n** ''Plymouth Meeting Journal'' of [[Plymouth Meeting, Pennsylvania]]\n** ''Lafayette Hill Journal'' of [[Lafayette Hill, Pennsylvania]]\n* Montgomery Newspapers {{WS|montgomerynews.com}} \n** ''Ambler Gazette'' of [[Ambler, Pennsylvania]]\n** ''Central Bucks Life'' of [[Bucks County, Pennsylvania]]\n** ''The Colonial'' of [[Plymouth Meeting, Pennsylvania]]\n** ''Glenside News'' of [[Glenside, Pennsylvania]]\n** ''The Globe'' of [[Lower Moreland Township, Pennsylvania]]\n** ''Main Line Life'' of [[Ardmore, Pennsylvania]]\n** ''Montgomery Life'' of [[Fort Washington, Pennsylvania]]\n** ''North Penn Life'' of [[Lansdale, Pennsylvania]]\n** ''Perkasie News Herald'' of [[Perkasie, Pennsylvania]]\n** ''Public Spirit'' of [[Hatboro, Pennsylvania]]\n** ''Souderton Independent'' of [[Souderton, Pennsylvania]]\n** ''Springfield Sun'' of [[Springfield, Pennsylvania]]\n** ''Spring-Ford Reporter'' of [[Royersford, Pennsylvania]]\n** ''Times Chronicle'' of [[Jenkintown, Pennsylvania]]\n** ''Valley Item'' of [[Perkiomenville, Pennsylvania]]\n** ''Willow Grove Guide'' of [[Willow Grove, Pennsylvania]]\n* News Gleaner Publications (closed December 2008) {{WS|newsgleaner.com}} \n** ''Life Newspapers'' of [[Philadelphia, Pennsylvania]]\n* Suburban Publications\n** ''The Suburban & Wayne Times'' {{WS|waynesuburban.com}} of [[Wayne, Pennsylvania]]\n** ''The Suburban Advertiser'' of [[Exton, Pennsylvania]]\n** ''The King of Prussia Courier'' of [[King of Prussia, Pennsylvania]]\n* Press Newspapers {{WS|countypressonline.com}} \n** ''County Press'' of [[Newtown Square, Pennsylvania]]\n** ''Garnet Valley Press'' of [[Glen Mills, Pennsylvania]]\n** ''Haverford Press'' of [[Newtown Square, Pennsylvania]] (closed January 2009)\n** ''Hometown Press'' of [[Glen Mills, Pennsylvania]] (closed January 2009)\n** ''Media Press'' of [[Newtown Square, Pennsylvania]] (closed January 2009)\n** ''Springfield Press'' of [[Springfield, Pennsylvania]]\n* Berks-Mont Newspapers {{WS|berksmontnews.com}} \n** ''The Boyertown Area Times'' of [[Boyertown, Pennsylvania]]\n** ''The Kutztown Area Patriot'' of [[Kutztown, Pennsylvania]]\n** ''The Hamburg Area Item'' of [[Hamburg, Pennsylvania]]\n** ''The Southern Berks News'' of [[Exeter Township, Berks County, Pennsylvania]]\n** ''The Free Press'' of [[Quakertown, Pennsylvania]]\n** ''The Saucon News'' of [[Quakertown, Pennsylvania]]\n** ''Westside Weekly'' of [[Reading, Pennsylvania]]\n\n* Magazines\n** ''Bucks Co. Town & Country Living''\n** ''Chester Co. Town & Country Living''\n** ''Montomgery Co. Town & Country Living''\n** ''Garden State Town & Country Living''\n** ''Montgomery Homes''\n** ''Philadelphia Golfer''\n** ''Parents Express''\n** ''Art Matters''\n\n{{JRC}}\n\n==References==\n<references />\n\n[[Category:Journal Register publications|*]]\n"
  },
  {
    "path": "python2/tests/speedtest2.txt",
    "content": "This is a '''list of newspapers published by [[Journal Register Company]]'''.\n\nThe company owns daily and weekly newspapers, other print media properties and newspaper-affiliated local Websites in the [[U.S.]] states of [[Connecticut]], [[Michigan]], [[New York]], [[Ohio]], [[Pennsylvania]] and [[New Jersey]], organized in six geographic \"clusters\":<ref>[http://www.journalregister.com/publications.html Journal Register Company: Our Publications], accessed April 21, 2010.</ref>\n\n== Capital-Saratoga ==\nThree dailies, associated weeklies and [[pennysaver]]s in greater [[Albany, New York]]; also [http://www.capitalcentral.com capitalcentral.com] and [http://www.jobsinnewyork.com JobsInNewYork.com].\n\n* ''The Oneida Daily Dispatch'' {{WS|oneidadispatch.com}} of [[Oneida, New York]]\n* ''[[The Record (Troy)|The Record]]'' {{WS|troyrecord.com}} of [[Troy, New York]]\n* ''[[The Saratogian]]'' {{WS|saratogian.com}} of [[Saratoga Springs, New York]]\n* Weeklies:\n** ''Community News'' {{WS|cnweekly.com}} weekly of [[Clifton Park, New York]]\n** ''Rome Observer'' {{WS|romeobserver.com}} of [[Rome, New York]]\n** ''WG Life '' {{WS|saratogian.com/wglife/}} of [[Wilton, New York]]\n** ''Ballston Spa Life '' {{WS|saratogian.com/bspalife}} of [[Ballston Spa, New York]]\n** ''Greenbush Life'' {{WS|troyrecord.com/greenbush}} of [[Troy, New York]]\n** ''Latham Life'' {{WS|troyrecord.com/latham}} of [[Latham, New York]]\n** ''River Life'' {{WS|troyrecord.com/river}} of [[Troy, New York]]\n\n== Connecticut ==\nThree dailies, associated weeklies and [[pennysaver]]s in the state of [[Connecticut]]; also [http://www.ctcentral.com CTcentral.com], [http://www.ctcarsandtrucks.com CTCarsAndTrucks.com] and [http://www.jobsinct.com JobsInCT.com].\n\n* ''The Middletown Press'' {{WS|middletownpress.com}} of [[Middletown, Connecticut|Middletown]]\n* ''[[New Haven Register]]'' {{WS|newhavenregister.com}} of [[New Haven, Connecticut|New Haven]]\n* ''The Register Citizen'' {{WS|registercitizen.com}} of [[Torrington, Connecticut|Torrington]]\n\n* Housatonic Publications \n** ''The Housatonic Times'' {{WS|housatonictimes.com}} of [[New Milford, Connecticut|New Milford]]\n** ''Litchfield County Times'' {{WS|countytimes.com}} of [[Litchfield, Connecticut|Litchfield]]\n\n* Minuteman Publications\n** ''[[Fairfield Minuteman]]'' {{WS|fairfieldminuteman.com}}of [[Fairfield, Connecticut|Fairfield]]\n** ''The Westport Minuteman'' {{WS|westportminuteman.com}} of [[Westport, Connecticut|Westport]]\n\n* Shoreline Newspapers \n** ''The Dolphin'' {{WS|dolphin-news.com}} of [[Naval Submarine Base New London]] in [[New London, Connecticut|New London]]\n** ''Shoreline Times'' {{WS|shorelinetimes.com}} of [[Guilford, Connecticut|Guilford]]\n\n* Foothills Media Group {{WS|foothillsmediagroup.com}}\n** ''Thomaston Express'' {{WS|thomastonexpress.com}} of [[Thomaston, Connecticut|Thomaston]]\n** ''Good News About Torrington'' {{WS|goodnewsabouttorrington.com}} of [[Torrington, Connecticut|Torrington]]\n** ''Granby News'' {{WS|foothillsmediagroup.com/granby}} of [[Granby, Connecticut|Granby]]\n** ''Canton News'' {{WS|foothillsmediagroup.com/canton}} of [[Canton, Connecticut|Canton]]\n** ''Avon News'' {{WS|foothillsmediagroup.com/avon}} of [[Avon, Connecticut|Avon]]\n** ''Simsbury News'' {{WS|foothillsmediagroup.com/simsbury}} of [[Simsbury, Connecticut|Simsbury]]\n** ''Litchfield News'' {{WS|foothillsmediagroup.com/litchfield}} of [[Litchfield, Connecticut|Litchfield]]\n** ''Foothills Trader'' {{WS|foothillstrader.com}} of Torrington, Bristol, Canton\n\n* Other weeklies\n** ''The Milford-Orange Bulletin'' {{WS|ctbulletin.com}} of [[Orange, Connecticut|Orange]]\n** ''The Post-Chronicle'' {{WS|ctpostchronicle.com}} of [[North Haven, Connecticut|North Haven]]\n** ''West Hartford News'' {{WS|westhartfordnews.com}} of [[West Hartford, Connecticut|West Hartford]]\n\n* Magazines\n** ''The Connecticut Bride'' {{WS|connecticutmag.com}}\n** ''Connecticut Magazine'' {{WS|theconnecticutbride.com}}\n** ''Passport Magazine'' {{WS|passport-mag.com}}\n\n== Michigan ==\nFour dailies, associated weeklies and [[pennysaver]]s in the state of [[Michigan]]; also [http://www.micentralhomes.com MIcentralhomes.com] and [http://www.micentralautos.com MIcentralautos.com]\n* ''[[Oakland Press]]'' {{WS|theoaklandpress.com}} of [[Oakland, Michigan|Oakland]]\n* ''Daily Tribune'' {{WS|dailytribune.com}} of [[Royal Oak, Michigan|Royal Oak]]\n* ''Macomb Daily'' {{WS|macombdaily.com}} of [[Mt. Clemens, Michigan|Mt. Clemens]]\n* ''[[Morning Sun]]'' {{WS|themorningsun.com}} of  [[Mount Pleasant, Michigan|Mount Pleasant]]\n\n* Heritage Newspapers {{WS|heritage.com}}\n** ''Belleville View'' {{WS|bellevilleview.com}}\n** ''Ile Camera'' {{WS|thenewsherald.com/ile_camera}}\n** ''Monroe Guardian''  {{WS|monreguardian.com}}\n** ''Ypsilanti Courier'' {{WS|ypsilanticourier.com}}\n** ''News-Herald'' {{WS|thenewsherald.com}}\n** ''Press & Guide'' {{WS|pressandguide.com}}\n** ''Chelsea Standard & Dexter Leader'' {{WS|chelseastandard.com}}\n** ''Manchester Enterprise'' {{WS|manchesterguardian.com}}\n** ''Milan News-Leader'' {{WS|milannews.com}}\n** ''Saline Reporter'' {{WS|salinereporter.com}}\n* Independent Newspapers \n** ''Advisor'' {{WS|sourcenewspapers.com}}\n** ''Source'' {{WS|sourcenewspapers.com}}\n* Morning Star {{WS|morningstarpublishing.com}}\n** ''The Leader & Kalkaskian'' {{WS|leaderandkalkaskian.com}}\n** ''Grand Traverse Insider'' {{WS|grandtraverseinsider.com}}\n** ''Alma Reminder''\n** ''Alpena Star''\n** ''Ogemaw/Oscoda County Star''\n** ''Presque Isle Star''\n** ''St. Johns Reminder''\n\n* Voice Newspapers {{WS|voicenews.com}}\n** ''Armada Times''\n** ''Bay Voice''\n** ''Blue Water Voice''\n** ''Downriver Voice''\n** ''Macomb Township Voice''\n** ''North Macomb Voice''\n** ''Weekend Voice''\n\n== Mid-Hudson ==\nOne daily, associated magazines in the [[Hudson River Valley]] of [[New York]]; also [http://www.midhudsoncentral.com MidHudsonCentral.com] and [http://www.jobsinnewyork.com JobsInNewYork.com].\n\n* ''[[Daily Freeman]]'' {{WS|dailyfreeman.com}} of [[Kingston, New York]]\n* ''Las Noticias'' {{WS|lasnoticiasny.com}} of [[Kingston, New York]]\n\n== Ohio ==\nTwo dailies, associated magazines and three shared Websites, all in the state of [[Ohio]]: [http://www.allaroundcleveland.com AllAroundCleveland.com], [http://www.allaroundclevelandcars.com AllAroundClevelandCars.com] and [http://www.allaroundclevelandjobs.com AllAroundClevelandJobs.com].\n\n* ''[[The News-Herald (Ohio)|The News-Herald]]'' {{WS|news-herald.com}} of [[Willoughby, Ohio|Willoughby]]\n* ''[[The Morning Journal]]'' {{WS|morningjournal.com}} of [[Lorain, Ohio|Lorain]]\n* ''El Latino Expreso'' {{WS|lorainlatino.com}} of [[Lorain, Ohio|Lorain]]\n\n== Philadelphia area ==\nSeven dailies and associated weeklies and magazines in [[Pennsylvania]] and [[New Jersey]], and associated Websites: [http://www.allaroundphilly.com AllAroundPhilly.com], [http://www.jobsinnj.com JobsInNJ.com], [http://www.jobsinpa.com JobsInPA.com], and [http://www.phillycarsearch.com PhillyCarSearch.com].\n\n* ''[[The Daily Local News]]'' {{WS|dailylocal.com}} of [[West Chester, Pennsylvania|West Chester]]\n* ''[[Delaware County Daily and Sunday Times]] {{WS|delcotimes.com}} of Primos [[Upper Darby Township, Pennsylvania]]\n* ''[[The Mercury (Pennsylvania)|The Mercury]]'' {{WS|pottstownmercury.com}} of [[Pottstown, Pennsylvania|Pottstown]]\n* ''[[The Reporter (Lansdale)|The Reporter]]'' {{WS|thereporteronline.com}} of [[Lansdale, Pennsylvania|Lansdale]]\n* ''The Times Herald'' {{WS|timesherald.com}} of [[Norristown, Pennsylvania|Norristown]]\n* ''[[The Trentonian]]'' {{WS|trentonian.com}} of [[Trenton, New Jersey]]\n\n* Weeklies\n* ''The Phoenix'' {{WS|phoenixvillenews.com}} of [[Phoenixville, Pennsylvania]]\n** ''El Latino Expreso'' {{WS|njexpreso.com}} of [[Trenton, New Jersey]]\n** ''La Voz'' {{WS|lavozpa.com}} of [[Norristown, Pennsylvania]]\n** ''The Tri County Record'' {{WS|tricountyrecord.com}} of [[Morgantown, Pennsylvania]]\n** ''Penny Pincher'' {{WS|pennypincherpa.com}}of [[Pottstown, Pennsylvania]]\n\n* Chesapeake Publishing  {{WS|southernchestercountyweeklies.com}}\n** ''The Kennett Paper'' {{WS|kennettpaper.com}} of [[Kennett Square, Pennsylvania]]\n** ''Avon Grove Sun'' {{WS|avongrovesun.com}} of [[West Grove, Pennsylvania]]\n** ''The Central Record'' {{WS|medfordcentralrecord.com}} of [[Medford, New Jersey]]\n** ''Maple Shade Progress'' {{WS|mapleshadeprogress.com}} of [[Maple Shade, New Jersey]]\n\n* Intercounty Newspapers {{WS|buckslocalnews.com}} {{WS|southjerseylocalnews.com}} \n** ''The Pennington Post'' {{WS|penningtonpost.com}} of [[Pennington, New Jersey]]\n** ''The Bristol Pilot'' {{WS|bristolpilot.com}} of [[Bristol, Pennsylvania]]\n** ''Yardley News'' {{WS|yardleynews.com}} of [[Yardley, Pennsylvania]]\n** ''Advance of Bucks County'' {{WS|advanceofbucks.com}} of [[Newtown, Pennsylvania]]\n** ''Record Breeze'' {{WS|recordbreeze.com}} of [[Berlin, New Jersey]]\n** ''Community News'' {{WS|sjcommunitynews.com}} of [[Pemberton, New Jersey]]\n\n* Montgomery Newspapers {{WS|montgomerynews.com}} \n** ''Ambler Gazette'' {{WS|amblergazette.com}} of [[Ambler, Pennsylvania]]\n** ''The Colonial'' {{WS|colonialnews.com}} of [[Plymouth Meeting, Pennsylvania]]\n** ''Glenside News'' {{WS|glensidenews.com}} of [[Glenside, Pennsylvania]]\n** ''The Globe'' {{WS|globenewspaper.com}} of [[Lower Moreland Township, Pennsylvania]]\n** ''Montgomery Life'' {{WS|montgomerylife.com}} of [[Fort Washington, Pennsylvania]]\n** ''North Penn Life'' {{WS|northpennlife.com}} of [[Lansdale, Pennsylvania]]\n** ''Perkasie News Herald'' {{WS|perkasienewsherald.com}} of [[Perkasie, Pennsylvania]]\n** ''Public Spirit'' {{WS|thepublicspirit.com}} of [[Hatboro, Pennsylvania]]\n** ''Souderton Independent'' {{WS|soudertonindependent.com}} of [[Souderton, Pennsylvania]]\n** ''Springfield Sun'' {{WS|springfieldsun.com}} of [[Springfield, Pennsylvania]]\n** ''Spring-Ford Reporter'' {{WS|springfordreporter.com}} of [[Royersford, Pennsylvania]]\n** ''Times Chronicle'' {{WS|thetimeschronicle.com}} of [[Jenkintown, Pennsylvania]]\n** ''Valley Item'' {{WS|valleyitem.com}} of [[Perkiomenville, Pennsylvania]]\n** ''Willow Grove Guide'' {{WS|willowgroveguide.com}} of [[Willow Grove, Pennsylvania]]\n** ''The Review'' {{WS|roxreview.com}} of [[Roxborough, Philadelphia, Pennsylvania]]\n\n* Main Line Media News {{WS|mainlinemedianews.com}}\n** ''Main Line Times'' {{WS|mainlinetimes.com}} of [[Ardmore, Pennsylvania]]\n** ''Main Line Life'' {{WS|mainlinelife.com}} of [[Ardmore, Pennsylvania]]\n** ''The King of Prussia Courier'' {{WS|kingofprussiacourier.com}} of [[King of Prussia, Pennsylvania]]\n\n* Delaware County News Network {{WS|delconewsnetwork.com}} \n** ''News of Delaware County'' {{WS|newsofdelawarecounty.com}} of [[Havertown, Pennsylvania]]\n** ''County Press'' {{WS|countypressonline.com}} of [[Newtown Square, Pennsylvania]]\n** ''Garnet Valley Press'' {{WS|countypressonline.com}} of [[Glen Mills, Pennsylvania]]\n** ''Springfield Press'' {{WS|countypressonline.com}} of [[Springfield, Pennsylvania]]\n** ''Town Talk'' {{WS|towntalknews.com}} of [[Ridley, Pennsylvania]]\n\n* Berks-Mont Newspapers {{WS|berksmontnews.com}} \n** ''The Boyertown Area Times'' {{WS|berksmontnews.com/boyertown_area_times}} of [[Boyertown, Pennsylvania]]\n** ''The Kutztown Area Patriot'' {{WS|berksmontnews.com/kutztown_area_patriot}} of [[Kutztown, Pennsylvania]]\n** ''The Hamburg Area Item'' {{WS|berksmontnews.com/hamburg_area_item}} of [[Hamburg, Pennsylvania]]\n** ''The Southern Berks News'' {{WS|berksmontnews.com/southern_berks_news}} of [[Exeter Township, Berks County, Pennsylvania]]\n** ''Community Connection'' {{WS|berksmontnews.com/community_connection}} of [[Boyertown, Pennsylvania]]\n\n* Magazines\n** ''Bucks Co. Town & Country Living'' {{WS|buckscountymagazine.com}} \n** ''Parents Express'' {{WS|parents-express.com}} \n** ''Real Men, Rednecks'' {{WS|realmenredneck.com}} \n\n{{JRC}}\n\n==References==\n<references />\n\n[[Category:Journal Register publications|*]]\n"
  },
  {
    "path": "python3/__init__.py",
    "content": "from .diff_match_patch import diff_match_patch, patch_obj\n\n"
  },
  {
    "path": "python3/diff_match_patch.py",
    "content": "#!/usr/bin/python3\n\n\"\"\"Diff Match and Patch\nCopyright 2018 The diff-match-patch Authors.\nhttps://github.com/google/diff-match-patch\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n  http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n\"\"\"\n\n\"\"\"Functions for diff, match and patch.\n\nComputes the difference between two texts to create a patch.\nApplies the patch onto another text, allowing for errors.\n\"\"\"\n\n__author__ = 'fraser@google.com (Neil Fraser)'\n\nimport re\nimport sys\nimport time\nimport urllib.parse\n\n\nclass diff_match_patch:\n  \"\"\"Class containing the diff, match and patch methods.\n\n  Also contains the behaviour settings.\n  \"\"\"\n\n  def __init__(self):\n    \"\"\"Inits a diff_match_patch object with default settings.\n    Redefine these in your program to override the defaults.\n    \"\"\"\n\n    # Number of seconds to map a diff before giving up (0 for infinity).\n    self.Diff_Timeout = 1.0\n    # Cost of an empty edit operation in terms of edit characters.\n    self.Diff_EditCost = 4\n    # At what point is no match declared (0.0 = perfection, 1.0 = very loose).\n    self.Match_Threshold = 0.5\n    # How far to search for a match (0 = exact location, 1000+ = broad match).\n    # A match this many characters away from the expected location will add\n    # 1.0 to the score (0.0 is a perfect match).\n    self.Match_Distance = 1000\n    # When deleting a large block of text (over ~64 characters), how close do\n    # the contents have to be to match the expected contents. (0.0 = perfection,\n    # 1.0 = very loose).  Note that Match_Threshold controls how closely the\n    # end points of a delete need to match.\n    self.Patch_DeleteThreshold = 0.5\n    # Chunk size for context length.\n    self.Patch_Margin = 4\n\n    # The number of bits in an int.\n    # Python has no maximum, thus to disable patch splitting set to 0.\n    # However to avoid long patches in certain pathological cases, use 32.\n    # Multiple short patches (using native ints) are much faster than long ones.\n    self.Match_MaxBits = 32\n\n  #  DIFF FUNCTIONS\n\n  # The data structure representing a diff is an array of tuples:\n  # [(DIFF_DELETE, \"Hello\"), (DIFF_INSERT, \"Goodbye\"), (DIFF_EQUAL, \" world.\")]\n  # which means: delete \"Hello\", add \"Goodbye\" and keep \" world.\"\n  DIFF_DELETE = -1\n  DIFF_INSERT = 1\n  DIFF_EQUAL = 0\n\n  def diff_main(self, text1, text2, checklines=True, deadline=None):\n    \"\"\"Find the differences between two texts.  Simplifies the problem by\n      stripping any common prefix or suffix off the texts before diffing.\n\n    Args:\n      text1: Old string to be diffed.\n      text2: New string to be diffed.\n      checklines: Optional speedup flag.  If present and false, then don't run\n        a line-level diff first to identify the changed areas.\n        Defaults to true, which does a faster, slightly less optimal diff.\n      deadline: Optional time when the diff should be complete by.  Used\n        internally for recursive calls.  Users should set DiffTimeout instead.\n\n    Returns:\n      Array of changes.\n    \"\"\"\n    # Set a deadline by which time the diff must be complete.\n    if deadline == None:\n      # Unlike in most languages, Python counts time in seconds.\n      if self.Diff_Timeout <= 0:\n        deadline = sys.maxsize\n      else:\n        deadline = time.time() + self.Diff_Timeout\n\n    # Check for null inputs.\n    if text1 == None or text2 == None:\n      raise ValueError(\"Null inputs. (diff_main)\")\n\n    # Check for equality (speedup).\n    if text1 == text2:\n      if text1:\n        return [(self.DIFF_EQUAL, text1)]\n      return []\n\n    # Trim off common prefix (speedup).\n    commonlength = self.diff_commonPrefix(text1, text2)\n    commonprefix = text1[:commonlength]\n    text1 = text1[commonlength:]\n    text2 = text2[commonlength:]\n\n    # Trim off common suffix (speedup).\n    commonlength = self.diff_commonSuffix(text1, text2)\n    if commonlength == 0:\n      commonsuffix = ''\n    else:\n      commonsuffix = text1[-commonlength:]\n      text1 = text1[:-commonlength]\n      text2 = text2[:-commonlength]\n\n    # Compute the diff on the middle block.\n    diffs = self.diff_compute(text1, text2, checklines, deadline)\n\n    # Restore the prefix and suffix.\n    if commonprefix:\n      diffs[:0] = [(self.DIFF_EQUAL, commonprefix)]\n    if commonsuffix:\n      diffs.append((self.DIFF_EQUAL, commonsuffix))\n    self.diff_cleanupMerge(diffs)\n    return diffs\n\n  def diff_compute(self, text1, text2, checklines, deadline):\n    \"\"\"Find the differences between two texts.  Assumes that the texts do not\n      have any common prefix or suffix.\n\n    Args:\n      text1: Old string to be diffed.\n      text2: New string to be diffed.\n      checklines: Speedup flag.  If false, then don't run a line-level diff\n        first to identify the changed areas.\n        If true, then run a faster, slightly less optimal diff.\n      deadline: Time when the diff should be complete by.\n\n    Returns:\n      Array of changes.\n    \"\"\"\n    if not text1:\n      # Just add some text (speedup).\n      return [(self.DIFF_INSERT, text2)]\n\n    if not text2:\n      # Just delete some text (speedup).\n      return [(self.DIFF_DELETE, text1)]\n\n    if len(text1) > len(text2):\n      (longtext, shorttext) = (text1, text2)\n    else:\n      (shorttext, longtext) = (text1, text2)\n    i = longtext.find(shorttext)\n    if i != -1:\n      # Shorter text is inside the longer text (speedup).\n      diffs = [(self.DIFF_INSERT, longtext[:i]), (self.DIFF_EQUAL, shorttext),\n               (self.DIFF_INSERT, longtext[i + len(shorttext):])]\n      # Swap insertions for deletions if diff is reversed.\n      if len(text1) > len(text2):\n        diffs[0] = (self.DIFF_DELETE, diffs[0][1])\n        diffs[2] = (self.DIFF_DELETE, diffs[2][1])\n      return diffs\n\n    if len(shorttext) == 1:\n      # Single character string.\n      # After the previous speedup, the character can't be an equality.\n      return [(self.DIFF_DELETE, text1), (self.DIFF_INSERT, text2)]\n\n    # Check to see if the problem can be split in two.\n    hm = self.diff_halfMatch(text1, text2)\n    if hm:\n      # A half-match was found, sort out the return data.\n      (text1_a, text1_b, text2_a, text2_b, mid_common) = hm\n      # Send both pairs off for separate processing.\n      diffs_a = self.diff_main(text1_a, text2_a, checklines, deadline)\n      diffs_b = self.diff_main(text1_b, text2_b, checklines, deadline)\n      # Merge the results.\n      return diffs_a + [(self.DIFF_EQUAL, mid_common)] + diffs_b\n\n    if checklines and len(text1) > 100 and len(text2) > 100:\n      return self.diff_lineMode(text1, text2, deadline)\n\n    return self.diff_bisect(text1, text2, deadline)\n\n  def diff_lineMode(self, text1, text2, deadline):\n    \"\"\"Do a quick line-level diff on both strings, then rediff the parts for\n      greater accuracy.\n      This speedup can produce non-minimal diffs.\n\n    Args:\n      text1: Old string to be diffed.\n      text2: New string to be diffed.\n      deadline: Time when the diff should be complete by.\n\n    Returns:\n      Array of changes.\n    \"\"\"\n\n    # Scan the text on a line-by-line basis first.\n    (text1, text2, linearray) = self.diff_linesToChars(text1, text2)\n\n    diffs = self.diff_main(text1, text2, False, deadline)\n\n    # Convert the diff back to original text.\n    self.diff_charsToLines(diffs, linearray)\n    # Eliminate freak matches (e.g. blank lines)\n    self.diff_cleanupSemantic(diffs)\n\n    # Rediff any replacement blocks, this time character-by-character.\n    # Add a dummy entry at the end.\n    diffs.append((self.DIFF_EQUAL, ''))\n    pointer = 0\n    count_delete = 0\n    count_insert = 0\n    text_delete = ''\n    text_insert = ''\n    while pointer < len(diffs):\n      if diffs[pointer][0] == self.DIFF_INSERT:\n        count_insert += 1\n        text_insert += diffs[pointer][1]\n      elif diffs[pointer][0] == self.DIFF_DELETE:\n        count_delete += 1\n        text_delete += diffs[pointer][1]\n      elif diffs[pointer][0] == self.DIFF_EQUAL:\n        # Upon reaching an equality, check for prior redundancies.\n        if count_delete >= 1 and count_insert >= 1:\n          # Delete the offending records and add the merged ones.\n          subDiff = self.diff_main(text_delete, text_insert, False, deadline)\n          diffs[pointer - count_delete - count_insert : pointer] = subDiff\n          pointer = pointer - count_delete - count_insert + len(subDiff)\n        count_insert = 0\n        count_delete = 0\n        text_delete = ''\n        text_insert = ''\n\n      pointer += 1\n\n    diffs.pop()  # Remove the dummy entry at the end.\n\n    return diffs\n\n  def diff_bisect(self, text1, text2, deadline):\n    \"\"\"Find the 'middle snake' of a diff, split the problem in two\n      and return the recursively constructed diff.\n      See Myers 1986 paper: An O(ND) Difference Algorithm and Its Variations.\n\n    Args:\n      text1: Old string to be diffed.\n      text2: New string to be diffed.\n      deadline: Time at which to bail if not yet complete.\n\n    Returns:\n      Array of diff tuples.\n    \"\"\"\n\n    # Cache the text lengths to prevent multiple calls.\n    text1_length = len(text1)\n    text2_length = len(text2)\n    max_d = (text1_length + text2_length + 1) // 2\n    v_offset = max_d\n    v_length = 2 * max_d\n    v1 = [-1] * v_length\n    v1[v_offset + 1] = 0\n    v2 = v1[:]\n    delta = text1_length - text2_length\n    # If the total number of characters is odd, then the front path will\n    # collide with the reverse path.\n    front = (delta % 2 != 0)\n    # Offsets for start and end of k loop.\n    # Prevents mapping of space beyond the grid.\n    k1start = 0\n    k1end = 0\n    k2start = 0\n    k2end = 0\n    for d in range(max_d):\n      # Bail out if deadline is reached.\n      if time.time() > deadline:\n        break\n\n      # Walk the front path one step.\n      for k1 in range(-d + k1start, d + 1 - k1end, 2):\n        k1_offset = v_offset + k1\n        if k1 == -d or (k1 != d and\n            v1[k1_offset - 1] < v1[k1_offset + 1]):\n          x1 = v1[k1_offset + 1]\n        else:\n          x1 = v1[k1_offset - 1] + 1\n        y1 = x1 - k1\n        while (x1 < text1_length and y1 < text2_length and\n               text1[x1] == text2[y1]):\n          x1 += 1\n          y1 += 1\n        v1[k1_offset] = x1\n        if x1 > text1_length:\n          # Ran off the right of the graph.\n          k1end += 2\n        elif y1 > text2_length:\n          # Ran off the bottom of the graph.\n          k1start += 2\n        elif front:\n          k2_offset = v_offset + delta - k1\n          if k2_offset >= 0 and k2_offset < v_length and v2[k2_offset] != -1:\n            # Mirror x2 onto top-left coordinate system.\n            x2 = text1_length - v2[k2_offset]\n            if x1 >= x2:\n              # Overlap detected.\n              return self.diff_bisectSplit(text1, text2, x1, y1, deadline)\n\n      # Walk the reverse path one step.\n      for k2 in range(-d + k2start, d + 1 - k2end, 2):\n        k2_offset = v_offset + k2\n        if k2 == -d or (k2 != d and\n            v2[k2_offset - 1] < v2[k2_offset + 1]):\n          x2 = v2[k2_offset + 1]\n        else:\n          x2 = v2[k2_offset - 1] + 1\n        y2 = x2 - k2\n        while (x2 < text1_length and y2 < text2_length and\n               text1[-x2 - 1] == text2[-y2 - 1]):\n          x2 += 1\n          y2 += 1\n        v2[k2_offset] = x2\n        if x2 > text1_length:\n          # Ran off the left of the graph.\n          k2end += 2\n        elif y2 > text2_length:\n          # Ran off the top of the graph.\n          k2start += 2\n        elif not front:\n          k1_offset = v_offset + delta - k2\n          if k1_offset >= 0 and k1_offset < v_length and v1[k1_offset] != -1:\n            x1 = v1[k1_offset]\n            y1 = v_offset + x1 - k1_offset\n            # Mirror x2 onto top-left coordinate system.\n            x2 = text1_length - x2\n            if x1 >= x2:\n              # Overlap detected.\n              return self.diff_bisectSplit(text1, text2, x1, y1, deadline)\n\n    # Diff took too long and hit the deadline or\n    # number of diffs equals number of characters, no commonality at all.\n    return [(self.DIFF_DELETE, text1), (self.DIFF_INSERT, text2)]\n\n  def diff_bisectSplit(self, text1, text2, x, y, deadline):\n    \"\"\"Given the location of the 'middle snake', split the diff in two parts\n    and recurse.\n\n    Args:\n      text1: Old string to be diffed.\n      text2: New string to be diffed.\n      x: Index of split point in text1.\n      y: Index of split point in text2.\n      deadline: Time at which to bail if not yet complete.\n\n    Returns:\n      Array of diff tuples.\n    \"\"\"\n    text1a = text1[:x]\n    text2a = text2[:y]\n    text1b = text1[x:]\n    text2b = text2[y:]\n\n    # Compute both diffs serially.\n    diffs = self.diff_main(text1a, text2a, False, deadline)\n    diffsb = self.diff_main(text1b, text2b, False, deadline)\n\n    return diffs + diffsb\n\n  def diff_linesToChars(self, text1, text2):\n    \"\"\"Split two texts into an array of strings.  Reduce the texts to a string\n    of hashes where each Unicode character represents one line.\n\n    Args:\n      text1: First string.\n      text2: Second string.\n\n    Returns:\n      Three element tuple, containing the encoded text1, the encoded text2 and\n      the array of unique strings.  The zeroth element of the array of unique\n      strings is intentionally blank.\n    \"\"\"\n    lineArray = []  # e.g. lineArray[4] == \"Hello\\n\"\n    lineHash = {}   # e.g. lineHash[\"Hello\\n\"] == 4\n\n    # \"\\x00\" is a valid character, but various debuggers don't like it.\n    # So we'll insert a junk entry to avoid generating a null character.\n    lineArray.append('')\n\n    def diff_linesToCharsMunge(text):\n      \"\"\"Split a text into an array of strings.  Reduce the texts to a string\n      of hashes where each Unicode character represents one line.\n      Modifies linearray and linehash through being a closure.\n\n      Args:\n        text: String to encode.\n\n      Returns:\n        Encoded string.\n      \"\"\"\n      chars = []\n      # Walk the text, pulling out a substring for each line.\n      # text.split('\\n') would would temporarily double our memory footprint.\n      # Modifying text would create many large strings to garbage collect.\n      lineStart = 0\n      lineEnd = -1\n      while lineEnd < len(text) - 1:\n        lineEnd = text.find('\\n', lineStart)\n        if lineEnd == -1:\n          lineEnd = len(text) - 1\n        line = text[lineStart:lineEnd + 1]\n\n        if line in lineHash:\n          chars.append(chr(lineHash[line]))\n        else:\n          if len(lineArray) == maxLines:\n            # Bail out at 1114111 because chr(1114112) throws.\n            line = text[lineStart:]\n            lineEnd = len(text)\n          lineArray.append(line)\n          lineHash[line] = len(lineArray) - 1\n          chars.append(chr(len(lineArray) - 1))\n        lineStart = lineEnd + 1\n      return \"\".join(chars)\n\n    # Allocate 2/3rds of the space for text1, the rest for text2.\n    maxLines = 666666\n    chars1 = diff_linesToCharsMunge(text1)\n    maxLines = 1114111\n    chars2 = diff_linesToCharsMunge(text2)\n    return (chars1, chars2, lineArray)\n\n  def diff_charsToLines(self, diffs, lineArray):\n    \"\"\"Rehydrate the text in a diff from a string of line hashes to real lines\n    of text.\n\n    Args:\n      diffs: Array of diff tuples.\n      lineArray: Array of unique strings.\n    \"\"\"\n    for i in range(len(diffs)):\n      text = []\n      for char in diffs[i][1]:\n        text.append(lineArray[ord(char)])\n      diffs[i] = (diffs[i][0], \"\".join(text))\n\n  def diff_commonPrefix(self, text1, text2):\n    \"\"\"Determine the common prefix of two strings.\n\n    Args:\n      text1: First string.\n      text2: Second string.\n\n    Returns:\n      The number of characters common to the start of each string.\n    \"\"\"\n    # Quick check for common null cases.\n    if not text1 or not text2 or text1[0] != text2[0]:\n      return 0\n    # Binary search.\n    # Performance analysis: https://neil.fraser.name/news/2007/10/09/\n    pointermin = 0\n    pointermax = min(len(text1), len(text2))\n    pointermid = pointermax\n    pointerstart = 0\n    while pointermin < pointermid:\n      if text1[pointerstart:pointermid] == text2[pointerstart:pointermid]:\n        pointermin = pointermid\n        pointerstart = pointermin\n      else:\n        pointermax = pointermid\n      pointermid = (pointermax - pointermin) // 2 + pointermin\n    return pointermid\n\n  def diff_commonSuffix(self, text1, text2):\n    \"\"\"Determine the common suffix of two strings.\n\n    Args:\n      text1: First string.\n      text2: Second string.\n\n    Returns:\n      The number of characters common to the end of each string.\n    \"\"\"\n    # Quick check for common null cases.\n    if not text1 or not text2 or text1[-1] != text2[-1]:\n      return 0\n    # Binary search.\n    # Performance analysis: https://neil.fraser.name/news/2007/10/09/\n    pointermin = 0\n    pointermax = min(len(text1), len(text2))\n    pointermid = pointermax\n    pointerend = 0\n    while pointermin < pointermid:\n      if (text1[-pointermid:len(text1) - pointerend] ==\n          text2[-pointermid:len(text2) - pointerend]):\n        pointermin = pointermid\n        pointerend = pointermin\n      else:\n        pointermax = pointermid\n      pointermid = (pointermax - pointermin) // 2 + pointermin\n    return pointermid\n\n  def diff_commonOverlap(self, text1, text2):\n    \"\"\"Determine if the suffix of one string is the prefix of another.\n\n    Args:\n      text1 First string.\n      text2 Second string.\n\n    Returns:\n      The number of characters common to the end of the first\n      string and the start of the second string.\n    \"\"\"\n    # Cache the text lengths to prevent multiple calls.\n    text1_length = len(text1)\n    text2_length = len(text2)\n    # Eliminate the null case.\n    if text1_length == 0 or text2_length == 0:\n      return 0\n    # Truncate the longer string.\n    if text1_length > text2_length:\n      text1 = text1[-text2_length:]\n    elif text1_length < text2_length:\n      text2 = text2[:text1_length]\n    text_length = min(text1_length, text2_length)\n    # Quick check for the worst case.\n    if text1 == text2:\n      return text_length\n\n    # Start by looking for a single character match\n    # and increase length until no match is found.\n    # Performance analysis: https://neil.fraser.name/news/2010/11/04/\n    best = 0\n    length = 1\n    while True:\n      pattern = text1[-length:]\n      found = text2.find(pattern)\n      if found == -1:\n        return best\n      length += found\n      if found == 0 or text1[-length:] == text2[:length]:\n        best = length\n        length += 1\n\n  def diff_halfMatch(self, text1, text2):\n    \"\"\"Do the two texts share a substring which is at least half the length of\n    the longer text?\n    This speedup can produce non-minimal diffs.\n\n    Args:\n      text1: First string.\n      text2: Second string.\n\n    Returns:\n      Five element Array, containing the prefix of text1, the suffix of text1,\n      the prefix of text2, the suffix of text2 and the common middle.  Or None\n      if there was no match.\n    \"\"\"\n    if self.Diff_Timeout <= 0:\n      # Don't risk returning a non-optimal diff if we have unlimited time.\n      return None\n    if len(text1) > len(text2):\n      (longtext, shorttext) = (text1, text2)\n    else:\n      (shorttext, longtext) = (text1, text2)\n    if len(longtext) < 4 or len(shorttext) * 2 < len(longtext):\n      return None  # Pointless.\n\n    def diff_halfMatchI(longtext, shorttext, i):\n      \"\"\"Does a substring of shorttext exist within longtext such that the\n      substring is at least half the length of longtext?\n      Closure, but does not reference any external variables.\n\n      Args:\n        longtext: Longer string.\n        shorttext: Shorter string.\n        i: Start index of quarter length substring within longtext.\n\n      Returns:\n        Five element Array, containing the prefix of longtext, the suffix of\n        longtext, the prefix of shorttext, the suffix of shorttext and the\n        common middle.  Or None if there was no match.\n      \"\"\"\n      seed = longtext[i:i + len(longtext) // 4]\n      best_common = ''\n      j = shorttext.find(seed)\n      while j != -1:\n        prefixLength = self.diff_commonPrefix(longtext[i:], shorttext[j:])\n        suffixLength = self.diff_commonSuffix(longtext[:i], shorttext[:j])\n        if len(best_common) < suffixLength + prefixLength:\n          best_common = (shorttext[j - suffixLength:j] +\n              shorttext[j:j + prefixLength])\n          best_longtext_a = longtext[:i - suffixLength]\n          best_longtext_b = longtext[i + prefixLength:]\n          best_shorttext_a = shorttext[:j - suffixLength]\n          best_shorttext_b = shorttext[j + prefixLength:]\n        j = shorttext.find(seed, j + 1)\n\n      if len(best_common) * 2 >= len(longtext):\n        return (best_longtext_a, best_longtext_b,\n                best_shorttext_a, best_shorttext_b, best_common)\n      else:\n        return None\n\n    # First check if the second quarter is the seed for a half-match.\n    hm1 = diff_halfMatchI(longtext, shorttext, (len(longtext) + 3) // 4)\n    # Check again based on the third quarter.\n    hm2 = diff_halfMatchI(longtext, shorttext, (len(longtext) + 1) // 2)\n    if not hm1 and not hm2:\n      return None\n    elif not hm2:\n      hm = hm1\n    elif not hm1:\n      hm = hm2\n    else:\n      # Both matched.  Select the longest.\n      if len(hm1[4]) > len(hm2[4]):\n        hm = hm1\n      else:\n        hm = hm2\n\n    # A half-match was found, sort out the return data.\n    if len(text1) > len(text2):\n      (text1_a, text1_b, text2_a, text2_b, mid_common) = hm\n    else:\n      (text2_a, text2_b, text1_a, text1_b, mid_common) = hm\n    return (text1_a, text1_b, text2_a, text2_b, mid_common)\n\n  def diff_cleanupSemantic(self, diffs):\n    \"\"\"Reduce the number of edits by eliminating semantically trivial\n    equalities.\n\n    Args:\n      diffs: Array of diff tuples.\n    \"\"\"\n    changes = False\n    equalities = []  # Stack of indices where equalities are found.\n    lastEquality = None  # Always equal to diffs[equalities[-1]][1]\n    pointer = 0  # Index of current position.\n    # Number of chars that changed prior to the equality.\n    length_insertions1, length_deletions1 = 0, 0\n    # Number of chars that changed after the equality.\n    length_insertions2, length_deletions2 = 0, 0\n    while pointer < len(diffs):\n      if diffs[pointer][0] == self.DIFF_EQUAL:  # Equality found.\n        equalities.append(pointer)\n        length_insertions1, length_insertions2 = length_insertions2, 0\n        length_deletions1, length_deletions2 = length_deletions2, 0\n        lastEquality = diffs[pointer][1]\n      else:  # An insertion or deletion.\n        if diffs[pointer][0] == self.DIFF_INSERT:\n          length_insertions2 += len(diffs[pointer][1])\n        else:\n          length_deletions2 += len(diffs[pointer][1])\n        # Eliminate an equality that is smaller or equal to the edits on both\n        # sides of it.\n        if (lastEquality and (len(lastEquality) <=\n            max(length_insertions1, length_deletions1)) and\n            (len(lastEquality) <= max(length_insertions2, length_deletions2))):\n          # Duplicate record.\n          diffs.insert(equalities[-1], (self.DIFF_DELETE, lastEquality))\n          # Change second copy to insert.\n          diffs[equalities[-1] + 1] = (self.DIFF_INSERT,\n              diffs[equalities[-1] + 1][1])\n          # Throw away the equality we just deleted.\n          equalities.pop()\n          # Throw away the previous equality (it needs to be reevaluated).\n          if len(equalities):\n            equalities.pop()\n          if len(equalities):\n            pointer = equalities[-1]\n          else:\n            pointer = -1\n          # Reset the counters.\n          length_insertions1, length_deletions1 = 0, 0\n          length_insertions2, length_deletions2 = 0, 0\n          lastEquality = None\n          changes = True\n      pointer += 1\n\n    # Normalize the diff.\n    if changes:\n      self.diff_cleanupMerge(diffs)\n    self.diff_cleanupSemanticLossless(diffs)\n\n    # Find any overlaps between deletions and insertions.\n    # e.g: <del>abcxxx</del><ins>xxxdef</ins>\n    #   -> <del>abc</del>xxx<ins>def</ins>\n    # e.g: <del>xxxabc</del><ins>defxxx</ins>\n    #   -> <ins>def</ins>xxx<del>abc</del>\n    # Only extract an overlap if it is as big as the edit ahead or behind it.\n    pointer = 1\n    while pointer < len(diffs):\n      if (diffs[pointer - 1][0] == self.DIFF_DELETE and\n          diffs[pointer][0] == self.DIFF_INSERT):\n        deletion = diffs[pointer - 1][1]\n        insertion = diffs[pointer][1]\n        overlap_length1 = self.diff_commonOverlap(deletion, insertion)\n        overlap_length2 = self.diff_commonOverlap(insertion, deletion)\n        if overlap_length1 >= overlap_length2:\n          if (overlap_length1 >= len(deletion) / 2.0 or\n              overlap_length1 >= len(insertion) / 2.0):\n            # Overlap found.  Insert an equality and trim the surrounding edits.\n            diffs.insert(pointer, (self.DIFF_EQUAL,\n                                   insertion[:overlap_length1]))\n            diffs[pointer - 1] = (self.DIFF_DELETE,\n                                  deletion[:len(deletion) - overlap_length1])\n            diffs[pointer + 1] = (self.DIFF_INSERT,\n                                  insertion[overlap_length1:])\n            pointer += 1\n        else:\n          if (overlap_length2 >= len(deletion) / 2.0 or\n              overlap_length2 >= len(insertion) / 2.0):\n            # Reverse overlap found.\n            # Insert an equality and swap and trim the surrounding edits.\n            diffs.insert(pointer, (self.DIFF_EQUAL, deletion[:overlap_length2]))\n            diffs[pointer - 1] = (self.DIFF_INSERT,\n                                  insertion[:len(insertion) - overlap_length2])\n            diffs[pointer + 1] = (self.DIFF_DELETE, deletion[overlap_length2:])\n            pointer += 1\n        pointer += 1\n      pointer += 1\n\n  def diff_cleanupSemanticLossless(self, diffs):\n    \"\"\"Look for single edits surrounded on both sides by equalities\n    which can be shifted sideways to align the edit to a word boundary.\n    e.g: The c<ins>at c</ins>ame. -> The <ins>cat </ins>came.\n\n    Args:\n      diffs: Array of diff tuples.\n    \"\"\"\n\n    def diff_cleanupSemanticScore(one, two):\n      \"\"\"Given two strings, compute a score representing whether the\n      internal boundary falls on logical boundaries.\n      Scores range from 6 (best) to 0 (worst).\n      Closure, but does not reference any external variables.\n\n      Args:\n        one: First string.\n        two: Second string.\n\n      Returns:\n        The score.\n      \"\"\"\n      if not one or not two:\n        # Edges are the best.\n        return 6\n\n      # Each port of this function behaves slightly differently due to\n      # subtle differences in each language's definition of things like\n      # 'whitespace'.  Since this function's purpose is largely cosmetic,\n      # the choice has been made to use each language's native features\n      # rather than force total conformity.\n      char1 = one[-1]\n      char2 = two[0]\n      nonAlphaNumeric1 = not char1.isalnum()\n      nonAlphaNumeric2 = not char2.isalnum()\n      whitespace1 = nonAlphaNumeric1 and char1.isspace()\n      whitespace2 = nonAlphaNumeric2 and char2.isspace()\n      lineBreak1 = whitespace1 and (char1 == \"\\r\" or char1 == \"\\n\")\n      lineBreak2 = whitespace2 and (char2 == \"\\r\" or char2 == \"\\n\")\n      blankLine1 = lineBreak1 and self.BLANKLINEEND.search(one)\n      blankLine2 = lineBreak2 and self.BLANKLINESTART.match(two)\n\n      if blankLine1 or blankLine2:\n        # Five points for blank lines.\n        return 5\n      elif lineBreak1 or lineBreak2:\n        # Four points for line breaks.\n        return 4\n      elif nonAlphaNumeric1 and not whitespace1 and whitespace2:\n        # Three points for end of sentences.\n        return 3\n      elif whitespace1 or whitespace2:\n        # Two points for whitespace.\n        return 2\n      elif nonAlphaNumeric1 or nonAlphaNumeric2:\n        # One point for non-alphanumeric.\n        return 1\n      return 0\n\n    pointer = 1\n    # Intentionally ignore the first and last element (don't need checking).\n    while pointer < len(diffs) - 1:\n      if (diffs[pointer - 1][0] == self.DIFF_EQUAL and\n          diffs[pointer + 1][0] == self.DIFF_EQUAL):\n        # This is a single edit surrounded by equalities.\n        equality1 = diffs[pointer - 1][1]\n        edit = diffs[pointer][1]\n        equality2 = diffs[pointer + 1][1]\n\n        # First, shift the edit as far left as possible.\n        commonOffset = self.diff_commonSuffix(equality1, edit)\n        if commonOffset:\n          commonString = edit[-commonOffset:]\n          equality1 = equality1[:-commonOffset]\n          edit = commonString + edit[:-commonOffset]\n          equality2 = commonString + equality2\n\n        # Second, step character by character right, looking for the best fit.\n        bestEquality1 = equality1\n        bestEdit = edit\n        bestEquality2 = equality2\n        bestScore = (diff_cleanupSemanticScore(equality1, edit) +\n            diff_cleanupSemanticScore(edit, equality2))\n        while edit and equality2 and edit[0] == equality2[0]:\n          equality1 += edit[0]\n          edit = edit[1:] + equality2[0]\n          equality2 = equality2[1:]\n          score = (diff_cleanupSemanticScore(equality1, edit) +\n              diff_cleanupSemanticScore(edit, equality2))\n          # The >= encourages trailing rather than leading whitespace on edits.\n          if score >= bestScore:\n            bestScore = score\n            bestEquality1 = equality1\n            bestEdit = edit\n            bestEquality2 = equality2\n\n        if diffs[pointer - 1][1] != bestEquality1:\n          # We have an improvement, save it back to the diff.\n          if bestEquality1:\n            diffs[pointer - 1] = (diffs[pointer - 1][0], bestEquality1)\n          else:\n            del diffs[pointer - 1]\n            pointer -= 1\n          diffs[pointer] = (diffs[pointer][0], bestEdit)\n          if bestEquality2:\n            diffs[pointer + 1] = (diffs[pointer + 1][0], bestEquality2)\n          else:\n            del diffs[pointer + 1]\n            pointer -= 1\n      pointer += 1\n\n  # Define some regex patterns for matching boundaries.\n  BLANKLINEEND = re.compile(r\"\\n\\r?\\n$\")\n  BLANKLINESTART = re.compile(r\"^\\r?\\n\\r?\\n\")\n\n  def diff_cleanupEfficiency(self, diffs):\n    \"\"\"Reduce the number of edits by eliminating operationally trivial\n    equalities.\n\n    Args:\n      diffs: Array of diff tuples.\n    \"\"\"\n    changes = False\n    equalities = []  # Stack of indices where equalities are found.\n    lastEquality = None  # Always equal to diffs[equalities[-1]][1]\n    pointer = 0  # Index of current position.\n    pre_ins = False  # Is there an insertion operation before the last equality.\n    pre_del = False  # Is there a deletion operation before the last equality.\n    post_ins = False  # Is there an insertion operation after the last equality.\n    post_del = False  # Is there a deletion operation after the last equality.\n    while pointer < len(diffs):\n      if diffs[pointer][0] == self.DIFF_EQUAL:  # Equality found.\n        if (len(diffs[pointer][1]) < self.Diff_EditCost and\n            (post_ins or post_del)):\n          # Candidate found.\n          equalities.append(pointer)\n          pre_ins = post_ins\n          pre_del = post_del\n          lastEquality = diffs[pointer][1]\n        else:\n          # Not a candidate, and can never become one.\n          equalities = []\n          lastEquality = None\n\n        post_ins = post_del = False\n      else:  # An insertion or deletion.\n        if diffs[pointer][0] == self.DIFF_DELETE:\n          post_del = True\n        else:\n          post_ins = True\n\n        # Five types to be split:\n        # <ins>A</ins><del>B</del>XY<ins>C</ins><del>D</del>\n        # <ins>A</ins>X<ins>C</ins><del>D</del>\n        # <ins>A</ins><del>B</del>X<ins>C</ins>\n        # <ins>A</del>X<ins>C</ins><del>D</del>\n        # <ins>A</ins><del>B</del>X<del>C</del>\n\n        if lastEquality and ((pre_ins and pre_del and post_ins and post_del) or\n                             ((len(lastEquality) < self.Diff_EditCost / 2) and\n                              (pre_ins + pre_del + post_ins + post_del) == 3)):\n          # Duplicate record.\n          diffs.insert(equalities[-1], (self.DIFF_DELETE, lastEquality))\n          # Change second copy to insert.\n          diffs[equalities[-1] + 1] = (self.DIFF_INSERT,\n              diffs[equalities[-1] + 1][1])\n          equalities.pop()  # Throw away the equality we just deleted.\n          lastEquality = None\n          if pre_ins and pre_del:\n            # No changes made which could affect previous entry, keep going.\n            post_ins = post_del = True\n            equalities = []\n          else:\n            if len(equalities):\n              equalities.pop()  # Throw away the previous equality.\n            if len(equalities):\n              pointer = equalities[-1]\n            else:\n              pointer = -1\n            post_ins = post_del = False\n          changes = True\n      pointer += 1\n\n    if changes:\n      self.diff_cleanupMerge(diffs)\n\n  def diff_cleanupMerge(self, diffs):\n    \"\"\"Reorder and merge like edit sections.  Merge equalities.\n    Any edit section can move as long as it doesn't cross an equality.\n\n    Args:\n      diffs: Array of diff tuples.\n    \"\"\"\n    diffs.append((self.DIFF_EQUAL, ''))  # Add a dummy entry at the end.\n    pointer = 0\n    count_delete = 0\n    count_insert = 0\n    text_delete = ''\n    text_insert = ''\n    while pointer < len(diffs):\n      if diffs[pointer][0] == self.DIFF_INSERT:\n        count_insert += 1\n        text_insert += diffs[pointer][1]\n        pointer += 1\n      elif diffs[pointer][0] == self.DIFF_DELETE:\n        count_delete += 1\n        text_delete += diffs[pointer][1]\n        pointer += 1\n      elif diffs[pointer][0] == self.DIFF_EQUAL:\n        # Upon reaching an equality, check for prior redundancies.\n        if count_delete + count_insert > 1:\n          if count_delete != 0 and count_insert != 0:\n            # Factor out any common prefixies.\n            commonlength = self.diff_commonPrefix(text_insert, text_delete)\n            if commonlength != 0:\n              x = pointer - count_delete - count_insert - 1\n              if x >= 0 and diffs[x][0] == self.DIFF_EQUAL:\n                diffs[x] = (diffs[x][0], diffs[x][1] +\n                            text_insert[:commonlength])\n              else:\n                diffs.insert(0, (self.DIFF_EQUAL, text_insert[:commonlength]))\n                pointer += 1\n              text_insert = text_insert[commonlength:]\n              text_delete = text_delete[commonlength:]\n            # Factor out any common suffixies.\n            commonlength = self.diff_commonSuffix(text_insert, text_delete)\n            if commonlength != 0:\n              diffs[pointer] = (diffs[pointer][0], text_insert[-commonlength:] +\n                  diffs[pointer][1])\n              text_insert = text_insert[:-commonlength]\n              text_delete = text_delete[:-commonlength]\n          # Delete the offending records and add the merged ones.\n          new_ops = []\n          if len(text_delete) != 0:\n            new_ops.append((self.DIFF_DELETE, text_delete))\n          if len(text_insert) != 0:\n            new_ops.append((self.DIFF_INSERT, text_insert))\n          pointer -= count_delete + count_insert\n          diffs[pointer : pointer + count_delete + count_insert] = new_ops\n          pointer += len(new_ops) + 1\n        elif pointer != 0 and diffs[pointer - 1][0] == self.DIFF_EQUAL:\n          # Merge this equality with the previous one.\n          diffs[pointer - 1] = (diffs[pointer - 1][0],\n                                diffs[pointer - 1][1] + diffs[pointer][1])\n          del diffs[pointer]\n        else:\n          pointer += 1\n\n        count_insert = 0\n        count_delete = 0\n        text_delete = ''\n        text_insert = ''\n\n    if diffs[-1][1] == '':\n      diffs.pop()  # Remove the dummy entry at the end.\n\n    # Second pass: look for single edits surrounded on both sides by equalities\n    # which can be shifted sideways to eliminate an equality.\n    # e.g: A<ins>BA</ins>C -> <ins>AB</ins>AC\n    changes = False\n    pointer = 1\n    # Intentionally ignore the first and last element (don't need checking).\n    while pointer < len(diffs) - 1:\n      if (diffs[pointer - 1][0] == self.DIFF_EQUAL and\n          diffs[pointer + 1][0] == self.DIFF_EQUAL):\n        # This is a single edit surrounded by equalities.\n        if diffs[pointer][1].endswith(diffs[pointer - 1][1]):\n          # Shift the edit over the previous equality.\n          if diffs[pointer - 1][1] != \"\":\n            diffs[pointer] = (diffs[pointer][0],\n                diffs[pointer - 1][1] +\n                diffs[pointer][1][:-len(diffs[pointer - 1][1])])\n            diffs[pointer + 1] = (diffs[pointer + 1][0],\n                                  diffs[pointer - 1][1] + diffs[pointer + 1][1])\n          del diffs[pointer - 1]\n          changes = True\n        elif diffs[pointer][1].startswith(diffs[pointer + 1][1]):\n          # Shift the edit over the next equality.\n          diffs[pointer - 1] = (diffs[pointer - 1][0],\n                                diffs[pointer - 1][1] + diffs[pointer + 1][1])\n          diffs[pointer] = (diffs[pointer][0],\n              diffs[pointer][1][len(diffs[pointer + 1][1]):] +\n              diffs[pointer + 1][1])\n          del diffs[pointer + 1]\n          changes = True\n      pointer += 1\n\n    # If shifts were made, the diff needs reordering and another shift sweep.\n    if changes:\n      self.diff_cleanupMerge(diffs)\n\n  def diff_xIndex(self, diffs, loc):\n    \"\"\"loc is a location in text1, compute and return the equivalent location\n    in text2.  e.g. \"The cat\" vs \"The big cat\", 1->1, 5->8\n\n    Args:\n      diffs: Array of diff tuples.\n      loc: Location within text1.\n\n    Returns:\n      Location within text2.\n    \"\"\"\n    chars1 = 0\n    chars2 = 0\n    last_chars1 = 0\n    last_chars2 = 0\n    for x in range(len(diffs)):\n      (op, text) = diffs[x]\n      if op != self.DIFF_INSERT:  # Equality or deletion.\n        chars1 += len(text)\n      if op != self.DIFF_DELETE:  # Equality or insertion.\n        chars2 += len(text)\n      if chars1 > loc:  # Overshot the location.\n        break\n      last_chars1 = chars1\n      last_chars2 = chars2\n\n    if len(diffs) != x and diffs[x][0] == self.DIFF_DELETE:\n      # The location was deleted.\n      return last_chars2\n    # Add the remaining len(character).\n    return last_chars2 + (loc - last_chars1)\n\n  def diff_prettyHtml(self, diffs):\n    \"\"\"Convert a diff array into a pretty HTML report.\n\n    Args:\n      diffs: Array of diff tuples.\n\n    Returns:\n      HTML representation.\n    \"\"\"\n    html = []\n    for (op, data) in diffs:\n      text = (data.replace(\"&\", \"&amp;\").replace(\"<\", \"&lt;\")\n                 .replace(\">\", \"&gt;\").replace(\"\\n\", \"&para;<br>\"))\n      if op == self.DIFF_INSERT:\n        html.append(\"<ins style=\\\"background:#e6ffe6;\\\">%s</ins>\" % text)\n      elif op == self.DIFF_DELETE:\n        html.append(\"<del style=\\\"background:#ffe6e6;\\\">%s</del>\" % text)\n      elif op == self.DIFF_EQUAL:\n        html.append(\"<span>%s</span>\" % text)\n    return \"\".join(html)\n\n  def diff_text1(self, diffs):\n    \"\"\"Compute and return the source text (all equalities and deletions).\n\n    Args:\n      diffs: Array of diff tuples.\n\n    Returns:\n      Source text.\n    \"\"\"\n    text = []\n    for (op, data) in diffs:\n      if op != self.DIFF_INSERT:\n        text.append(data)\n    return \"\".join(text)\n\n  def diff_text2(self, diffs):\n    \"\"\"Compute and return the destination text (all equalities and insertions).\n\n    Args:\n      diffs: Array of diff tuples.\n\n    Returns:\n      Destination text.\n    \"\"\"\n    text = []\n    for (op, data) in diffs:\n      if op != self.DIFF_DELETE:\n        text.append(data)\n    return \"\".join(text)\n\n  def diff_levenshtein(self, diffs):\n    \"\"\"Compute the Levenshtein distance; the number of inserted, deleted or\n    substituted characters.\n\n    Args:\n      diffs: Array of diff tuples.\n\n    Returns:\n      Number of changes.\n    \"\"\"\n    levenshtein = 0\n    insertions = 0\n    deletions = 0\n    for (op, data) in diffs:\n      if op == self.DIFF_INSERT:\n        insertions += len(data)\n      elif op == self.DIFF_DELETE:\n        deletions += len(data)\n      elif op == self.DIFF_EQUAL:\n        # A deletion and an insertion is one substitution.\n        levenshtein += max(insertions, deletions)\n        insertions = 0\n        deletions = 0\n    levenshtein += max(insertions, deletions)\n    return levenshtein\n\n  def diff_toDelta(self, diffs):\n    \"\"\"Crush the diff into an encoded string which describes the operations\n    required to transform text1 into text2.\n    E.g. =3\\t-2\\t+ing  -> Keep 3 chars, delete 2 chars, insert 'ing'.\n    Operations are tab-separated.  Inserted text is escaped using %xx notation.\n\n    Args:\n      diffs: Array of diff tuples.\n\n    Returns:\n      Delta text.\n    \"\"\"\n    text = []\n    for (op, data) in diffs:\n      if op == self.DIFF_INSERT:\n        # High ascii will raise UnicodeDecodeError.  Use Unicode instead.\n        data = data.encode(\"utf-8\")\n        text.append(\"+\" + urllib.parse.quote(data, \"!~*'();/?:@&=+$,# \"))\n      elif op == self.DIFF_DELETE:\n        text.append(\"-%d\" % len(data))\n      elif op == self.DIFF_EQUAL:\n        text.append(\"=%d\" % len(data))\n    return \"\\t\".join(text)\n\n  def diff_fromDelta(self, text1, delta):\n    \"\"\"Given the original text1, and an encoded string which describes the\n    operations required to transform text1 into text2, compute the full diff.\n\n    Args:\n      text1: Source string for the diff.\n      delta: Delta text.\n\n    Returns:\n      Array of diff tuples.\n\n    Raises:\n      ValueError: If invalid input.\n    \"\"\"\n    diffs = []\n    pointer = 0  # Cursor in text1\n    tokens = delta.split(\"\\t\")\n    for token in tokens:\n      if token == \"\":\n        # Blank tokens are ok (from a trailing \\t).\n        continue\n      # Each token begins with a one character parameter which specifies the\n      # operation of this token (delete, insert, equality).\n      param = token[1:]\n      if token[0] == \"+\":\n        param = urllib.parse.unquote(param)\n        diffs.append((self.DIFF_INSERT, param))\n      elif token[0] == \"-\" or token[0] == \"=\":\n        try:\n          n = int(param)\n        except ValueError:\n          raise ValueError(\"Invalid number in diff_fromDelta: \" + param)\n        if n < 0:\n          raise ValueError(\"Negative number in diff_fromDelta: \" + param)\n        text = text1[pointer : pointer + n]\n        pointer += n\n        if token[0] == \"=\":\n          diffs.append((self.DIFF_EQUAL, text))\n        else:\n          diffs.append((self.DIFF_DELETE, text))\n      else:\n        # Anything else is an error.\n        raise ValueError(\"Invalid diff operation in diff_fromDelta: \" +\n            token[0])\n    if pointer != len(text1):\n      raise ValueError(\n          \"Delta length (%d) does not equal source text length (%d).\" %\n         (pointer, len(text1)))\n    return diffs\n\n  #  MATCH FUNCTIONS\n\n  def match_main(self, text, pattern, loc):\n    \"\"\"Locate the best instance of 'pattern' in 'text' near 'loc'.\n\n    Args:\n      text: The text to search.\n      pattern: The pattern to search for.\n      loc: The location to search around.\n\n    Returns:\n      Best match index or -1.\n    \"\"\"\n    # Check for null inputs.\n    if text == None or pattern == None:\n      raise ValueError(\"Null inputs. (match_main)\")\n\n    loc = max(0, min(loc, len(text)))\n    if text == pattern:\n      # Shortcut (potentially not guaranteed by the algorithm)\n      return 0\n    elif not text:\n      # Nothing to match.\n      return -1\n    elif text[loc:loc + len(pattern)] == pattern:\n      # Perfect match at the perfect spot!  (Includes case of null pattern)\n      return loc\n    else:\n      # Do a fuzzy compare.\n      match = self.match_bitap(text, pattern, loc)\n      return match\n\n  def match_bitap(self, text, pattern, loc):\n    \"\"\"Locate the best instance of 'pattern' in 'text' near 'loc' using the\n    Bitap algorithm.\n\n    Args:\n      text: The text to search.\n      pattern: The pattern to search for.\n      loc: The location to search around.\n\n    Returns:\n      Best match index or -1.\n    \"\"\"\n    # Python doesn't have a maxint limit, so ignore this check.\n    #if self.Match_MaxBits != 0 and len(pattern) > self.Match_MaxBits:\n    #  raise ValueError(\"Pattern too long for this application.\")\n\n    # Initialise the alphabet.\n    s = self.match_alphabet(pattern)\n\n    def match_bitapScore(e, x):\n      \"\"\"Compute and return the score for a match with e errors and x location.\n      Accesses loc and pattern through being a closure.\n\n      Args:\n        e: Number of errors in match.\n        x: Location of match.\n\n      Returns:\n        Overall score for match (0.0 = good, 1.0 = bad).\n      \"\"\"\n      accuracy = float(e) / len(pattern)\n      proximity = abs(loc - x)\n      if not self.Match_Distance:\n        # Dodge divide by zero error.\n        return proximity and 1.0 or accuracy\n      return accuracy + (proximity / float(self.Match_Distance))\n\n    # Highest score beyond which we give up.\n    score_threshold = self.Match_Threshold\n    # Is there a nearby exact match? (speedup)\n    best_loc = text.find(pattern, loc)\n    if best_loc != -1:\n      score_threshold = min(match_bitapScore(0, best_loc), score_threshold)\n      # What about in the other direction? (speedup)\n      best_loc = text.rfind(pattern, loc + len(pattern))\n      if best_loc != -1:\n        score_threshold = min(match_bitapScore(0, best_loc), score_threshold)\n\n    # Initialise the bit arrays.\n    matchmask = 1 << (len(pattern) - 1)\n    best_loc = -1\n\n    bin_max = len(pattern) + len(text)\n    # Empty initialization added to appease pychecker.\n    last_rd = None\n    for d in range(len(pattern)):\n      # Scan for the best match each iteration allows for one more error.\n      # Run a binary search to determine how far from 'loc' we can stray at\n      # this error level.\n      bin_min = 0\n      bin_mid = bin_max\n      while bin_min < bin_mid:\n        if match_bitapScore(d, loc + bin_mid) <= score_threshold:\n          bin_min = bin_mid\n        else:\n          bin_max = bin_mid\n        bin_mid = (bin_max - bin_min) // 2 + bin_min\n\n      # Use the result from this iteration as the maximum for the next.\n      bin_max = bin_mid\n      start = max(1, loc - bin_mid + 1)\n      finish = min(loc + bin_mid, len(text)) + len(pattern)\n\n      rd = [0] * (finish + 2)\n      rd[finish + 1] = (1 << d) - 1\n      for j in range(finish, start - 1, -1):\n        if len(text) <= j - 1:\n          # Out of range.\n          charMatch = 0\n        else:\n          charMatch = s.get(text[j - 1], 0)\n        if d == 0:  # First pass: exact match.\n          rd[j] = ((rd[j + 1] << 1) | 1) & charMatch\n        else:  # Subsequent passes: fuzzy match.\n          rd[j] = (((rd[j + 1] << 1) | 1) & charMatch) | (\n              ((last_rd[j + 1] | last_rd[j]) << 1) | 1) | last_rd[j + 1]\n        if rd[j] & matchmask:\n          score = match_bitapScore(d, j - 1)\n          # This match will almost certainly be better than any existing match.\n          # But check anyway.\n          if score <= score_threshold:\n            # Told you so.\n            score_threshold = score\n            best_loc = j - 1\n            if best_loc > loc:\n              # When passing loc, don't exceed our current distance from loc.\n              start = max(1, 2 * loc - best_loc)\n            else:\n              # Already passed loc, downhill from here on in.\n              break\n      # No hope for a (better) match at greater error levels.\n      if match_bitapScore(d + 1, loc) > score_threshold:\n        break\n      last_rd = rd\n    return best_loc\n\n  def match_alphabet(self, pattern):\n    \"\"\"Initialise the alphabet for the Bitap algorithm.\n\n    Args:\n      pattern: The text to encode.\n\n    Returns:\n      Hash of character locations.\n    \"\"\"\n    s = {}\n    for char in pattern:\n      s[char] = 0\n    for i in range(len(pattern)):\n      s[pattern[i]] |= 1 << (len(pattern) - i - 1)\n    return s\n\n  #  PATCH FUNCTIONS\n\n  def patch_addContext(self, patch, text):\n    \"\"\"Increase the context until it is unique,\n    but don't let the pattern expand beyond Match_MaxBits.\n\n    Args:\n      patch: The patch to grow.\n      text: Source text.\n    \"\"\"\n    if len(text) == 0:\n      return\n    pattern = text[patch.start2 : patch.start2 + patch.length1]\n    padding = 0\n\n    # Look for the first and last matches of pattern in text.  If two different\n    # matches are found, increase the pattern length.\n    while (text.find(pattern) != text.rfind(pattern) and (self.Match_MaxBits ==\n        0 or len(pattern) < self.Match_MaxBits - self.Patch_Margin -\n        self.Patch_Margin)):\n      padding += self.Patch_Margin\n      pattern = text[max(0, patch.start2 - padding) :\n                     patch.start2 + patch.length1 + padding]\n    # Add one chunk for good luck.\n    padding += self.Patch_Margin\n\n    # Add the prefix.\n    prefix = text[max(0, patch.start2 - padding) : patch.start2]\n    if prefix:\n      patch.diffs[:0] = [(self.DIFF_EQUAL, prefix)]\n    # Add the suffix.\n    suffix = text[patch.start2 + patch.length1 :\n                  patch.start2 + patch.length1 + padding]\n    if suffix:\n      patch.diffs.append((self.DIFF_EQUAL, suffix))\n\n    # Roll back the start points.\n    patch.start1 -= len(prefix)\n    patch.start2 -= len(prefix)\n    # Extend lengths.\n    patch.length1 += len(prefix) + len(suffix)\n    patch.length2 += len(prefix) + len(suffix)\n\n  def patch_make(self, a, b=None, c=None):\n    \"\"\"Compute a list of patches to turn text1 into text2.\n    Use diffs if provided, otherwise compute it ourselves.\n    There are four ways to call this function, depending on what data is\n    available to the caller:\n    Method 1:\n    a = text1, b = text2\n    Method 2:\n    a = diffs\n    Method 3 (optimal):\n    a = text1, b = diffs\n    Method 4 (deprecated, use method 3):\n    a = text1, b = text2, c = diffs\n\n    Args:\n      a: text1 (methods 1,3,4) or Array of diff tuples for text1 to\n          text2 (method 2).\n      b: text2 (methods 1,4) or Array of diff tuples for text1 to\n          text2 (method 3) or undefined (method 2).\n      c: Array of diff tuples for text1 to text2 (method 4) or\n          undefined (methods 1,2,3).\n\n    Returns:\n      Array of Patch objects.\n    \"\"\"\n    text1 = None\n    diffs = None\n    if isinstance(a, str) and isinstance(b, str) and c is None:\n      # Method 1: text1, text2\n      # Compute diffs from text1 and text2.\n      text1 = a\n      diffs = self.diff_main(text1, b, True)\n      if len(diffs) > 2:\n        self.diff_cleanupSemantic(diffs)\n        self.diff_cleanupEfficiency(diffs)\n    elif isinstance(a, list) and b is None and c is None:\n      # Method 2: diffs\n      # Compute text1 from diffs.\n      diffs = a\n      text1 = self.diff_text1(diffs)\n    elif isinstance(a, str) and isinstance(b, list) and c is None:\n      # Method 3: text1, diffs\n      text1 = a\n      diffs = b\n    elif (isinstance(a, str) and isinstance(b, str) and\n          isinstance(c, list)):\n      # Method 4: text1, text2, diffs\n      # text2 is not used.\n      text1 = a\n      diffs = c\n    else:\n      raise ValueError(\"Unknown call format to patch_make.\")\n\n    if not diffs:\n      return []  # Get rid of the None case.\n    patches = []\n    patch = patch_obj()\n    char_count1 = 0  # Number of characters into the text1 string.\n    char_count2 = 0  # Number of characters into the text2 string.\n    prepatch_text = text1  # Recreate the patches to determine context info.\n    postpatch_text = text1\n    for x in range(len(diffs)):\n      (diff_type, diff_text) = diffs[x]\n      if len(patch.diffs) == 0 and diff_type != self.DIFF_EQUAL:\n        # A new patch starts here.\n        patch.start1 = char_count1\n        patch.start2 = char_count2\n      if diff_type == self.DIFF_INSERT:\n        # Insertion\n        patch.diffs.append(diffs[x])\n        patch.length2 += len(diff_text)\n        postpatch_text = (postpatch_text[:char_count2] + diff_text +\n                          postpatch_text[char_count2:])\n      elif diff_type == self.DIFF_DELETE:\n        # Deletion.\n        patch.length1 += len(diff_text)\n        patch.diffs.append(diffs[x])\n        postpatch_text = (postpatch_text[:char_count2] +\n                          postpatch_text[char_count2 + len(diff_text):])\n      elif (diff_type == self.DIFF_EQUAL and\n            len(diff_text) <= 2 * self.Patch_Margin and\n            len(patch.diffs) != 0 and len(diffs) != x + 1):\n        # Small equality inside a patch.\n        patch.diffs.append(diffs[x])\n        patch.length1 += len(diff_text)\n        patch.length2 += len(diff_text)\n\n      if (diff_type == self.DIFF_EQUAL and\n          len(diff_text) >= 2 * self.Patch_Margin):\n        # Time for a new patch.\n        if len(patch.diffs) != 0:\n          self.patch_addContext(patch, prepatch_text)\n          patches.append(patch)\n          patch = patch_obj()\n          # Unlike Unidiff, our patch lists have a rolling context.\n          # https://github.com/google/diff-match-patch/wiki/Unidiff\n          # Update prepatch text & pos to reflect the application of the\n          # just completed patch.\n          prepatch_text = postpatch_text\n          char_count1 = char_count2\n\n      # Update the current character count.\n      if diff_type != self.DIFF_INSERT:\n        char_count1 += len(diff_text)\n      if diff_type != self.DIFF_DELETE:\n        char_count2 += len(diff_text)\n\n    # Pick up the leftover patch if not empty.\n    if len(patch.diffs) != 0:\n      self.patch_addContext(patch, prepatch_text)\n      patches.append(patch)\n    return patches\n\n  def patch_deepCopy(self, patches):\n    \"\"\"Given an array of patches, return another array that is identical.\n\n    Args:\n      patches: Array of Patch objects.\n\n    Returns:\n      Array of Patch objects.\n    \"\"\"\n    patchesCopy = []\n    for patch in patches:\n      patchCopy = patch_obj()\n      # No need to deep copy the tuples since they are immutable.\n      patchCopy.diffs = patch.diffs[:]\n      patchCopy.start1 = patch.start1\n      patchCopy.start2 = patch.start2\n      patchCopy.length1 = patch.length1\n      patchCopy.length2 = patch.length2\n      patchesCopy.append(patchCopy)\n    return patchesCopy\n\n  def patch_apply(self, patches, text):\n    \"\"\"Merge a set of patches onto the text.  Return a patched text, as well\n    as a list of true/false values indicating which patches were applied.\n\n    Args:\n      patches: Array of Patch objects.\n      text: Old text.\n\n    Returns:\n      Two element Array, containing the new text and an array of boolean values.\n    \"\"\"\n    if not patches:\n      return (text, [])\n\n    # Deep copy the patches so that no changes are made to originals.\n    patches = self.patch_deepCopy(patches)\n\n    nullPadding = self.patch_addPadding(patches)\n    text = nullPadding + text + nullPadding\n    self.patch_splitMax(patches)\n\n    # delta keeps track of the offset between the expected and actual location\n    # of the previous patch.  If there are patches expected at positions 10 and\n    # 20, but the first patch was found at 12, delta is 2 and the second patch\n    # has an effective expected position of 22.\n    delta = 0\n    results = []\n    for patch in patches:\n      expected_loc = patch.start2 + delta\n      text1 = self.diff_text1(patch.diffs)\n      end_loc = -1\n      if len(text1) > self.Match_MaxBits:\n        # patch_splitMax will only provide an oversized pattern in the case of\n        # a monster delete.\n        start_loc = self.match_main(text, text1[:self.Match_MaxBits],\n                                    expected_loc)\n        if start_loc != -1:\n          end_loc = self.match_main(text, text1[-self.Match_MaxBits:],\n              expected_loc + len(text1) - self.Match_MaxBits)\n          if end_loc == -1 or start_loc >= end_loc:\n            # Can't find valid trailing context.  Drop this patch.\n            start_loc = -1\n      else:\n        start_loc = self.match_main(text, text1, expected_loc)\n      if start_loc == -1:\n        # No match found.  :(\n        results.append(False)\n        # Subtract the delta for this failed patch from subsequent patches.\n        delta -= patch.length2 - patch.length1\n      else:\n        # Found a match.  :)\n        results.append(True)\n        delta = start_loc - expected_loc\n        if end_loc == -1:\n          text2 = text[start_loc : start_loc + len(text1)]\n        else:\n          text2 = text[start_loc : end_loc + self.Match_MaxBits]\n        if text1 == text2:\n          # Perfect match, just shove the replacement text in.\n          text = (text[:start_loc] + self.diff_text2(patch.diffs) +\n                      text[start_loc + len(text1):])\n        else:\n          # Imperfect match.\n          # Run a diff to get a framework of equivalent indices.\n          diffs = self.diff_main(text1, text2, False)\n          if (len(text1) > self.Match_MaxBits and\n              self.diff_levenshtein(diffs) / float(len(text1)) >\n              self.Patch_DeleteThreshold):\n            # The end points match, but the content is unacceptably bad.\n            results[-1] = False\n          else:\n            self.diff_cleanupSemanticLossless(diffs)\n            index1 = 0\n            for (op, data) in patch.diffs:\n              if op != self.DIFF_EQUAL:\n                index2 = self.diff_xIndex(diffs, index1)\n              if op == self.DIFF_INSERT:  # Insertion\n                text = text[:start_loc + index2] + data + text[start_loc +\n                                                               index2:]\n              elif op == self.DIFF_DELETE:  # Deletion\n                text = text[:start_loc + index2] + text[start_loc +\n                    self.diff_xIndex(diffs, index1 + len(data)):]\n              if op != self.DIFF_DELETE:\n                index1 += len(data)\n    # Strip the padding off.\n    text = text[len(nullPadding):-len(nullPadding)]\n    return (text, results)\n\n  def patch_addPadding(self, patches):\n    \"\"\"Add some padding on text start and end so that edges can match\n    something.  Intended to be called only from within patch_apply.\n\n    Args:\n      patches: Array of Patch objects.\n\n    Returns:\n      The padding string added to each side.\n    \"\"\"\n    paddingLength = self.Patch_Margin\n    nullPadding = \"\"\n    for x in range(1, paddingLength + 1):\n      nullPadding += chr(x)\n\n    # Bump all the patches forward.\n    for patch in patches:\n      patch.start1 += paddingLength\n      patch.start2 += paddingLength\n\n    # Add some padding on start of first diff.\n    patch = patches[0]\n    diffs = patch.diffs\n    if not diffs or diffs[0][0] != self.DIFF_EQUAL:\n      # Add nullPadding equality.\n      diffs.insert(0, (self.DIFF_EQUAL, nullPadding))\n      patch.start1 -= paddingLength  # Should be 0.\n      patch.start2 -= paddingLength  # Should be 0.\n      patch.length1 += paddingLength\n      patch.length2 += paddingLength\n    elif paddingLength > len(diffs[0][1]):\n      # Grow first equality.\n      extraLength = paddingLength - len(diffs[0][1])\n      newText = nullPadding[len(diffs[0][1]):] + diffs[0][1]\n      diffs[0] = (diffs[0][0], newText)\n      patch.start1 -= extraLength\n      patch.start2 -= extraLength\n      patch.length1 += extraLength\n      patch.length2 += extraLength\n\n    # Add some padding on end of last diff.\n    patch = patches[-1]\n    diffs = patch.diffs\n    if not diffs or diffs[-1][0] != self.DIFF_EQUAL:\n      # Add nullPadding equality.\n      diffs.append((self.DIFF_EQUAL, nullPadding))\n      patch.length1 += paddingLength\n      patch.length2 += paddingLength\n    elif paddingLength > len(diffs[-1][1]):\n      # Grow last equality.\n      extraLength = paddingLength - len(diffs[-1][1])\n      newText = diffs[-1][1] + nullPadding[:extraLength]\n      diffs[-1] = (diffs[-1][0], newText)\n      patch.length1 += extraLength\n      patch.length2 += extraLength\n\n    return nullPadding\n\n  def patch_splitMax(self, patches):\n    \"\"\"Look through the patches and break up any which are longer than the\n    maximum limit of the match algorithm.\n    Intended to be called only from within patch_apply.\n\n    Args:\n      patches: Array of Patch objects.\n    \"\"\"\n    patch_size = self.Match_MaxBits\n    if patch_size == 0:\n      # Python has the option of not splitting strings due to its ability\n      # to handle integers of arbitrary precision.\n      return\n    for x in range(len(patches)):\n      if patches[x].length1 <= patch_size:\n        continue\n      bigpatch = patches[x]\n      # Remove the big old patch.\n      del patches[x]\n      x -= 1\n      start1 = bigpatch.start1\n      start2 = bigpatch.start2\n      precontext = ''\n      while len(bigpatch.diffs) != 0:\n        # Create one of several smaller patches.\n        patch = patch_obj()\n        empty = True\n        patch.start1 = start1 - len(precontext)\n        patch.start2 = start2 - len(precontext)\n        if precontext:\n          patch.length1 = patch.length2 = len(precontext)\n          patch.diffs.append((self.DIFF_EQUAL, precontext))\n\n        while (len(bigpatch.diffs) != 0 and\n               patch.length1 < patch_size - self.Patch_Margin):\n          (diff_type, diff_text) = bigpatch.diffs[0]\n          if diff_type == self.DIFF_INSERT:\n            # Insertions are harmless.\n            patch.length2 += len(diff_text)\n            start2 += len(diff_text)\n            patch.diffs.append(bigpatch.diffs.pop(0))\n            empty = False\n          elif (diff_type == self.DIFF_DELETE and len(patch.diffs) == 1 and\n              patch.diffs[0][0] == self.DIFF_EQUAL and\n              len(diff_text) > 2 * patch_size):\n            # This is a large deletion.  Let it pass in one chunk.\n            patch.length1 += len(diff_text)\n            start1 += len(diff_text)\n            empty = False\n            patch.diffs.append((diff_type, diff_text))\n            del bigpatch.diffs[0]\n          else:\n            # Deletion or equality.  Only take as much as we can stomach.\n            diff_text = diff_text[:patch_size - patch.length1 -\n                                  self.Patch_Margin]\n            patch.length1 += len(diff_text)\n            start1 += len(diff_text)\n            if diff_type == self.DIFF_EQUAL:\n              patch.length2 += len(diff_text)\n              start2 += len(diff_text)\n            else:\n              empty = False\n\n            patch.diffs.append((diff_type, diff_text))\n            if diff_text == bigpatch.diffs[0][1]:\n              del bigpatch.diffs[0]\n            else:\n              bigpatch.diffs[0] = (bigpatch.diffs[0][0],\n                                   bigpatch.diffs[0][1][len(diff_text):])\n\n        # Compute the head context for the next patch.\n        precontext = self.diff_text2(patch.diffs)\n        precontext = precontext[-self.Patch_Margin:]\n        # Append the end context for this patch.\n        postcontext = self.diff_text1(bigpatch.diffs)[:self.Patch_Margin]\n        if postcontext:\n          patch.length1 += len(postcontext)\n          patch.length2 += len(postcontext)\n          if len(patch.diffs) != 0 and patch.diffs[-1][0] == self.DIFF_EQUAL:\n            patch.diffs[-1] = (self.DIFF_EQUAL, patch.diffs[-1][1] +\n                               postcontext)\n          else:\n            patch.diffs.append((self.DIFF_EQUAL, postcontext))\n\n        if not empty:\n          x += 1\n          patches.insert(x, patch)\n\n  def patch_toText(self, patches):\n    \"\"\"Take a list of patches and return a textual representation.\n\n    Args:\n      patches: Array of Patch objects.\n\n    Returns:\n      Text representation of patches.\n    \"\"\"\n    text = []\n    for patch in patches:\n      text.append(str(patch))\n    return \"\".join(text)\n\n  def patch_fromText(self, textline):\n    \"\"\"Parse a textual representation of patches and return a list of patch\n    objects.\n\n    Args:\n      textline: Text representation of patches.\n\n    Returns:\n      Array of Patch objects.\n\n    Raises:\n      ValueError: If invalid input.\n    \"\"\"\n    patches = []\n    if not textline:\n      return patches\n    text = textline.split('\\n')\n    while len(text) != 0:\n      m = re.match(r\"^@@ -(\\d+),?(\\d*) \\+(\\d+),?(\\d*) @@$\", text[0])\n      if not m:\n        raise ValueError(\"Invalid patch string: \" + text[0])\n      patch = patch_obj()\n      patches.append(patch)\n      patch.start1 = int(m.group(1))\n      if m.group(2) == '':\n        patch.start1 -= 1\n        patch.length1 = 1\n      elif m.group(2) == '0':\n        patch.length1 = 0\n      else:\n        patch.start1 -= 1\n        patch.length1 = int(m.group(2))\n\n      patch.start2 = int(m.group(3))\n      if m.group(4) == '':\n        patch.start2 -= 1\n        patch.length2 = 1\n      elif m.group(4) == '0':\n        patch.length2 = 0\n      else:\n        patch.start2 -= 1\n        patch.length2 = int(m.group(4))\n\n      del text[0]\n\n      while len(text) != 0:\n        if text[0]:\n          sign = text[0][0]\n        else:\n          sign = ''\n        line = urllib.parse.unquote(text[0][1:])\n        if sign == '+':\n          # Insertion.\n          patch.diffs.append((self.DIFF_INSERT, line))\n        elif sign == '-':\n          # Deletion.\n          patch.diffs.append((self.DIFF_DELETE, line))\n        elif sign == ' ':\n          # Minor equality.\n          patch.diffs.append((self.DIFF_EQUAL, line))\n        elif sign == '@':\n          # Start of next patch.\n          break\n        elif sign == '':\n          # Blank line?  Whatever.\n          pass\n        else:\n          # WTF?\n          raise ValueError(\"Invalid patch mode: '%s'\\n%s\" % (sign, line))\n        del text[0]\n    return patches\n\n\nclass patch_obj:\n  \"\"\"Class representing one patch operation.\n  \"\"\"\n\n  def __init__(self):\n    \"\"\"Initializes with an empty list of diffs.\n    \"\"\"\n    self.diffs = []\n    self.start1 = None\n    self.start2 = None\n    self.length1 = 0\n    self.length2 = 0\n\n  def __str__(self):\n    \"\"\"Emulate GNU diff's format.\n    Header: @@ -382,8 +481,9 @@\n    Indices are printed as 1-based, not 0-based.\n\n    Returns:\n      The GNU diff string.\n    \"\"\"\n    if self.length1 == 0:\n      coords1 = str(self.start1) + \",0\"\n    elif self.length1 == 1:\n      coords1 = str(self.start1 + 1)\n    else:\n      coords1 = str(self.start1 + 1) + \",\" + str(self.length1)\n    if self.length2 == 0:\n      coords2 = str(self.start2) + \",0\"\n    elif self.length2 == 1:\n      coords2 = str(self.start2 + 1)\n    else:\n      coords2 = str(self.start2 + 1) + \",\" + str(self.length2)\n    text = [\"@@ -\", coords1, \" +\", coords2, \" @@\\n\"]\n    # Escape the body of the patch with %xx notation.\n    for (op, data) in self.diffs:\n      if op == diff_match_patch.DIFF_INSERT:\n        text.append(\"+\")\n      elif op == diff_match_patch.DIFF_DELETE:\n        text.append(\"-\")\n      elif op == diff_match_patch.DIFF_EQUAL:\n        text.append(\" \")\n      # High ascii will raise UnicodeDecodeError.  Use Unicode instead.\n      data = data.encode(\"utf-8\")\n      text.append(urllib.parse.quote(data, \"!~*'();/?:@&=+$,# \") + \"\\n\")\n    return \"\".join(text)\n"
  },
  {
    "path": "python3/tests/diff_match_patch_test.py",
    "content": "#!/usr/bin/python3\n\n\"\"\"Diff Match and Patch -- Test harness\nCopyright 2018 The diff-match-patch Authors.\nhttps://github.com/google/diff-match-patch\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n  http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n\"\"\"\n\nimport imp\nimport os\nimport sys\nimport time\nimport unittest\nparentPath = os.path.abspath(\"..\")\nif parentPath not in sys.path:\n    sys.path.insert(0, parentPath)\nimport diff_match_patch as dmp_module\n\n# Force a module reload.  Allows one to edit the DMP module and rerun the tests\n# without leaving the Python interpreter.\nimp.reload(dmp_module)\n\nclass DiffMatchPatchTest(unittest.TestCase):\n\n  def setUp(self):\n    \"Test harness for dmp_module.\"\n    self.dmp = dmp_module.diff_match_patch()\n\n  def diff_rebuildtexts(self, diffs):\n    # Construct the two texts which made up the diff originally.\n    text1 = \"\"\n    text2 = \"\"\n    for x in range(0, len(diffs)):\n      if diffs[x][0] != dmp_module.diff_match_patch.DIFF_INSERT:\n        text1 += diffs[x][1]\n      if diffs[x][0] != dmp_module.diff_match_patch.DIFF_DELETE:\n        text2 += diffs[x][1]\n    return (text1, text2)\n\n\nclass DiffTest(DiffMatchPatchTest):\n  \"\"\"DIFF TEST FUNCTIONS\"\"\"\n\n  def testDiffCommonPrefix(self):\n    # Detect any common prefix.\n    # Null case.\n    self.assertEqual(0, self.dmp.diff_commonPrefix(\"abc\", \"xyz\"))\n\n    # Non-null case.\n    self.assertEqual(4, self.dmp.diff_commonPrefix(\"1234abcdef\", \"1234xyz\"))\n\n    # Whole case.\n    self.assertEqual(4, self.dmp.diff_commonPrefix(\"1234\", \"1234xyz\"))\n\n  def testDiffCommonSuffix(self):\n    # Detect any common suffix.\n    # Null case.\n    self.assertEqual(0, self.dmp.diff_commonSuffix(\"abc\", \"xyz\"))\n\n    # Non-null case.\n    self.assertEqual(4, self.dmp.diff_commonSuffix(\"abcdef1234\", \"xyz1234\"))\n\n    # Whole case.\n    self.assertEqual(4, self.dmp.diff_commonSuffix(\"1234\", \"xyz1234\"))\n\n  def testDiffCommonOverlap(self):\n    # Null case.\n    self.assertEqual(0, self.dmp.diff_commonOverlap(\"\", \"abcd\"))\n\n    # Whole case.\n    self.assertEqual(3, self.dmp.diff_commonOverlap(\"abc\", \"abcd\"))\n\n    # No overlap.\n    self.assertEqual(0, self.dmp.diff_commonOverlap(\"123456\", \"abcd\"))\n\n    # Overlap.\n    self.assertEqual(3, self.dmp.diff_commonOverlap(\"123456xxx\", \"xxxabcd\"))\n\n    # Unicode.\n    # Some overly clever languages (C#) may treat ligatures as equal to their\n    # component letters.  E.g. U+FB01 == 'fi'\n    self.assertEqual(0, self.dmp.diff_commonOverlap(\"fi\", \"\\ufb01i\"))\n\n  def testDiffHalfMatch(self):\n    # Detect a halfmatch.\n    self.dmp.Diff_Timeout = 1\n    # No match.\n    self.assertEqual(None, self.dmp.diff_halfMatch(\"1234567890\", \"abcdef\"))\n\n    self.assertEqual(None, self.dmp.diff_halfMatch(\"12345\", \"23\"))\n\n    # Single Match.\n    self.assertEqual((\"12\", \"90\", \"a\", \"z\", \"345678\"), self.dmp.diff_halfMatch(\"1234567890\", \"a345678z\"))\n\n    self.assertEqual((\"a\", \"z\", \"12\", \"90\", \"345678\"), self.dmp.diff_halfMatch(\"a345678z\", \"1234567890\"))\n\n    self.assertEqual((\"abc\", \"z\", \"1234\", \"0\", \"56789\"), self.dmp.diff_halfMatch(\"abc56789z\", \"1234567890\"))\n\n    self.assertEqual((\"a\", \"xyz\", \"1\", \"7890\", \"23456\"), self.dmp.diff_halfMatch(\"a23456xyz\", \"1234567890\"))\n\n    # Multiple Matches.\n    self.assertEqual((\"12123\", \"123121\", \"a\", \"z\", \"1234123451234\"), self.dmp.diff_halfMatch(\"121231234123451234123121\", \"a1234123451234z\"))\n\n    self.assertEqual((\"\", \"-=-=-=-=-=\", \"x\", \"\", \"x-=-=-=-=-=-=-=\"), self.dmp.diff_halfMatch(\"x-=-=-=-=-=-=-=-=-=-=-=-=\", \"xx-=-=-=-=-=-=-=\"))\n\n    self.assertEqual((\"-=-=-=-=-=\", \"\", \"\", \"y\", \"-=-=-=-=-=-=-=y\"), self.dmp.diff_halfMatch(\"-=-=-=-=-=-=-=-=-=-=-=-=y\", \"-=-=-=-=-=-=-=yy\"))\n\n    # Non-optimal halfmatch.\n    # Optimal diff would be -q+x=H-i+e=lloHe+Hu=llo-Hew+y not -qHillo+x=HelloHe-w+Hulloy\n    self.assertEqual((\"qHillo\", \"w\", \"x\", \"Hulloy\", \"HelloHe\"), self.dmp.diff_halfMatch(\"qHilloHelloHew\", \"xHelloHeHulloy\"))\n\n    # Optimal no halfmatch.\n    self.dmp.Diff_Timeout = 0\n    self.assertEqual(None, self.dmp.diff_halfMatch(\"qHilloHelloHew\", \"xHelloHeHulloy\"))\n\n  def testDiffLinesToChars(self):\n    # Convert lines down to characters.\n    self.assertEqual((\"\\x01\\x02\\x01\", \"\\x02\\x01\\x02\", [\"\", \"alpha\\n\", \"beta\\n\"]), self.dmp.diff_linesToChars(\"alpha\\nbeta\\nalpha\\n\", \"beta\\nalpha\\nbeta\\n\"))\n\n    self.assertEqual((\"\", \"\\x01\\x02\\x03\\x03\", [\"\", \"alpha\\r\\n\", \"beta\\r\\n\", \"\\r\\n\"]), self.dmp.diff_linesToChars(\"\", \"alpha\\r\\nbeta\\r\\n\\r\\n\\r\\n\"))\n\n    self.assertEqual((\"\\x01\", \"\\x02\", [\"\", \"a\", \"b\"]), self.dmp.diff_linesToChars(\"a\", \"b\"))\n\n    # More than 256 to reveal any 8-bit limitations.\n    n = 300\n    lineList = []\n    charList = []\n    for i in range(1, n + 1):\n      lineList.append(str(i) + \"\\n\")\n      charList.append(chr(i))\n    self.assertEqual(n, len(lineList))\n    lines = \"\".join(lineList)\n    chars = \"\".join(charList)\n    self.assertEqual(n, len(chars))\n    lineList.insert(0, \"\")\n    self.assertEqual((chars, \"\", lineList), self.dmp.diff_linesToChars(lines, \"\"))\n\n  def testDiffCharsToLines(self):\n    # Convert chars up to lines.\n    diffs = [(self.dmp.DIFF_EQUAL, \"\\x01\\x02\\x01\"), (self.dmp.DIFF_INSERT, \"\\x02\\x01\\x02\")]\n    self.dmp.diff_charsToLines(diffs, [\"\", \"alpha\\n\", \"beta\\n\"])\n    self.assertEqual([(self.dmp.DIFF_EQUAL, \"alpha\\nbeta\\nalpha\\n\"), (self.dmp.DIFF_INSERT, \"beta\\nalpha\\nbeta\\n\")], diffs)\n\n    # More than 256 to reveal any 8-bit limitations.\n    n = 300\n    lineList = []\n    charList = []\n    for i in range(1, n + 1):\n      lineList.append(str(i) + \"\\n\")\n      charList.append(chr(i))\n    self.assertEqual(n, len(lineList))\n    lines = \"\".join(lineList)\n    chars = \"\".join(charList)\n    self.assertEqual(n, len(chars))\n    lineList.insert(0, \"\")\n    diffs = [(self.dmp.DIFF_DELETE, chars)]\n    self.dmp.diff_charsToLines(diffs, lineList)\n    self.assertEqual([(self.dmp.DIFF_DELETE, lines)], diffs)\n\n    # More than 1,114,112 to verify any 17 * 16-bit limitation.\n    lineList = []\n    for i in range(1, 1115000 + 1):\n      lineList.append(str(i) + \"\\n\")\n    chars = \"\".join(lineList)\n    results = self.dmp.diff_linesToChars(chars, \"\")\n    diffs = [(self.dmp.DIFF_INSERT, results[0])]\n    self.dmp.diff_charsToLines(diffs, results[2])\n    self.assertEqual(chars, diffs[0][1])\n\n  def testDiffCleanupMerge(self):\n    # Cleanup a messy diff.\n    # Null case.\n    diffs = []\n    self.dmp.diff_cleanupMerge(diffs)\n    self.assertEqual([], diffs)\n\n    # No change case.\n    diffs = [(self.dmp.DIFF_EQUAL, \"a\"), (self.dmp.DIFF_DELETE, \"b\"), (self.dmp.DIFF_INSERT, \"c\")]\n    self.dmp.diff_cleanupMerge(diffs)\n    self.assertEqual([(self.dmp.DIFF_EQUAL, \"a\"), (self.dmp.DIFF_DELETE, \"b\"), (self.dmp.DIFF_INSERT, \"c\")], diffs)\n\n    # Merge equalities.\n    diffs = [(self.dmp.DIFF_EQUAL, \"a\"), (self.dmp.DIFF_EQUAL, \"b\"), (self.dmp.DIFF_EQUAL, \"c\")]\n    self.dmp.diff_cleanupMerge(diffs)\n    self.assertEqual([(self.dmp.DIFF_EQUAL, \"abc\")], diffs)\n\n    # Merge deletions.\n    diffs = [(self.dmp.DIFF_DELETE, \"a\"), (self.dmp.DIFF_DELETE, \"b\"), (self.dmp.DIFF_DELETE, \"c\")]\n    self.dmp.diff_cleanupMerge(diffs)\n    self.assertEqual([(self.dmp.DIFF_DELETE, \"abc\")], diffs)\n\n    # Merge insertions.\n    diffs = [(self.dmp.DIFF_INSERT, \"a\"), (self.dmp.DIFF_INSERT, \"b\"), (self.dmp.DIFF_INSERT, \"c\")]\n    self.dmp.diff_cleanupMerge(diffs)\n    self.assertEqual([(self.dmp.DIFF_INSERT, \"abc\")], diffs)\n\n    # Merge interweave.\n    diffs = [(self.dmp.DIFF_DELETE, \"a\"), (self.dmp.DIFF_INSERT, \"b\"), (self.dmp.DIFF_DELETE, \"c\"), (self.dmp.DIFF_INSERT, \"d\"), (self.dmp.DIFF_EQUAL, \"e\"), (self.dmp.DIFF_EQUAL, \"f\")]\n    self.dmp.diff_cleanupMerge(diffs)\n    self.assertEqual([(self.dmp.DIFF_DELETE, \"ac\"), (self.dmp.DIFF_INSERT, \"bd\"), (self.dmp.DIFF_EQUAL, \"ef\")], diffs)\n\n    # Prefix and suffix detection.\n    diffs = [(self.dmp.DIFF_DELETE, \"a\"), (self.dmp.DIFF_INSERT, \"abc\"), (self.dmp.DIFF_DELETE, \"dc\")]\n    self.dmp.diff_cleanupMerge(diffs)\n    self.assertEqual([(self.dmp.DIFF_EQUAL, \"a\"), (self.dmp.DIFF_DELETE, \"d\"), (self.dmp.DIFF_INSERT, \"b\"), (self.dmp.DIFF_EQUAL, \"c\")], diffs)\n\n    # Prefix and suffix detection with equalities.\n    diffs = [(self.dmp.DIFF_EQUAL, \"x\"), (self.dmp.DIFF_DELETE, \"a\"), (self.dmp.DIFF_INSERT, \"abc\"), (self.dmp.DIFF_DELETE, \"dc\"), (self.dmp.DIFF_EQUAL, \"y\")]\n    self.dmp.diff_cleanupMerge(diffs)\n    self.assertEqual([(self.dmp.DIFF_EQUAL, \"xa\"), (self.dmp.DIFF_DELETE, \"d\"), (self.dmp.DIFF_INSERT, \"b\"), (self.dmp.DIFF_EQUAL, \"cy\")], diffs)\n\n    # Slide edit left.\n    diffs = [(self.dmp.DIFF_EQUAL, \"a\"), (self.dmp.DIFF_INSERT, \"ba\"), (self.dmp.DIFF_EQUAL, \"c\")]\n    self.dmp.diff_cleanupMerge(diffs)\n    self.assertEqual([(self.dmp.DIFF_INSERT, \"ab\"), (self.dmp.DIFF_EQUAL, \"ac\")], diffs)\n\n    # Slide edit right.\n    diffs = [(self.dmp.DIFF_EQUAL, \"c\"), (self.dmp.DIFF_INSERT, \"ab\"), (self.dmp.DIFF_EQUAL, \"a\")]\n    self.dmp.diff_cleanupMerge(diffs)\n    self.assertEqual([(self.dmp.DIFF_EQUAL, \"ca\"), (self.dmp.DIFF_INSERT, \"ba\")], diffs)\n\n    # Slide edit left recursive.\n    diffs = [(self.dmp.DIFF_EQUAL, \"a\"), (self.dmp.DIFF_DELETE, \"b\"), (self.dmp.DIFF_EQUAL, \"c\"), (self.dmp.DIFF_DELETE, \"ac\"), (self.dmp.DIFF_EQUAL, \"x\")]\n    self.dmp.diff_cleanupMerge(diffs)\n    self.assertEqual([(self.dmp.DIFF_DELETE, \"abc\"), (self.dmp.DIFF_EQUAL, \"acx\")], diffs)\n\n    # Slide edit right recursive.\n    diffs = [(self.dmp.DIFF_EQUAL, \"x\"), (self.dmp.DIFF_DELETE, \"ca\"), (self.dmp.DIFF_EQUAL, \"c\"), (self.dmp.DIFF_DELETE, \"b\"), (self.dmp.DIFF_EQUAL, \"a\")]\n    self.dmp.diff_cleanupMerge(diffs)\n    self.assertEqual([(self.dmp.DIFF_EQUAL, \"xca\"), (self.dmp.DIFF_DELETE, \"cba\")], diffs)\n\n    # Empty merge.\n    diffs = [(self.dmp.DIFF_DELETE, \"b\"), (self.dmp.DIFF_INSERT, \"ab\"), (self.dmp.DIFF_EQUAL, \"c\")]\n    self.dmp.diff_cleanupMerge(diffs)\n    self.assertEqual([(self.dmp.DIFF_INSERT, \"a\"), (self.dmp.DIFF_EQUAL, \"bc\")], diffs)\n\n    # Empty equality.\n    diffs = [(self.dmp.DIFF_EQUAL, \"\"), (self.dmp.DIFF_INSERT, \"a\"), (self.dmp.DIFF_EQUAL, \"b\")]\n    self.dmp.diff_cleanupMerge(diffs)\n    self.assertEqual([(self.dmp.DIFF_INSERT, \"a\"), (self.dmp.DIFF_EQUAL, \"b\")], diffs)\n\n  def testDiffCleanupSemanticLossless(self):\n    # Slide diffs to match logical boundaries.\n    # Null case.\n    diffs = []\n    self.dmp.diff_cleanupSemanticLossless(diffs)\n    self.assertEqual([], diffs)\n\n    # Blank lines.\n    diffs = [(self.dmp.DIFF_EQUAL, \"AAA\\r\\n\\r\\nBBB\"), (self.dmp.DIFF_INSERT, \"\\r\\nDDD\\r\\n\\r\\nBBB\"), (self.dmp.DIFF_EQUAL, \"\\r\\nEEE\")]\n    self.dmp.diff_cleanupSemanticLossless(diffs)\n    self.assertEqual([(self.dmp.DIFF_EQUAL, \"AAA\\r\\n\\r\\n\"), (self.dmp.DIFF_INSERT, \"BBB\\r\\nDDD\\r\\n\\r\\n\"), (self.dmp.DIFF_EQUAL, \"BBB\\r\\nEEE\")], diffs)\n\n    # Line boundaries.\n    diffs = [(self.dmp.DIFF_EQUAL, \"AAA\\r\\nBBB\"), (self.dmp.DIFF_INSERT, \" DDD\\r\\nBBB\"), (self.dmp.DIFF_EQUAL, \" EEE\")]\n    self.dmp.diff_cleanupSemanticLossless(diffs)\n    self.assertEqual([(self.dmp.DIFF_EQUAL, \"AAA\\r\\n\"), (self.dmp.DIFF_INSERT, \"BBB DDD\\r\\n\"), (self.dmp.DIFF_EQUAL, \"BBB EEE\")], diffs)\n\n    # Word boundaries.\n    diffs = [(self.dmp.DIFF_EQUAL, \"The c\"), (self.dmp.DIFF_INSERT, \"ow and the c\"), (self.dmp.DIFF_EQUAL, \"at.\")]\n    self.dmp.diff_cleanupSemanticLossless(diffs)\n    self.assertEqual([(self.dmp.DIFF_EQUAL, \"The \"), (self.dmp.DIFF_INSERT, \"cow and the \"), (self.dmp.DIFF_EQUAL, \"cat.\")], diffs)\n\n    # Alphanumeric boundaries.\n    diffs = [(self.dmp.DIFF_EQUAL, \"The-c\"), (self.dmp.DIFF_INSERT, \"ow-and-the-c\"), (self.dmp.DIFF_EQUAL, \"at.\")]\n    self.dmp.diff_cleanupSemanticLossless(diffs)\n    self.assertEqual([(self.dmp.DIFF_EQUAL, \"The-\"), (self.dmp.DIFF_INSERT, \"cow-and-the-\"), (self.dmp.DIFF_EQUAL, \"cat.\")], diffs)\n\n    # Hitting the start.\n    diffs = [(self.dmp.DIFF_EQUAL, \"a\"), (self.dmp.DIFF_DELETE, \"a\"), (self.dmp.DIFF_EQUAL, \"ax\")]\n    self.dmp.diff_cleanupSemanticLossless(diffs)\n    self.assertEqual([(self.dmp.DIFF_DELETE, \"a\"), (self.dmp.DIFF_EQUAL, \"aax\")], diffs)\n\n    # Hitting the end.\n    diffs = [(self.dmp.DIFF_EQUAL, \"xa\"), (self.dmp.DIFF_DELETE, \"a\"), (self.dmp.DIFF_EQUAL, \"a\")]\n    self.dmp.diff_cleanupSemanticLossless(diffs)\n    self.assertEqual([(self.dmp.DIFF_EQUAL, \"xaa\"), (self.dmp.DIFF_DELETE, \"a\")], diffs)\n\n    # Sentence boundaries.\n    diffs = [(self.dmp.DIFF_EQUAL, \"The xxx. The \"), (self.dmp.DIFF_INSERT, \"zzz. The \"), (self.dmp.DIFF_EQUAL, \"yyy.\")]\n    self.dmp.diff_cleanupSemanticLossless(diffs)\n    self.assertEqual([(self.dmp.DIFF_EQUAL, \"The xxx.\"), (self.dmp.DIFF_INSERT, \" The zzz.\"), (self.dmp.DIFF_EQUAL, \" The yyy.\")], diffs)\n\n  def testDiffCleanupSemantic(self):\n    # Cleanup semantically trivial equalities.\n    # Null case.\n    diffs = []\n    self.dmp.diff_cleanupSemantic(diffs)\n    self.assertEqual([], diffs)\n\n    # No elimination #1.\n    diffs = [(self.dmp.DIFF_DELETE, \"ab\"), (self.dmp.DIFF_INSERT, \"cd\"), (self.dmp.DIFF_EQUAL, \"12\"), (self.dmp.DIFF_DELETE, \"e\")]\n    self.dmp.diff_cleanupSemantic(diffs)\n    self.assertEqual([(self.dmp.DIFF_DELETE, \"ab\"), (self.dmp.DIFF_INSERT, \"cd\"), (self.dmp.DIFF_EQUAL, \"12\"), (self.dmp.DIFF_DELETE, \"e\")], diffs)\n\n    # No elimination #2.\n    diffs = [(self.dmp.DIFF_DELETE, \"abc\"), (self.dmp.DIFF_INSERT, \"ABC\"), (self.dmp.DIFF_EQUAL, \"1234\"), (self.dmp.DIFF_DELETE, \"wxyz\")]\n    self.dmp.diff_cleanupSemantic(diffs)\n    self.assertEqual([(self.dmp.DIFF_DELETE, \"abc\"), (self.dmp.DIFF_INSERT, \"ABC\"), (self.dmp.DIFF_EQUAL, \"1234\"), (self.dmp.DIFF_DELETE, \"wxyz\")], diffs)\n\n    # Simple elimination.\n    diffs = [(self.dmp.DIFF_DELETE, \"a\"), (self.dmp.DIFF_EQUAL, \"b\"), (self.dmp.DIFF_DELETE, \"c\")]\n    self.dmp.diff_cleanupSemantic(diffs)\n    self.assertEqual([(self.dmp.DIFF_DELETE, \"abc\"), (self.dmp.DIFF_INSERT, \"b\")], diffs)\n\n    # Backpass elimination.\n    diffs = [(self.dmp.DIFF_DELETE, \"ab\"), (self.dmp.DIFF_EQUAL, \"cd\"), (self.dmp.DIFF_DELETE, \"e\"), (self.dmp.DIFF_EQUAL, \"f\"), (self.dmp.DIFF_INSERT, \"g\")]\n    self.dmp.diff_cleanupSemantic(diffs)\n    self.assertEqual([(self.dmp.DIFF_DELETE, \"abcdef\"), (self.dmp.DIFF_INSERT, \"cdfg\")], diffs)\n\n    # Multiple eliminations.\n    diffs = [(self.dmp.DIFF_INSERT, \"1\"), (self.dmp.DIFF_EQUAL, \"A\"), (self.dmp.DIFF_DELETE, \"B\"), (self.dmp.DIFF_INSERT, \"2\"), (self.dmp.DIFF_EQUAL, \"_\"), (self.dmp.DIFF_INSERT, \"1\"), (self.dmp.DIFF_EQUAL, \"A\"), (self.dmp.DIFF_DELETE, \"B\"), (self.dmp.DIFF_INSERT, \"2\")]\n    self.dmp.diff_cleanupSemantic(diffs)\n    self.assertEqual([(self.dmp.DIFF_DELETE, \"AB_AB\"), (self.dmp.DIFF_INSERT, \"1A2_1A2\")], diffs)\n\n    # Word boundaries.\n    diffs = [(self.dmp.DIFF_EQUAL, \"The c\"), (self.dmp.DIFF_DELETE, \"ow and the c\"), (self.dmp.DIFF_EQUAL, \"at.\")]\n    self.dmp.diff_cleanupSemantic(diffs)\n    self.assertEqual([(self.dmp.DIFF_EQUAL, \"The \"), (self.dmp.DIFF_DELETE, \"cow and the \"), (self.dmp.DIFF_EQUAL, \"cat.\")], diffs)\n\n    # No overlap elimination.\n    diffs = [(self.dmp.DIFF_DELETE, \"abcxx\"), (self.dmp.DIFF_INSERT, \"xxdef\")]\n    self.dmp.diff_cleanupSemantic(diffs)\n    self.assertEqual([(self.dmp.DIFF_DELETE, \"abcxx\"), (self.dmp.DIFF_INSERT, \"xxdef\")], diffs)\n\n    # Overlap elimination.\n    diffs = [(self.dmp.DIFF_DELETE, \"abcxxx\"), (self.dmp.DIFF_INSERT, \"xxxdef\")]\n    self.dmp.diff_cleanupSemantic(diffs)\n    self.assertEqual([(self.dmp.DIFF_DELETE, \"abc\"), (self.dmp.DIFF_EQUAL, \"xxx\"), (self.dmp.DIFF_INSERT, \"def\")], diffs)\n\n    # Reverse overlap elimination.\n    diffs = [(self.dmp.DIFF_DELETE, \"xxxabc\"), (self.dmp.DIFF_INSERT, \"defxxx\")]\n    self.dmp.diff_cleanupSemantic(diffs)\n    self.assertEqual([(self.dmp.DIFF_INSERT, \"def\"), (self.dmp.DIFF_EQUAL, \"xxx\"), (self.dmp.DIFF_DELETE, \"abc\")], diffs)\n\n    # Two overlap eliminations.\n    diffs = [(self.dmp.DIFF_DELETE, \"abcd1212\"), (self.dmp.DIFF_INSERT, \"1212efghi\"), (self.dmp.DIFF_EQUAL, \"----\"), (self.dmp.DIFF_DELETE, \"A3\"), (self.dmp.DIFF_INSERT, \"3BC\")]\n    self.dmp.diff_cleanupSemantic(diffs)\n    self.assertEqual([(self.dmp.DIFF_DELETE, \"abcd\"), (self.dmp.DIFF_EQUAL, \"1212\"), (self.dmp.DIFF_INSERT, \"efghi\"), (self.dmp.DIFF_EQUAL, \"----\"), (self.dmp.DIFF_DELETE, \"A\"), (self.dmp.DIFF_EQUAL, \"3\"), (self.dmp.DIFF_INSERT, \"BC\")], diffs)\n\n  def testDiffCleanupEfficiency(self):\n    # Cleanup operationally trivial equalities.\n    self.dmp.Diff_EditCost = 4\n    # Null case.\n    diffs = []\n    self.dmp.diff_cleanupEfficiency(diffs)\n    self.assertEqual([], diffs)\n\n    # No elimination.\n    diffs = [(self.dmp.DIFF_DELETE, \"ab\"), (self.dmp.DIFF_INSERT, \"12\"), (self.dmp.DIFF_EQUAL, \"wxyz\"), (self.dmp.DIFF_DELETE, \"cd\"), (self.dmp.DIFF_INSERT, \"34\")]\n    self.dmp.diff_cleanupEfficiency(diffs)\n    self.assertEqual([(self.dmp.DIFF_DELETE, \"ab\"), (self.dmp.DIFF_INSERT, \"12\"), (self.dmp.DIFF_EQUAL, \"wxyz\"), (self.dmp.DIFF_DELETE, \"cd\"), (self.dmp.DIFF_INSERT, \"34\")], diffs)\n\n    # Four-edit elimination.\n    diffs = [(self.dmp.DIFF_DELETE, \"ab\"), (self.dmp.DIFF_INSERT, \"12\"), (self.dmp.DIFF_EQUAL, \"xyz\"), (self.dmp.DIFF_DELETE, \"cd\"), (self.dmp.DIFF_INSERT, \"34\")]\n    self.dmp.diff_cleanupEfficiency(diffs)\n    self.assertEqual([(self.dmp.DIFF_DELETE, \"abxyzcd\"), (self.dmp.DIFF_INSERT, \"12xyz34\")], diffs)\n\n    # Three-edit elimination.\n    diffs = [(self.dmp.DIFF_INSERT, \"12\"), (self.dmp.DIFF_EQUAL, \"x\"), (self.dmp.DIFF_DELETE, \"cd\"), (self.dmp.DIFF_INSERT, \"34\")]\n    self.dmp.diff_cleanupEfficiency(diffs)\n    self.assertEqual([(self.dmp.DIFF_DELETE, \"xcd\"), (self.dmp.DIFF_INSERT, \"12x34\")], diffs)\n\n    # Backpass elimination.\n    diffs = [(self.dmp.DIFF_DELETE, \"ab\"), (self.dmp.DIFF_INSERT, \"12\"), (self.dmp.DIFF_EQUAL, \"xy\"), (self.dmp.DIFF_INSERT, \"34\"), (self.dmp.DIFF_EQUAL, \"z\"), (self.dmp.DIFF_DELETE, \"cd\"), (self.dmp.DIFF_INSERT, \"56\")]\n    self.dmp.diff_cleanupEfficiency(diffs)\n    self.assertEqual([(self.dmp.DIFF_DELETE, \"abxyzcd\"), (self.dmp.DIFF_INSERT, \"12xy34z56\")], diffs)\n\n    # High cost elimination.\n    self.dmp.Diff_EditCost = 5\n    diffs = [(self.dmp.DIFF_DELETE, \"ab\"), (self.dmp.DIFF_INSERT, \"12\"), (self.dmp.DIFF_EQUAL, \"wxyz\"), (self.dmp.DIFF_DELETE, \"cd\"), (self.dmp.DIFF_INSERT, \"34\")]\n    self.dmp.diff_cleanupEfficiency(diffs)\n    self.assertEqual([(self.dmp.DIFF_DELETE, \"abwxyzcd\"), (self.dmp.DIFF_INSERT, \"12wxyz34\")], diffs)\n    self.dmp.Diff_EditCost = 4\n\n  def testDiffPrettyHtml(self):\n    # Pretty print.\n    diffs = [(self.dmp.DIFF_EQUAL, \"a\\n\"), (self.dmp.DIFF_DELETE, \"<B>b</B>\"), (self.dmp.DIFF_INSERT, \"c&d\")]\n    self.assertEqual(\"<span>a&para;<br></span><del style=\\\"background:#ffe6e6;\\\">&lt;B&gt;b&lt;/B&gt;</del><ins style=\\\"background:#e6ffe6;\\\">c&amp;d</ins>\", self.dmp.diff_prettyHtml(diffs))\n\n  def testDiffText(self):\n    # Compute the source and destination texts.\n    diffs = [(self.dmp.DIFF_EQUAL, \"jump\"), (self.dmp.DIFF_DELETE, \"s\"), (self.dmp.DIFF_INSERT, \"ed\"), (self.dmp.DIFF_EQUAL, \" over \"), (self.dmp.DIFF_DELETE, \"the\"), (self.dmp.DIFF_INSERT, \"a\"), (self.dmp.DIFF_EQUAL, \" lazy\")]\n    self.assertEqual(\"jumps over the lazy\", self.dmp.diff_text1(diffs))\n\n    self.assertEqual(\"jumped over a lazy\", self.dmp.diff_text2(diffs))\n\n  def testDiffDelta(self):\n    # Convert a diff into delta string.\n    diffs = [(self.dmp.DIFF_EQUAL, \"jump\"), (self.dmp.DIFF_DELETE, \"s\"), (self.dmp.DIFF_INSERT, \"ed\"), (self.dmp.DIFF_EQUAL, \" over \"), (self.dmp.DIFF_DELETE, \"the\"), (self.dmp.DIFF_INSERT, \"a\"), (self.dmp.DIFF_EQUAL, \" lazy\"), (self.dmp.DIFF_INSERT, \"old dog\")]\n    text1 = self.dmp.diff_text1(diffs)\n    self.assertEqual(\"jumps over the lazy\", text1)\n\n    delta = self.dmp.diff_toDelta(diffs)\n    self.assertEqual(\"=4\\t-1\\t+ed\\t=6\\t-3\\t+a\\t=5\\t+old dog\", delta)\n\n    # Convert delta string into a diff.\n    self.assertEqual(diffs, self.dmp.diff_fromDelta(text1, delta))\n\n    # Generates error (19 != 20).\n    try:\n      self.dmp.diff_fromDelta(text1 + \"x\", delta)\n      self.assertFalse(True)\n    except ValueError:\n      # Exception expected.\n      pass\n\n    # Generates error (19 != 18).\n    try:\n      self.dmp.diff_fromDelta(text1[1:], delta)\n      self.assertFalse(True)\n    except ValueError:\n      # Exception expected.\n      pass\n\n    # Generates error (%c3%xy invalid Unicode).\n    # Note: Python 3 can decode this.\n    #try:\n    #  self.dmp.diff_fromDelta(\"\", \"+%c3xy\")\n    #  self.assertFalse(True)\n    #except ValueError:\n    #  # Exception expected.\n    #  pass\n\n    # Test deltas with special characters.\n    diffs = [(self.dmp.DIFF_EQUAL, \"\\u0680 \\x00 \\t %\"), (self.dmp.DIFF_DELETE, \"\\u0681 \\x01 \\n ^\"), (self.dmp.DIFF_INSERT, \"\\u0682 \\x02 \\\\ |\")]\n    text1 = self.dmp.diff_text1(diffs)\n    self.assertEqual(\"\\u0680 \\x00 \\t %\\u0681 \\x01 \\n ^\", text1)\n\n    delta = self.dmp.diff_toDelta(diffs)\n    self.assertEqual(\"=7\\t-7\\t+%DA%82 %02 %5C %7C\", delta)\n\n    # Convert delta string into a diff.\n    self.assertEqual(diffs, self.dmp.diff_fromDelta(text1, delta))\n\n    # Verify pool of unchanged characters.\n    diffs = [(self.dmp.DIFF_INSERT, \"A-Z a-z 0-9 - _ . ! ~ * ' ( ) ; / ? : @ & = + $ , # \")]\n    text2 = self.dmp.diff_text2(diffs)\n    self.assertEqual(\"A-Z a-z 0-9 - _ . ! ~ * \\' ( ) ; / ? : @ & = + $ , # \", text2)\n\n    delta = self.dmp.diff_toDelta(diffs)\n    self.assertEqual(\"+A-Z a-z 0-9 - _ . ! ~ * \\' ( ) ; / ? : @ & = + $ , # \", delta)\n\n    # Convert delta string into a diff.\n    self.assertEqual(diffs, self.dmp.diff_fromDelta(\"\", delta))\n\n    # 160 kb string.\n    a = \"abcdefghij\"\n    for i in range(14):\n      a += a\n    diffs = [(self.dmp.DIFF_INSERT, a)]\n    delta = self.dmp.diff_toDelta(diffs);\n    self.assertEqual('+' + a, delta);\n\n    # Convert delta string into a diff.\n    self.assertEqual(diffs, self.dmp.diff_fromDelta(\"\", delta));\n\n  def testDiffXIndex(self):\n    # Translate a location in text1 to text2.\n    self.assertEqual(5, self.dmp.diff_xIndex([(self.dmp.DIFF_DELETE, \"a\"), (self.dmp.DIFF_INSERT, \"1234\"), (self.dmp.DIFF_EQUAL, \"xyz\")], 2))\n\n    # Translation on deletion.\n    self.assertEqual(1, self.dmp.diff_xIndex([(self.dmp.DIFF_EQUAL, \"a\"), (self.dmp.DIFF_DELETE, \"1234\"), (self.dmp.DIFF_EQUAL, \"xyz\")], 3))\n\n  def testDiffLevenshtein(self):\n    # Levenshtein with trailing equality.\n    self.assertEqual(4, self.dmp.diff_levenshtein([(self.dmp.DIFF_DELETE, \"abc\"), (self.dmp.DIFF_INSERT, \"1234\"), (self.dmp.DIFF_EQUAL, \"xyz\")]))\n    # Levenshtein with leading equality.\n    self.assertEqual(4, self.dmp.diff_levenshtein([(self.dmp.DIFF_EQUAL, \"xyz\"), (self.dmp.DIFF_DELETE, \"abc\"), (self.dmp.DIFF_INSERT, \"1234\")]))\n    # Levenshtein with middle equality.\n    self.assertEqual(7, self.dmp.diff_levenshtein([(self.dmp.DIFF_DELETE, \"abc\"), (self.dmp.DIFF_EQUAL, \"xyz\"), (self.dmp.DIFF_INSERT, \"1234\")]))\n\n  def testDiffBisect(self):\n    # Normal.\n    a = \"cat\"\n    b = \"map\"\n    # Since the resulting diff hasn't been normalized, it would be ok if\n    # the insertion and deletion pairs are swapped.\n    # If the order changes, tweak this test as required.\n    self.assertEqual([(self.dmp.DIFF_DELETE, \"c\"), (self.dmp.DIFF_INSERT, \"m\"), (self.dmp.DIFF_EQUAL, \"a\"), (self.dmp.DIFF_DELETE, \"t\"), (self.dmp.DIFF_INSERT, \"p\")], self.dmp.diff_bisect(a, b, sys.maxsize))\n\n    # Timeout.\n    self.assertEqual([(self.dmp.DIFF_DELETE, \"cat\"), (self.dmp.DIFF_INSERT, \"map\")], self.dmp.diff_bisect(a, b, 0))\n\n  def testDiffMain(self):\n    # Perform a trivial diff.\n    # Null case.\n    self.assertEqual([], self.dmp.diff_main(\"\", \"\", False))\n\n    # Equality.\n    self.assertEqual([(self.dmp.DIFF_EQUAL, \"abc\")], self.dmp.diff_main(\"abc\", \"abc\", False))\n\n    # Simple insertion.\n    self.assertEqual([(self.dmp.DIFF_EQUAL, \"ab\"), (self.dmp.DIFF_INSERT, \"123\"), (self.dmp.DIFF_EQUAL, \"c\")], self.dmp.diff_main(\"abc\", \"ab123c\", False))\n\n    # Simple deletion.\n    self.assertEqual([(self.dmp.DIFF_EQUAL, \"a\"), (self.dmp.DIFF_DELETE, \"123\"), (self.dmp.DIFF_EQUAL, \"bc\")], self.dmp.diff_main(\"a123bc\", \"abc\", False))\n\n    # Two insertions.\n    self.assertEqual([(self.dmp.DIFF_EQUAL, \"a\"), (self.dmp.DIFF_INSERT, \"123\"), (self.dmp.DIFF_EQUAL, \"b\"), (self.dmp.DIFF_INSERT, \"456\"), (self.dmp.DIFF_EQUAL, \"c\")], self.dmp.diff_main(\"abc\", \"a123b456c\", False))\n\n    # Two deletions.\n    self.assertEqual([(self.dmp.DIFF_EQUAL, \"a\"), (self.dmp.DIFF_DELETE, \"123\"), (self.dmp.DIFF_EQUAL, \"b\"), (self.dmp.DIFF_DELETE, \"456\"), (self.dmp.DIFF_EQUAL, \"c\")], self.dmp.diff_main(\"a123b456c\", \"abc\", False))\n\n    # Perform a real diff.\n    # Switch off the timeout.\n    self.dmp.Diff_Timeout = 0\n    # Simple cases.\n    self.assertEqual([(self.dmp.DIFF_DELETE, \"a\"), (self.dmp.DIFF_INSERT, \"b\")], self.dmp.diff_main(\"a\", \"b\", False))\n\n    self.assertEqual([(self.dmp.DIFF_DELETE, \"Apple\"), (self.dmp.DIFF_INSERT, \"Banana\"), (self.dmp.DIFF_EQUAL, \"s are a\"), (self.dmp.DIFF_INSERT, \"lso\"), (self.dmp.DIFF_EQUAL, \" fruit.\")], self.dmp.diff_main(\"Apples are a fruit.\", \"Bananas are also fruit.\", False))\n\n    self.assertEqual([(self.dmp.DIFF_DELETE, \"a\"), (self.dmp.DIFF_INSERT, \"\\u0680\"), (self.dmp.DIFF_EQUAL, \"x\"), (self.dmp.DIFF_DELETE, \"\\t\"), (self.dmp.DIFF_INSERT, \"\\x00\")], self.dmp.diff_main(\"ax\\t\", \"\\u0680x\\x00\", False))\n\n    # Overlaps.\n    self.assertEqual([(self.dmp.DIFF_DELETE, \"1\"), (self.dmp.DIFF_EQUAL, \"a\"), (self.dmp.DIFF_DELETE, \"y\"), (self.dmp.DIFF_EQUAL, \"b\"), (self.dmp.DIFF_DELETE, \"2\"), (self.dmp.DIFF_INSERT, \"xab\")], self.dmp.diff_main(\"1ayb2\", \"abxab\", False))\n\n    self.assertEqual([(self.dmp.DIFF_INSERT, \"xaxcx\"), (self.dmp.DIFF_EQUAL, \"abc\"), (self.dmp.DIFF_DELETE, \"y\")], self.dmp.diff_main(\"abcy\", \"xaxcxabc\", False))\n\n    self.assertEqual([(self.dmp.DIFF_DELETE, \"ABCD\"), (self.dmp.DIFF_EQUAL, \"a\"), (self.dmp.DIFF_DELETE, \"=\"), (self.dmp.DIFF_INSERT, \"-\"), (self.dmp.DIFF_EQUAL, \"bcd\"), (self.dmp.DIFF_DELETE, \"=\"), (self.dmp.DIFF_INSERT, \"-\"), (self.dmp.DIFF_EQUAL, \"efghijklmnopqrs\"), (self.dmp.DIFF_DELETE, \"EFGHIJKLMNOefg\")], self.dmp.diff_main(\"ABCDa=bcd=efghijklmnopqrsEFGHIJKLMNOefg\", \"a-bcd-efghijklmnopqrs\", False))\n\n    # Large equality.\n    self.assertEqual([(self.dmp.DIFF_INSERT, \" \"), (self.dmp.DIFF_EQUAL,\"a\"), (self.dmp.DIFF_INSERT,\"nd\"), (self.dmp.DIFF_EQUAL,\" [[Pennsylvania]]\"), (self.dmp.DIFF_DELETE,\" and [[New\")], self.dmp.diff_main(\"a [[Pennsylvania]] and [[New\", \" and [[Pennsylvania]]\", False))\n\n    # Timeout.\n    self.dmp.Diff_Timeout = 0.1  # 100ms\n    a = \"`Twas brillig, and the slithy toves\\nDid gyre and gimble in the wabe:\\nAll mimsy were the borogoves,\\nAnd the mome raths outgrabe.\\n\"\n    b = \"I am the very model of a modern major general,\\nI've information vegetable, animal, and mineral,\\nI know the kings of England, and I quote the fights historical,\\nFrom Marathon to Waterloo, in order categorical.\\n\"\n    # Increase the text lengths by 1024 times to ensure a timeout.\n    for i in range(10):\n      a += a\n      b += b\n    startTime = time.time()\n    self.dmp.diff_main(a, b)\n    endTime = time.time()\n    # Test that we took at least the timeout period.\n    self.assertTrue(self.dmp.Diff_Timeout <= endTime - startTime)\n    # Test that we didn't take forever (be forgiving).\n    # Theoretically this test could fail very occasionally if the\n    # OS task swaps or locks up for a second at the wrong moment.\n    self.assertTrue(self.dmp.Diff_Timeout * 2 > endTime - startTime)\n    self.dmp.Diff_Timeout = 0\n\n    # Test the linemode speedup.\n    # Must be long to pass the 100 char cutoff.\n    # Simple line-mode.\n    a = \"1234567890\\n\" * 13\n    b = \"abcdefghij\\n\" * 13\n    self.assertEqual(self.dmp.diff_main(a, b, False), self.dmp.diff_main(a, b, True))\n\n    # Single line-mode.\n    a = \"1234567890\" * 13\n    b = \"abcdefghij\" * 13\n    self.assertEqual(self.dmp.diff_main(a, b, False), self.dmp.diff_main(a, b, True))\n\n    # Overlap line-mode.\n    a = \"1234567890\\n\" * 13\n    b = \"abcdefghij\\n1234567890\\n1234567890\\n1234567890\\nabcdefghij\\n1234567890\\n1234567890\\n1234567890\\nabcdefghij\\n1234567890\\n1234567890\\n1234567890\\nabcdefghij\\n\"\n    texts_linemode = self.diff_rebuildtexts(self.dmp.diff_main(a, b, True))\n    texts_textmode = self.diff_rebuildtexts(self.dmp.diff_main(a, b, False))\n    self.assertEqual(texts_textmode, texts_linemode)\n\n    # Test null inputs.\n    try:\n      self.dmp.diff_main(None, None)\n      self.assertFalse(True)\n    except ValueError:\n      # Exception expected.\n      pass\n\n\nclass MatchTest(DiffMatchPatchTest):\n  \"\"\"MATCH TEST FUNCTIONS\"\"\"\n\n  def testMatchAlphabet(self):\n    # Initialise the bitmasks for Bitap.\n    self.assertEqual({\"a\":4, \"b\":2, \"c\":1}, self.dmp.match_alphabet(\"abc\"))\n\n    self.assertEqual({\"a\":37, \"b\":18, \"c\":8}, self.dmp.match_alphabet(\"abcaba\"))\n\n  def testMatchBitap(self):\n    self.dmp.Match_Distance = 100\n    self.dmp.Match_Threshold = 0.5\n    # Exact matches.\n    self.assertEqual(5, self.dmp.match_bitap(\"abcdefghijk\", \"fgh\", 5))\n\n    self.assertEqual(5, self.dmp.match_bitap(\"abcdefghijk\", \"fgh\", 0))\n\n    # Fuzzy matches.\n    self.assertEqual(4, self.dmp.match_bitap(\"abcdefghijk\", \"efxhi\", 0))\n\n    self.assertEqual(2, self.dmp.match_bitap(\"abcdefghijk\", \"cdefxyhijk\", 5))\n\n    self.assertEqual(-1, self.dmp.match_bitap(\"abcdefghijk\", \"bxy\", 1))\n\n    # Overflow.\n    self.assertEqual(2, self.dmp.match_bitap(\"123456789xx0\", \"3456789x0\", 2))\n\n    self.assertEqual(0, self.dmp.match_bitap(\"abcdef\", \"xxabc\", 4))\n\n    self.assertEqual(3, self.dmp.match_bitap(\"abcdef\", \"defyy\", 4))\n\n    self.assertEqual(0, self.dmp.match_bitap(\"abcdef\", \"xabcdefy\", 0))\n\n    # Threshold test.\n    self.dmp.Match_Threshold = 0.4\n    self.assertEqual(4, self.dmp.match_bitap(\"abcdefghijk\", \"efxyhi\", 1))\n\n    self.dmp.Match_Threshold = 0.3\n    self.assertEqual(-1, self.dmp.match_bitap(\"abcdefghijk\", \"efxyhi\", 1))\n\n    self.dmp.Match_Threshold = 0.0\n    self.assertEqual(1, self.dmp.match_bitap(\"abcdefghijk\", \"bcdef\", 1))\n    self.dmp.Match_Threshold = 0.5\n\n    # Multiple select.\n    self.assertEqual(0, self.dmp.match_bitap(\"abcdexyzabcde\", \"abccde\", 3))\n\n    self.assertEqual(8, self.dmp.match_bitap(\"abcdexyzabcde\", \"abccde\", 5))\n\n    # Distance test.\n    self.dmp.Match_Distance = 10  # Strict location.\n    self.assertEqual(-1, self.dmp.match_bitap(\"abcdefghijklmnopqrstuvwxyz\", \"abcdefg\", 24))\n\n    self.assertEqual(0, self.dmp.match_bitap(\"abcdefghijklmnopqrstuvwxyz\", \"abcdxxefg\", 1))\n\n    self.dmp.Match_Distance = 1000  # Loose location.\n    self.assertEqual(0, self.dmp.match_bitap(\"abcdefghijklmnopqrstuvwxyz\", \"abcdefg\", 24))\n\n\n  def testMatchMain(self):\n    # Full match.\n    # Shortcut matches.\n    self.assertEqual(0, self.dmp.match_main(\"abcdef\", \"abcdef\", 1000))\n\n    self.assertEqual(-1, self.dmp.match_main(\"\", \"abcdef\", 1))\n\n    self.assertEqual(3, self.dmp.match_main(\"abcdef\", \"\", 3))\n\n    self.assertEqual(3, self.dmp.match_main(\"abcdef\", \"de\", 3))\n\n    self.assertEqual(3, self.dmp.match_main(\"abcdef\", \"defy\", 4))\n\n    self.assertEqual(0, self.dmp.match_main(\"abcdef\", \"abcdefy\", 0))\n\n    # Complex match.\n    self.dmp.Match_Threshold = 0.7\n    self.assertEqual(4, self.dmp.match_main(\"I am the very model of a modern major general.\", \" that berry \", 5))\n    self.dmp.Match_Threshold = 0.5\n\n    # Test null inputs.\n    try:\n      self.dmp.match_main(None, None, 0)\n      self.assertFalse(True)\n    except ValueError:\n      # Exception expected.\n      pass\n\n\nclass PatchTest(DiffMatchPatchTest):\n  \"\"\"PATCH TEST FUNCTIONS\"\"\"\n\n  def testPatchObj(self):\n    # Patch Object.\n    p = dmp_module.patch_obj()\n    p.start1 = 20\n    p.start2 = 21\n    p.length1 = 18\n    p.length2 = 17\n    p.diffs = [(self.dmp.DIFF_EQUAL, \"jump\"), (self.dmp.DIFF_DELETE, \"s\"), (self.dmp.DIFF_INSERT, \"ed\"), (self.dmp.DIFF_EQUAL, \" over \"), (self.dmp.DIFF_DELETE, \"the\"), (self.dmp.DIFF_INSERT, \"a\"), (self.dmp.DIFF_EQUAL, \"\\nlaz\")]\n    strp = str(p)\n    self.assertEqual(\"@@ -21,18 +22,17 @@\\n jump\\n-s\\n+ed\\n  over \\n-the\\n+a\\n %0Alaz\\n\", strp)\n\n  def testPatchFromText(self):\n    self.assertEqual([], self.dmp.patch_fromText(\"\"))\n\n    strp = \"@@ -21,18 +22,17 @@\\n jump\\n-s\\n+ed\\n  over \\n-the\\n+a\\n %0Alaz\\n\"\n    self.assertEqual(strp, str(self.dmp.patch_fromText(strp)[0]))\n\n    self.assertEqual(\"@@ -1 +1 @@\\n-a\\n+b\\n\", str(self.dmp.patch_fromText(\"@@ -1 +1 @@\\n-a\\n+b\\n\")[0]))\n\n    self.assertEqual(\"@@ -1,3 +0,0 @@\\n-abc\\n\", str(self.dmp.patch_fromText(\"@@ -1,3 +0,0 @@\\n-abc\\n\")[0]))\n\n    self.assertEqual(\"@@ -0,0 +1,3 @@\\n+abc\\n\", str(self.dmp.patch_fromText(\"@@ -0,0 +1,3 @@\\n+abc\\n\")[0]))\n\n    # Generates error.\n    try:\n      self.dmp.patch_fromText(\"Bad\\nPatch\\n\")\n      self.assertFalse(True)\n    except ValueError:\n      # Exception expected.\n      pass\n\n  def testPatchToText(self):\n    strp = \"@@ -21,18 +22,17 @@\\n jump\\n-s\\n+ed\\n  over \\n-the\\n+a\\n  laz\\n\"\n    p = self.dmp.patch_fromText(strp)\n    self.assertEqual(strp, self.dmp.patch_toText(p))\n\n    strp = \"@@ -1,9 +1,9 @@\\n-f\\n+F\\n oo+fooba\\n@@ -7,9 +7,9 @@\\n obar\\n-,\\n+.\\n tes\\n\"\n    p = self.dmp.patch_fromText(strp)\n    self.assertEqual(strp, self.dmp.patch_toText(p))\n\n  def testPatchAddContext(self):\n    self.dmp.Patch_Margin = 4\n    p = self.dmp.patch_fromText(\"@@ -21,4 +21,10 @@\\n-jump\\n+somersault\\n\")[0]\n    self.dmp.patch_addContext(p, \"The quick brown fox jumps over the lazy dog.\")\n    self.assertEqual(\"@@ -17,12 +17,18 @@\\n fox \\n-jump\\n+somersault\\n s ov\\n\", str(p))\n\n    # Same, but not enough trailing context.\n    p = self.dmp.patch_fromText(\"@@ -21,4 +21,10 @@\\n-jump\\n+somersault\\n\")[0]\n    self.dmp.patch_addContext(p, \"The quick brown fox jumps.\")\n    self.assertEqual(\"@@ -17,10 +17,16 @@\\n fox \\n-jump\\n+somersault\\n s.\\n\", str(p))\n\n    # Same, but not enough leading context.\n    p = self.dmp.patch_fromText(\"@@ -3 +3,2 @@\\n-e\\n+at\\n\")[0]\n    self.dmp.patch_addContext(p, \"The quick brown fox jumps.\")\n    self.assertEqual(\"@@ -1,7 +1,8 @@\\n Th\\n-e\\n+at\\n  qui\\n\", str(p))\n\n    # Same, but with ambiguity.\n    p = self.dmp.patch_fromText(\"@@ -3 +3,2 @@\\n-e\\n+at\\n\")[0]\n    self.dmp.patch_addContext(p, \"The quick brown fox jumps.  The quick brown fox crashes.\")\n    self.assertEqual(\"@@ -1,27 +1,28 @@\\n Th\\n-e\\n+at\\n  quick brown fox jumps. \\n\", str(p))\n\n  def testPatchMake(self):\n    # Null case.\n    patches = self.dmp.patch_make(\"\", \"\")\n    self.assertEqual(\"\", self.dmp.patch_toText(patches))\n\n    text1 = \"The quick brown fox jumps over the lazy dog.\"\n    text2 = \"That quick brown fox jumped over a lazy dog.\"\n    # Text2+Text1 inputs.\n    expectedPatch = \"@@ -1,8 +1,7 @@\\n Th\\n-at\\n+e\\n  qui\\n@@ -21,17 +21,18 @@\\n jump\\n-ed\\n+s\\n  over \\n-a\\n+the\\n  laz\\n\"\n    # The second patch must be \"-21,17 +21,18\", not \"-22,17 +21,18\" due to rolling context.\n    patches = self.dmp.patch_make(text2, text1)\n    self.assertEqual(expectedPatch, self.dmp.patch_toText(patches))\n\n    # Text1+Text2 inputs.\n    expectedPatch = \"@@ -1,11 +1,12 @@\\n Th\\n-e\\n+at\\n  quick b\\n@@ -22,18 +22,17 @@\\n jump\\n-s\\n+ed\\n  over \\n-the\\n+a\\n  laz\\n\"\n    patches = self.dmp.patch_make(text1, text2)\n    self.assertEqual(expectedPatch, self.dmp.patch_toText(patches))\n\n    # Diff input.\n    diffs = self.dmp.diff_main(text1, text2, False)\n    patches = self.dmp.patch_make(diffs)\n    self.assertEqual(expectedPatch, self.dmp.patch_toText(patches))\n\n    # Text1+Diff inputs.\n    patches = self.dmp.patch_make(text1, diffs)\n    self.assertEqual(expectedPatch, self.dmp.patch_toText(patches))\n\n    # Text1+Text2+Diff inputs (deprecated).\n    patches = self.dmp.patch_make(text1, text2, diffs)\n    self.assertEqual(expectedPatch, self.dmp.patch_toText(patches))\n\n    # Character encoding.\n    patches = self.dmp.patch_make(\"`1234567890-=[]\\\\;',./\", \"~!@#$%^&*()_+{}|:\\\"<>?\")\n    self.assertEqual(\"@@ -1,21 +1,21 @@\\n-%601234567890-=%5B%5D%5C;',./\\n+~!@#$%25%5E&*()_+%7B%7D%7C:%22%3C%3E?\\n\", self.dmp.patch_toText(patches))\n\n    # Character decoding.\n    diffs = [(self.dmp.DIFF_DELETE, \"`1234567890-=[]\\\\;',./\"), (self.dmp.DIFF_INSERT, \"~!@#$%^&*()_+{}|:\\\"<>?\")]\n    self.assertEqual(diffs, self.dmp.patch_fromText(\"@@ -1,21 +1,21 @@\\n-%601234567890-=%5B%5D%5C;',./\\n+~!@#$%25%5E&*()_+%7B%7D%7C:%22%3C%3E?\\n\")[0].diffs)\n\n    # Long string with repeats.\n    text1 = \"\"\n    for x in range(100):\n      text1 += \"abcdef\"\n    text2 = text1 + \"123\"\n    expectedPatch = \"@@ -573,28 +573,31 @@\\n cdefabcdefabcdefabcdefabcdef\\n+123\\n\"\n    patches = self.dmp.patch_make(text1, text2)\n    self.assertEqual(expectedPatch, self.dmp.patch_toText(patches))\n\n    # Test null inputs.\n    try:\n      self.dmp.patch_make(None, None)\n      self.assertFalse(True)\n    except ValueError:\n      # Exception expected.\n      pass\n\n  def testPatchSplitMax(self):\n    # Assumes that Match_MaxBits is 32.\n    patches = self.dmp.patch_make(\"abcdefghijklmnopqrstuvwxyz01234567890\", \"XabXcdXefXghXijXklXmnXopXqrXstXuvXwxXyzX01X23X45X67X89X0\")\n    self.dmp.patch_splitMax(patches)\n    self.assertEqual(\"@@ -1,32 +1,46 @@\\n+X\\n ab\\n+X\\n cd\\n+X\\n ef\\n+X\\n gh\\n+X\\n ij\\n+X\\n kl\\n+X\\n mn\\n+X\\n op\\n+X\\n qr\\n+X\\n st\\n+X\\n uv\\n+X\\n wx\\n+X\\n yz\\n+X\\n 012345\\n@@ -25,13 +39,18 @@\\n zX01\\n+X\\n 23\\n+X\\n 45\\n+X\\n 67\\n+X\\n 89\\n+X\\n 0\\n\", self.dmp.patch_toText(patches))\n\n    patches = self.dmp.patch_make(\"abcdef1234567890123456789012345678901234567890123456789012345678901234567890uvwxyz\", \"abcdefuvwxyz\")\n    oldToText = self.dmp.patch_toText(patches)\n    self.dmp.patch_splitMax(patches)\n    self.assertEqual(oldToText, self.dmp.patch_toText(patches))\n\n    patches = self.dmp.patch_make(\"1234567890123456789012345678901234567890123456789012345678901234567890\", \"abc\")\n    self.dmp.patch_splitMax(patches)\n    self.assertEqual(\"@@ -1,32 +1,4 @@\\n-1234567890123456789012345678\\n 9012\\n@@ -29,32 +1,4 @@\\n-9012345678901234567890123456\\n 7890\\n@@ -57,14 +1,3 @@\\n-78901234567890\\n+abc\\n\", self.dmp.patch_toText(patches))\n\n    patches = self.dmp.patch_make(\"abcdefghij , h : 0 , t : 1 abcdefghij , h : 0 , t : 1 abcdefghij , h : 0 , t : 1\", \"abcdefghij , h : 1 , t : 1 abcdefghij , h : 1 , t : 1 abcdefghij , h : 0 , t : 1\")\n    self.dmp.patch_splitMax(patches)\n    self.assertEqual(\"@@ -2,32 +2,32 @@\\n bcdefghij , h : \\n-0\\n+1\\n  , t : 1 abcdef\\n@@ -29,32 +29,32 @@\\n bcdefghij , h : \\n-0\\n+1\\n  , t : 1 abcdef\\n\", self.dmp.patch_toText(patches))\n\n  def testPatchAddPadding(self):\n    # Both edges full.\n    patches = self.dmp.patch_make(\"\", \"test\")\n    self.assertEqual(\"@@ -0,0 +1,4 @@\\n+test\\n\", self.dmp.patch_toText(patches))\n    self.dmp.patch_addPadding(patches)\n    self.assertEqual(\"@@ -1,8 +1,12 @@\\n %01%02%03%04\\n+test\\n %01%02%03%04\\n\", self.dmp.patch_toText(patches))\n\n    # Both edges partial.\n    patches = self.dmp.patch_make(\"XY\", \"XtestY\")\n    self.assertEqual(\"@@ -1,2 +1,6 @@\\n X\\n+test\\n Y\\n\", self.dmp.patch_toText(patches))\n    self.dmp.patch_addPadding(patches)\n    self.assertEqual(\"@@ -2,8 +2,12 @@\\n %02%03%04X\\n+test\\n Y%01%02%03\\n\", self.dmp.patch_toText(patches))\n\n    # Both edges none.\n    patches = self.dmp.patch_make(\"XXXXYYYY\", \"XXXXtestYYYY\")\n    self.assertEqual(\"@@ -1,8 +1,12 @@\\n XXXX\\n+test\\n YYYY\\n\", self.dmp.patch_toText(patches))\n    self.dmp.patch_addPadding(patches)\n    self.assertEqual(\"@@ -5,8 +5,12 @@\\n XXXX\\n+test\\n YYYY\\n\", self.dmp.patch_toText(patches))\n\n  def testPatchApply(self):\n    self.dmp.Match_Distance = 1000\n    self.dmp.Match_Threshold = 0.5\n    self.dmp.Patch_DeleteThreshold = 0.5\n    # Null case.\n    patches = self.dmp.patch_make(\"\", \"\")\n    results = self.dmp.patch_apply(patches, \"Hello world.\")\n    self.assertEqual((\"Hello world.\", []), results)\n\n    # Exact match.\n    patches = self.dmp.patch_make(\"The quick brown fox jumps over the lazy dog.\", \"That quick brown fox jumped over a lazy dog.\")\n    results = self.dmp.patch_apply(patches, \"The quick brown fox jumps over the lazy dog.\")\n    self.assertEqual((\"That quick brown fox jumped over a lazy dog.\", [True, True]), results)\n\n    # Partial match.\n    results = self.dmp.patch_apply(patches, \"The quick red rabbit jumps over the tired tiger.\")\n    self.assertEqual((\"That quick red rabbit jumped over a tired tiger.\", [True, True]), results)\n\n    # Failed match.\n    results = self.dmp.patch_apply(patches, \"I am the very model of a modern major general.\")\n    self.assertEqual((\"I am the very model of a modern major general.\", [False, False]), results)\n\n    # Big delete, small change.\n    patches = self.dmp.patch_make(\"x1234567890123456789012345678901234567890123456789012345678901234567890y\", \"xabcy\")\n    results = self.dmp.patch_apply(patches, \"x123456789012345678901234567890-----++++++++++-----123456789012345678901234567890y\")\n    self.assertEqual((\"xabcy\", [True, True]), results)\n\n    # Big delete, big change 1.\n    patches = self.dmp.patch_make(\"x1234567890123456789012345678901234567890123456789012345678901234567890y\", \"xabcy\")\n    results = self.dmp.patch_apply(patches, \"x12345678901234567890---------------++++++++++---------------12345678901234567890y\")\n    self.assertEqual((\"xabc12345678901234567890---------------++++++++++---------------12345678901234567890y\", [False, True]), results)\n\n    # Big delete, big change 2.\n    self.dmp.Patch_DeleteThreshold = 0.6\n    patches = self.dmp.patch_make(\"x1234567890123456789012345678901234567890123456789012345678901234567890y\", \"xabcy\")\n    results = self.dmp.patch_apply(patches, \"x12345678901234567890---------------++++++++++---------------12345678901234567890y\")\n    self.assertEqual((\"xabcy\", [True, True]), results)\n    self.dmp.Patch_DeleteThreshold = 0.5\n\n    # Compensate for failed patch.\n    self.dmp.Match_Threshold = 0.0\n    self.dmp.Match_Distance = 0\n    patches = self.dmp.patch_make(\"abcdefghijklmnopqrstuvwxyz--------------------1234567890\", \"abcXXXXXXXXXXdefghijklmnopqrstuvwxyz--------------------1234567YYYYYYYYYY890\")\n    results = self.dmp.patch_apply(patches, \"ABCDEFGHIJKLMNOPQRSTUVWXYZ--------------------1234567890\")\n    self.assertEqual((\"ABCDEFGHIJKLMNOPQRSTUVWXYZ--------------------1234567YYYYYYYYYY890\", [False, True]), results)\n    self.dmp.Match_Threshold = 0.5\n    self.dmp.Match_Distance = 1000\n\n    # No side effects.\n    patches = self.dmp.patch_make(\"\", \"test\")\n    patchstr = self.dmp.patch_toText(patches)\n    results = self.dmp.patch_apply(patches, \"\")\n    self.assertEqual(patchstr, self.dmp.patch_toText(patches))\n\n    # No side effects with major delete.\n    patches = self.dmp.patch_make(\"The quick brown fox jumps over the lazy dog.\", \"Woof\")\n    patchstr = self.dmp.patch_toText(patches)\n    self.dmp.patch_apply(patches, \"The quick brown fox jumps over the lazy dog.\")\n    self.assertEqual(patchstr, self.dmp.patch_toText(patches))\n\n    # Edge exact match.\n    patches = self.dmp.patch_make(\"\", \"test\")\n    self.dmp.patch_apply(patches, \"\")\n    self.assertEqual((\"test\", [True]), results)\n\n    # Near edge exact match.\n    patches = self.dmp.patch_make(\"XY\", \"XtestY\")\n    results = self.dmp.patch_apply(patches, \"XY\")\n    self.assertEqual((\"XtestY\", [True]), results)\n\n    # Edge partial match.\n    patches = self.dmp.patch_make(\"y\", \"y123\")\n    results = self.dmp.patch_apply(patches, \"x\")\n    self.assertEqual((\"x123\", [True]), results)\n\n\nif __name__ == \"__main__\":\n  unittest.main()\n"
  },
  {
    "path": "python3/tests/speedtest.py",
    "content": "#!/usr/bin/python3\n\n\"\"\"Diff Speed Test\nCopyright 2018 The diff-match-patch Authors.\nhttps://github.com/google/diff-match-patch\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n  http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n\"\"\"\n\nimport imp\nimport gc\nimport os\nimport sys\nimport time\nparentPath = os.path.abspath(\"..\")\nif parentPath not in sys.path:\n    sys.path.insert(0, parentPath)\nimport diff_match_patch as dmp_module\n# Force a module reload.  Allows one to edit the DMP module and rerun the test\n# without leaving the Python interpreter.\nimp.reload(dmp_module)\n\ndef main():\n  text1 = open(\"speedtest1.txt\").read()\n  text2 = open(\"speedtest2.txt\").read()\n\n  dmp = dmp_module.diff_match_patch()\n  dmp.Diff_Timeout = 0.0\n\n  # Execute one reverse diff as a warmup.\n  dmp.diff_main(text2, text1, False)\n  gc.collect()\n\n  start_time = time.time()\n  dmp.diff_main(text1, text2, False)\n  end_time = time.time()\n  print(\"Elapsed time: %f\" % (end_time - start_time))\n\nif __name__ == \"__main__\":\n  main()\n"
  },
  {
    "path": "python3/tests/speedtest1.txt",
    "content": "This is a '''list of newspapers published by [[Journal Register Company]]'''.\n\nThe company owns daily and weekly newspapers, other print media properties and newspaper-affiliated local Websites in the [[U.S.]] states of [[Connecticut]], [[Michigan]], [[New York]], [[Ohio]] and [[Pennsylvania]], organized in six geographic \"clusters\":<ref>[http://www.journalregister.com/newspapers.html Journal Register Company: Our Newspapers], accessed February 10, 2008.</ref>\n\n== Capital-Saratoga ==\nThree dailies, associated weeklies and [[pennysaver]]s in greater [[Albany, New York]]; also [http://www.capitalcentral.com capitalcentral.com] and [http://www.jobsinnewyork.com JobsInNewYork.com].\n\n* ''The Oneida Daily Dispatch'' {{WS|oneidadispatch.com}} of [[Oneida, New York]]\n* ''[[The Record (Troy)|The Record]]'' {{WS|troyrecord.com}} of [[Troy, New York]]\n* ''[[The Saratogian]]'' {{WS|saratogian.com}} of [[Saratoga Springs, New York]]\n* Weeklies:\n** ''Community News'' {{WS|cnweekly.com}} weekly of [[Clifton Park, New York]]\n** ''Rome Observer'' of [[Rome, New York]]\n** ''Life & Times of Utica'' of [[Utica, New York]]\n\n== Connecticut ==\nFive dailies, associated weeklies and [[pennysaver]]s in the state of [[Connecticut]]; also [http://www.ctcentral.com CTcentral.com], [http://www.ctcarsandtrucks.com CTCarsAndTrucks.com] and [http://www.jobsinct.com JobsInCT.com].\n\n* ''The Middletown Press'' {{WS|middletownpress.com}} of [[Middletown, Connecticut|Middletown]]\n* ''[[New Haven Register]]'' {{WS|newhavenregister.com}} of [[New Haven, Connecticut|New Haven]]\n* ''The Register Citizen'' {{WS|registercitizen.com}} of [[Torrington, Connecticut|Torrington]]\n\n* [[New Haven Register#Competitors|Elm City Newspapers]] {{WS|ctcentral.com}}\n** ''The Advertiser'' of [[East Haven, Connecticut|East Haven]]\n** ''Hamden Chronicle'' of [[Hamden, Connecticut|Hamden]]\n** ''Milford Weekly'' of [[Milford, Connecticut|Milford]]\n** ''The Orange Bulletin'' of [[Orange, Connecticut|Orange]]\n** ''The Post'' of [[North Haven, Connecticut|North Haven]]\n** ''Shelton Weekly'' of [[Shelton, Connecticut|Shelton]]\n** ''The Stratford Bard'' of [[Stratford, Connecticut|Stratford]]\n** ''Wallingford Voice'' of [[Wallingford, Connecticut|Wallingford]]\n** ''West Haven News'' of [[West Haven, Connecticut|West Haven]]\n* Housatonic Publications \n** ''The New Milford Times'' {{WS|newmilfordtimes.com}} of [[New Milford, Connecticut|New Milford]]\n** ''The Brookfield Journal'' of [[Brookfield, Connecticut|Brookfield]]\n** ''The Kent Good Times Dispatch'' of [[Kent, Connecticut|Kent]]\n** ''The Bethel Beacon'' of [[Bethel, Connecticut|Bethel]]\n** ''The Litchfield Enquirer'' of [[Litchfield, Connecticut|Litchfield]]\n** ''Litchfield County Times'' of [[Litchfield, Connecticut|Litchfield]]\n* Imprint Newspapers {{WS|imprintnewspapers.com}}\n** ''West Hartford News'' of [[West Hartford, Connecticut|West Hartford]]\n** ''Windsor Journal'' of [[Windsor, Connecticut|Windsor]]\n** ''Windsor Locks Journal'' of [[Windsor Locks, Connecticut|Windsor Locks]]\n** ''Avon Post'' of [[Avon, Connecticut|Avon]]\n** ''Farmington Post'' of [[Farmington, Connecticut|Farmington]]\n** ''Simsbury Post'' of [[Simsbury, Connecticut|Simsbury]]\n** ''Tri-Town Post'' of [[Burlington, Connecticut|Burlington]], [[Canton, Connecticut|Canton]] and [[Harwinton, Connecticut|Harwinton]]\n* Minuteman Publications\n** ''[[Fairfield Minuteman]]'' of [[Fairfield, Connecticut|Fairfield]]\n** ''The Westport Minuteman'' {{WS|westportminuteman.com}} of [[Westport, Connecticut|Westport]]\n* Shoreline Newspapers weeklies:\n** ''Branford Review'' of [[Branford, Connecticut|Branford]]\n** ''Clinton Recorder'' of [[Clinton, Connecticut|Clinton]]\n** ''The Dolphin'' of [[Naval Submarine Base New London]] in [[New London, Connecticut|New London]]\n** ''Main Street News'' {{WS|ctmainstreetnews.com}} of [[Essex, Connecticut|Essex]]\n** ''Pictorial Gazette'' of [[Old Saybrook, Connecticut|Old Saybrook]]\n** ''Regional Express'' of [[Colchester, Connecticut|Colchester]]\n** ''Regional Standard'' of [[Colchester, Connecticut|Colchester]]\n** ''Shoreline Times'' {{WS|shorelinetimes.com}} of [[Guilford, Connecticut|Guilford]]\n** ''Shore View East'' of [[Madison, Connecticut|Madison]]\n** ''Shore View West'' of [[Guilford, Connecticut|Guilford]]\n* Other weeklies:\n** ''Registro'' {{WS|registroct.com}} of [[New Haven, Connecticut|New Haven]]\n** ''Thomaston Express'' {{WS|thomastownexpress.com}} of [[Thomaston, Connecticut|Thomaston]]\n** ''Foothills Traders'' {{WS|foothillstrader.com}} of Torrington, Bristol, Canton\n\n== Michigan ==\nFour dailies, associated weeklies and [[pennysaver]]s in the state of [[Michigan]]; also [http://www.micentralhomes.com MIcentralhomes.com] and [http://www.micentralautos.com MIcentralautos.com]\n* ''[[Oakland Press]]'' {{WS|theoaklandpress.com}} of [[Oakland, Michigan|Oakland]]\n* ''Daily Tribune'' {{WS|dailytribune.com}} of [[Royal Oak, Michigan|Royal Oak]]\n* ''Macomb Daily'' {{WS|macombdaily.com}} of [[Mt. Clemens, Michigan|Mt. Clemens]]\n* ''[[Morning Sun]]'' {{WS|themorningsun.com}} of  [[Mount Pleasant, Michigan|Mount Pleasant]]\n* Heritage Newspapers {{WS|heritage.com}}\n** ''Belleville View''\n** ''Ile Camera''\n** ''Monroe Guardian''\n** ''Ypsilanti Courier''\n** ''News-Herald''\n** ''Press & Guide''\n** ''Chelsea Standard & Dexter Leader''\n** ''Manchester Enterprise''\n** ''Milan News-Leader''\n** ''Saline Reporter''\n* Independent Newspapers {{WS|sourcenewspapers.com}}\n** ''Advisor''\n** ''Source''\n* Morning Star {{WS|morningstarpublishing.com}}\n** ''Alma Reminder''\n** ''Alpena Star''\n** ''Antrim County News''\n** ''Carson City Reminder''\n** ''The Leader & Kalkaskian''\n** ''Ogemaw/Oscoda County Star''\n** ''Petoskey/Charlevoix Star''\n** ''Presque Isle Star''\n** ''Preview Community Weekly''\n** ''Roscommon County Star''\n** ''St. Johns Reminder''\n** ''Straits Area Star''\n** ''The (Edmore) Advertiser'' \n* Voice Newspapers {{WS|voicenews.com}}\n** ''Armada Times''\n** ''Bay Voice''\n** ''Blue Water Voice''\n** ''Downriver Voice''\n** ''Macomb Township Voice''\n** ''North Macomb Voice''\n** ''Weekend Voice''\n** ''Suburban Lifestyles'' {{WS|suburbanlifestyles.com}}\n\n== Mid-Hudson ==\nOne daily, associated magazines in the [[Hudson River Valley]] of [[New York]]; also [http://www.midhudsoncentral.com MidHudsonCentral.com] and [http://www.jobsinnewyork.com JobsInNewYork.com].\n\n* ''[[Daily Freeman]]'' {{WS|dailyfreeman.com}} of [[Kingston, New York]]\n\n== Ohio ==\nTwo dailies, associated magazines and three shared Websites, all in the state of [[Ohio]]: [http://www.allaroundcleveland.com AllAroundCleveland.com], [http://www.allaroundclevelandcars.com AllAroundClevelandCars.com] and [http://www.allaroundclevelandjobs.com AllAroundClevelandJobs.com].\n\n* ''[[The News-Herald (Ohio)|The News-Herald]]'' {{WS|news-herald.com}} of [[Willoughby, Ohio|Willoughby]]\n* ''[[The Morning Journal]]'' {{WS|morningjournal.com}} of [[Lorain, Ohio|Lorain]]\n\n== Philadelphia area ==\nSeven dailies and associated weeklies and magazines in [[Pennsylvania]] and [[New Jersey]], and associated Websites: [http://www.allaroundphilly.com AllAroundPhilly.com], [http://www.jobsinnj.com JobsInNJ.com], [http://www.jobsinpa.com JobsInPA.com], and [http://www.phillycarsearch.com PhillyCarSearch.com].\n\n* ''The Daily Local'' {{WS|dailylocal.com}} of [[West Chester, Pennsylvania|West Chester]]\n* ''[[Delaware County Daily and Sunday Times]] {{WS|delcotimes.com}} of Primos\n* ''[[The Mercury (Pennsylvania)|The Mercury]]'' {{WS|pottstownmercury.com}} of [[Pottstown, Pennsylvania|Pottstown]]\n* ''The Phoenix'' {{WS|phoenixvillenews.com}} of [[Phoenixville, Pennsylvania|Phoenixville]]\n* ''[[The Reporter (Lansdale)|The Reporter]]'' {{WS|thereporteronline.com}} of [[Lansdale, Pennsylvania|Lansdale]]\n* ''The Times Herald'' {{WS|timesherald.com}} of [[Norristown, Pennsylvania|Norristown]]\n* ''[[The Trentonian]]'' {{WS|trentonian.com}} of [[Trenton, New Jersey]]\n\n* Weeklies\n** ''El Latino Expreso'' of [[Trenton, New Jersey]]\n** ''La Voz'' of [[Norristown, Pennsylvania]]\n** ''The Village News'' of [[Downingtown, Pennsylvania]]\n** ''The Times Record'' of [[Kennett Square, Pennsylvania]]\n** ''The Tri-County Record'' {{WS|tricountyrecord.com}} of [[Morgantown, Pennsylvania]]\n** ''News of Delaware County'' {{WS|newsofdelawarecounty.com}}of [[Havertown, Pennsylvania]]\n** ''Main Line Times'' {{WS|mainlinetimes.com}}of [[Ardmore, Pennsylvania]]\n** ''Penny Pincher'' of [[Pottstown, Pennsylvania]]\n** ''Town Talk'' {{WS|towntalknews.com}} of [[Ridley, Pennsylvania]]\n* Chesapeake Publishing {{WS|pa8newsgroup.com}} \n** ''Solanco Sun Ledger'' of [[Quarryville, Pennsylvania]]\n** ''Columbia Ledger'' of [[Columbia, Pennsylvania]]\n** ''Coatesville Ledger'' of [[Downingtown, Pennsylvania]]\n** ''Parkesburg Post Ledger'' of [[Quarryville, Pennsylvania]]\n** ''Downingtown Ledger'' of [[Downingtown, Pennsylvania]]\n** ''The Kennett Paper'' of [[Kennett Square, Pennsylvania]]\n** ''Avon Grove Sun'' of [[West Grove, Pennsylvania]]\n** ''Oxford Tribune'' of [[Oxford, Pennsylvania]]\n** ''Elizabethtown Chronicle'' of [[Elizabethtown, Pennsylvania]]\n** ''Donegal Ledger'' of [[Donegal, Pennsylvania]]\n** ''Chadds Ford Post'' of [[Chadds Ford, Pennsylvania]]\n** ''The Central Record'' of [[Medford, New Jersey]]\n** ''Maple Shade Progress'' of [[Maple Shade, New Jersey]]\n* Intercounty Newspapers {{WS|buckslocalnews.com}} \n** ''The Review'' of Roxborough, Pennsylvania\n** ''The Recorder'' of [[Conshohocken, Pennsylvania]]\n** ''The Leader'' of [[Mount Airy, Pennsylvania|Mount Airy]] and West Oak Lake, Pennsylvania\n** ''The Pennington Post'' of [[Pennington, New Jersey]]\n** ''The Bristol Pilot'' of [[Bristol, Pennsylvania]]\n** ''Yardley News'' of [[Yardley, Pennsylvania]]\n** ''New Hope Gazette'' of [[New Hope, Pennsylvania]]\n** ''Doylestown Patriot'' of [[Doylestown, Pennsylvania]]\n** ''Newtown Advance'' of [[Newtown, Pennsylvania]]\n** ''The Plain Dealer'' of [[Williamstown, New Jersey]]\n** ''News Report'' of [[Sewell, New Jersey]]\n** ''Record Breeze'' of [[Berlin, New Jersey]]\n** ''Newsweekly'' of [[Moorestown, New Jersey]]\n** ''Haddon Herald'' of [[Haddonfield, New Jersey]]\n** ''New Egypt Press'' of [[New Egypt, New Jersey]]\n** ''Community News'' of [[Pemberton, New Jersey]]\n** ''Plymouth Meeting Journal'' of [[Plymouth Meeting, Pennsylvania]]\n** ''Lafayette Hill Journal'' of [[Lafayette Hill, Pennsylvania]]\n* Montgomery Newspapers {{WS|montgomerynews.com}} \n** ''Ambler Gazette'' of [[Ambler, Pennsylvania]]\n** ''Central Bucks Life'' of [[Bucks County, Pennsylvania]]\n** ''The Colonial'' of [[Plymouth Meeting, Pennsylvania]]\n** ''Glenside News'' of [[Glenside, Pennsylvania]]\n** ''The Globe'' of [[Lower Moreland Township, Pennsylvania]]\n** ''Main Line Life'' of [[Ardmore, Pennsylvania]]\n** ''Montgomery Life'' of [[Fort Washington, Pennsylvania]]\n** ''North Penn Life'' of [[Lansdale, Pennsylvania]]\n** ''Perkasie News Herald'' of [[Perkasie, Pennsylvania]]\n** ''Public Spirit'' of [[Hatboro, Pennsylvania]]\n** ''Souderton Independent'' of [[Souderton, Pennsylvania]]\n** ''Springfield Sun'' of [[Springfield, Pennsylvania]]\n** ''Spring-Ford Reporter'' of [[Royersford, Pennsylvania]]\n** ''Times Chronicle'' of [[Jenkintown, Pennsylvania]]\n** ''Valley Item'' of [[Perkiomenville, Pennsylvania]]\n** ''Willow Grove Guide'' of [[Willow Grove, Pennsylvania]]\n* News Gleaner Publications (closed December 2008) {{WS|newsgleaner.com}} \n** ''Life Newspapers'' of [[Philadelphia, Pennsylvania]]\n* Suburban Publications\n** ''The Suburban & Wayne Times'' {{WS|waynesuburban.com}} of [[Wayne, Pennsylvania]]\n** ''The Suburban Advertiser'' of [[Exton, Pennsylvania]]\n** ''The King of Prussia Courier'' of [[King of Prussia, Pennsylvania]]\n* Press Newspapers {{WS|countypressonline.com}} \n** ''County Press'' of [[Newtown Square, Pennsylvania]]\n** ''Garnet Valley Press'' of [[Glen Mills, Pennsylvania]]\n** ''Haverford Press'' of [[Newtown Square, Pennsylvania]] (closed January 2009)\n** ''Hometown Press'' of [[Glen Mills, Pennsylvania]] (closed January 2009)\n** ''Media Press'' of [[Newtown Square, Pennsylvania]] (closed January 2009)\n** ''Springfield Press'' of [[Springfield, Pennsylvania]]\n* Berks-Mont Newspapers {{WS|berksmontnews.com}} \n** ''The Boyertown Area Times'' of [[Boyertown, Pennsylvania]]\n** ''The Kutztown Area Patriot'' of [[Kutztown, Pennsylvania]]\n** ''The Hamburg Area Item'' of [[Hamburg, Pennsylvania]]\n** ''The Southern Berks News'' of [[Exeter Township, Berks County, Pennsylvania]]\n** ''The Free Press'' of [[Quakertown, Pennsylvania]]\n** ''The Saucon News'' of [[Quakertown, Pennsylvania]]\n** ''Westside Weekly'' of [[Reading, Pennsylvania]]\n\n* Magazines\n** ''Bucks Co. Town & Country Living''\n** ''Chester Co. Town & Country Living''\n** ''Montomgery Co. Town & Country Living''\n** ''Garden State Town & Country Living''\n** ''Montgomery Homes''\n** ''Philadelphia Golfer''\n** ''Parents Express''\n** ''Art Matters''\n\n{{JRC}}\n\n==References==\n<references />\n\n[[Category:Journal Register publications|*]]\n"
  },
  {
    "path": "python3/tests/speedtest2.txt",
    "content": "This is a '''list of newspapers published by [[Journal Register Company]]'''.\n\nThe company owns daily and weekly newspapers, other print media properties and newspaper-affiliated local Websites in the [[U.S.]] states of [[Connecticut]], [[Michigan]], [[New York]], [[Ohio]], [[Pennsylvania]] and [[New Jersey]], organized in six geographic \"clusters\":<ref>[http://www.journalregister.com/publications.html Journal Register Company: Our Publications], accessed April 21, 2010.</ref>\n\n== Capital-Saratoga ==\nThree dailies, associated weeklies and [[pennysaver]]s in greater [[Albany, New York]]; also [http://www.capitalcentral.com capitalcentral.com] and [http://www.jobsinnewyork.com JobsInNewYork.com].\n\n* ''The Oneida Daily Dispatch'' {{WS|oneidadispatch.com}} of [[Oneida, New York]]\n* ''[[The Record (Troy)|The Record]]'' {{WS|troyrecord.com}} of [[Troy, New York]]\n* ''[[The Saratogian]]'' {{WS|saratogian.com}} of [[Saratoga Springs, New York]]\n* Weeklies:\n** ''Community News'' {{WS|cnweekly.com}} weekly of [[Clifton Park, New York]]\n** ''Rome Observer'' {{WS|romeobserver.com}} of [[Rome, New York]]\n** ''WG Life '' {{WS|saratogian.com/wglife/}} of [[Wilton, New York]]\n** ''Ballston Spa Life '' {{WS|saratogian.com/bspalife}} of [[Ballston Spa, New York]]\n** ''Greenbush Life'' {{WS|troyrecord.com/greenbush}} of [[Troy, New York]]\n** ''Latham Life'' {{WS|troyrecord.com/latham}} of [[Latham, New York]]\n** ''River Life'' {{WS|troyrecord.com/river}} of [[Troy, New York]]\n\n== Connecticut ==\nThree dailies, associated weeklies and [[pennysaver]]s in the state of [[Connecticut]]; also [http://www.ctcentral.com CTcentral.com], [http://www.ctcarsandtrucks.com CTCarsAndTrucks.com] and [http://www.jobsinct.com JobsInCT.com].\n\n* ''The Middletown Press'' {{WS|middletownpress.com}} of [[Middletown, Connecticut|Middletown]]\n* ''[[New Haven Register]]'' {{WS|newhavenregister.com}} of [[New Haven, Connecticut|New Haven]]\n* ''The Register Citizen'' {{WS|registercitizen.com}} of [[Torrington, Connecticut|Torrington]]\n\n* Housatonic Publications \n** ''The Housatonic Times'' {{WS|housatonictimes.com}} of [[New Milford, Connecticut|New Milford]]\n** ''Litchfield County Times'' {{WS|countytimes.com}} of [[Litchfield, Connecticut|Litchfield]]\n\n* Minuteman Publications\n** ''[[Fairfield Minuteman]]'' {{WS|fairfieldminuteman.com}}of [[Fairfield, Connecticut|Fairfield]]\n** ''The Westport Minuteman'' {{WS|westportminuteman.com}} of [[Westport, Connecticut|Westport]]\n\n* Shoreline Newspapers \n** ''The Dolphin'' {{WS|dolphin-news.com}} of [[Naval Submarine Base New London]] in [[New London, Connecticut|New London]]\n** ''Shoreline Times'' {{WS|shorelinetimes.com}} of [[Guilford, Connecticut|Guilford]]\n\n* Foothills Media Group {{WS|foothillsmediagroup.com}}\n** ''Thomaston Express'' {{WS|thomastonexpress.com}} of [[Thomaston, Connecticut|Thomaston]]\n** ''Good News About Torrington'' {{WS|goodnewsabouttorrington.com}} of [[Torrington, Connecticut|Torrington]]\n** ''Granby News'' {{WS|foothillsmediagroup.com/granby}} of [[Granby, Connecticut|Granby]]\n** ''Canton News'' {{WS|foothillsmediagroup.com/canton}} of [[Canton, Connecticut|Canton]]\n** ''Avon News'' {{WS|foothillsmediagroup.com/avon}} of [[Avon, Connecticut|Avon]]\n** ''Simsbury News'' {{WS|foothillsmediagroup.com/simsbury}} of [[Simsbury, Connecticut|Simsbury]]\n** ''Litchfield News'' {{WS|foothillsmediagroup.com/litchfield}} of [[Litchfield, Connecticut|Litchfield]]\n** ''Foothills Trader'' {{WS|foothillstrader.com}} of Torrington, Bristol, Canton\n\n* Other weeklies\n** ''The Milford-Orange Bulletin'' {{WS|ctbulletin.com}} of [[Orange, Connecticut|Orange]]\n** ''The Post-Chronicle'' {{WS|ctpostchronicle.com}} of [[North Haven, Connecticut|North Haven]]\n** ''West Hartford News'' {{WS|westhartfordnews.com}} of [[West Hartford, Connecticut|West Hartford]]\n\n* Magazines\n** ''The Connecticut Bride'' {{WS|connecticutmag.com}}\n** ''Connecticut Magazine'' {{WS|theconnecticutbride.com}}\n** ''Passport Magazine'' {{WS|passport-mag.com}}\n\n== Michigan ==\nFour dailies, associated weeklies and [[pennysaver]]s in the state of [[Michigan]]; also [http://www.micentralhomes.com MIcentralhomes.com] and [http://www.micentralautos.com MIcentralautos.com]\n* ''[[Oakland Press]]'' {{WS|theoaklandpress.com}} of [[Oakland, Michigan|Oakland]]\n* ''Daily Tribune'' {{WS|dailytribune.com}} of [[Royal Oak, Michigan|Royal Oak]]\n* ''Macomb Daily'' {{WS|macombdaily.com}} of [[Mt. Clemens, Michigan|Mt. Clemens]]\n* ''[[Morning Sun]]'' {{WS|themorningsun.com}} of  [[Mount Pleasant, Michigan|Mount Pleasant]]\n\n* Heritage Newspapers {{WS|heritage.com}}\n** ''Belleville View'' {{WS|bellevilleview.com}}\n** ''Ile Camera'' {{WS|thenewsherald.com/ile_camera}}\n** ''Monroe Guardian''  {{WS|monreguardian.com}}\n** ''Ypsilanti Courier'' {{WS|ypsilanticourier.com}}\n** ''News-Herald'' {{WS|thenewsherald.com}}\n** ''Press & Guide'' {{WS|pressandguide.com}}\n** ''Chelsea Standard & Dexter Leader'' {{WS|chelseastandard.com}}\n** ''Manchester Enterprise'' {{WS|manchesterguardian.com}}\n** ''Milan News-Leader'' {{WS|milannews.com}}\n** ''Saline Reporter'' {{WS|salinereporter.com}}\n* Independent Newspapers \n** ''Advisor'' {{WS|sourcenewspapers.com}}\n** ''Source'' {{WS|sourcenewspapers.com}}\n* Morning Star {{WS|morningstarpublishing.com}}\n** ''The Leader & Kalkaskian'' {{WS|leaderandkalkaskian.com}}\n** ''Grand Traverse Insider'' {{WS|grandtraverseinsider.com}}\n** ''Alma Reminder''\n** ''Alpena Star''\n** ''Ogemaw/Oscoda County Star''\n** ''Presque Isle Star''\n** ''St. Johns Reminder''\n\n* Voice Newspapers {{WS|voicenews.com}}\n** ''Armada Times''\n** ''Bay Voice''\n** ''Blue Water Voice''\n** ''Downriver Voice''\n** ''Macomb Township Voice''\n** ''North Macomb Voice''\n** ''Weekend Voice''\n\n== Mid-Hudson ==\nOne daily, associated magazines in the [[Hudson River Valley]] of [[New York]]; also [http://www.midhudsoncentral.com MidHudsonCentral.com] and [http://www.jobsinnewyork.com JobsInNewYork.com].\n\n* ''[[Daily Freeman]]'' {{WS|dailyfreeman.com}} of [[Kingston, New York]]\n* ''Las Noticias'' {{WS|lasnoticiasny.com}} of [[Kingston, New York]]\n\n== Ohio ==\nTwo dailies, associated magazines and three shared Websites, all in the state of [[Ohio]]: [http://www.allaroundcleveland.com AllAroundCleveland.com], [http://www.allaroundclevelandcars.com AllAroundClevelandCars.com] and [http://www.allaroundclevelandjobs.com AllAroundClevelandJobs.com].\n\n* ''[[The News-Herald (Ohio)|The News-Herald]]'' {{WS|news-herald.com}} of [[Willoughby, Ohio|Willoughby]]\n* ''[[The Morning Journal]]'' {{WS|morningjournal.com}} of [[Lorain, Ohio|Lorain]]\n* ''El Latino Expreso'' {{WS|lorainlatino.com}} of [[Lorain, Ohio|Lorain]]\n\n== Philadelphia area ==\nSeven dailies and associated weeklies and magazines in [[Pennsylvania]] and [[New Jersey]], and associated Websites: [http://www.allaroundphilly.com AllAroundPhilly.com], [http://www.jobsinnj.com JobsInNJ.com], [http://www.jobsinpa.com JobsInPA.com], and [http://www.phillycarsearch.com PhillyCarSearch.com].\n\n* ''[[The Daily Local News]]'' {{WS|dailylocal.com}} of [[West Chester, Pennsylvania|West Chester]]\n* ''[[Delaware County Daily and Sunday Times]] {{WS|delcotimes.com}} of Primos [[Upper Darby Township, Pennsylvania]]\n* ''[[The Mercury (Pennsylvania)|The Mercury]]'' {{WS|pottstownmercury.com}} of [[Pottstown, Pennsylvania|Pottstown]]\n* ''[[The Reporter (Lansdale)|The Reporter]]'' {{WS|thereporteronline.com}} of [[Lansdale, Pennsylvania|Lansdale]]\n* ''The Times Herald'' {{WS|timesherald.com}} of [[Norristown, Pennsylvania|Norristown]]\n* ''[[The Trentonian]]'' {{WS|trentonian.com}} of [[Trenton, New Jersey]]\n\n* Weeklies\n* ''The Phoenix'' {{WS|phoenixvillenews.com}} of [[Phoenixville, Pennsylvania]]\n** ''El Latino Expreso'' {{WS|njexpreso.com}} of [[Trenton, New Jersey]]\n** ''La Voz'' {{WS|lavozpa.com}} of [[Norristown, Pennsylvania]]\n** ''The Tri County Record'' {{WS|tricountyrecord.com}} of [[Morgantown, Pennsylvania]]\n** ''Penny Pincher'' {{WS|pennypincherpa.com}}of [[Pottstown, Pennsylvania]]\n\n* Chesapeake Publishing  {{WS|southernchestercountyweeklies.com}}\n** ''The Kennett Paper'' {{WS|kennettpaper.com}} of [[Kennett Square, Pennsylvania]]\n** ''Avon Grove Sun'' {{WS|avongrovesun.com}} of [[West Grove, Pennsylvania]]\n** ''The Central Record'' {{WS|medfordcentralrecord.com}} of [[Medford, New Jersey]]\n** ''Maple Shade Progress'' {{WS|mapleshadeprogress.com}} of [[Maple Shade, New Jersey]]\n\n* Intercounty Newspapers {{WS|buckslocalnews.com}} {{WS|southjerseylocalnews.com}} \n** ''The Pennington Post'' {{WS|penningtonpost.com}} of [[Pennington, New Jersey]]\n** ''The Bristol Pilot'' {{WS|bristolpilot.com}} of [[Bristol, Pennsylvania]]\n** ''Yardley News'' {{WS|yardleynews.com}} of [[Yardley, Pennsylvania]]\n** ''Advance of Bucks County'' {{WS|advanceofbucks.com}} of [[Newtown, Pennsylvania]]\n** ''Record Breeze'' {{WS|recordbreeze.com}} of [[Berlin, New Jersey]]\n** ''Community News'' {{WS|sjcommunitynews.com}} of [[Pemberton, New Jersey]]\n\n* Montgomery Newspapers {{WS|montgomerynews.com}} \n** ''Ambler Gazette'' {{WS|amblergazette.com}} of [[Ambler, Pennsylvania]]\n** ''The Colonial'' {{WS|colonialnews.com}} of [[Plymouth Meeting, Pennsylvania]]\n** ''Glenside News'' {{WS|glensidenews.com}} of [[Glenside, Pennsylvania]]\n** ''The Globe'' {{WS|globenewspaper.com}} of [[Lower Moreland Township, Pennsylvania]]\n** ''Montgomery Life'' {{WS|montgomerylife.com}} of [[Fort Washington, Pennsylvania]]\n** ''North Penn Life'' {{WS|northpennlife.com}} of [[Lansdale, Pennsylvania]]\n** ''Perkasie News Herald'' {{WS|perkasienewsherald.com}} of [[Perkasie, Pennsylvania]]\n** ''Public Spirit'' {{WS|thepublicspirit.com}} of [[Hatboro, Pennsylvania]]\n** ''Souderton Independent'' {{WS|soudertonindependent.com}} of [[Souderton, Pennsylvania]]\n** ''Springfield Sun'' {{WS|springfieldsun.com}} of [[Springfield, Pennsylvania]]\n** ''Spring-Ford Reporter'' {{WS|springfordreporter.com}} of [[Royersford, Pennsylvania]]\n** ''Times Chronicle'' {{WS|thetimeschronicle.com}} of [[Jenkintown, Pennsylvania]]\n** ''Valley Item'' {{WS|valleyitem.com}} of [[Perkiomenville, Pennsylvania]]\n** ''Willow Grove Guide'' {{WS|willowgroveguide.com}} of [[Willow Grove, Pennsylvania]]\n** ''The Review'' {{WS|roxreview.com}} of [[Roxborough, Philadelphia, Pennsylvania]]\n\n* Main Line Media News {{WS|mainlinemedianews.com}}\n** ''Main Line Times'' {{WS|mainlinetimes.com}} of [[Ardmore, Pennsylvania]]\n** ''Main Line Life'' {{WS|mainlinelife.com}} of [[Ardmore, Pennsylvania]]\n** ''The King of Prussia Courier'' {{WS|kingofprussiacourier.com}} of [[King of Prussia, Pennsylvania]]\n\n* Delaware County News Network {{WS|delconewsnetwork.com}} \n** ''News of Delaware County'' {{WS|newsofdelawarecounty.com}} of [[Havertown, Pennsylvania]]\n** ''County Press'' {{WS|countypressonline.com}} of [[Newtown Square, Pennsylvania]]\n** ''Garnet Valley Press'' {{WS|countypressonline.com}} of [[Glen Mills, Pennsylvania]]\n** ''Springfield Press'' {{WS|countypressonline.com}} of [[Springfield, Pennsylvania]]\n** ''Town Talk'' {{WS|towntalknews.com}} of [[Ridley, Pennsylvania]]\n\n* Berks-Mont Newspapers {{WS|berksmontnews.com}} \n** ''The Boyertown Area Times'' {{WS|berksmontnews.com/boyertown_area_times}} of [[Boyertown, Pennsylvania]]\n** ''The Kutztown Area Patriot'' {{WS|berksmontnews.com/kutztown_area_patriot}} of [[Kutztown, Pennsylvania]]\n** ''The Hamburg Area Item'' {{WS|berksmontnews.com/hamburg_area_item}} of [[Hamburg, Pennsylvania]]\n** ''The Southern Berks News'' {{WS|berksmontnews.com/southern_berks_news}} of [[Exeter Township, Berks County, Pennsylvania]]\n** ''Community Connection'' {{WS|berksmontnews.com/community_connection}} of [[Boyertown, Pennsylvania]]\n\n* Magazines\n** ''Bucks Co. Town & Country Living'' {{WS|buckscountymagazine.com}} \n** ''Parents Express'' {{WS|parents-express.com}} \n** ''Real Men, Rednecks'' {{WS|realmenredneck.com}} \n\n{{JRC}}\n\n==References==\n<references />\n\n[[Category:Journal Register publications|*]]\n"
  }
]