Browse Source

collections

David Sehnal 7 years ago
parent
commit
a80c3a34a2

+ 6 - 0
src/mol-base/_spec/collections.spec.ts

@@ -184,6 +184,7 @@ describe('ordered set', () => {
         expect(OrderedSet.isSubset(arr136, OrderedSet.ofSortedArray([1, 3, 7]))).toBe(false);
         expect(OrderedSet.isSubset(OrderedSet.ofSortedArray([0, 1, 2, 3, 7, 10]), OrderedSet.ofSortedArray([1, 3, 7]))).toBe(true);
         expect(OrderedSet.isSubset(arr136, OrderedSet.ofSortedArray([1, 3, 10, 45]))).toBe(false);
+        expect(OrderedSet.isSubset(arr136, OrderedSet.ofSortedArray([12, 13, 16]))).toBe(false);
     });
 
     it('access/membership', () => {
@@ -235,6 +236,10 @@ describe('ordered set', () => {
     testEq('union AA', OrderedSet.union(arr136, OrderedSet.ofSortedArray([2, 4, 6, 7])), [1, 2, 3, 4, 6, 7]);
     testEq('union AA1', OrderedSet.union(arr136, OrderedSet.ofSortedArray([2, 3, 4, 6, 7])), [1, 2, 3, 4, 6, 7]);
     testEq('union AA2', OrderedSet.union(arr136, OrderedSet.ofSortedArray([2, 4, 5, 6, 7])), [1, 2, 3, 4, 5, 6, 7]);
+    testEq('union AA3', OrderedSet.union(OrderedSet.ofSortedArray([1, 3]), OrderedSet.ofSortedArray([2, 4])), [1, 2, 3, 4]);
+    testEq('union AA4', OrderedSet.union(OrderedSet.ofSortedArray([1, 3]), OrderedSet.ofSortedArray([1, 3, 4])), [1, 3, 4]);
+    testEq('union AA5', OrderedSet.union(OrderedSet.ofSortedArray([1, 3, 4]), OrderedSet.ofSortedArray([1, 3])), [1, 3, 4]);
+    it('union AA6', () => expect(OrderedSet.union(arr136, OrderedSet.ofSortedArray([1, 3, 6]))).toBe(arr136));
 
     testEq('intersect ES', OrderedSet.intersect(empty, singleton10), []);
     testEq('intersect ER', OrderedSet.intersect(empty, range1_4), []);
@@ -246,6 +251,7 @@ describe('ordered set', () => {
     testEq('intersect RR2', OrderedSet.intersect(range1_4, OrderedSet.ofRange(3, 5)), [3, 4]);
     testEq('intersect RA', OrderedSet.intersect(range1_4, arr136), [1, 3]);
     testEq('intersect AA', OrderedSet.intersect(arr136, OrderedSet.ofSortedArray([2, 3, 4, 6, 7])), [3, 6]);
+    it('intersect AA1', () => expect(OrderedSet.union(arr136, OrderedSet.ofSortedArray([1, 3, 6]))).toBe(arr136));
 
     testEq('subtract ES', OrderedSet.subtract(empty, singleton10), []);
     testEq('subtract ER', OrderedSet.subtract(empty, range1_4), []);

+ 47 - 23
src/mol-base/collections/ordered-set.ts

@@ -152,7 +152,11 @@ function hasA(set: SortedArray, x: number) { return x >= set[0] && x <= set[set.
 function indexOfA(set: SortedArray, x: number) { return x >= set[0] && x <= set[set.length - 1] ? binarySearch(set, x) : -1; }
 
 function binarySearch(xs: SortedArray, value: number) {
-    let min = 0, max = xs.length - 1;
+    return binarySearchRange(xs, value, 0, xs.length);
+}
+
+function binarySearchRange(xs: SortedArray, value: number, start: number, end: number) {
+    let min = start, max = end - 1;
     while (min <= max) {
         if (min + 11 > max) {
             for (let i = min; i <= max; i++) {
@@ -234,17 +238,17 @@ function areIntersectingAA(xs: SortedArray, ys: SortedArray) {
     return false;
 }
 
-function isSubsetAA(xs: SortedArray, ys: SortedArray) {
-    if (xs === ys) return true;
+function isSubsetAA(a: SortedArray, b: SortedArray) {
+    if (a === b) return true;
 
-    const lenB = ys.length;
-    let { i, j, endA, endB } = getMaxIntersectionRange(xs, ys);
-    // the 2nd array must be able to advance by at least lenB elements
-    if (endB - j + 1 < lenB || endA - j + 1 < lenB) return false;
+    const lenB = b.length;
+    let { i, j, endA, endB } = getMaxIntersectionRange(a, b);
+    // must be able to advance by lenB elements
+    if (endB - j + 1 < lenB || endA - i + 1 < lenB) return false;
 
     let equal = 0;
     while (i <= endA && j <= endB) {
-        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++; }
@@ -302,8 +306,6 @@ function unionAR(a: SortedArray, b: Range) {
 function unionAA(a: SortedArray, b: SortedArray) {
     if (a === b) return a;
 
-    const lenA = a.length, lenB = b.length;
-
     let { i: sI, j: sJ, endA, endB } = getMaxIntersectionRange(a, b);
     let i = sI, j = sJ;
     let commonCount = 0;
@@ -314,8 +316,11 @@ function unionAA(a: SortedArray, b: SortedArray) {
         else { i++; j++; commonCount++; }
     }
 
-    if (!commonCount) return a;
-    if (commonCount >= lenA) return OrderedSet.Empty
+    const lenA = a.length, lenB = b.length;
+    // A === B || B is subset of A ==> A
+    if ((commonCount === lenA && commonCount === lenB) || commonCount === lenB) return a;
+    // A is subset of B ===> B
+    if (commonCount === lenA) return b;
 
     const resultSize = lenA + lenB - commonCount;
     const l = Math.min(a[0], b[0]), r = Math.max(a[lenA - 1], b[lenB - 1]);
@@ -375,27 +380,33 @@ function intersectAR(a: SortedArray, r: Range) {
     return OrderedSet.ofSortedArray(indices);
 }
 
-function intersectAA(xs: SortedArray, ys: SortedArray) {
-    if (xs === ys) return xs;
+function intersectAA(a: SortedArray, b: SortedArray) {
+    if (a === b) return a;
 
-    let { i: sI, j: sJ, endA, endB } = getMaxIntersectionRange(xs, ys);
+    let { i: sI, j: sJ, endA, endB } = getMaxIntersectionRange(a, b);
     let i = sI, j = sJ;
-    let resultSize = 0;
+    let commonCount = 0;
     while (i <= endA && j <= endB) {
-        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++; resultSize++; }
+        else { i++; j++; commonCount++; }
     }
 
-    if (!resultSize) return OrderedSet.Empty;
-
-    const indices = new Int32Array(resultSize);
+    const lenA = a.length, lenB = b.length;
+    // no common elements
+    if (!commonCount) return OrderedSet.Empty;
+    // A === B || B is subset of A ==> B
+    if ((commonCount === lenA && commonCount === lenB) || commonCount === lenB) return b;
+    // A is subset of B ==> A
+    if (commonCount === lenA) return a;
+
+    const indices = new Int32Array(commonCount);
     let offset = 0;
     i = sI;
     j = sJ;
     while (i <= endA && j <= endB) {
-        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++; }
@@ -417,6 +428,7 @@ function substractRR(a: Range, b: Range) {
     if (isRangeSubset(a, b)) {
         // this splits the interval into two, gotta represent it as a set.
         const l = _sRB.fst - _sRA.fst, r = _sRA.snd - _sRB.snd;
+        // TODO: check if l === 0 || r === 0 ==> result would be a range
         const ret = new Int32Array(l + r);
         let offset = 0;
         for (let i = 0; i < l; i++) ret[offset++] = _sRA.fst + i;
@@ -439,7 +451,11 @@ function subtractAR(a: SortedArray, b: Range) {
     const min = _sAR.fst, max = _sAR.snd;
     const { start, end } = getStartEnd(a, min, max);
     const size = a.length - (end - start);
+    // A is subset of B
     if (size <= 0) return OrderedSet.Empty;
+    // No common elements
+    if (size === a.length) return a;
+
     const ret = new Int32Array(size);
     let offset = 0;
     for (let i = 0; i < start; i++) ret[offset++] = a[i];
@@ -458,15 +474,21 @@ function subtractRA(a: Range, b: SortedArray) {
     const rSize = max - min + 1;
     const { start, end } = getStartEnd(b, min, max);
     const commonCount = end - start;
+
+    // No common elements.
+    if (commonCount === 0) return a;
+
     const resultSize = rSize - commonCount;
+    // A is subset of B
     if (resultSize <= 0) return OrderedSet.Empty;
+
     const ret = new Int32Array(resultSize);
     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++) {
-        if (binarySearch(b, i) < 0) ret[offset++] = i;
+        if (binarySearchRange(b, i, start, end) < 0) ret[offset++] = i;
     }
     for (let i = last + 1; i <= max; i++) ret[offset++] = i;
     return OrderedSet.ofSortedArray(ret);
@@ -487,7 +509,9 @@ function subtractAA(a: SortedArray, b: SortedArray) {
         else { i++; j++; commonCount++; }
     }
 
+    // A isnt intersecting B ===> A
     if (!commonCount) return a;
+    // A === B || A is subset of B ===> Empty
     if (commonCount >= lenA) return OrderedSet.Empty;
 
     const indices = new Int32Array(lenA - commonCount);

+ 5 - 0
src/mol-data/model.ts

@@ -8,6 +8,11 @@
 
 interface Model {
 
+    // Incremented when data changes
+    dataVersion: number,
+
+    // Incremented when the underlying conformation changes
+    conformationVersion: number,
 }
 
 export default Model

+ 2 - 2
src/mol-data/structure.ts

@@ -21,7 +21,7 @@ export interface Unit extends Readonly<{
     id: number,
 
     // Each unit can only contain atoms from a single "chain"
-    // the reason for this is to make symmetry and assembly transforms fast
+    // the reason for this is to make certain basic data transforms fast
     // without having to look at the actual contents of the unit
     // multiple units can point to the same chain
     chainIndex: number,
@@ -30,7 +30,7 @@ export interface Unit extends Readonly<{
     model: Model,
 
     // Determines the operation applied to this unit.
-    // The transform and and inverse a baked into the "getPosition" function
+    // The transform and and inverse are baked into the "getPosition" function
     operator: Operator
 }> {
     // returns the untransformed position. Used for spatial queries.