inversionmap.js 5.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156
  1. // Copyright 2008 The Closure Library Authors. All Rights Reserved.
  2. //
  3. // Licensed under the Apache License, Version 2.0 (the "License");
  4. // you may not use this file except in compliance with the License.
  5. // You may obtain a copy of the License at
  6. //
  7. // http://www.apache.org/licenses/LICENSE-2.0
  8. //
  9. // Unless required by applicable law or agreed to in writing, software
  10. // distributed under the License is distributed on an "AS-IS" BASIS,
  11. // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  12. // See the License for the specific language governing permissions and
  13. // limitations under the License.
  14. /**
  15. * @fileoverview Provides inversion and inversion map functionality for storing
  16. * integer ranges and corresponding values.
  17. *
  18. */
  19. goog.provide('goog.structs.InversionMap');
  20. goog.require('goog.array');
  21. goog.require('goog.asserts');
  22. /**
  23. * Maps ranges to values.
  24. * @param {Array<number>} rangeArray An array of monotonically
  25. * increasing integer values, with at least one instance.
  26. * @param {Array<T>} valueArray An array of corresponding values.
  27. * Length must be the same as rangeArray.
  28. * @param {boolean=} opt_delta If true, saves only delta from previous value.
  29. * @constructor
  30. * @template T
  31. */
  32. goog.structs.InversionMap = function(rangeArray, valueArray, opt_delta) {
  33. /**
  34. * @protected {Array<number>}
  35. */
  36. this.rangeArray = null;
  37. goog.asserts.assert(
  38. rangeArray.length == valueArray.length,
  39. 'rangeArray and valueArray must have the same length.');
  40. this.storeInversion_(rangeArray, opt_delta);
  41. /** @protected {Array<T>} */
  42. this.values = valueArray;
  43. };
  44. /**
  45. * Stores the integers as ranges (half-open).
  46. * If delta is true, the integers are delta from the previous value and
  47. * will be restored to the absolute value.
  48. * When used as a set, even indices are IN, and odd are OUT.
  49. * @param {Array<number>} rangeArray An array of monotonically
  50. * increasing integer values, with at least one instance.
  51. * @param {boolean=} opt_delta If true, saves only delta from previous value.
  52. * @private
  53. */
  54. goog.structs.InversionMap.prototype.storeInversion_ = function(
  55. rangeArray, opt_delta) {
  56. this.rangeArray = rangeArray;
  57. for (var i = 1; i < rangeArray.length; i++) {
  58. if (rangeArray[i] == null) {
  59. rangeArray[i] = rangeArray[i - 1] + 1;
  60. } else if (opt_delta) {
  61. rangeArray[i] += rangeArray[i - 1];
  62. }
  63. }
  64. };
  65. /**
  66. * Splices a range -> value map into this inversion map.
  67. * @param {Array<number>} rangeArray An array of monotonically
  68. * increasing integer values, with at least one instance.
  69. * @param {Array<T>} valueArray An array of corresponding values.
  70. * Length must be the same as rangeArray.
  71. * @param {boolean=} opt_delta If true, saves only delta from previous value.
  72. */
  73. goog.structs.InversionMap.prototype.spliceInversion = function(
  74. rangeArray, valueArray, opt_delta) {
  75. // By building another inversion map, we build the arrays that we need
  76. // to splice in.
  77. var otherMap =
  78. new goog.structs.InversionMap(rangeArray, valueArray, opt_delta);
  79. // Figure out where to splice those arrays.
  80. var startRange = otherMap.rangeArray[0];
  81. var endRange =
  82. /** @type {number} */ (goog.array.peek(otherMap.rangeArray));
  83. var startSplice = this.getLeast(startRange);
  84. var endSplice = this.getLeast(endRange);
  85. // The inversion map works by storing the start points of ranges...
  86. if (startRange != this.rangeArray[startSplice]) {
  87. // ...if we're splicing in a start point that isn't already here,
  88. // then we need to insert it after the insertion point.
  89. startSplice++;
  90. } // otherwise we overwrite the insertion point.
  91. this.rangeArray = this.rangeArray.slice(0, startSplice)
  92. .concat(otherMap.rangeArray)
  93. .concat(this.rangeArray.slice(endSplice + 1));
  94. this.values = this.values.slice(0, startSplice)
  95. .concat(otherMap.values)
  96. .concat(this.values.slice(endSplice + 1));
  97. };
  98. /**
  99. * Gets the value corresponding to a number from the inversion map.
  100. * @param {number} intKey The number for which value needs to be retrieved
  101. * from inversion map.
  102. * @return {T|null} Value retrieved from inversion map; null if not found.
  103. */
  104. goog.structs.InversionMap.prototype.at = function(intKey) {
  105. var index = this.getLeast(intKey);
  106. if (index < 0) {
  107. return null;
  108. }
  109. return this.values[index];
  110. };
  111. /**
  112. * Gets the largest index such that rangeArray[index] <= intKey from the
  113. * inversion map.
  114. * @param {number} intKey The probe for which rangeArray is searched.
  115. * @return {number} Largest index such that rangeArray[index] <= intKey.
  116. * @protected
  117. */
  118. goog.structs.InversionMap.prototype.getLeast = function(intKey) {
  119. var arr = this.rangeArray;
  120. var low = 0;
  121. var high = arr.length;
  122. while (high - low > 8) {
  123. var mid = (high + low) >> 1;
  124. if (arr[mid] <= intKey) {
  125. low = mid;
  126. } else {
  127. high = mid;
  128. }
  129. }
  130. for (; low < high; ++low) {
  131. if (intKey < arr[low]) {
  132. break;
  133. }
  134. }
  135. return low - 1;
  136. };