diff --git a/src/mol-base/_spec/collections.spec.ts b/src/mol-base/_spec/collections.spec.ts
index 89e956cd8266b505cb6dae62471818c3d9b3834c..56a7c6009f0780982b4ec38c658af0c5a3c4c94c 100644
--- a/src/mol-base/_spec/collections.spec.ts
+++ b/src/mol-base/_spec/collections.spec.ts
@@ -7,7 +7,6 @@
 import Iterator from '../collections/iterator'
 import IntTuple from '../collections/int-tuple'
 import * as Sort from '../collections/sort'
-import OrderedSet from '../collections/ordered-set'
 import LinkedIndex from '../collections/linked-index'
 import EquivalenceClasses from '../collections/equivalence-classes'
 import Interval from '../collections/interval'
@@ -235,175 +234,6 @@ describe('sortedArray', () => {
     testI('findRange', SortedArray.findRange(a2468, 2, 4), Interval.ofRange(0, 1));
 });
 
-describe('ordered set', () => {
-    function ordSetToArray(set: OrderedSet) {
-        const ret = [];
-        for (let i = 0, _i = OrderedSet.size(set); i < _i; i++) ret.push(OrderedSet.getAt(set, i));
-        return ret;
-    }
-
-    function testEq(name: string, set: OrderedSet, expected: number[]) {
-        it(name, () => {
-            // copy the arrays to ensure "compatibility" between typed and native arrays
-            expect(Array.prototype.slice.call(ordSetToArray(set))).toEqual(Array.prototype.slice.call(expected));
-        });
-    }
-
-    const empty = OrderedSet.Empty;
-    const singleton10 = OrderedSet.ofSingleton(10);
-    const range1_4 = OrderedSet.ofRange(1, 4);
-    const arr136 = OrderedSet.ofSortedArray([1, 3, 6]);
-
-    testEq('empty', empty, []);
-    testEq('singleton', singleton10, [10]);
-    testEq('range', range1_4, [1, 2, 3, 4]);
-    testEq('sorted array', arr136, [1, 3, 6]);
-
-    it('equality', () => {
-        expect(OrderedSet.areEqual(empty, singleton10)).toBe(false);
-        expect(OrderedSet.areEqual(singleton10, singleton10)).toBe(true);
-        expect(OrderedSet.areEqual(range1_4, singleton10)).toBe(false);
-        expect(OrderedSet.areEqual(arr136, OrderedSet.ofSortedArray([1, 3, 6]))).toBe(true);
-        expect(OrderedSet.areEqual(arr136, OrderedSet.ofSortedArray([1, 4, 6]))).toBe(false);
-    });
-
-    it('areIntersecting', () => {
-        expect(OrderedSet.areIntersecting(range1_4, arr136)).toBe(true);
-        expect(OrderedSet.areIntersecting(empty, empty)).toBe(true);
-        expect(OrderedSet.areIntersecting(empty, singleton10)).toBe(false);
-        expect(OrderedSet.areIntersecting(empty, range1_4)).toBe(false);
-        expect(OrderedSet.areIntersecting(empty, arr136)).toBe(false);
-    });
-
-    it('isSubset', () => {
-        expect(OrderedSet.isSubset(singleton10, empty)).toBe(true);
-        expect(OrderedSet.isSubset(range1_4, empty)).toBe(true);
-        expect(OrderedSet.isSubset(arr136, empty)).toBe(true);
-        expect(OrderedSet.isSubset(empty, empty)).toBe(true);
-        expect(OrderedSet.isSubset(empty, singleton10)).toBe(false);
-        expect(OrderedSet.isSubset(empty, range1_4)).toBe(false);
-        expect(OrderedSet.isSubset(empty, arr136)).toBe(false);
-
-        expect(OrderedSet.isSubset(singleton10, range1_4)).toBe(false);
-        expect(OrderedSet.isSubset(range1_4, OrderedSet.ofRange(2, 3))).toBe(true);
-        expect(OrderedSet.isSubset(arr136, range1_4)).toBe(false);
-        expect(OrderedSet.isSubset(arr136, arr136)).toBe(true);
-        expect(OrderedSet.isSubset(arr136, OrderedSet.ofSortedArray([1, 3]))).toBe(true);
-        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', () => {
-        expect(OrderedSet.has(empty, 10)).toBe(false);
-        expect(OrderedSet.indexOf(empty, 10)).toBe(-1);
-
-        expect(OrderedSet.has(singleton10, 10)).toBe(true);
-        expect(OrderedSet.has(singleton10, 11)).toBe(false);
-        expect(OrderedSet.indexOf(singleton10, 10)).toBe(0);
-        expect(OrderedSet.indexOf(singleton10, 11)).toBe(-1);
-
-        expect(OrderedSet.has(range1_4, 4)).toBe(true);
-        expect(OrderedSet.has(range1_4, 5)).toBe(false);
-        expect(OrderedSet.indexOf(range1_4, 4)).toBe(3);
-        expect(OrderedSet.indexOf(range1_4, 11)).toBe(-1);
-
-        expect(OrderedSet.has(arr136, 3)).toBe(true);
-        expect(OrderedSet.has(arr136, 4)).toBe(false);
-        expect(OrderedSet.indexOf(arr136, 3)).toBe(1);
-        expect(OrderedSet.indexOf(arr136, 11)).toBe(-1);
-    });
-
-    it('interval range', () => {
-        expect(OrderedSet.findIntervalRange(empty, 9, 11)).toEqual({ start: 0, end: 0 });
-        expect(OrderedSet.findIntervalRange(empty, -9, -6)).toEqual({ start: 0, end: 0 });
-        expect(OrderedSet.findIntervalRange(singleton10, 9, 11)).toEqual({ start: 0, end: 1 });
-        expect(OrderedSet.findIntervalRange(range1_4, 2, 3)).toEqual({ start: 1, end: 3 });
-        expect(OrderedSet.findIntervalRange(range1_4, -10, 2)).toEqual({ start: 0, end: 2 });
-        expect(OrderedSet.findIntervalRange(range1_4, -10, 20)).toEqual({ start: 0, end: 4 });
-        expect(OrderedSet.findIntervalRange(range1_4, 3, 20)).toEqual({ start: 2, end: 4 });
-        expect(OrderedSet.findIntervalRange(arr136, 0, 1)).toEqual({ start: 0, end: 1 });
-        expect(OrderedSet.findIntervalRange(arr136, 0, 3)).toEqual({ start: 0, end: 2 });
-        expect(OrderedSet.findIntervalRange(arr136, 0, 4)).toEqual({ start: 0, end: 2 });
-        expect(OrderedSet.findIntervalRange(arr136, 2, 4)).toEqual({ start: 1, end: 2 });
-        expect(OrderedSet.findIntervalRange(arr136, 2, 7)).toEqual({ start: 1, end: 3 });
-    })
-
-    testEq('union ES', OrderedSet.union(empty, singleton10), [10]);
-    testEq('union ER', OrderedSet.union(empty, range1_4), [1, 2, 3, 4]);
-    testEq('union EA', OrderedSet.union(empty, arr136), [1, 3, 6]);
-    testEq('union SS', OrderedSet.union(singleton10, OrderedSet.ofSingleton(16)), [10, 16]);
-    testEq('union SR', OrderedSet.union(range1_4, singleton10), [1, 2, 3, 4, 10]);
-    testEq('union SA', OrderedSet.union(arr136, singleton10), [1, 3, 6, 10]);
-    testEq('union SA1', OrderedSet.union(arr136, OrderedSet.ofSingleton(3)), [1, 3, 6]);
-    testEq('union RR', OrderedSet.union(range1_4, range1_4), [1, 2, 3, 4]);
-    testEq('union RR1', OrderedSet.union(range1_4, OrderedSet.ofRange(6, 7)), [1, 2, 3, 4, 6, 7]);
-    testEq('union RR2', OrderedSet.union(range1_4, OrderedSet.ofRange(3, 5)), [1, 2, 3, 4, 5]);
-    testEq('union RA', OrderedSet.union(range1_4, arr136), [1, 2, 3, 4, 6]);
-    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), []);
-    testEq('intersect EA', OrderedSet.intersect(empty, arr136), []);
-    testEq('intersect SS', OrderedSet.intersect(singleton10, OrderedSet.ofSingleton(16)), []);
-    testEq('intersect SS1', OrderedSet.intersect(singleton10, singleton10), [10]);
-    testEq('intersect SR', OrderedSet.intersect(range1_4, singleton10), []);
-    testEq('intersect RR', OrderedSet.intersect(range1_4, range1_4), [1, 2, 3, 4]);
-    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), []);
-    testEq('subtract EA', OrderedSet.subtract(empty, arr136), []);
-    testEq('subtract SS', OrderedSet.subtract(singleton10, OrderedSet.ofSingleton(16)), [10]);
-    testEq('subtract SS1', OrderedSet.subtract(singleton10, singleton10), []);
-    testEq('subtract SR', OrderedSet.subtract(range1_4, singleton10), [1, 2, 3, 4]);
-    testEq('subtract SR1', OrderedSet.subtract(range1_4, OrderedSet.ofSingleton(4)), [1, 2, 3]);
-    testEq('subtract SR2', OrderedSet.subtract(range1_4, OrderedSet.ofSingleton(3)), [1, 2, 4]);
-    testEq('subtract RR', OrderedSet.subtract(range1_4, range1_4), []);
-    testEq('subtract RR1', OrderedSet.subtract(range1_4, OrderedSet.ofRange(3, 5)), [1, 2]);
-
-    testEq('subtract RA', OrderedSet.subtract(range1_4, arr136), [2, 4]);
-    testEq('subtract RA1', OrderedSet.subtract(range1_4, OrderedSet.ofSortedArray([0, 1, 2, 3, 4, 7])), []);
-    testEq('subtract RA2', OrderedSet.subtract(range1_4, OrderedSet.ofSortedArray([0, 2, 3])), [1, 4]);
-
-    testEq('subtract AR', OrderedSet.subtract(arr136, range1_4), [6]);
-    testEq('subtract AR1', OrderedSet.subtract(arr136, OrderedSet.ofRange(0, 10)), []);
-    testEq('subtract AR1', OrderedSet.subtract(arr136, OrderedSet.ofRange(2, 10)), [1]);
-
-    testEq('subtract AA', OrderedSet.subtract(arr136, arr136), []);
-    testEq('subtract AA1', OrderedSet.subtract(arr136, OrderedSet.ofSortedArray([2, 3, 4, 6, 7])), [1]);
-    testEq('subtract AA2', OrderedSet.subtract(arr136, OrderedSet.ofSortedArray([0, 1, 6])), [3]);
-
-    it('segments', () => {
-        const data = OrderedSet.ofSortedArray([4, 9, 10, 11, 14, 15, 16]);
-        const segs = OrderedSet.ofSortedArray([0, 4, 10, 12, 13, 15, 25]);
-        const it = OrderedSet.segments(segs, data);
-
-        const t = Object.create(null);
-        for (let s = it.move(); !it.done; s = it.move()) {
-            for (let j = s.start; j < s.end; j++) {
-                const x = t[s.segment];
-                const v = OrderedSet.getAt(data, j);
-                if (!x) t[s.segment] = [v];
-                else x[x.length] = v;
-            }
-        }
-
-        expect(t).toEqual({ 1: [4, 9], 2: [10, 11], 4: [14], 5: [15, 16] });
-    });
-});
-
-
 describe('linked-index', () => {
     it('initial state', () => {
         const index = LinkedIndex(2);
diff --git a/src/mol-base/_spec/ordered-set.spec.ts b/src/mol-base/_spec/ordered-set.spec.ts
new file mode 100644
index 0000000000000000000000000000000000000000..11ff74e711206d4254959de1e109af0dc4e5accf
--- /dev/null
+++ b/src/mol-base/_spec/ordered-set.spec.ts
@@ -0,0 +1,178 @@
+/**
+ * Copyright (c) 2017 molio contributors, licensed under MIT, See LICENSE file for more info.
+ *
+ * @author David Sehnal <david.sehnal@gmail.com>
+ */
+
+import OrderedSet from '../collections/ordered-set'
+import Interval from '../collections/interval'
+
+describe('ordered set', () => {
+    function ordSetToArray(set: OrderedSet) {
+        const ret = [];
+        for (let i = 0, _i = OrderedSet.size(set); i < _i; i++) ret.push(OrderedSet.getAt(set, i));
+        return ret;
+    }
+
+    function testEq(name: string, set: OrderedSet, expected: number[]) {
+        it(name, () => {
+            // copy the arrays to ensure "compatibility" between typed and native arrays
+            expect(Array.prototype.slice.call(ordSetToArray(set))).toEqual(Array.prototype.slice.call(expected));
+        });
+    }
+
+    const empty = OrderedSet.Empty;
+    const singleton10 = OrderedSet.ofSingleton(10);
+    const range1_4 = OrderedSet.ofRange(1, 4);
+    const arr136 = OrderedSet.ofSortedArray([1, 3, 6]);
+
+    const iB = (s: number, e: number) => Interval.ofBounds(s, e);
+
+    testEq('empty', empty, []);
+    testEq('singleton', singleton10, [10]);
+    testEq('range', range1_4, [1, 2, 3, 4]);
+    testEq('sorted array', arr136, [1, 3, 6]);
+
+    it('equality', () => {
+        expect(OrderedSet.areEqual(empty, singleton10)).toBe(false);
+        expect(OrderedSet.areEqual(singleton10, singleton10)).toBe(true);
+        expect(OrderedSet.areEqual(range1_4, singleton10)).toBe(false);
+        expect(OrderedSet.areEqual(arr136, OrderedSet.ofSortedArray([1, 3, 6]))).toBe(true);
+        expect(OrderedSet.areEqual(arr136, OrderedSet.ofSortedArray([1, 4, 6]))).toBe(false);
+    });
+
+    it('areIntersecting', () => {
+        expect(OrderedSet.areIntersecting(range1_4, arr136)).toBe(true);
+        expect(OrderedSet.areIntersecting(empty, empty)).toBe(true);
+        expect(OrderedSet.areIntersecting(empty, singleton10)).toBe(false);
+        expect(OrderedSet.areIntersecting(empty, range1_4)).toBe(false);
+        expect(OrderedSet.areIntersecting(empty, arr136)).toBe(false);
+    });
+
+    it('isSubset', () => {
+        expect(OrderedSet.isSubset(singleton10, empty)).toBe(true);
+        expect(OrderedSet.isSubset(range1_4, empty)).toBe(true);
+        expect(OrderedSet.isSubset(arr136, empty)).toBe(true);
+        expect(OrderedSet.isSubset(empty, empty)).toBe(true);
+        expect(OrderedSet.isSubset(empty, singleton10)).toBe(false);
+        expect(OrderedSet.isSubset(empty, range1_4)).toBe(false);
+        expect(OrderedSet.isSubset(empty, arr136)).toBe(false);
+
+        expect(OrderedSet.isSubset(singleton10, range1_4)).toBe(false);
+        expect(OrderedSet.isSubset(range1_4, OrderedSet.ofRange(2, 3))).toBe(true);
+        expect(OrderedSet.isSubset(arr136, range1_4)).toBe(false);
+        expect(OrderedSet.isSubset(arr136, arr136)).toBe(true);
+        expect(OrderedSet.isSubset(arr136, OrderedSet.ofSortedArray([1, 3]))).toBe(true);
+        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', () => {
+        expect(OrderedSet.has(empty, 10)).toBe(false);
+        expect(OrderedSet.indexOf(empty, 10)).toBe(-1);
+
+        expect(OrderedSet.has(singleton10, 10)).toBe(true);
+        expect(OrderedSet.has(singleton10, 11)).toBe(false);
+        expect(OrderedSet.indexOf(singleton10, 10)).toBe(0);
+        expect(OrderedSet.indexOf(singleton10, 11)).toBe(-1);
+
+        expect(OrderedSet.has(range1_4, 4)).toBe(true);
+        expect(OrderedSet.has(range1_4, 5)).toBe(false);
+        expect(OrderedSet.indexOf(range1_4, 4)).toBe(3);
+        expect(OrderedSet.indexOf(range1_4, 11)).toBe(-1);
+
+        expect(OrderedSet.has(arr136, 3)).toBe(true);
+        expect(OrderedSet.has(arr136, 4)).toBe(false);
+        expect(OrderedSet.indexOf(arr136, 3)).toBe(1);
+        expect(OrderedSet.indexOf(arr136, 11)).toBe(-1);
+    });
+
+    it('interval range', () => {
+        expect(OrderedSet.findRange(empty, 9, 11)).toEqual(iB(0, 0));
+        expect(OrderedSet.findRange(empty, -9, -6)).toEqual(iB(0, 0));
+        expect(OrderedSet.findRange(singleton10, 9, 11)).toEqual(iB(0, 1));
+        expect(OrderedSet.findRange(range1_4, 2, 3)).toEqual(iB(1, 3));
+        expect(OrderedSet.findRange(range1_4, -10, 2)).toEqual(iB(0, 2));
+        expect(OrderedSet.findRange(range1_4, -10, 20)).toEqual(iB(0, 4));
+        expect(OrderedSet.findRange(range1_4, 3, 20)).toEqual(iB(2, 4));
+        expect(OrderedSet.findRange(arr136, 0, 1)).toEqual(iB(0, 1));
+        expect(OrderedSet.findRange(arr136, 0, 3)).toEqual(iB(0, 2));
+        expect(OrderedSet.findRange(arr136, 0, 4)).toEqual(iB(0, 2));
+        expect(OrderedSet.findRange(arr136, 2, 4)).toEqual(iB(1, 2));
+        expect(OrderedSet.findRange(arr136, 2, 7)).toEqual(iB(1, 3));
+    })
+
+    testEq('union ES', OrderedSet.union(empty, singleton10), [10]);
+    testEq('union ER', OrderedSet.union(empty, range1_4), [1, 2, 3, 4]);
+    testEq('union EA', OrderedSet.union(empty, arr136), [1, 3, 6]);
+    testEq('union SS', OrderedSet.union(singleton10, OrderedSet.ofSingleton(16)), [10, 16]);
+    testEq('union SR', OrderedSet.union(range1_4, singleton10), [1, 2, 3, 4, 10]);
+    testEq('union SA', OrderedSet.union(arr136, singleton10), [1, 3, 6, 10]);
+    testEq('union SA1', OrderedSet.union(arr136, OrderedSet.ofSingleton(3)), [1, 3, 6]);
+    testEq('union RR', OrderedSet.union(range1_4, range1_4), [1, 2, 3, 4]);
+    testEq('union RR1', OrderedSet.union(range1_4, OrderedSet.ofRange(6, 7)), [1, 2, 3, 4, 6, 7]);
+    testEq('union RR2', OrderedSet.union(range1_4, OrderedSet.ofRange(3, 5)), [1, 2, 3, 4, 5]);
+    testEq('union RA', OrderedSet.union(range1_4, arr136), [1, 2, 3, 4, 6]);
+    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), []);
+    testEq('intersect EA', OrderedSet.intersect(empty, arr136), []);
+    testEq('intersect SS', OrderedSet.intersect(singleton10, OrderedSet.ofSingleton(16)), []);
+    testEq('intersect SS1', OrderedSet.intersect(singleton10, singleton10), [10]);
+    testEq('intersect SR', OrderedSet.intersect(range1_4, singleton10), []);
+    testEq('intersect RR', OrderedSet.intersect(range1_4, range1_4), [1, 2, 3, 4]);
+    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), []);
+    testEq('subtract EA', OrderedSet.subtract(empty, arr136), []);
+    testEq('subtract SS', OrderedSet.subtract(singleton10, OrderedSet.ofSingleton(16)), [10]);
+    testEq('subtract SS1', OrderedSet.subtract(singleton10, singleton10), []);
+    testEq('subtract SR', OrderedSet.subtract(range1_4, singleton10), [1, 2, 3, 4]);
+    testEq('subtract SR1', OrderedSet.subtract(range1_4, OrderedSet.ofSingleton(4)), [1, 2, 3]);
+    testEq('subtract SR2', OrderedSet.subtract(range1_4, OrderedSet.ofSingleton(3)), [1, 2, 4]);
+    testEq('subtract RR', OrderedSet.subtract(range1_4, range1_4), []);
+    testEq('subtract RR1', OrderedSet.subtract(range1_4, OrderedSet.ofRange(3, 5)), [1, 2]);
+
+    testEq('subtract RA', OrderedSet.subtract(range1_4, arr136), [2, 4]);
+    testEq('subtract RA1', OrderedSet.subtract(range1_4, OrderedSet.ofSortedArray([0, 1, 2, 3, 4, 7])), []);
+    testEq('subtract RA2', OrderedSet.subtract(range1_4, OrderedSet.ofSortedArray([0, 2, 3])), [1, 4]);
+
+    testEq('subtract AR', OrderedSet.subtract(arr136, range1_4), [6]);
+    testEq('subtract AR1', OrderedSet.subtract(arr136, OrderedSet.ofRange(0, 10)), []);
+    testEq('subtract AR1', OrderedSet.subtract(arr136, OrderedSet.ofRange(2, 10)), [1]);
+
+    testEq('subtract AA', OrderedSet.subtract(arr136, arr136), []);
+    testEq('subtract AA1', OrderedSet.subtract(arr136, OrderedSet.ofSortedArray([2, 3, 4, 6, 7])), [1]);
+    testEq('subtract AA2', OrderedSet.subtract(arr136, OrderedSet.ofSortedArray([0, 1, 6])), [3]);
+
+    it('segments', () => {
+        const data = OrderedSet.ofSortedArray([4, 9, 10, 11, 14, 15, 16]);
+        const segs = OrderedSet.ofSortedArray([0, 4, 10, 12, 13, 15, 25]);
+        const it = OrderedSet.segments(segs, data);
+
+        const t = Object.create(null);
+        for (let s = it.move(); !it.done; s = it.move()) {
+            for (let j = s.start; j < s.end; j++) {
+                const x = t[s.segment];
+                const v = OrderedSet.getAt(data, j);
+                if (!x) t[s.segment] = [v];
+                else x[x.length] = v;
+            }
+        }
+
+        expect(t).toEqual({ 1: [4, 9], 2: [10, 11], 4: [14], 5: [15, 16] });
+    });
+});
\ No newline at end of file
diff --git a/src/mol-base/collections/impl/interval.ts b/src/mol-base/collections/impl/interval.ts
index d0290c22915b6942727b2d5cb32b487aec261789..6f810a8f1e39dddc356b57ab54dde24b05a58d13 100644
--- a/src/mol-base/collections/impl/interval.ts
+++ b/src/mol-base/collections/impl/interval.ts
@@ -7,8 +7,8 @@
 import IntTuple from '../int-tuple'
 
 export const Empty = IntTuple.Zero;
-export function ofRange(min: number, max: number) { return max < min ? Empty : IntTuple.create(min, max + 1); }
-export function ofBounds(min: number, max: number) { return max <= min ? Empty : IntTuple.create(min, max); }
+export function ofRange(min: number, max: number) { return max < min ? IntTuple.create(min, min) : IntTuple.create(min, max + 1); }
+export function ofBounds(min: number, max: number) { return max <= min ? IntTuple.create(min, min) : IntTuple.create(min, max); }
 export const is = IntTuple.is;
 
 export const start = IntTuple.fst;
diff --git a/src/mol-base/collections/impl/ordered-set.ts b/src/mol-base/collections/impl/ordered-set.ts
new file mode 100644
index 0000000000000000000000000000000000000000..4dfeb269106756bd2e1b82849fad6ad9e4669d3c
--- /dev/null
+++ b/src/mol-base/collections/impl/ordered-set.ts
@@ -0,0 +1,429 @@
+/**
+ * Copyright (c) 2017 molio contributors, licensed under MIT, See LICENSE file for more info.
+ *
+ * @author David Sehnal <david.sehnal@gmail.com>
+ */
+
+import S from '../sorted-array'
+import I from '../interval'
+
+type OrderedSetImpl = I | S
+type Nums = ArrayLike<number>
+
+export const Empty: OrderedSetImpl = I.Empty;
+
+export const ofSingleton = I.ofSingleton
+export const ofRange = I.ofRange
+
+export function ofSortedArray(xs: Nums): OrderedSetImpl {
+    if (!xs.length) return Empty;
+    // check if the array is just a range
+    if (xs[xs.length - 1] - xs[0] + 1 === xs.length) return I.ofRange(xs[0], xs[xs.length - 1]);
+    return xs as any;
+}
+
+export function size(set: OrderedSetImpl) { return I.is(set) ? I.size(set) : S.size(set); }
+export function has(set: OrderedSetImpl, x: number) { return I.is(set) ? I.has(set, x) : S.has(set, x); }
+export function indexOf(set: OrderedSetImpl, x: number) { return I.is(set) ? I.indexOf(set, x) : S.indexOf(set, x); }
+export function getAt(set: OrderedSetImpl, i: number) { return I.is(set) ? I.getAt(set, i) : S.getAt(set, i); }
+export function min(set: OrderedSetImpl) { return I.is(set) ? I.min(set) : S.min(set); }
+export function max(set: OrderedSetImpl) { return I.is(set) ? I.max(set) : S.max(set); }
+
+export function hashCode(set: OrderedSetImpl) { return I.is(set) ? I.hashCode(set) : S.hashCode(set); }
+// TODO: possibly add more hash functions to allow for multilevel hashing.
+
+export function areEqual(a: OrderedSetImpl, b: OrderedSetImpl) {
+    if (I.is(a)) {
+        if (I.is(b)) return I.areEqual(a, b);
+        return areEqualIS(a, b);
+    } else if (I.is(b)) return areEqualIS(b, a);
+    return S.areEqual(a, b);
+}
+
+export function areIntersecting(a: OrderedSetImpl, b: OrderedSetImpl) {
+    if (I.is(a)) {
+        if (I.is(b)) return I.areIntersecting(a, b);
+        return areIntersectingSI(b, a);
+    } else if (I.is(b)) return areIntersectingSI(a, b);
+    return areIntersectingSS(a, b);
+}
+
+/** Check if the 2nd argument is a subset of the 1st */
+export function isSubset(a: OrderedSetImpl, b: OrderedSetImpl) {
+    if (I.is(a)) {
+        if (I.is(b)) return I.isSubInterval(a, b);
+        return isSubsetIS(a, b);
+    } else if (I.is(b)) return isSubsetSI(a, b);
+    return isSubsetSS(a, b);
+}
+
+export function findPredecessorIndex(set: OrderedSetImpl, x: number) {
+    return I.is(set) ? I.findPredecessorIndex(set, x) : S.findPredecessorIndex(set, x);
+}
+
+export function findPredecessorIndexInInterval(set: OrderedSetImpl, x: number, bounds: I) {
+    return I.is(set) ? I.findPredecessorIndexInInterval(set, x, bounds) : S.findPredecessorIndexInInterval(set, x, bounds);
+}
+
+export function findRange(set: OrderedSetImpl, min: number, max: number) {
+    return I.is(set) ? I.findRange(set, min, max) : S.findRange(set, min, max);
+}
+
+export function union(a: OrderedSetImpl, b: OrderedSetImpl) {
+    if (I.is(a)) {
+        if (I.is(b)) return unionII(a, b);
+        return unionSI(b, a);
+    } else if (I.is(b)) return unionSI(a, b);
+    return unionSS(a, b);
+}
+
+export function intersect(a: OrderedSetImpl, b: OrderedSetImpl) {
+    if (I.is(a)) {
+        if (I.is(b)) return I.intersect(a, b);
+        return intersectSI(b, a);
+    } else if (I.is(b)) return intersectSI(a, b);
+    return intersectSS(a, b);
+}
+
+export function subtract(a: OrderedSetImpl, b: OrderedSetImpl) {
+    if (I.is(a)) {
+        if (I.is(b)) return subtractII(a, b);
+        return subtractIS(a, b);
+    } else if (I.is(b)) return subtractSI(a, b);
+    return subtractSS(a, b);
+}
+
+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));
+    return _maxIntRangeRet;
+}
+
+function areEqualIS(a: I, b: S) { return I.size(a) === S.size(b) && I.start(a) === S.start(b) && I.end(a) === S.end(b); }
+
+function areIntersectingSI(a: S, b: I) {
+    return areRangesIntersecting(a, b);
+}
+
+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);
+    while (i < endI && j < endJ) {
+        const x = xs[i], y = ys[j];
+        if (x < y) { i++; }
+        else if (x > y) { j++; }
+        else return true;
+    }
+    return false;
+}
+
+function isSubsetSI(a: S, b: I) {
+    const minB = I.min(b), maxB = I.max(b);
+    if (maxB - minB + 1 === 0) return true;
+    const minA = S.min(a), maxA = S.max(a);
+    if (minB < minA || maxB > maxA) return false;
+    const r = S.findRange(a, minB, maxB);
+    return I.size(r) === I.size(b);
+}
+
+function isSubsetIS(a: I, b: S) {
+    const minA = I.min(a), maxA = I.max(a);
+    if (maxA - minA + 1 === 0) return false;
+    const minB = S.min(b), maxB = S.max(b);
+    return minB >= minA && maxA <= maxB;
+}
+
+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);
+    // 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];
+        if (x < y) { i++; }
+        else if (x > y) { j++; }
+        else { i++; j++; equal++; }
+    }
+    return equal === lenB;
+}
+
+function areRangesIntersecting(a: OrderedSetImpl, b: OrderedSetImpl) {
+    const sa = size(a), sb = size(b);
+    if (sa === 0 && sb === 0) return true;
+    return sa > 0 && sb > 0 && max(a) >= min(b) && min(a) <= max(b);
+}
+
+function isRangeSubset(a: OrderedSetImpl, b: OrderedSetImpl) {
+    if (!size(a)) return size(b) === 0;
+    if (!size(b)) return true;
+    return min(a) <= min(b) && max(a) >= max(b);
+}
+
+function unionII(a: I, b: I) {
+    if (I.areEqual(a, b)) return a;
+
+    const sizeA = I.size(a), sizeB = I.size(b);
+    if (!sizeA) return b;
+    if (!sizeB) return a;
+    const minA = I.min(a), minB = I.min(b);
+    if (areRangesIntersecting(a, b)) return I.ofRange(Math.min(minA, minB), Math.max(I.max(a), I.max(b)));
+    let lSize, lMin, rSize, rMin;
+    if (minA < minB) { lSize = sizeA; lMin = minA; rSize = sizeB; rMin = minB; }
+    else { lSize = sizeB; lMin = minB; rSize = sizeA; rMin = minA; }
+    const arr = new Int32Array(sizeA + sizeB);
+    for (let i = 0; i < lSize; i++) arr[i] = i + lMin;
+    for (let i = 0; i < rSize; i++) arr[i + lSize] = i + rMin;
+    return ofSortedArray(arr);
+}
+
+function unionSI(a: S, b: I) {
+    const bSize = I.size(b);
+    if (!bSize) return a;
+    // is the array fully contained in the range?
+    if (isRangeSubset(b, a)) return b;
+
+    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);
+    let offset = 0;
+    for (let i = 0; i < start; i++) indices[offset++] = xs[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];
+
+    return ofSortedArray(indices);
+}
+
+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);
+    let i = sI, j = sJ;
+    let commonCount = 0;
+    while (i < endI && j < endJ) {
+        const x = xs[i], y = ys[j];
+        if (x < y) { i++; }
+        else if (x > y) { j++; }
+        else { i++; j++; commonCount++; }
+    }
+
+    const lenA = xs.length, lenB = ys.length;
+    // A === B || B is subset of A ==> A
+    if ((commonCount === lenA && commonCount === lenB) || commonCount === lenB) return xs;
+    // A is subset of B ===> B
+    if (commonCount === lenA) return ys;
+
+    const resultSize = lenA + lenB - commonCount;
+    const l = Math.min(xs[0], ys[0]), r = Math.max(xs[lenA - 1], ys[lenB - 1]);
+    // is this just a range?
+    if (resultSize === r - l + 1) {
+        return I.ofRange(l, r);
+    }
+
+    const indices = new Int32Array(lenA + lenB - commonCount);
+    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];
+
+    // insert the common part
+    i = sI;
+    j = sJ;
+    while (i < endI && j < endJ) {
+        const x = xs[i], y = ys[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];
+
+    return ofSortedArray(indices);
+}
+
+function intersectSI(a: S, b: I) {
+    if (!I.size(b)) return Empty;
+
+    const r = S.findRange(a, I.min(b), I.max(b));
+    const start = I.start(r), end = I.end(r);
+    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];
+    }
+    return ofSortedArray(indices);
+}
+
+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);
+    let i = sI, j = sJ;
+    let commonCount = 0;
+    while (i < endI && j < endJ) {
+        const x = xs[i], y = ys[j];
+        if (x < y) { i++; }
+        else if (x > y) { j++; }
+        else { i++; j++; commonCount++; }
+    }
+
+    const lenA = xs.length, lenB = ys.length;
+    // no common elements
+    if (!commonCount) return 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 < endI && j < endJ) {
+        const x = xs[i], y = ys[j];
+        if (x < y) { i++; }
+        else if (x > y) { j++; }
+        else { indices[offset++] = x; i++; j++; }
+    }
+
+    return ofSortedArray(indices);
+}
+
+function subtractII(a: I, b: I) {
+    if (I.areEqual(a, b)) return Empty;
+    if (!I.areIntersecting(a, b)) return a;
+
+    const minA = I.min(a), maxA = I.max(a);
+    const minB = I.min(b), maxB = I.max(b);
+
+    if (maxA < minA || maxB < minB) return a;
+    // is A subset of B? ==> Empty
+    if (I.isSubInterval(b, a)) return Empty;
+    if (I.isSubInterval(a, b)) {
+        // this splits the interval into two, gotta represent it as a set.
+        const l = minB - minA, r = maxA - maxB;
+        if (l <= 0) return I.ofRange(maxB + 1, maxB + r);
+        if (r <= 0) return I.ofRange(minA, minA + l - 1);
+        const ret = new Int32Array(l + r);
+        let offset = 0;
+        for (let i = 0; i < l; i++) ret[offset++] = minA + i;
+        for (let i = 1; i <= r; i++) ret[offset++] = maxB + i;
+        return ofSortedArray(ret);
+    }
+    if (minA < minB) return I.ofRange(minA, minB - 1);
+    return I.ofRange(maxB + 1, maxA);
+}
+
+function subtractSI(a: S, b: I) {
+    const min = I.min(b), max = I.max(b);
+    // 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);
+    // A is subset of B
+    if (resultSize <= 0) return Empty;
+    // No common elements
+    if (resultSize === xs.length) return xs;
+
+    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];
+    return ofSortedArray(ret);
+}
+
+function subtractIS(a: I, b: S) {
+    const min = I.min(a), max = I.max(a);
+
+    // is empty?
+    if (max < min) return a;
+
+    const rSize = max - min + 1;
+    const interval = S.findRange(b, min, max);
+    const start = I.start(interval), end = I.end(interval);
+    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 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)];
+    let offset = 0;
+    for (let i = min; i < fst; i++) ret[offset++] = i;
+    for (let i = fst; i <= last; i++) {
+        if (S.indexOfInterval(b, i, interval) < 0) ret[offset++] = i;
+    }
+    for (let i = last + 1; i <= max; i++) ret[offset++] = i;
+    return ofSortedArray(ret);
+}
+
+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);
+    let i = sI, j = sJ;
+    let commonCount = 0;
+    while (i < endI && j < endJ) {
+        const x = xs[i], y = ys[j];
+        if (x < y) { i++; }
+        else if (x > y) { j++; }
+        else { i++; j++; commonCount++; }
+    }
+
+    // A isnt intersecting B ===> A
+    if (!commonCount) return xs;
+    // A === B || A is subset of B ===> Empty
+    if (commonCount >= lenA) return Empty;
+
+    const indices = new Int32Array(lenA - commonCount);
+    let offset = 0;
+
+    // insert the "prefix"
+    for (let k = 0; k < sI; k++) indices[offset++] = xs[k];
+
+    i = sI;
+    j = sJ;
+    while (i < endI && j < endJ) {
+        const x = xs[i], y = ys[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];
+
+    return ofSortedArray(indices);
+}
\ No newline at end of file
diff --git a/src/mol-base/collections/impl/sorted-array.ts b/src/mol-base/collections/impl/sorted-array.ts
index 134b1dbf3dd05afafec623a76fadf887e11059e0..376a4bf19705f382a2b695cda57709179f05ec39 100644
--- a/src/mol-base/collections/impl/sorted-array.ts
+++ b/src/mol-base/collections/impl/sorted-array.ts
@@ -34,6 +34,11 @@ export function indexOf(xs: Nums, v: number) {
     const l = xs.length;
     return l === 0 ? -1 : xs[0] <= v && v <= xs[l - 1] ? binarySearchRange(xs, v, 0, l) : -1;
 }
+export function indexOfInterval(xs: Nums, v: number, bounds: Interval) {
+    const l = xs.length;
+    const s = Interval.start(bounds), e = Interval.end(bounds);
+    return l === 0 || e <= s ? -1 : xs[s] <= v && v <= xs[e - 1] ? binarySearchRange(xs, v, s, e) : -1;
+}
 export function has(xs: Nums, v: number) { return indexOf(xs, v) >= 0; }
 
 export function getAt(xs: Nums, i: number) { return xs[i]; }
diff --git a/src/mol-base/collections/interval.ts b/src/mol-base/collections/interval.ts
index 5736efc0d973341eb2ad3eca6818f613f770b1e6..522d5eb7332f390df8937da8fde72a8e31614d42 100644
--- a/src/mol-base/collections/interval.ts
+++ b/src/mol-base/collections/interval.ts
@@ -9,6 +9,7 @@ import * as Impl from './impl/interval'
 namespace Interval {
     export const Empty: Interval = Impl.Empty as any;
 
+    export const ofSingleton: (value: number) => Interval = (v) => Impl.ofRange(v, v) as any;
     /** Create interval [min, max] */
     export const ofRange: (min: number, max: number) => Interval = Impl.ofRange as any;
     /** Create interval [min, max) */
diff --git a/src/mol-base/collections/ordered-set.ts b/src/mol-base/collections/ordered-set.ts
index c1a697a91713661d1af08c3659c16f70d9cd72e8..37b9806580ca9f53573b2e0271dd32413974c2f8 100644
--- a/src/mol-base/collections/ordered-set.ts
+++ b/src/mol-base/collections/ordered-set.ts
@@ -4,14 +4,12 @@
  * @author David Sehnal <david.sehnal@gmail.com>
  */
 
-import * as Base from './ordered-set/base'
+//import * as Base from './ordered-set/base'
+import * as Base from './impl/ordered-set'
+import Interval from './interval'
 import SegmentIterator from './ordered-set/segment-iterator'
 
 namespace OrderedSet {
-    /** Range of indices to be iterated as start <= i < end. */
-    export interface IndexRange { start: number, end: number }
-    export function IndexRange(start?: number, end?: number): IndexRange { return { start: start || 0, end: end || 0 }; }
-
     export const Empty: OrderedSet = Base.Empty as any;
     export const ofSingleton: (value: number) => OrderedSet = Base.ofSingleton as any;
     export const ofRange: (min: number, max: number) => OrderedSet = Base.ofRange as any;
@@ -22,8 +20,8 @@ namespace OrderedSet {
     export const indexOf: (set: OrderedSet, x: number) => number = Base.indexOf as any;
     export const getAt: (set: OrderedSet, i: number) => number = Base.getAt as any;
 
-    export const min: (set: OrderedSet) => number = Base.minValue as any;
-    export const max: (set: OrderedSet) => number = Base.maxValue as any;
+    export const min: (set: OrderedSet) => number = Base.min as any;
+    export const max: (set: OrderedSet) => number = Base.max as any;
     export const size: (set: OrderedSet) => number = Base.size as any;
     export const hashCode: (set: OrderedSet) => number = Base.hashCode as any;
 
@@ -35,9 +33,9 @@ namespace OrderedSet {
     export const intersect: (a: OrderedSet, b: OrderedSet) => OrderedSet = Base.intersect as any;
     export const subtract: (a: OrderedSet, b: OrderedSet) => OrderedSet = Base.subtract as any;
 
-    export const findPredecessorIndex: (set: OrderedSet, x: number) => number = Base.getPredIndex as any;
-    export const findPredecessorIndexInRange: (set: OrderedSet, x: number, range: IndexRange) => number = Base.getPredIndexInRange as any;
-    export const findIntervalRange: (set: OrderedSet, min: number, max: number) => IndexRange = Base.getIntervalRange as any;
+    export const findPredecessorIndex: (set: OrderedSet, x: number) => number = Base.findPredecessorIndex as any;
+    export const findPredecessorIndexInRange: (set: OrderedSet, x: number, range: Interval) => number = Base.findPredecessorIndexInInterval as any;
+    export const findRange: (set: OrderedSet, min: number, max: number) => Interval = Base.findRange as any;
 
     export const segments = SegmentIterator;
 }
diff --git a/src/mol-base/collections/ordered-set/segment-iterator.ts b/src/mol-base/collections/ordered-set/segment-iterator.ts
index f5c7a712a2205339426d6fdc94be722269f8b0d6..634f7d245e7b6ba9d476b2322d7f53d4f3900a49 100644
--- a/src/mol-base/collections/ordered-set/segment-iterator.ts
+++ b/src/mol-base/collections/ordered-set/segment-iterator.ts
@@ -6,14 +6,17 @@
 
 import Iterator from '../iterator'
 import OrderedSet from '../ordered-set'
+import Interval from '../interval'
 
-class SegmentIterator implements Iterator<{ segment: number } & OrderedSet.IndexRange> {
-    private segmentRange = OrderedSet.IndexRange();
-    private setRange = OrderedSet.IndexRange();
+class SegmentIterator implements Iterator<{ segment: number, start: number, end: number }> {
+    private segmentStart = 0;
+    private segmentEnd = 0;
+    // private segmentRange = Interval.Empty;
+    private setRange = Interval.Empty;
     private value = { segment: 0, start: 0, end: 0 };
     private last: number = 0;
 
-    [Symbol.iterator]() { return new SegmentIterator(this.segments, this.set, this.start, this.end); };
+    [Symbol.iterator]() { return new SegmentIterator(this.segments, this.set, this.inputRange); };
     done: boolean = false;
 
     next() {
@@ -22,12 +25,12 @@ class SegmentIterator implements Iterator<{ segment: number } & OrderedSet.Index
     }
 
     move() {
-        this.done = this.segmentRange.end <= this.segmentRange.start;
+        this.done = this.segmentEnd <= this.segmentStart;
         while (!this.done) {
             if (!this.updateValue()) {
                 this.updateSegmentRange();
             } else {
-                this.value.segment = this.segmentRange.start++;
+                this.value.segment = this.segmentStart++;
                 break;
             }
         }
@@ -40,33 +43,34 @@ class SegmentIterator implements Iterator<{ segment: number } & OrderedSet.Index
     }
 
     private updateValue() {
-        const segmentEnd = OrderedSet.getAt(this.segments, this.segmentRange.start + 1);
+        const segmentEnd = OrderedSet.getAt(this.segments, this.segmentStart + 1);
         const setEnd = OrderedSet.findPredecessorIndexInRange(this.set, segmentEnd, this.setRange);
-        this.value.start = this.setRange.start;
+        this.value.start = Interval.start(this.setRange);
         this.value.end = setEnd;
-        this.setRange.start = setEnd;
+        this.setRange = Interval.ofBounds(setEnd, Interval.end(this.setRange))
+        //this.setRange.start = setEnd;
+        //throw '';
         return setEnd > this.value.start;
     }
 
     private updateSegmentRange() {
-        const min = OrderedSet.getAt(this.set, this.setRange.start), max = OrderedSet.getAt(this.set, this.setRange.end - 1);
-        this.segmentRange.start = this.getSegmentIndex(min);
-        this.segmentRange.end = this.getSegmentIndex(max) + 1;
-        this.done = this.segmentRange.end <= this.segmentRange.start;
+        const min = OrderedSet.getAt(this.set, Interval.min(this.setRange));
+        const max = OrderedSet.getAt(this.set, Interval.max(this.setRange));
+        this.segmentStart = this.getSegmentIndex(min);
+        this.segmentEnd = this.getSegmentIndex(max) + 1;
+        this.done = this.segmentEnd <= this.segmentStart;
     }
 
-    constructor(private segments: OrderedSet, private set: OrderedSet, private start: number, private end: number) {
+    constructor(private segments: OrderedSet, private set: OrderedSet, private inputRange: Interval) {
         this.last = OrderedSet.max(segments);
-        this.setRange.start = start;
-        this.setRange.end = end;
+        this.setRange = inputRange;
         this.updateSegmentRange();
     }
 }
 
-function createIterator(segments: OrderedSet, set: OrderedSet, range?: OrderedSet.IndexRange) {
-    const start = !!range ? range.start : 0;
-    const end = !!range ? range.end : OrderedSet.size(set);
-    return new SegmentIterator(segments, set, start, end);
+function createIterator(segments: OrderedSet, set: OrderedSet, range?: Interval) {
+    const int = typeof range !== 'undefined' ? range : Interval.ofBounds(0, OrderedSet.size(set));
+    return new SegmentIterator(segments, set, int);
 }
 
 export default createIterator
\ No newline at end of file
diff --git a/src/mol-base/collections/sorted-array.ts b/src/mol-base/collections/sorted-array.ts
index 11c5c31abb79d483fa9cbf204ab8133c71522585..a98b9a0f9680141fea690e31c9b0172ec062acea 100644
--- a/src/mol-base/collections/sorted-array.ts
+++ b/src/mol-base/collections/sorted-array.ts
@@ -14,22 +14,25 @@ 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 has: (interval: SortedArray, x: number) => boolean = Impl.has as any;
-    export const indexOf: (interval: SortedArray, x: number) => number = Impl.indexOf as any;
-    export const getAt: (interval: SortedArray, i: number) => number = Impl.getAt as any;
+    export const values: (array: SortedArray) => ArrayLike<number> = (xs) => xs as any;
 
-    export const start: (interval: SortedArray) => number = Impl.start as any;
-    export const end: (interval: SortedArray) => number = Impl.end as any;
-    export const min: (interval: SortedArray) => number = Impl.min as any;
-    export const max: (interval: SortedArray) => number = Impl.max as any;
-    export const size: (interval: SortedArray) => number = Impl.size as any;
-    export const hashCode: (interval: SortedArray) => number = Impl.hashCode 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;
+    export const getAt: (array: SortedArray, i: number) => number = Impl.getAt as any;
+
+    export const start: (array: SortedArray) => number = Impl.start as any;
+    export const end: (array: SortedArray) => number = Impl.end as any;
+    export const min: (array: SortedArray) => number = Impl.min as any;
+    export const max: (array: SortedArray) => number = Impl.max as any;
+    export const size: (array: SortedArray) => number = Impl.size as any;
+    export const hashCode: (array: SortedArray) => number = Impl.hashCode as any;
 
     export const areEqual: (a: SortedArray, b: SortedArray) => boolean = Impl.areEqual as any;
 
-    export const findPredecessorIndex: (interval: SortedArray, x: number) => number = Impl.findPredecessorIndex as any;
-    export const findPredecessorIndexInInterval: (interval: SortedArray, x: number, bounds: Interval) => number = Impl.findPredecessorIndexInInterval as any;
-    export const findRange: (interval: SortedArray, min: number, max: number) => Interval = Impl.findRange as any;
+    export const findPredecessorIndex: (array: SortedArray, x: number) => number = Impl.findPredecessorIndex as any;
+    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;
 }
 
 interface SortedArray { '@type': 'int-sorted-array' }
diff --git a/src/mol-data/atom-set/base.ts b/src/mol-data/atom-set/base.ts
index ea1aff390ac057e3fcacd0633f0972012e5d8aa9..c5105a51e8440a8b65c8fb93e39ec45724a5e343 100644
--- a/src/mol-data/atom-set/base.ts
+++ b/src/mol-data/atom-set/base.ts
@@ -7,6 +7,7 @@
 import OrderedSet from '../../mol-base/collections/ordered-set'
 import Iterator from '../../mol-base/collections/iterator'
 import IntTuple from '../../mol-base/collections/int-tuple'
+import Interval from '../../mol-base/collections/interval'
 import { sortArray } from '../../mol-base/collections/sort'
 import { hash1 } from '../../mol-base/collections/hash-functions'
 
@@ -345,7 +346,8 @@ function areIntersectingEE(a: AtomSetElements, b: AtomSetElements) {
     if (a === b) return true;
     const keysA = a.keys, keysB = b.keys;
     if (!OrderedSet.areIntersecting(a.keys, b.keys)) return false;
-    const { start, end } = OrderedSet.findIntervalRange(keysA, OrderedSet.min(keysB), OrderedSet.max(keysB));
+    const r = OrderedSet.findRange(keysA, OrderedSet.min(keysB), OrderedSet.max(keysB));
+    const start = Interval.start(r), end = Interval.end(r);
     for (let i = start; i < end; i++) {
         const k = OrderedSet.getAt(keysA, i);
         if (OrderedSet.has(keysB, k) && OrderedSet.areIntersecting(a[k], b[k])) return true;
@@ -363,7 +365,8 @@ function intersectEE(a: AtomSetElements, b: AtomSetElements) {
 
     const keysA = a.keys, keysB = b.keys;
     if (!OrderedSet.areIntersecting(a.keys, b.keys)) return Empty;
-    const { start, end } = OrderedSet.findIntervalRange(keysA, OrderedSet.min(keysB), OrderedSet.max(keysB));
+    const r = OrderedSet.findRange(keysA, OrderedSet.min(keysB), OrderedSet.max(keysB));
+    const start = Interval.start(r), end = Interval.end(r);
 
     const keys = [], ret = Object.create(null);
     for (let i = start; i < end; i++) {
@@ -409,7 +412,8 @@ function subtractEE(a: AtomSetElements, b: AtomSetElements) {
 
     const keysA = a.keys, keysB = b.keys;
     if (!OrderedSet.areIntersecting(a.keys, b.keys)) return Empty;
-    const { start, end } = OrderedSet.findIntervalRange(keysA, OrderedSet.min(keysB), OrderedSet.max(keysB));
+    const r = OrderedSet.findRange(keysA, OrderedSet.min(keysB), OrderedSet.max(keysB));
+    const start = Interval.start(r), end = Interval.end(r);
 
     const keys = [], ret = Object.create(null);
     for (let i = 0; i < start; i++) {
diff --git a/src/perf-tests/sets.ts b/src/perf-tests/sets.ts
index 4930c3e84826bcea4508daa695e64b4203d441f5..1a22451b85e60c0a090b00040a3253f4544e961e 100644
--- a/src/perf-tests/sets.ts
+++ b/src/perf-tests/sets.ts
@@ -279,7 +279,9 @@ export function testSegments() {
     }
 }
 
-Tuples.run();
+testSegments();
+
+//Tuples.run();
 
 // interface AA { kind: 'a' }
 // //interface BB { kind: 'b' }