From dd0ad070c819aa8ba316b487f04696c0ef88aa91 Mon Sep 17 00:00:00 2001
From: David Sehnal <david.sehnal@gmail.com>
Date: Thu, 7 Jun 2018 13:16:23 +0200
Subject: [PATCH] Induce IntGraph subgraph

---
 src/helpers.d.ts                              |  1 +
 src/mol-data/int/_spec/sorted-array.spec.ts   |  4 ++
 src/mol-data/int/impl/sorted-array.ts         | 35 +++++++++-
 src/mol-data/int/sorted-array.ts              |  4 +-
 src/mol-data/util/array.ts                    | 24 +++++++
 src/mol-math/graph.ts                         |  2 +-
 src/mol-math/graph/_spec/int-graph.spec.ts    | 11 ++-
 .../graph/{int/graph.ts => int-graph.ts}      | 67 +++++++++++++++----
 .../structure/structure/unit/bonds/data.ts    |  1 +
 .../structure/unit/bonds/inter-compute.ts     |  1 +
 .../structure/unit/bonds/inter-data.ts        |  1 +
 .../structure/structure/unit/rings/compute.ts |  2 +-
 12 files changed, 135 insertions(+), 18 deletions(-)
 rename src/mol-math/graph/{int/graph.ts => int-graph.ts} (61%)
 create mode 100644 src/mol-model/structure/structure/unit/bonds/data.ts
 create mode 100644 src/mol-model/structure/structure/unit/bonds/inter-compute.ts
 create mode 100644 src/mol-model/structure/structure/unit/bonds/inter-data.ts

diff --git a/src/helpers.d.ts b/src/helpers.d.ts
index 3dbdd2dc7..69dca2592 100644
--- a/src/helpers.d.ts
+++ b/src/helpers.d.ts
@@ -13,4 +13,5 @@ declare module Helpers {
     export type NumberArray = TypedArray | number[]
     export type UintArray = Uint8Array | Uint16Array | Uint32Array | number[]
     export type ValueOf<T> = T[keyof T]
+    export type ArrayCtor<T> = { new(size: number): { [i: number]: T, length: number } }
 }
\ No newline at end of file
diff --git a/src/mol-data/int/_spec/sorted-array.spec.ts b/src/mol-data/int/_spec/sorted-array.spec.ts
index 2cd82af32..c5ca33cf5 100644
--- a/src/mol-data/int/_spec/sorted-array.spec.ts
+++ b/src/mol-data/int/_spec/sorted-array.spec.ts
@@ -59,6 +59,10 @@ describe('sortedArray', () => {
         compareArrays(SortedArray.deduplicate(SortedArray.ofSortedArray([1, 2, 3])), [1, 2, 3]);
     });
 
+    it('indicesOf', () => {
+        compareArrays(SortedArray.indicesOf(SortedArray.ofSortedArray([10, 11, 12]), SortedArray.ofSortedArray([10, 12, 14])), [0, 2]);
+    })
+
     // console.log(Interval.findPredecessorIndexInInterval(Interval.ofBounds(0, 3), 2, Interval.ofBounds(0, 3)))
     // console.log(SortedArray.findPredecessorIndexInInterval(SortedArray.ofSortedArray([0, 1, 2]), 2, Interval.ofBounds(0, 3)))
 });
\ No newline at end of file
diff --git a/src/mol-data/int/impl/sorted-array.ts b/src/mol-data/int/impl/sorted-array.ts
index 4a5476c16..09b787726 100644
--- a/src/mol-data/int/impl/sorted-array.ts
+++ b/src/mol-data/int/impl/sorted-array.ts
@@ -4,7 +4,7 @@
  * @author David Sehnal <david.sehnal@gmail.com>
  */
 
-import { sortArray, hash3, hash4 } from '../../util'
+import { sortArray, hash3, hash4, createRangeArray } from '../../util'
 import Interval from '../interval'
 
 type Nums = ArrayLike<number>
@@ -289,6 +289,39 @@ export function deduplicate(xs: Nums) {
     return ret;
 }
 
+export function indicesOf(a: Nums, b: Nums): Nums {
+    if (a === b) return ofSortedArray(createRangeArray(0, a.length - 1));
+
+    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 = a[i], y = b[j];
+        if (x < y) { i++; }
+        else if (x > y) { j++; }
+        else { i++; j++; commonCount++; }
+    }
+
+    const lenA = a.length;
+    // no common elements
+    if (!commonCount) return Empty;
+    // A is subset of B ==> A
+    if (commonCount === lenA) return ofSortedArray(createRangeArray(0, a.length - 1));
+
+    const indices = new Int32Array(commonCount);
+    let offset = 0;
+    i = sI;
+    j = sJ;
+    while (i < endI && j < endJ) {
+        const x = a[i], y = b[j];
+        if (x < y) { i++; }
+        else if (x > y) { j++; }
+        else { indices[offset++] = i; i++; j++; }
+    }
+
+    return ofSortedArray(indices);
+}
+
 const _maxIntRangeRet = { startI: 0, startJ: 0, endI: 0, endJ: 0 };
 // for small sets, just gets the whole range, for large sets does a bunch of binary searches
 function getSuitableIntersectionRange(a: Nums, b: Nums) {
diff --git a/src/mol-data/int/sorted-array.ts b/src/mol-data/int/sorted-array.ts
index 928e383dd..2c92aa329 100644
--- a/src/mol-data/int/sorted-array.ts
+++ b/src/mol-data/int/sorted-array.ts
@@ -41,7 +41,9 @@ namespace SortedArray {
     export const findPredecessorIndexInInterval: (array: SortedArray, x: number, bounds: Interval) => number = Impl.findPredecessorIndexInInterval as any;
     export const findRange: (array: SortedArray, min: number, max: number) => Interval = Impl.findRange as any;
 
-    export const deduplicate: (arrat: SortedArray) => SortedArray = Impl.deduplicate as any;
+    export const deduplicate: (array: SortedArray) => SortedArray = Impl.deduplicate as any;
+    /** Returns indices of xs in the array. E.g. indicesOf([10, 11, 12], [10, 12]) ==> [0, 2] */
+    export const indicesOf: (array: SortedArray, xs: SortedArray) => SortedArray = Impl.indicesOf as any;
 }
 
 interface SortedArray extends ArrayLike<number> { '@type': 'int-sorted-array' }
diff --git a/src/mol-data/util/array.ts b/src/mol-data/util/array.ts
index 93707e797..94311c38c 100644
--- a/src/mol-data/util/array.ts
+++ b/src/mol-data/util/array.ts
@@ -22,4 +22,28 @@ export function iterableToArray<T>(it: IterableIterator<T>): T[] {
         ret[ret.length] = value;
     }
     return ret;
+}
+
+/** Fills the array so that array[0] = start and array[array.length - 1] = end */
+export function createRangeArray(start: number, end: number, ctor?: Helpers.ArrayCtor<number>) {
+    const len = end - start + 1;
+    const array = ctor ? new ctor(len) : new Int32Array(len);
+    for (let i = 0; i < len; i++) {
+        array[i] = i + start;
+    }
+    return array;
+}
+
+export function arrayPickIndices<T>(array: ArrayLike<T>, indices: ArrayLike<number>) {
+    const ret = new (arrayGetCtor(array))(indices.length);
+    for (let i = 0, _i = indices.length; i < _i; i++) {
+        ret[i] = array[indices[i]];
+    }
+    return ret;
+}
+
+export function arrayGetCtor<T>(data: ArrayLike<T>): Helpers.ArrayCtor<T> {
+    const ret = (data as any).constructor;
+    if (!ret) throw new Error('data does not define a constructor and it should');
+    return ret;
 }
\ No newline at end of file
diff --git a/src/mol-math/graph.ts b/src/mol-math/graph.ts
index ce0429662..f152b30c2 100644
--- a/src/mol-math/graph.ts
+++ b/src/mol-math/graph.ts
@@ -4,4 +4,4 @@
  * @author David Sehnal <david.sehnal@gmail.com>
  */
 
-export * from './graph/int/graph'
\ No newline at end of file
+export * from './graph/int-graph'
\ No newline at end of file
diff --git a/src/mol-math/graph/_spec/int-graph.spec.ts b/src/mol-math/graph/_spec/int-graph.spec.ts
index 3d75dddb0..25319d118 100644
--- a/src/mol-math/graph/_spec/int-graph.spec.ts
+++ b/src/mol-math/graph/_spec/int-graph.spec.ts
@@ -4,7 +4,7 @@
  * @author David Sehnal <david.sehnal@gmail.com>
  */
 
-import { IntGraph } from '../int/graph';
+import { IntGraph } from '../int-graph';
 
 describe('IntGraph', () => {
     const vc = 3;
@@ -28,9 +28,16 @@ describe('IntGraph', () => {
     });
 
     it('triangle-propAndEdgeIndex', () => {
-        const prop = graph.prop;
+        const prop = graph.edgeProps.prop;
         expect(prop[graph.getEdgeIndex(0, 1)]).toBe(10);
         expect(prop[graph.getEdgeIndex(1, 2)]).toBe(11);
         expect(prop[graph.getEdgeIndex(2, 0)]).toBe(12);
     });
+
+    it('induce', () => {
+        const induced = IntGraph.induceByVertices(graph, [1, 2]);
+        expect(induced.vertexCount).toBe(2);
+        expect(induced.edgeCount).toBe(1);
+        expect(induced.edgeProps.prop[induced.getEdgeIndex(0, 1)]).toBe(11);
+    })
 });
\ No newline at end of file
diff --git a/src/mol-math/graph/int/graph.ts b/src/mol-math/graph/int-graph.ts
similarity index 61%
rename from src/mol-math/graph/int/graph.ts
rename to src/mol-math/graph/int-graph.ts
index fb50ee13f..f6576942c 100644
--- a/src/mol-math/graph/int/graph.ts
+++ b/src/mol-math/graph/int-graph.ts
@@ -1,3 +1,5 @@
+import { arrayPickIndices } from 'mol-data/util';
+
 /**
  * Copyright (c) 2018 mol* contributors, licensed under MIT, See LICENSE file for more info.
  *
@@ -12,12 +14,13 @@
  *
  * Edge properties are indexed same as in the arrays a and b.
  */
-type IntGraph<EdgeProperties extends object = { }> = {
+interface IntGraph<EdgeProps extends IntGraph.EdgePropsBase = {}> {
     readonly offset: ArrayLike<number>,
     readonly a: ArrayLike<number>,
     readonly b: ArrayLike<number>,
     readonly vertexCount: number,
     readonly edgeCount: number,
+    readonly edgeProps: Readonly<EdgeProps>
 
     /**
      * Get the edge index between i-th and j-th vertex.
@@ -28,11 +31,14 @@ type IntGraph<EdgeProperties extends object = { }> = {
      */
     getEdgeIndex(i: number, j: number): number,
     getVertexEdgeCount(i: number): number
-} & EdgeProperties
+}
 
 namespace IntGraph {
-    class Impl implements IntGraph<any> {
+    export type EdgePropsBase = { [name: string]: ArrayLike<any> }
+
+    class IntGraphImpl implements IntGraph<any> {
         readonly vertexCount: number;
+        readonly edgeProps: object;
 
         getEdgeIndex(i: number, j: number): number {
             let a, b;
@@ -48,18 +54,14 @@ namespace IntGraph {
             return this.offset[i + 1] - this.offset[i];
         }
 
-        constructor(public offset: ArrayLike<number>, public a: ArrayLike<number>, public b: ArrayLike<number>, public edgeCount: number, props?: any) {
+        constructor(public offset: ArrayLike<number>, public a: ArrayLike<number>, public b: ArrayLike<number>, public edgeCount: number, edgeProps?: any) {
             this.vertexCount = offset.length - 1;
-            if (props) {
-                for (const p of Object.keys(props)) {
-                    (this as any)[p] = props[p];
-                }
-            }
+            this.edgeProps = edgeProps || {};
         }
     }
 
-    export function create<EdgeProps extends object = { }>(offset: ArrayLike<number>, a: ArrayLike<number>, b: ArrayLike<number>, edgeCount: number, edgeProps?: EdgeProps): IntGraph<EdgeProps> {
-        return new Impl(offset, a, b, edgeCount, edgeProps) as IntGraph<EdgeProps>;
+    export function create<EdgeProps extends IntGraph.EdgePropsBase = {}>(offset: ArrayLike<number>, a: ArrayLike<number>, b: ArrayLike<number>, edgeCount: number, edgeProps?: EdgeProps): IntGraph<EdgeProps> {
+        return new IntGraphImpl(offset, a, b, edgeCount, edgeProps) as IntGraph<EdgeProps>;
     }
 
     export class EdgeBuilder {
@@ -75,7 +77,7 @@ namespace IntGraph {
         a: Int32Array;
         b: Int32Array;
 
-        createGraph<EdgeProps extends object = { }>(edgeProps?: EdgeProps) {
+        createGraph<EdgeProps extends IntGraph.EdgePropsBase = {}>(edgeProps?: EdgeProps) {
             return create(this.offsets, this.a, this.b, this.edgeCount, edgeProps);
         }
 
@@ -132,6 +134,47 @@ namespace IntGraph {
             this.b = new Int32Array(offset);
         }
     }
+
+    export function induceByVertices<P extends IntGraph.EdgePropsBase>(graph: IntGraph<P>, vertexIndices: ArrayLike<number>): IntGraph<P> {
+        const { b, offset, vertexCount, edgeProps } = graph;
+        const vertexMap = new Int32Array(vertexCount);
+        for (let i = 0, _i = vertexIndices.length; i < _i; i++) vertexMap[vertexIndices[i]] = i + 1;
+
+        let newEdgeCount = 0;
+        for (let i = 0; i < vertexCount; i++) {
+            if (vertexMap[i] === 0) continue;
+            for (let j = offset[i], _j = offset[i + 1]; j < _j; j++) {
+                if (b[j] > i && vertexMap[b[j]] !== 0) newEdgeCount++;
+            }
+        }
+
+        const newOffsets = new Int32Array(vertexIndices.length + 1);
+        const edgeIndices = new Int32Array(2 * newEdgeCount);
+        const newA = new Int32Array(2 * newEdgeCount);
+        const newB = new Int32Array(2 * newEdgeCount);
+        let eo = 0, vo = 0;
+        for (let i = 0; i < vertexCount; i++) {
+            if (vertexMap[i] === 0) continue;
+            const aa = vertexMap[i] - 1;
+            for (let j = offset[i], _j = offset[i + 1]; j < _j; j++) {
+                const bb = vertexMap[b[j]];
+                if (bb === 0) continue;
+
+                newA[eo] = aa;
+                newB[eo] = bb - 1;
+                edgeIndices[eo] = j;
+                eo++;
+            }
+            newOffsets[++vo] = eo;
+        }
+
+        const newEdgeProps: P = {} as any;
+        for (const key of Object.keys(edgeProps)) {
+            newEdgeProps[key] = arrayPickIndices(edgeProps[key], edgeIndices);
+        }
+
+        return create(newOffsets, newA, newB, newEdgeCount, newEdgeProps);
+    }
 }
 
 export { IntGraph }
\ No newline at end of file
diff --git a/src/mol-model/structure/structure/unit/bonds/data.ts b/src/mol-model/structure/structure/unit/bonds/data.ts
new file mode 100644
index 000000000..0ffdd02fc
--- /dev/null
+++ b/src/mol-model/structure/structure/unit/bonds/data.ts
@@ -0,0 +1 @@
+// TODO
\ No newline at end of file
diff --git a/src/mol-model/structure/structure/unit/bonds/inter-compute.ts b/src/mol-model/structure/structure/unit/bonds/inter-compute.ts
new file mode 100644
index 000000000..0ffdd02fc
--- /dev/null
+++ b/src/mol-model/structure/structure/unit/bonds/inter-compute.ts
@@ -0,0 +1 @@
+// TODO
\ No newline at end of file
diff --git a/src/mol-model/structure/structure/unit/bonds/inter-data.ts b/src/mol-model/structure/structure/unit/bonds/inter-data.ts
new file mode 100644
index 000000000..0ffdd02fc
--- /dev/null
+++ b/src/mol-model/structure/structure/unit/bonds/inter-data.ts
@@ -0,0 +1 @@
+// TODO
\ No newline at end of file
diff --git a/src/mol-model/structure/structure/unit/rings/compute.ts b/src/mol-model/structure/structure/unit/rings/compute.ts
index a76cd6e47..3a8359412 100644
--- a/src/mol-model/structure/structure/unit/rings/compute.ts
+++ b/src/mol-model/structure/structure/unit/rings/compute.ts
@@ -148,7 +148,7 @@ function addRing(state: State, a: number, b: number) {
 
 function findRings(state: State, from: number) {
     const { bonds, startVertex, endVertex, visited, queue, pred } = state;
-    const { b: neighbor, flags: bondFlags, offset } = bonds;
+    const { b: neighbor, edgeProps: { flags: bondFlags }, offset } = bonds;
     visited[from] = 1;
     queue[0] = from;
     let head = 0, size = 1;
-- 
GitLab