From 6b79fa851292a322f96764b4ec865e86e0f65519 Mon Sep 17 00:00:00 2001
From: David Sehnal <david.sehnal@gmail.com>
Date: Sun, 29 Oct 2017 16:10:26 +0100
Subject: [PATCH] tweaked sorted set impl

---
 package-lock.json                             | Bin 154251 -> 156689 bytes
 package.json                                  |   4 +-
 .../collections/integer/impl/ordered-set.ts   | 104 +++++++++---------
 .../collections/integer/sorted-array.ts       |   4 +-
 4 files changed, 55 insertions(+), 57 deletions(-)

diff --git a/package-lock.json b/package-lock.json
index cfd951679b3a503d8fb86785c98a3e978ea34abc..7c59de544f879018656eb92fe3ca8a026f1778de 100644
GIT binary patch
delta 773
zcmX9*OG_JJ6lJ~|CG}Z~_0ftNjcrLyGMUM1Bj#l?lgVS^BqlBjiAg3iNz7!TGbRP=
zx-EV1Ej}nhabIYmk3cuIt9EHYEJZ8Wu1cYQKqwt`cg{WM9`4~je5Zf*MZfhH`@XNU
znXQI9==Cu+hEDgf_wbS8f>Ku>+*3>`j^>DTnZ|7<T=9!GG9I;uV|F>0lnFA~3UG>b
znI=WX+r+74iCysKT<#4iSa<vQnv$oBH9oekN(*a+P_tqcg&Y~E#QZG}WYtl$HG&PI
z569R*yM2H?n1egrHq;mdLlA#H4&zrklKHxwR@BhyhT34Wgd-mf#@K)qO03YH<@H=l
z;r+!h#i}aKknv<I!*R>81;&<=3(b;{ES6&_zaTrE60yp$i?XEn6kJ%~&`AJuz&&$k
zd+r)YW1YCwY&8>YHVwKkw0(eex3|I|bP4?(2AAR8Z)5PqZV%csg5MXSHok|!`-|Oe
zvj-T@5zHM<bezSYPa~KUy;=gge#@+-nU(p*>_t5MdT2PW<ga@r))n<6(`2w2k6Q%M
z9}G3<W|myb)EQP5<FeZxiZ*0CqcVgkpY;_LugW`3JmqsxdAgLal|5XEDCHcuBDNw+
zfi*D?<Ej@vKGLJp5Ewx5X-o%S9!;TN9GE~R3QQrI0tWPt16}CJ8U1L$bm$EQZlhNW
z(4ilc=6h<y^hjbr59*_}kaNp8eIe-3JPn4?#FVy{(NxR@cc*90U_T_MhBZ1r<38(s
zLxYp4Ti2<4!WL2OSzah)7fGH=<XpiF%Lu`h4BRo!!^%-FV$z@&{yCa%Z;pb`dhI2X
zoj9`G)Nb6I23Ixi*)}9Ca--1P2!sQvh_~jlIo*}IRHJ#n!)FmJK1wjTIohu*Rdc3v
x%~7pK!m8+TQ8j{!geog(wk5jV#X`c-lB2FFq1Nk4)v}s_CqIYU6Gl+B{|Acx0o4Ei

delta 430
zcmV;f0a5;u$O((J34pW#+0+FwE;1r4m$2Re9hdvm0k)S<eE|%UD`zZ|a0d<nHJ32D
z0Vb1B5d>CmIW|UGOiDsfSut#ORCP*uY)3&kLTfX0FGovIP)bKQNHBS1QBiC#F=KLi
zcS>h-aA7n-MM-fla8^`UX=iOqFi=QVQD|vFbZ$6TO+rjeIYcs7L6cE;8kc|>0Tq|J
z-T@G|x7GoQK9hpGGM94|0vD4+jUSUx5d>9eR8mQHcu#71Ye;2vVNOFiSZicXYb#`8
za&$3HS$bA8LpCo%PBc_`Ic-C9LRWG`Oin{pbZ&V$XG?EMN>eaZba!-RbyICcWN%Do
zax!9ROe<nUm!Z%BHj~gU2Dd&I0$2f;%0mG%lS(QKw=gdP4*{2))&UE*9UTID0hgfK
z0Ts6dBm&0?lhnR4m;EgQ8k2#aACpiK1XXlXacoaFFGpHPK~__DHf>^bN^~-7dN5jJ
zRdP#JNH<t#IWliGGGRq4XG}11He*<6K~q{;G-Pl{aZxu?R!3NEZ&7tnK{9bcYezIx
YYfv>gW;j@r;o=vQ5L_U)#x4R&HOh>WH~;_u

diff --git a/package.json b/package.json
index 8b2461c3b..11d5d2928 100644
--- a/package.json
+++ b/package.json
@@ -41,10 +41,10 @@
     "rollup-plugin-json": "^2.3.0",
     "rollup-plugin-node-resolve": "^3.0.0",
     "rollup-watch": "^4.3.1",
-    "ts-jest": "^21.1.3",
+    "ts-jest": "^21.1.4",
     "tslint": "^5.8.0",
     "typescript": "^2.5.3",
-    "uglify-js": "^3.1.5",
+    "uglify-js": "^3.1.6",
     "util.promisify": "^1.0.0"
   },
   "dependencies": {}
diff --git a/src/mol-base/collections/integer/impl/ordered-set.ts b/src/mol-base/collections/integer/impl/ordered-set.ts
index 68e152c0d..050a41ca0 100644
--- a/src/mol-base/collections/integer/impl/ordered-set.ts
+++ b/src/mol-base/collections/integer/impl/ordered-set.ts
@@ -95,11 +95,21 @@ export function subtract(a: OrderedSetImpl, b: OrderedSetImpl) {
 }
 
 const _maxIntRangeRet = { startI: 0, startJ: 0, endI: 0, endJ: 0 };
-function getMaxIntersectionRange(a: S, b: S) {
-    _maxIntRangeRet.startI = S.findPredecessorIndex(a, S.start(b));
-    _maxIntRangeRet.startJ = S.findPredecessorIndex(b, S.start(a));
-    _maxIntRangeRet.endI = S.findPredecessorIndex(a, S.end(b));
-    _maxIntRangeRet.endJ = S.findPredecessorIndex(b, S.end(a));
+// for small sets, just gets the whole range, for large sets does a bunch of binary searches
+function getSuitableIntersectionRange(a: S, b: S) {
+    const la = a.length, lb = b.length;
+    const ratio = la / lb;
+    if (ratio <= 0.5 || ratio >= 2 || (la >= 128 && lb >= 128)) {
+        _maxIntRangeRet.startI = S.findPredecessorIndex(a, S.start(b));
+        _maxIntRangeRet.startJ = S.findPredecessorIndex(b, S.start(a));
+        _maxIntRangeRet.endI = S.findPredecessorIndex(a, S.end(b));
+        _maxIntRangeRet.endJ = S.findPredecessorIndex(b, S.end(a));
+    } else {
+        _maxIntRangeRet.startI = 0;
+        _maxIntRangeRet.startJ = 0;
+        _maxIntRangeRet.endI = la;
+        _maxIntRangeRet.endJ = lb;
+    }
     return _maxIntRangeRet;
 }
 
@@ -112,10 +122,9 @@ function areIntersectingSI(a: S, b: I) {
 function areIntersectingSS(a: S, b: S) {
     if (a === b) return true;
 
-    const xs = S.values(a), ys = S.values(b);
-    let { startI: i, startJ: j, endI, endJ } = getMaxIntersectionRange(a, b);
+    let { startI: i, startJ: j, endI, endJ } = getSuitableIntersectionRange(a, b);
     while (i < endI && j < endJ) {
-        const x = xs[i], y = ys[j];
+        const x = a[i], y = b[j];
         if (x < y) { i++; }
         else if (x > y) { j++; }
         else return true;
@@ -142,15 +151,14 @@ function isSubsetIS(a: I, b: S) {
 function isSubsetSS(a: S, b: S) {
     if (a === b) return true;
 
-    const xs = S.values(a), ys = S.values(b);
-    const lenB = ys.length;
-    let { startI: i, startJ: j, endI, endJ } = getMaxIntersectionRange(a, b);
+    const lenB = b.length;
+    let { startI: i, startJ: j, endI, endJ } = getSuitableIntersectionRange(a, b);
     // must be able to advance by lenB elements
     if (endJ - j < lenB || endI - i < lenB) return false;
 
     let equal = 0;
     while (i < endI && j < endJ) {
-        const x = xs[i], y = ys[j];
+        const x = a[i], y = b[j];
         if (x < y) { i++; }
         else if (x > y) { j++; }
         else { i++; j++; equal++; }
@@ -196,12 +204,11 @@ function unionSI(a: S, b: I) {
     const min = I.min(b), max = I.max(b);
     const r = S.findRange(a, min, max);
     const start = I.start(r), end = I.end(r);
-    const xs = S.values(a);
-    const indices = new Int32Array(start + (xs.length - end) + bSize);
+    const indices = new Int32Array(start + (a.length - end) + bSize);
     let offset = 0;
-    for (let i = 0; i < start; i++) indices[offset++] = xs[i];
+    for (let i = 0; i < start; i++) indices[offset++] = a[i];
     for (let i = min; i <= max; i++) indices[offset++] = i;
-    for (let i = end, _i = xs.length; i < _i; i++) indices[offset] = xs[i];
+    for (let i = end, _i = a.length; i < _i; i++) indices[offset] = a[i];
 
     return ofSortedArray(indices);
 }
@@ -209,25 +216,24 @@ function unionSI(a: S, b: I) {
 function unionSS(a: S, b: S) {
     if (a === b) return a;
 
-    const xs = S.values(a), ys = S.values(b);
-    const { startI: sI, startJ: sJ, endI, endJ } = getMaxIntersectionRange(a, b);
+    const { startI: sI, startJ: sJ, endI, endJ } = getSuitableIntersectionRange(a, b);
     let i = sI, j = sJ;
     let commonCount = 0;
     while (i < endI && j < endJ) {
-        const x = xs[i], y = ys[j];
+        const x = a[i], y = b[j];
         if (x < y) { i++; }
         else if (x > y) { j++; }
         else { i++; j++; commonCount++; }
     }
 
-    const lenA = xs.length, lenB = ys.length;
+    const lenA = a.length, lenB = b.length;
     // A === B || B is subset of A ==> A
-    if ((commonCount === lenA && commonCount === lenB) || commonCount === lenB) return xs;
+    if ((commonCount === lenA && commonCount === lenB) || commonCount === lenB) return a;
     // A is subset of B ===> B
-    if (commonCount === lenA) return ys;
+    if (commonCount === lenA) return b;
 
     const resultSize = lenA + lenB - commonCount;
-    const l = Math.min(xs[0], ys[0]), r = Math.max(xs[lenA - 1], ys[lenB - 1]);
+    const l = Math.min(a[0], b[0]), r = Math.max(a[lenA - 1], b[lenB - 1]);
     // is this just a range?
     if (resultSize === r - l + 1) {
         return I.ofRange(l, r);
@@ -237,22 +243,22 @@ function unionSS(a: S, b: S) {
     let offset = 0;
 
     // insert the "prefixes"
-    for (let k = 0; k < sI; k++) indices[offset++] = xs[k];
-    for (let k = 0; k < sJ; k++) indices[offset++] = ys[k];
+    for (let k = 0; k < sI; k++) indices[offset++] = a[k];
+    for (let k = 0; k < sJ; k++) indices[offset++] = b[k];
 
     // insert the common part
     i = sI;
     j = sJ;
     while (i < endI && j < endJ) {
-        const x = xs[i], y = ys[j];
+        const x = a[i], y = b[j];
         if (x < y) { indices[offset++] = x; i++; }
         else if (x > y) { indices[offset++] = y; j++; }
         else { indices[offset++] = x; i++; j++; }
     }
 
     // insert the "tail"
-    for (; i < lenA; i++) indices[offset++] = xs[i];
-    for (; j < lenB; j++) indices[offset++] = ys[j];
+    for (; i < lenA; i++) indices[offset++] = a[i];
+    for (; j < lenB; j++) indices[offset++] = b[j];
 
     return ofSortedArray(indices);
 }
@@ -265,11 +271,10 @@ function intersectSI(a: S, b: I) {
     const resultSize = end - start;
     if (!resultSize) return Empty;
 
-    const xs = S.values(a);
     const indices = new Int32Array(resultSize);
     let offset = 0;
     for (let i = start; i < end; i++) {
-        indices[offset++] = xs[i];
+        indices[offset++] = a[i];
     }
     return ofSortedArray(indices);
 }
@@ -277,18 +282,17 @@ function intersectSI(a: S, b: I) {
 function intersectSS(a: S, b: S) {
     if (a === b) return a;
 
-    const xs = S.values(a), ys = S.values(b);
-    const { startI: sI, startJ: sJ, endI, endJ } = getMaxIntersectionRange(a, b);
+    const { startI: sI, startJ: sJ, endI, endJ } = getSuitableIntersectionRange(a, b);
     let i = sI, j = sJ;
     let commonCount = 0;
     while (i < endI && j < endJ) {
-        const x = xs[i], y = ys[j];
+        const x = a[i], y = b[j];
         if (x < y) { i++; }
         else if (x > y) { j++; }
         else { i++; j++; commonCount++; }
     }
 
-    const lenA = xs.length, lenB = ys.length;
+    const lenA = a.length, lenB = b.length;
     // no common elements
     if (!commonCount) return Empty;
     // A === B || B is subset of A ==> B
@@ -301,7 +305,7 @@ function intersectSS(a: S, b: S) {
     i = sI;
     j = sJ;
     while (i < endI && j < endJ) {
-        const x = xs[i], y = ys[j];
+        const x = a[i], y = b[j];
         if (x < y) { i++; }
         else if (x > y) { j++; }
         else { indices[offset++] = x; i++; j++; }
@@ -340,19 +344,18 @@ function subtractSI(a: S, b: I) {
     // is empty?
     if (max < min) return a;
 
-    const xs = S.values(a);
     const r = S.findRange(a, min, max);
     const start = I.start(r), end = I.end(r);
-    const resultSize = xs.length - (end - start);
+    const resultSize = a.length - (end - start);
     // A is subset of B
     if (resultSize <= 0) return Empty;
     // No common elements
-    if (resultSize === xs.length) return xs;
+    if (resultSize === a.length) return a;
 
     const ret = new Int32Array(resultSize);
     let offset = 0;
-    for (let i = 0; i < start; i++) ret[offset++] = xs[i];
-    for (let i = end, _i = xs.length; i < _i; i++) ret[offset++] = xs[i];
+    for (let i = 0; i < start; i++) ret[offset++] = a[i];
+    for (let i = end, _i = a.length; i < _i; i++) ret[offset++] = a[i];
     return ofSortedArray(ret);
 }
 
@@ -374,10 +377,9 @@ function subtractIS(a: I, b: S) {
     // A is subset of B
     if (resultSize <= 0) return Empty;
 
-    const xs = S.values(b);
     const ret = new Int32Array(resultSize);
-    const li = xs.length - 1;
-    const fst = xs[Math.min(start, li)], last = xs[Math.min(end, li)];
+    const li = b.length - 1;
+    const fst = b[Math.min(start, li)], last = b[Math.min(end, li)];
     let offset = 0;
     for (let i = min; i < fst; i++) ret[offset++] = i;
     for (let i = fst; i <= last; i++) {
@@ -390,21 +392,19 @@ function subtractIS(a: I, b: S) {
 function subtractSS(a: S, b: S) {
     if (a === b) return Empty;
 
-    const xs = S.values(a), ys = S.values(b);
-    const lenA = xs.length;
-
-    const { startI: sI, startJ: sJ, endI, endJ } = getMaxIntersectionRange(a, b);
+    const lenA = a.length;
+    const { startI: sI, startJ: sJ, endI, endJ } = getSuitableIntersectionRange(a, b);
     let i = sI, j = sJ;
     let commonCount = 0;
     while (i < endI && j < endJ) {
-        const x = xs[i], y = ys[j];
+        const x = a[i], y = b[j];
         if (x < y) { i++; }
         else if (x > y) { j++; }
         else { i++; j++; commonCount++; }
     }
 
     // A isnt intersecting B ===> A
-    if (!commonCount) return xs;
+    if (!commonCount) return a;
     // A === B || A is subset of B ===> Empty
     if (commonCount >= lenA) return Empty;
 
@@ -412,19 +412,19 @@ function subtractSS(a: S, b: S) {
     let offset = 0;
 
     // insert the "prefix"
-    for (let k = 0; k < sI; k++) indices[offset++] = xs[k];
+    for (let k = 0; k < sI; k++) indices[offset++] = a[k];
 
     i = sI;
     j = sJ;
     while (i < endI && j < endJ) {
-        const x = xs[i], y = ys[j];
+        const x = a[i], y = b[j];
         if (x < y) { indices[offset++] = x; i++; }
         else if (x > y) { j++; }
         else { i++; j++; }
     }
 
     // insert the "tail"
-    for (; i < lenA; i++) indices[offset++] = xs[i];
+    for (; i < lenA; i++) indices[offset++] = a[i];
 
     return ofSortedArray(indices);
 }
\ No newline at end of file
diff --git a/src/mol-base/collections/integer/sorted-array.ts b/src/mol-base/collections/integer/sorted-array.ts
index d30f572c8..1a95711d7 100644
--- a/src/mol-base/collections/integer/sorted-array.ts
+++ b/src/mol-base/collections/integer/sorted-array.ts
@@ -12,8 +12,6 @@ namespace SortedArray {
     export const ofSortedArray: (xs: ArrayLike<number>) => SortedArray = Impl.ofSortedArray as any;
     export const is: (v: any) => v is Interval = Impl.is as any;
 
-    export const values: (array: SortedArray) => ArrayLike<number> = (xs) => xs as any;
-
     export const has: (array: SortedArray, x: number) => boolean = Impl.has as any;
     export const indexOf: (array: SortedArray, x: number) => number = Impl.indexOf as any;
     export const indexOfInterval: (array: SortedArray, x: number, bounds: Interval) => number = Impl.indexOfInterval as any;
@@ -33,6 +31,6 @@ namespace SortedArray {
     export const findRange: (array: SortedArray, min: number, max: number) => Interval = Impl.findRange as any;
 }
 
-interface SortedArray { '@type': 'int-sorted-array' }
+interface SortedArray extends ReadonlyArray<number> { '@type': 'int-sorted-array' }
 
 export default SortedArray
\ No newline at end of file
-- 
GitLab