Browse Source

tweaked sorted set impl

David Sehnal 7 years ago
parent
commit
6b79fa8512

+ 79 - 11
package-lock.json

@@ -3553,7 +3553,7 @@
       "integrity": "sha512-RxzILEgDOFvUgnuPVXNCfZ5f7Mv6RlQ1OyW925kmWRCgmV2V+g6G5qmKUmgW2HUQxp0YqqvRkqbez4Q/o6fRog==",
       "dev": true,
       "requires": {
-        "acorn": "5.1.2",
+        "acorn": "5.2.0",
         "estree-walker": "0.5.0",
         "magic-string": "0.22.4",
         "resolve": "1.5.0",
@@ -3561,9 +3561,9 @@
       },
       "dependencies": {
         "acorn": {
-          "version": "5.1.2",
-          "resolved": "https://registry.npmjs.org/acorn/-/acorn-5.1.2.tgz",
-          "integrity": "sha512-o96FZLJBPY1lvTuJylGA9Bk3t/GKPPJG8H0ydQQl01crzwJgspa4AEIq/pVTXigmK0PHVQhiAtn8WMBLL9D2WA==",
+          "version": "5.2.0",
+          "resolved": "https://registry.npmjs.org/acorn/-/acorn-5.2.0.tgz",
+          "integrity": "sha512-TJT3XkJ12+1vDj24PN5KO5rbUr34UzETv0ZJ4jLBy1IUhQFBb89xpGq9CgovdJfogOuwpFteHyl0jib4ElODzA==",
           "dev": true
         },
         "estree-walker": {
@@ -4034,9 +4034,9 @@
       "dev": true
     },
     "ts-jest": {
-      "version": "21.1.3",
-      "resolved": "https://registry.npmjs.org/ts-jest/-/ts-jest-21.1.3.tgz",
-      "integrity": "sha512-hTRIvxOjykHeuaNC9XkdNk+dbrt1NYzV3C6/CN4Ty9mCtBWrDLNCUtny9gKoIJS0UtwteuSmEdoLgr2biL+bDw==",
+      "version": "21.1.4",
+      "resolved": "https://registry.npmjs.org/ts-jest/-/ts-jest-21.1.4.tgz",
+      "integrity": "sha512-dq5JvwHnxwsQa36dNGOQEpHRcJAZubOvgDfKIQww1L4PUzYTTZOFL2VreyhiUfkOVDir77p3nTQSrpvCv1iFTg==",
       "dev": true,
       "requires": {
         "babel-core": "6.26.0",
@@ -4048,9 +4048,39 @@
         "jest-util": "21.2.1",
         "pkg-dir": "2.0.0",
         "source-map-support": "0.5.0",
-        "yargs": "9.0.1"
+        "yargs": "10.0.3"
       },
       "dependencies": {
+        "camelcase": {
+          "version": "4.1.0",
+          "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-4.1.0.tgz",
+          "integrity": "sha1-1UVjW+HjPFQmScaRc+Xeas+uNN0=",
+          "dev": true
+        },
+        "cliui": {
+          "version": "3.2.0",
+          "resolved": "https://registry.npmjs.org/cliui/-/cliui-3.2.0.tgz",
+          "integrity": "sha1-EgYBU3qRbSmUD5NNo7SNWFo5IT0=",
+          "dev": true,
+          "requires": {
+            "string-width": "1.0.2",
+            "strip-ansi": "3.0.1",
+            "wrap-ansi": "2.1.0"
+          },
+          "dependencies": {
+            "string-width": {
+              "version": "1.0.2",
+              "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz",
+              "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=",
+              "dev": true,
+              "requires": {
+                "code-point-at": "1.1.0",
+                "is-fullwidth-code-point": "1.0.0",
+                "strip-ansi": "3.0.1"
+              }
+            }
+          }
+        },
         "source-map": {
           "version": "0.6.1",
           "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
@@ -4065,6 +4095,44 @@
           "requires": {
             "source-map": "0.6.1"
           }
+        },
+        "strip-ansi": {
+          "version": "3.0.1",
+          "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz",
+          "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=",
+          "dev": true,
+          "requires": {
+            "ansi-regex": "2.1.1"
+          }
+        },
+        "yargs": {
+          "version": "10.0.3",
+          "resolved": "https://registry.npmjs.org/yargs/-/yargs-10.0.3.tgz",
+          "integrity": "sha512-DqBpQ8NAUX4GyPP/ijDGHsJya4tYqLQrjPr95HNsr1YwL3+daCfvBwg7+gIC6IdJhR2kATh3hb61vjzMWEtjdw==",
+          "dev": true,
+          "requires": {
+            "cliui": "3.2.0",
+            "decamelize": "1.2.0",
+            "find-up": "2.1.0",
+            "get-caller-file": "1.0.2",
+            "os-locale": "2.1.0",
+            "require-directory": "2.1.1",
+            "require-main-filename": "1.0.1",
+            "set-blocking": "2.0.0",
+            "string-width": "2.1.1",
+            "which-module": "2.0.0",
+            "y18n": "3.2.1",
+            "yargs-parser": "8.0.0"
+          }
+        },
+        "yargs-parser": {
+          "version": "8.0.0",
+          "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-8.0.0.tgz",
+          "integrity": "sha1-IdR2Mw5agieaS4gTRb8GYQLiGcY=",
+          "dev": true,
+          "requires": {
+            "camelcase": "4.1.0"
+          }
         }
       }
     },
@@ -4145,9 +4213,9 @@
       "dev": true
     },
     "uglify-js": {
-      "version": "3.1.5",
-      "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.1.5.tgz",
-      "integrity": "sha512-tSqlO7/GZHAVSw6mbtJt2kz0ZcUrKUH7Xg92o52aE+gL0r6cXiASZY4dpHqQ7RVGXmoQuPA2qAkG4TkP59f8XA==",
+      "version": "3.1.6",
+      "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.1.6.tgz",
+      "integrity": "sha512-/rseyxEKEVMBo8279lqpoJgD6C/i/CIi+9TJDvWmb+Xo6mqMKwjA8Io3IMHlcXQzj99feR6zrN8m3wqqvm/nYA==",
       "dev": true,
       "requires": {
         "commander": "2.11.0",

+ 2 - 2
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": {}

+ 52 - 52
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);
 }

+ 1 - 3
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