ordered-set.ts 10.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289
  1. /**
  2. * Copyright (c) 2017-2019 mol* contributors, licensed under MIT, See LICENSE file for more info.
  3. *
  4. * @author David Sehnal <david.sehnal@gmail.com>
  5. */
  6. import S from '../sorted-array'
  7. import I from '../interval'
  8. type OrderedSetImpl = I | S
  9. type Nums = ArrayLike<number>
  10. export const Empty: OrderedSetImpl = I.Empty;
  11. export const ofSingleton = I.ofSingleton
  12. export const ofRange = I.ofRange
  13. export const ofBounds = I.ofBounds
  14. export function ofSortedArray(xs: Nums): OrderedSetImpl {
  15. if (!xs.length) return Empty;
  16. // check if the array is just a range
  17. if (xs[xs.length - 1] - xs[0] + 1 === xs.length) return I.ofRange(xs[0], xs[xs.length - 1]);
  18. return xs as any;
  19. }
  20. export function size(set: OrderedSetImpl) { return I.is(set) ? I.size(set) : S.size(set); }
  21. export function has(set: OrderedSetImpl, x: number) { return I.is(set) ? I.has(set, x) : S.has(set, x); }
  22. /** Returns the index of `x` in `set` or -1 if not found. */
  23. export function indexOf(set: OrderedSetImpl, x: number) { return I.is(set) ? I.indexOf(set, x) : S.indexOf(set, x); }
  24. export function getAt(set: OrderedSetImpl, i: number) { return I.is(set) ? I.getAt(set, i) : set[i]; }
  25. export function min(set: OrderedSetImpl) { return I.is(set) ? I.min(set) : S.min(set); }
  26. export function max(set: OrderedSetImpl) { return I.is(set) ? I.max(set) : S.max(set); }
  27. export function start(set: OrderedSetImpl) { return I.is(set) ? I.start(set) : S.start(set); }
  28. export function end(set: OrderedSetImpl) { return I.is(set) ? I.end(set) : S.end(set); }
  29. export function hashCode(set: OrderedSetImpl) { return I.is(set) ? I.hashCode(set) : S.hashCode(set); }
  30. // TODO: possibly add more hash functions to allow for multilevel hashing.
  31. export function toString(set: OrderedSetImpl) { return I.is(set) ? I.toString(set) : S.toString(set); }
  32. export function areEqual(a: OrderedSetImpl, b: OrderedSetImpl) {
  33. if (I.is(a)) {
  34. if (I.is(b)) return I.areEqual(a, b);
  35. return areEqualIS(a, b);
  36. } else if (I.is(b)) return areEqualIS(b, a);
  37. return S.areEqual(a, b);
  38. }
  39. export function areIntersecting(a: OrderedSetImpl, b: OrderedSetImpl) {
  40. if (I.is(a)) {
  41. if (I.is(b)) return I.areIntersecting(a, b);
  42. return areIntersectingSI(b, a);
  43. } else if (I.is(b)) return areIntersectingSI(a, b);
  44. return S.areIntersecting(a, b);
  45. }
  46. /** Check if the 2nd argument is a subset of the 1st */
  47. export function isSubset(a: OrderedSetImpl, b: OrderedSetImpl) {
  48. if (I.is(a)) {
  49. if (I.is(b)) return I.isSubInterval(a, b);
  50. return isSubsetIS(a, b);
  51. } else if (I.is(b)) return isSubsetSI(a, b);
  52. return S.isSubset(a, b);
  53. }
  54. export function findPredecessorIndex(set: OrderedSetImpl, x: number) {
  55. return I.is(set) ? I.findPredecessorIndex(set, x) : S.findPredecessorIndex(set, x);
  56. }
  57. export function findPredecessorIndexInInterval(set: OrderedSetImpl, x: number, bounds: I) {
  58. return I.is(set) ? I.findPredecessorIndexInInterval(set, x, bounds) : S.findPredecessorIndexInInterval(set, x, bounds);
  59. }
  60. export function findRange(set: OrderedSetImpl, min: number, max: number) {
  61. return I.is(set) ? I.findRange(set, min, max) : S.findRange(set, min, max);
  62. }
  63. export function intersectionSize(a: OrderedSetImpl, b: OrderedSetImpl): number {
  64. if (I.is(a)) {
  65. if (I.is(b)) return I.intersectionSize(a, b);
  66. return intersectionSizeSI(b, a);
  67. } else if (I.is(b)) return intersectionSizeSI(a, b);
  68. return S.intersectionSize(a, b);
  69. }
  70. export function union(a: OrderedSetImpl, b: OrderedSetImpl) {
  71. if (I.is(a)) {
  72. if (I.is(b)) return unionII(a, b);
  73. return unionSI(b, a);
  74. } else if (I.is(b)) return unionSI(a, b);
  75. return ofSortedArray(S.union(a, b));
  76. }
  77. export function intersect(a: OrderedSetImpl, b: OrderedSetImpl) {
  78. if (I.is(a)) {
  79. if (I.is(b)) return I.intersect(a, b);
  80. return intersectSI(b, a);
  81. } else if (I.is(b)) return intersectSI(a, b);
  82. return ofSortedArray(S.intersect(a, b));
  83. }
  84. export function subtract(a: OrderedSetImpl, b: OrderedSetImpl) {
  85. if (I.is(a)) {
  86. if (I.is(b)) return subtractII(a, b);
  87. return subtractIS(a, b);
  88. } else if (I.is(b)) return subtractSI(a, b);
  89. return ofSortedArray(S.subtract(a, b));
  90. }
  91. function areEqualIS(a: I, b: S) { return I.size(a) === S.size(b) && I.start(a) === S.start(b) && I.end(a) === S.end(b); }
  92. function areIntersectingSI(a: S, b: I) {
  93. return a.length !== 0 && I.size(S.findRange(a, I.min(b), I.max(b))) !== 0;
  94. }
  95. function isSubsetSI(a: S, b: I) {
  96. const minB = I.min(b), maxB = I.max(b);
  97. if (maxB - minB + 1 === 0) return true;
  98. const minA = S.min(a), maxA = S.max(a);
  99. if (minB < minA || maxB > maxA) return false;
  100. const r = S.findRange(a, minB, maxB);
  101. return I.size(r) === I.size(b);
  102. }
  103. function isSubsetIS(a: I, b: S) {
  104. const minA = I.min(a), maxA = I.max(a);
  105. if (maxA - minA + 1 === 0) return false;
  106. const minB = S.min(b), maxB = S.max(b);
  107. return minB >= minA && maxB <= maxA;
  108. }
  109. function areRangesIntersecting(a: OrderedSetImpl, b: OrderedSetImpl) {
  110. const sa = size(a), sb = size(b);
  111. if (sa === 0 && sb === 0) return true;
  112. return sa > 0 && sb > 0 && max(a) >= min(b) && min(a) <= max(b);
  113. }
  114. function isRangeSubset(a: OrderedSetImpl, b: OrderedSetImpl) {
  115. if (!size(a)) return size(b) === 0;
  116. if (!size(b)) return true;
  117. return min(a) <= min(b) && max(a) >= max(b);
  118. }
  119. function unionII(a: I, b: I) {
  120. if (I.areEqual(a, b)) return a;
  121. const sizeA = I.size(a), sizeB = I.size(b);
  122. if (!sizeB) return a;
  123. if (!sizeA) return b;
  124. const minA = I.min(a), minB = I.min(b);
  125. if (areRangesIntersecting(a, b)) return I.ofRange(Math.min(minA, minB), Math.max(I.max(a), I.max(b)));
  126. let lSize, lMin, rSize, rMin;
  127. if (minA < minB) { lSize = sizeA; lMin = minA; rSize = sizeB; rMin = minB; }
  128. else { lSize = sizeB; lMin = minB; rSize = sizeA; rMin = minA; }
  129. const arr = new Int32Array(sizeA + sizeB);
  130. for (let i = 0; i < lSize; i++) arr[i] = i + lMin;
  131. for (let i = 0; i < rSize; i++) arr[i + lSize] = i + rMin;
  132. return ofSortedArray(arr);
  133. }
  134. function unionSI(a: S, b: I) {
  135. const bSize = I.size(b);
  136. if (!bSize) return a;
  137. // is the array fully contained in the range?
  138. if (isRangeSubset(b, a)) return b;
  139. const min = I.min(b), max = I.max(b);
  140. const r = S.findRange(a, min, max);
  141. const start = I.start(r), end = I.end(r);
  142. const indices = new Int32Array(start + (a.length - end) + bSize);
  143. let offset = 0;
  144. for (let i = 0; i < start; i++) indices[offset++] = a[i];
  145. for (let i = min; i <= max; i++) indices[offset++] = i;
  146. for (let i = end, _i = a.length; i < _i; i++) indices[offset++] = a[i];
  147. return ofSortedArray(indices);
  148. }
  149. function intersectionSizeSI(a: S, b: I): number {
  150. if (!I.size(b)) return 0;
  151. const r = S.findRange(a, I.min(b), I.max(b));
  152. return I.end(r) - I.start(r);
  153. }
  154. function intersectSI(a: S, b: I) {
  155. if (!I.size(b)) return Empty;
  156. const r = S.findRange(a, I.min(b), I.max(b));
  157. const start = I.start(r), end = I.end(r);
  158. const resultSize = end - start;
  159. if (!resultSize) return Empty;
  160. if (resultSize === a.length) return a;
  161. const indices = new Int32Array(resultSize);
  162. let offset = 0;
  163. for (let i = start; i < end; i++) {
  164. indices[offset++] = a[i];
  165. }
  166. return ofSortedArray(indices);
  167. }
  168. function subtractII(a: I, b: I) {
  169. if (I.areEqual(a, b)) return Empty;
  170. if (!I.areIntersecting(a, b)) return a;
  171. const minA = I.min(a), maxA = I.max(a);
  172. const minB = I.min(b), maxB = I.max(b);
  173. if (maxA < minA || maxB < minB) return a;
  174. // is A subset of B? ==> Empty
  175. if (I.isSubInterval(b, a)) return Empty;
  176. if (I.isSubInterval(a, b)) {
  177. // this splits the interval into two, gotta represent it as a set.
  178. const l = minB - minA, r = maxA - maxB;
  179. if (l <= 0) return I.ofRange(maxB + 1, maxB + r);
  180. if (r <= 0) return I.ofRange(minA, minA + l - 1);
  181. const ret = new Int32Array(l + r);
  182. let offset = 0;
  183. for (let i = 0; i < l; i++) ret[offset++] = minA + i;
  184. for (let i = 1; i <= r; i++) ret[offset++] = maxB + i;
  185. return ofSortedArray(ret);
  186. }
  187. if (minA < minB) return I.ofRange(minA, minB - 1);
  188. return I.ofRange(maxB + 1, maxA);
  189. }
  190. function subtractSI(a: S, b: I) {
  191. const min = I.min(b), max = I.max(b);
  192. // is empty?
  193. if (max < min) return a;
  194. const r = S.findRange(a, min, max);
  195. const start = I.start(r), end = I.end(r);
  196. const resultSize = a.length - (end - start);
  197. // A is subset of B
  198. if (resultSize <= 0) return Empty;
  199. // No common elements
  200. if (resultSize === a.length) return a;
  201. const ret = new Int32Array(resultSize);
  202. let offset = 0;
  203. for (let i = 0; i < start; i++) ret[offset++] = a[i];
  204. for (let i = end, _i = a.length; i < _i; i++) ret[offset++] = a[i];
  205. return ofSortedArray(ret);
  206. }
  207. function subtractIS(a: I, b: S) {
  208. const min = I.min(a), max = I.max(a);
  209. // is empty?
  210. if (max < min) return a;
  211. const rSize = max - min + 1;
  212. const interval = S.findRange(b, min, max);
  213. const start = I.start(interval), end = I.end(interval);
  214. const commonCount = end - start;
  215. // No common elements.
  216. if (commonCount === 0) return a;
  217. const resultSize = rSize - commonCount;
  218. // A is subset of B
  219. if (resultSize <= 0) return Empty;
  220. const ret = new Int32Array(resultSize);
  221. const li = b.length - 1;
  222. const fst = b[Math.min(start, li)], last = b[Math.min(end, li)];
  223. let offset = 0;
  224. for (let i = min; i < fst; i++) ret[offset++] = i;
  225. for (let i = fst; i <= last; i++) {
  226. if (S.indexOfInInterval(b, i, interval) < 0) ret[offset++] = i;
  227. }
  228. for (let i = last + 1; i <= max; i++) ret[offset++] = i;
  229. return ofSortedArray(ret);
  230. }
  231. export function forEach(set: OrderedSetImpl, f: (value: number, i: number, ctx: any) => void, ctx: any) {
  232. if (I.is(set)) {
  233. const start = I.min(set);
  234. for (let i = start, _i = I.max(set); i <= _i; i++) {
  235. f(i, i - start, ctx);
  236. }
  237. } else {
  238. for (let i = 0, _i = set.length; i < _i; i++) {
  239. f(set[i], i, ctx);
  240. }
  241. }
  242. return ctx;
  243. }