diff --git a/src/apps/structure-info/model.ts b/src/apps/structure-info/model.ts
index 13c6ed4422ff664de86e1eb801a6e224be21cb0e..1b6e2a7612d7486dd36fe09c92beff7828fa9d97 100644
--- a/src/apps/structure-info/model.ts
+++ b/src/apps/structure-info/model.ts
@@ -76,22 +76,15 @@ export function printBonds(structure: Structure) {
         if (!Unit.isAtomic(unit)) continue;
 
         const elements = unit.elements;
-        const { count, offset, neighbor } = unit.bonds;
+        const { a, b } = unit.bonds;
         const { model }  = unit;
 
-        if (!count) continue;
+        if (!a.length) continue;
 
-        for (let j = 0; j < offset.length - 1; ++j) {
-            const start = offset[j];
-            const end = offset[j + 1];
-
-            if (end <= start) continue;
-
-            const aI = elements[j];
-            for (let _bI = start; _bI < end; _bI++) {
-                const bI = elements[neighbor[_bI]];
-                console.log(`${atomLabel(model, aI)} -- ${atomLabel(model, bI)}`);
-            }
+        for (let bI = 0, _bI = a.length; bI < _bI; bI++) {
+            const x = a[bI], y = b[bI];
+            if (x >= y) continue;
+            console.log(`${atomLabel(model, elements[x])} -- ${atomLabel(model, elements[y])}`);
         }
     }
 }
@@ -153,7 +146,7 @@ async function run(mmcif: mmCIF_Database) {
     printSequence(models[0]);
     printIHMModels(models[0]);
     printUnits(structure);
-    // printBonds(structure);
+    printBonds(structure);
     printSecStructure(models[0]);
 }
 
diff --git a/src/mol-math/graph.ts b/src/mol-math/graph.ts
new file mode 100644
index 0000000000000000000000000000000000000000..ce0429662fe04ce07a6a24a56e8a25c188bb4cf1
--- /dev/null
+++ b/src/mol-math/graph.ts
@@ -0,0 +1,7 @@
+/**
+ * Copyright (c) 2018 mol* contributors, licensed under MIT, See LICENSE file for more info.
+ *
+ * @author David Sehnal <david.sehnal@gmail.com>
+ */
+
+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
new file mode 100644
index 0000000000000000000000000000000000000000..3d75dddb0296d749b60261249cfd666ddb1d8a35
--- /dev/null
+++ b/src/mol-math/graph/_spec/int-graph.spec.ts
@@ -0,0 +1,36 @@
+/**
+ * Copyright (c) 2017-2018 mol* contributors, licensed under MIT, See LICENSE file for more info.
+ *
+ * @author David Sehnal <david.sehnal@gmail.com>
+ */
+
+import { IntGraph } from '../int/graph';
+
+describe('IntGraph', () => {
+    const vc = 3;
+    const xs = [0, 1, 2];
+    const ys = [1, 2, 0];
+    const _prop = [10, 11, 12];
+
+    const builder = new IntGraph.EdgeBuilder(vc, xs, ys);
+    const prop: number[] = new Array(builder.slotCount);
+    for (let i = 0; i < builder.edgeCount; i++) {
+        builder.addNextEdge();
+        builder.assignProperty(prop, _prop[i]);
+    }
+    const graph = builder.createGraph({ prop });
+
+    it('triangle-edgeCount', () => expect(graph.edgeCount).toBe(3));
+    it('triangle-vertexEdgeCounts', () => {
+        expect(graph.getVertexEdgeCount(0)).toBe(2);
+        expect(graph.getVertexEdgeCount(1)).toBe(2);
+        expect(graph.getVertexEdgeCount(2)).toBe(2);
+    });
+
+    it('triangle-propAndEdgeIndex', () => {
+        const prop = graph.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);
+    });
+});
\ No newline at end of file
diff --git a/src/mol-math/graph/int/graph.ts b/src/mol-math/graph/int/graph.ts
new file mode 100644
index 0000000000000000000000000000000000000000..fb50ee13f123ed9deec41270e3e81b5923878f56
--- /dev/null
+++ b/src/mol-math/graph/int/graph.ts
@@ -0,0 +1,137 @@
+/**
+ * Copyright (c) 2018 mol* contributors, licensed under MIT, See LICENSE file for more info.
+ *
+ * @author David Sehnal <david.sehnal@gmail.com>
+ */
+
+/**
+ * Represent a graph using vertex adjacency list.
+ *
+ * Edges of the i-th vertex are stored in the arrays a and b
+ * for indices in the range [offset[i], offset[i+1]).
+ *
+ * Edge properties are indexed same as in the arrays a and b.
+ */
+type IntGraph<EdgeProperties extends object = { }> = {
+    readonly offset: ArrayLike<number>,
+    readonly a: ArrayLike<number>,
+    readonly b: ArrayLike<number>,
+    readonly vertexCount: number,
+    readonly edgeCount: number,
+
+    /**
+     * Get the edge index between i-th and j-th vertex.
+     * -1 if the edge does not exist.
+     *
+     * Because the a and b arrays contains each edge twice,
+     * this always returns the smaller of the indices.
+     */
+    getEdgeIndex(i: number, j: number): number,
+    getVertexEdgeCount(i: number): number
+} & EdgeProperties
+
+namespace IntGraph {
+    class Impl implements IntGraph<any> {
+        readonly vertexCount: number;
+
+        getEdgeIndex(i: number, j: number): number {
+            let a, b;
+            if (i < j) { a = i; b = j; }
+            else { a = j; b = i; }
+            for (let t = this.offset[a], _t = this.offset[a + 1]; t < _t; t++) {
+                if (this.b[t] === b) return t;
+            }
+            return -1;
+        }
+
+        getVertexEdgeCount(i: number): number {
+            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) {
+            this.vertexCount = offset.length - 1;
+            if (props) {
+                for (const p of Object.keys(props)) {
+                    (this as any)[p] = props[p];
+                }
+            }
+        }
+    }
+
+    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 class EdgeBuilder {
+        private bucketFill: Int32Array;
+        private current = 0;
+        private curA: number = 0;
+        private curB: number = 0;
+
+        offsets: Int32Array;
+        edgeCount: number;
+        /** the size of the A and B arrays */
+        slotCount: number;
+        a: Int32Array;
+        b: Int32Array;
+
+        createGraph<EdgeProps extends object = { }>(edgeProps?: EdgeProps) {
+            return create(this.offsets, this.a, this.b, this.edgeCount, edgeProps);
+        }
+
+        /**
+         * @example
+         *   const property = new Int32Array(builder.slotCount);
+         *   for (let i = 0; i < builder.edgeCount; i++) {
+         *     builder.addNextEdge();
+         *     builder.assignProperty(property, srcProp[i]);
+         *   }
+         * return builder.createGraph({ property });
+         */
+        addNextEdge() {
+            const a = this.xs[this.current], b = this.ys[this.current];
+
+            const oa = this.offsets[a] + this.bucketFill[a];
+            const ob = this.offsets[b] + this.bucketFill[b];
+
+            this.a[oa] = a;
+            this.b[oa] = b;
+            this.bucketFill[a]++;
+
+            this.a[ob] = b;
+            this.b[ob] = a;
+            this.bucketFill[b]++;
+
+            this.current++;
+            this.curA = oa;
+            this.curB = ob;
+        }
+
+        assignProperty<T>(prop: { [i: number]: T }, value: T) {
+            prop[this.curA] = value;
+            prop[this.curB] = value;
+        }
+
+        constructor(public vertexCount: number, public xs: ArrayLike<number>, public ys: ArrayLike<number>) {
+            this.edgeCount = xs.length;
+            this.offsets = new Int32Array(this.vertexCount + 1);
+            this.bucketFill = new Int32Array(this.vertexCount);
+
+            const bucketSizes = new Int32Array(this.vertexCount);
+            for (let i = 0, _i = this.xs.length; i < _i; i++) bucketSizes[this.xs[i]]++;
+            for (let i = 0, _i = this.ys.length; i < _i; i++) bucketSizes[this.ys[i]]++;
+
+            let offset = 0;
+            for (let i = 0; i < this.vertexCount; i++) {
+                this.offsets[i] = offset;
+                offset += bucketSizes[i];
+            }
+            this.offsets[this.vertexCount] = offset;
+            this.slotCount = offset;
+            this.a = new Int32Array(offset);
+            this.b = new Int32Array(offset);
+        }
+    }
+}
+
+export { IntGraph }
\ No newline at end of file
diff --git a/src/mol-model/sequence/TODO b/src/mol-model/sequence/TODO
deleted file mode 100644
index a14553cb9d4b45b94a29e6b28b9c0478205867b3..0000000000000000000000000000000000000000
--- a/src/mol-model/sequence/TODO
+++ /dev/null
@@ -1,2 +0,0 @@
-- Support for FASTA etc..
-- Mapping/properties for 'structure'
\ No newline at end of file
diff --git a/src/mol-math/graph/graph.ts b/src/mol-model/sequence/constants.ts
similarity index 100%
rename from src/mol-math/graph/graph.ts
rename to src/mol-model/sequence/constants.ts
diff --git a/src/mol-model/sequence/sequence.ts b/src/mol-model/sequence/sequence.ts
new file mode 100644
index 0000000000000000000000000000000000000000..c13501983abdf90f57512f75e1d4e60447759fc8
--- /dev/null
+++ b/src/mol-model/sequence/sequence.ts
@@ -0,0 +1,33 @@
+/**
+ * Copyright (c) 2018 mol* contributors, licensed under MIT, See LICENSE file for more info.
+ *
+ * @author David Sehnal <david.sehnal@gmail.com>
+ */
+
+// import { Column } from 'mol-data/db'
+// TODO
+
+interface Sequence {
+
+}
+
+namespace Sequence {
+    export const enum Kind {
+        Protein = 'protein',
+        RNA = 'RNA',
+        DNA = 'DNA',
+        Generic = 'generic'
+    }
+
+    export type ProteinAlphabet = 'X'
+    export type RnaAlphabet = 'X'
+    export type DnaAlphabet = 'C'
+    export type GenericAlphabet = 'X'
+
+    export interface Base<K extends Kind, Alphabet extends string> {
+        readonly kind: K,
+        readonly sequence: ReadonlyArray<Alphabet>
+    }
+}
+
+export { Sequence }
diff --git a/src/mol-model/structure/structure/unit/bonds/intra-compute.ts b/src/mol-model/structure/structure/unit/bonds/intra-compute.ts
index 816d72cb7e1fcbda3ee5f94a9e587784522598fd..ba344073c94604ba612a5dfafe4bd100770e30bb 100644
--- a/src/mol-model/structure/structure/unit/bonds/intra-compute.ts
+++ b/src/mol-model/structure/structure/unit/bonds/intra-compute.ts
@@ -8,6 +8,7 @@ import { BondType, ElementSymbol } from '../../../model/types'
 import { IntraUnitBonds } from './intra-data'
 import { StructConn, ComponentBondInfo } from '../../../model/formats/mmcif/bonds'
 import Unit from '../../unit'
+import { IntGraph } from 'mol-math/graph';
 
 export interface BondComputationParameters {
     maxHbondLength: number,
@@ -62,48 +63,17 @@ function isHydrogen(i: number) {
     return i === H_ID;
 }
 
-function computePerAtomBonds(atomA: number[], atomB: number[], _order: number[], _flags: number[], atomCount: number) {
-    const bucketSizes = new Int32Array(atomCount);
-    const bucketOffsets = new Int32Array(atomCount + 1) as any as number[];
-    const bucketFill = new Int32Array(atomCount);
-
-    for (const i of atomA) bucketSizes[i]++;
-    for (const i of atomB) bucketSizes[i]++;
-
-    let offset = 0;
-    for (let i = 0; i < atomCount; i++) {
-        bucketOffsets[i] = offset;
-        offset += bucketSizes[i];
+function getGraph(atomA: number[], atomB: number[], _order: number[], _flags: number[], atomCount: number): IntraUnitBonds {
+    const builder = new IntGraph.EdgeBuilder(atomCount, atomA, atomB);
+    const flags = new Uint16Array(builder.slotCount);
+    const order = new Int8Array(builder.slotCount);
+    for (let i = 0, _i = builder.edgeCount; i < _i; i++) {
+        builder.addNextEdge();
+        builder.assignProperty(flags, _flags[i]);
+        builder.assignProperty(order, _order[i]);
     }
-    bucketOffsets[atomCount] = offset;
-
-    const neighbor = new Int32Array(offset) as any as number[];
-    const flags = new Uint16Array(offset) as any as number[];
-    const order = new Int8Array(offset) as any as number[];
-
-    for (let i = 0, _i = atomA.length; i < _i; i++) {
-        const a = atomA[i], b = atomB[i], f = _flags[i], o = _order[i];
-
-        const oa = bucketOffsets[a] + bucketFill[a];
-        const ob = bucketOffsets[b] + bucketFill[b];
 
-        neighbor[oa] = b;
-        flags[oa] = f;
-        order[oa] = o;
-        bucketFill[a]++;
-
-        neighbor[ob] = a;
-        flags[ob] = f;
-        order[ob] = o;
-        bucketFill[b]++;
-    }
-
-    return {
-        offsets: bucketOffsets,
-        neighbor,
-        flags,
-        order
-    };
+    return builder.createGraph({ flags, order });
 }
 
 function _computeBonds(unit: Unit.Atomic, params: BondComputationParameters): IntraUnitBonds {
@@ -231,15 +201,7 @@ function _computeBonds(unit: Unit.Atomic, params: BondComputationParameters): In
         }
     }
 
-    const bonds = computePerAtomBonds(atomA, atomB, order, flags, atomCount);
-
-    return {
-        offset: bonds.offsets,
-        neighbor: bonds.neighbor,
-        flags: bonds.flags,
-        order: bonds.order,
-        count: atomA.length
-    };
+    return getGraph(atomA, atomB, order, flags, atomCount);
 }
 
 function computeIntraUnitBonds(unit: Unit.Atomic, params?: Partial<BondComputationParameters>) {
diff --git a/src/mol-model/structure/structure/unit/bonds/intra-data.ts b/src/mol-model/structure/structure/unit/bonds/intra-data.ts
index c897e6ca63ed8dff5071f1c52def66e41f3bf935..9914145379663e38ce3882ef4634bd57ae3da57e 100644
--- a/src/mol-model/structure/structure/unit/bonds/intra-data.ts
+++ b/src/mol-model/structure/structure/unit/bonds/intra-data.ts
@@ -6,24 +6,13 @@
  */
 
 import { BondType } from '../../../model/types'
+import { IntGraph } from 'mol-math/graph';
 
-interface IntraUnitBonds {
-    /**
-     * Where bonds for atom A start and end.
-     * Start offset at idx, end at idx + 1
-     */
-    offset: ArrayLike<number>,
-    neighbor: ArrayLike<number>,
-
-    order: ArrayLike<number>,
-    flags: ArrayLike<BondType.Flag>,
-
-    count: number
-}
+type IntraUnitBonds = IntGraph<{ readonly order: ArrayLike<number>, readonly flags: ArrayLike<BondType.Flag> }>
 
 namespace IntraUnitBonds {
     export function createEmpty(): IntraUnitBonds {
-        return { offset: [], neighbor: [], order: [], flags: [], count: 0 }
+        return IntGraph.create([], [], [], 0, { flags: [], order: [] });
     }
     export function isCovalent(flags: number) {
         return (flags & BondType.Flag.Covalent) !== 0;