bbb0026f23c1bca21b195cc0e374b2ca74453c6129b7cac1d589908ab3540c22dc29e0259ce42828d211b4c7ee08ca2d1db579f93c9145d14200ed2d6d831c 6.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189
  1. /*---------------------------------------------------------------------------------------------
  2. * Copyright (c) Microsoft Corporation. All rights reserved.
  3. * Licensed under the MIT License. See License.txt in the project root for license information.
  4. *--------------------------------------------------------------------------------------------*/
  5. import { ListAstNode } from './ast.js';
  6. /**
  7. * Concatenates a list of (2,3) AstNode's into a single (2,3) AstNode.
  8. * This mutates the items of the input array!
  9. * If all items have the same height, this method has runtime O(items.length).
  10. * Otherwise, it has runtime O(items.length * max(log(items.length), items.max(i => i.height))).
  11. */
  12. export function concat23Trees(items) {
  13. if (items.length === 0) {
  14. return null;
  15. }
  16. if (items.length === 1) {
  17. return items[0];
  18. }
  19. let i = 0;
  20. /**
  21. * Reads nodes of same height and concatenates them to a single node.
  22. */
  23. function readNode() {
  24. if (i >= items.length) {
  25. return null;
  26. }
  27. const start = i;
  28. const height = items[start].listHeight;
  29. i++;
  30. while (i < items.length && items[i].listHeight === height) {
  31. i++;
  32. }
  33. if (i - start >= 2) {
  34. return concat23TreesOfSameHeight(start === 0 && i === items.length ? items : items.slice(start, i), false);
  35. }
  36. else {
  37. return items[start];
  38. }
  39. }
  40. // The items might not have the same height.
  41. // We merge all items by using a binary concat operator.
  42. let first = readNode(); // There must be a first item
  43. let second = readNode();
  44. if (!second) {
  45. return first;
  46. }
  47. for (let item = readNode(); item; item = readNode()) {
  48. // Prefer concatenating smaller trees, as the runtime of concat depends on the tree height.
  49. if (heightDiff(first, second) <= heightDiff(second, item)) {
  50. first = concat(first, second);
  51. second = item;
  52. }
  53. else {
  54. second = concat(second, item);
  55. }
  56. }
  57. const result = concat(first, second);
  58. return result;
  59. }
  60. export function concat23TreesOfSameHeight(items, createImmutableLists = false) {
  61. if (items.length === 0) {
  62. return null;
  63. }
  64. if (items.length === 1) {
  65. return items[0];
  66. }
  67. let length = items.length;
  68. // All trees have same height, just create parent nodes.
  69. while (length > 3) {
  70. const newLength = length >> 1;
  71. for (let i = 0; i < newLength; i++) {
  72. const j = i << 1;
  73. items[i] = ListAstNode.create23(items[j], items[j + 1], j + 3 === length ? items[j + 2] : null, createImmutableLists);
  74. }
  75. length = newLength;
  76. }
  77. return ListAstNode.create23(items[0], items[1], length >= 3 ? items[2] : null, createImmutableLists);
  78. }
  79. function heightDiff(node1, node2) {
  80. return Math.abs(node1.listHeight - node2.listHeight);
  81. }
  82. function concat(node1, node2) {
  83. if (node1.listHeight === node2.listHeight) {
  84. return ListAstNode.create23(node1, node2, null, false);
  85. }
  86. else if (node1.listHeight > node2.listHeight) {
  87. // node1 is the tree we want to insert into
  88. return append(node1, node2);
  89. }
  90. else {
  91. return prepend(node2, node1);
  92. }
  93. }
  94. /**
  95. * Appends the given node to the end of this (2,3) tree.
  96. * Returns the new root.
  97. */
  98. function append(list, nodeToAppend) {
  99. list = list.toMutable();
  100. let curNode = list;
  101. const parents = new Array();
  102. let nodeToAppendOfCorrectHeight;
  103. while (true) {
  104. // assert nodeToInsert.listHeight <= curNode.listHeight
  105. if (nodeToAppend.listHeight === curNode.listHeight) {
  106. nodeToAppendOfCorrectHeight = nodeToAppend;
  107. break;
  108. }
  109. // assert 0 <= nodeToInsert.listHeight < curNode.listHeight
  110. if (curNode.kind !== 4 /* AstNodeKind.List */) {
  111. throw new Error('unexpected');
  112. }
  113. parents.push(curNode);
  114. // assert 2 <= curNode.childrenLength <= 3
  115. curNode = curNode.makeLastElementMutable();
  116. }
  117. // assert nodeToAppendOfCorrectHeight!.listHeight === curNode.listHeight
  118. for (let i = parents.length - 1; i >= 0; i--) {
  119. const parent = parents[i];
  120. if (nodeToAppendOfCorrectHeight) {
  121. // Can we take the element?
  122. if (parent.childrenLength >= 3) {
  123. // assert parent.childrenLength === 3 && parent.listHeight === nodeToAppendOfCorrectHeight.listHeight + 1
  124. // we need to split to maintain (2,3)-tree property.
  125. // Send the third element + the new element to the parent.
  126. nodeToAppendOfCorrectHeight = ListAstNode.create23(parent.unappendChild(), nodeToAppendOfCorrectHeight, null, false);
  127. }
  128. else {
  129. parent.appendChildOfSameHeight(nodeToAppendOfCorrectHeight);
  130. nodeToAppendOfCorrectHeight = undefined;
  131. }
  132. }
  133. else {
  134. parent.handleChildrenChanged();
  135. }
  136. }
  137. if (nodeToAppendOfCorrectHeight) {
  138. return ListAstNode.create23(list, nodeToAppendOfCorrectHeight, null, false);
  139. }
  140. else {
  141. return list;
  142. }
  143. }
  144. /**
  145. * Prepends the given node to the end of this (2,3) tree.
  146. * Returns the new root.
  147. */
  148. function prepend(list, nodeToAppend) {
  149. list = list.toMutable();
  150. let curNode = list;
  151. const parents = new Array();
  152. // assert nodeToInsert.listHeight <= curNode.listHeight
  153. while (nodeToAppend.listHeight !== curNode.listHeight) {
  154. // assert 0 <= nodeToInsert.listHeight < curNode.listHeight
  155. if (curNode.kind !== 4 /* AstNodeKind.List */) {
  156. throw new Error('unexpected');
  157. }
  158. parents.push(curNode);
  159. // assert 2 <= curNode.childrenFast.length <= 3
  160. curNode = curNode.makeFirstElementMutable();
  161. }
  162. let nodeToPrependOfCorrectHeight = nodeToAppend;
  163. // assert nodeToAppendOfCorrectHeight!.listHeight === curNode.listHeight
  164. for (let i = parents.length - 1; i >= 0; i--) {
  165. const parent = parents[i];
  166. if (nodeToPrependOfCorrectHeight) {
  167. // Can we take the element?
  168. if (parent.childrenLength >= 3) {
  169. // assert parent.childrenLength === 3 && parent.listHeight === nodeToAppendOfCorrectHeight.listHeight + 1
  170. // we need to split to maintain (2,3)-tree property.
  171. // Send the third element + the new element to the parent.
  172. nodeToPrependOfCorrectHeight = ListAstNode.create23(nodeToPrependOfCorrectHeight, parent.unprependChild(), null, false);
  173. }
  174. else {
  175. parent.prependChildOfSameHeight(nodeToPrependOfCorrectHeight);
  176. nodeToPrependOfCorrectHeight = undefined;
  177. }
  178. }
  179. else {
  180. parent.handleChildrenChanged();
  181. }
  182. }
  183. if (nodeToPrependOfCorrectHeight) {
  184. return ListAstNode.create23(nodeToPrependOfCorrectHeight, list, null, false);
  185. }
  186. else {
  187. return list;
  188. }
  189. }