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