/hunt-mcilroy.js

http://github.com/pgbovine/OnlinePythonTutor · JavaScript · 134 lines · 79 code · 20 blank · 35 comment · 23 complexity · a062d311aa7dd79a18554ae25d75d191 MD5 · raw file

  1. /* Copyright (c) 2006 Tony Garnock-Jones <tonyg@lshift.net>
  2. * Copyright (c) 2006 LShift Ltd. <query@lshift.net>
  3. *
  4. * Permission is hereby granted, free of charge, to any person
  5. * obtaining a copy of this software and associated documentation files
  6. * (the "Software"), to deal in the Software without restriction,
  7. * including without limitation the rights to use, copy, modify, merge,
  8. * publish, distribute, sublicense, and/or sell copies of the Software,
  9. * and to permit persons to whom the Software is furnished to do so,
  10. * subject to the following conditions:
  11. *
  12. * The above copyright notice and this permission notice shall be
  13. * included in all copies or substantial portions of the Software.
  14. *
  15. * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
  16. * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
  17. * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
  18. * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
  19. * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
  20. * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
  21. * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
  22. * SOFTWARE.
  23. */
  24. function diff(file1, file2) {
  25. /* Text diff algorithm following Hunt and McIlroy 1976.
  26. * J. W. Hunt and M. D. McIlroy, An algorithm for differential file
  27. * comparison, Bell Telephone Laboratories CSTR #41 (1976)
  28. * http://www.cs.dartmouth.edu/~doug/
  29. *
  30. * Expects two arrays of strings.
  31. */
  32. var equivalenceClasses = {};
  33. for (var j = 0; j < file2.length; j++) {
  34. var line = file2[j];
  35. if (equivalenceClasses[line]) {
  36. equivalenceClasses[line].push(j);
  37. } else {
  38. equivalenceClasses[line] = [j];
  39. }
  40. }
  41. var candidates = [{file1index: -1,
  42. file2index: -1,
  43. chain: null}];
  44. for (var i = 0; i < file1.length; i++) {
  45. var line = file1[i];
  46. var file2indices = equivalenceClasses[line] || [];
  47. var r = 0;
  48. var c = candidates[0];
  49. for (var jX = 0; jX < file2indices.length; jX++) {
  50. var j = file2indices[jX];
  51. for (var s = 0; s < candidates.length; s++) {
  52. if ((candidates[s].file2index < j) &&
  53. ((s == candidates.length - 1) ||
  54. (candidates[s + 1].file2index > j)))
  55. break;
  56. }
  57. if (s < candidates.length) {
  58. var newCandidate = {file1index: i,
  59. file2index: j,
  60. chain: candidates[s]};
  61. if (r == candidates.length) {
  62. candidates.push(c);
  63. } else {
  64. candidates[r] = c;
  65. }
  66. r = s + 1;
  67. c = newCandidate;
  68. if (r == candidates.length) {
  69. break; // no point in examining further (j)s
  70. }
  71. }
  72. }
  73. candidates[r] = c;
  74. }
  75. // At this point, we know the LCS: it's in the reverse of the
  76. // linked-list through .chain of
  77. // candidates[candidates.length - 1].
  78. // We now apply the LCS to build a "comm"-style picture of the
  79. // differences between file1 and file2.
  80. var result = [];
  81. var tail1 = file1.length;
  82. var tail2 = file2.length;
  83. var common = {common: []};
  84. function processCommon() {
  85. if (common.common.length) {
  86. common.common.reverse();
  87. result.push(common);
  88. common = {common: []};
  89. }
  90. }
  91. for (var candidate = candidates[candidates.length - 1];
  92. candidate != null;
  93. candidate = candidate.chain) {
  94. var different = {file1: [], file2: []};
  95. while (--tail1 > candidate.file1index) {
  96. different.file1.push(file1[tail1]);
  97. }
  98. while (--tail2 > candidate.file2index) {
  99. different.file2.push(file2[tail2]);
  100. }
  101. if (different.file1.length || different.file2.length) {
  102. processCommon();
  103. different.file1.reverse();
  104. different.file2.reverse();
  105. result.push(different);
  106. }
  107. if (tail1 >= 0) {
  108. common.common.push(file1[tail1]);
  109. }
  110. }
  111. processCommon();
  112. result.reverse();
  113. return result;
  114. }