diff --git a/src/ol/structs/integerset.js b/src/ol/structs/integerset.js
new file mode 100644
index 0000000000..bdccb8e854
--- /dev/null
+++ b/src/ol/structs/integerset.js
@@ -0,0 +1,333 @@
+// FIXME refactor to use a packed array of integers to reduce GC load
+
+goog.provide('ol.structs.IntegerRange');
+goog.provide('ol.structs.IntegerSet');
+
+goog.require('goog.array');
+goog.require('goog.asserts');
+
+
+/**
+ * @typedef {{start: number, stop: number}}
+ */
+ol.structs.IntegerRange;
+
+
+/**
+ * @param {ol.structs.IntegerRange} range1 Range 1.
+ * @param {ol.structs.IntegerRange} range2 Range 2.
+ * @return {number} Compare.
+ */
+ol.structs.IntegerRange.compare = function(range1, range2) {
+ return range1.start - range2.start || range1.stop - range2.stop;
+};
+
+
+
+/**
+ * A set of integers represented as a set of integer ranges.
+ * This implementation is designed for the case when the number of distinct
+ * integer ranges is small.
+ * @constructor
+ * @param {Array.
=} opt_ranges Ranges.
+ */
+ol.structs.IntegerSet = function(opt_ranges) {
+
+ /**
+ * @private
+ * @type {Array.}
+ */
+ this.ranges_ = goog.isDef(opt_ranges) ? opt_ranges : [];
+
+ if (goog.DEBUG) {
+ this.assertValid();
+ }
+
+};
+
+
+/**
+ * @param {Array.} arr Array.
+ * @return {ol.structs.IntegerSet} Integer set.
+ */
+ol.structs.IntegerSet.unpack = function(arr) {
+ var n = arr.length;
+ goog.asserts.assert(n % 2 === 0);
+ var ranges = new Array(n / 2);
+ var rangeIndex = 0;
+ var i;
+ for (i = 0; i < n; i += 2) {
+ ranges[rangeIndex++] = {
+ start: arr[i],
+ stop: arr[i + 1]
+ };
+ }
+ return new ol.structs.IntegerSet(ranges);
+};
+
+
+/**
+ * @param {number} addStart Start.
+ * @param {number} addStop Stop.
+ */
+ol.structs.IntegerSet.prototype.addRange = function(addStart, addStop) {
+ goog.asserts.assert(addStart <= addStop);
+ if (addStart == addStop) {
+ return;
+ }
+ var range = {start: addStart, stop: addStop};
+ goog.array.binaryInsert(this.ranges_, range, ol.structs.IntegerRange.compare);
+ this.compactRanges_();
+};
+
+
+/**
+ * FIXME empty description for jsdoc
+ */
+ol.structs.IntegerSet.prototype.assertValid = function() {
+ var arr = this.pack();
+ for (i = 1; i < arr.length; ++i) {
+ goog.asserts.assert(arr[i] > arr[i - 1]);
+ }
+};
+
+
+/**
+ * FIXME empty description for jsdoc
+ */
+ol.structs.IntegerSet.prototype.clear = function() {
+ this.ranges_.length = 0;
+};
+
+
+/**
+ * @private
+ */
+ol.structs.IntegerSet.prototype.compactRanges_ = function() {
+ var ranges = this.ranges_;
+ var n = ranges.length;
+ var rangeIndex = 0;
+ var lastRange = null;
+ var i;
+ for (i = 0; i < n; ++i) {
+ var range = ranges[i];
+ if (range.start == range.stop) {
+ // pass
+ } else if (!goog.isNull(lastRange) &&
+ lastRange.start <= range.start &&
+ range.start <= lastRange.stop) {
+ lastRange.stop = Math.max(lastRange.stop, range.stop);
+ } else {
+ lastRange = ranges[rangeIndex++] = range;
+ }
+ }
+ ranges.length = rangeIndex;
+};
+
+
+/**
+ * Finds the start of smallest range that is at least of length minSize, or -1
+ * if no such range exists.
+ * @param {number} minSize Minimum size.
+ * @return {number} Index.
+ */
+ol.structs.IntegerSet.prototype.findRange = function(minSize) {
+ goog.asserts.assert(minSize > 0);
+ var ranges = this.ranges_;
+ var n = ranges.length;
+ var bestRange = null;
+ var bestSize, i, size;
+ for (i = 0; i < n; ++i) {
+ range = ranges[i];
+ size = range.stop - range.start;
+ if (size == minSize) {
+ return range.start;
+ } else if (size > minSize && (goog.isNull(bestRange) || size < bestSize)) {
+ bestRange = range;
+ bestSize = size;
+ }
+ }
+ return goog.isNull(bestRange) ? -1 : bestRange.start;
+};
+
+
+/**
+ * Calls f with each integer range.
+ * @param {function(this: T, number, number)} f Callback.
+ * @param {T=} opt_obj The object to be used as the value of 'this' within f.
+ * @template T
+ */
+ol.structs.IntegerSet.prototype.forEachRange = function(f, opt_obj) {
+ var ranges = this.ranges_;
+ var n = ranges.length;
+ var i;
+ for (i = 0; i < n; ++i) {
+ f.call(opt_obj, ranges[i].start, ranges[i].stop);
+ }
+};
+
+
+/**
+ * Calls f with each integer range not in [start, stop) - 'this'.
+ * @param {number} start Start.
+ * @param {number} stop Stop.
+ * @param {function(this: T, number, number)} f Callback.
+ * @param {T=} opt_obj The object to be used as the value of 'this' within f.
+ * @template T
+ */
+ol.structs.IntegerSet.prototype.forEachRangeInverted =
+ function(start, stop, f, opt_obj) {
+ goog.asserts.assert(start < stop);
+ var ranges = this.ranges_;
+ var n = ranges.length;
+ if (n === 0) {
+ f.call(opt_obj, start, stop);
+ } else {
+ if (start < ranges[0].start) {
+ f.call(opt_obj, start, ranges[0].start);
+ }
+ var i;
+ for (i = 1; i < n; ++i) {
+ f.call(opt_obj, ranges[i - 1].stop, ranges[i].start);
+ }
+ if (ranges[n - 1].stop < stop) {
+ f.call(opt_obj, ranges[n - 1].stop, stop);
+ }
+ }
+};
+
+
+/**
+ * @return {Array.} Array.
+ */
+ol.structs.IntegerSet.prototype.getArray = function() {
+ // FIXME this should return the underlying array when the representation is
+ // FIXME updated to use a packed array
+ return this.pack();
+};
+
+
+/**
+ * Returns the first element in the set, or -1 if the set is empty.
+ * @return {number} Start.
+ */
+ol.structs.IntegerSet.prototype.getFirst = function() {
+ return this.ranges_.length === 0 ? -1 : this.ranges_[0].start;
+};
+
+
+/**
+ * Returns the first integer after the last element in the set, or -1 if the
+ * set is empty.
+ * @return {number} Last.
+ */
+ol.structs.IntegerSet.prototype.getLast = function() {
+ var n = this.ranges_.length;
+ return n === 0 ? -1 : this.ranges_[n - 1].stop;
+};
+
+
+/**
+ * @return {Array.} Array.
+ */
+ol.structs.IntegerSet.prototype.getRanges = function() {
+ // FIXME this should be removed when the implementation is updated to use a
+ // FIXME packed array
+ return this.ranges_;
+};
+
+
+/**
+ * Returns the number of integers in the set.
+ * @return {number} Size.
+ */
+ol.structs.IntegerSet.prototype.getSize = function() {
+ var ranges = this.ranges_;
+ var n = ranges.length;
+ var size = 0;
+ for (i = 0; i < n; ++i) {
+ size += ranges[i].stop - ranges[i].start;
+ }
+ return size;
+};
+
+
+/**
+ * @return {boolean} Is empty.
+ */
+ol.structs.IntegerSet.prototype.isEmpty = function() {
+ return this.ranges_.length === 0;
+};
+
+
+/**
+ * @return {Array.} Array.
+ */
+ol.structs.IntegerSet.prototype.pack = function() {
+ var ranges = this.ranges_;
+ var n = ranges.length;
+ var arr = new Array(2 * n);
+ var i;
+ for (i = 0; i < n; ++i) {
+ arr[2 * i] = ranges[i].start;
+ arr[2 * i + 1] = ranges[i].stop;
+ }
+ return arr;
+};
+
+
+/**
+ * @param {number} removeStart Start.
+ * @param {number} removeStop Stop.
+ */
+ol.structs.IntegerSet.prototype.removeRange =
+ function(removeStart, removeStop) {
+ // FIXME this could be more efficient
+ goog.asserts.assert(removeStart <= removeStop);
+ var ranges = this.ranges_;
+ var n = ranges.length;
+ for (i = 0; i < n; ++i) {
+ var range = ranges[i];
+ if (removeStop < range.start || range.stop < removeStart) {
+ continue;
+ } else if (range.start > removeStop) {
+ break;
+ }
+ if (removeStart < range.start) {
+ if (removeStop == range.start) {
+ break;
+ } else if (removeStop < range.stop) {
+ range.start = Math.max(range.start, removeStop);
+ break;
+ } else {
+ ranges.splice(i, 1);
+ --i;
+ --n;
+ }
+ } else if (removeStart == range.start) {
+ if (removeStop < range.stop) {
+ range.start = removeStop;
+ break;
+ } else if (removeStop == range.stop) {
+ ranges.splice(i, 1);
+ break;
+ } else {
+ ranges.splice(i, 1);
+ --i;
+ --n;
+ }
+ } else {
+ if (removeStop < range.stop) {
+ ranges.splice(i, 1, {start: range.start, stop: removeStart},
+ {start: removeStop, stop: range.stop});
+ break;
+ } else if (removeStop == range.stop) {
+ range.stop = removeStart;
+ break;
+ } else {
+ range.stop = removeStart;
+ }
+ }
+ }
+ this.compactRanges_();
+};
diff --git a/test/spec/ol/structs/integerset.test.js b/test/spec/ol/structs/integerset.test.js
new file mode 100644
index 0000000000..3513fde9e0
--- /dev/null
+++ b/test/spec/ol/structs/integerset.test.js
@@ -0,0 +1,552 @@
+goog.provide('ol.test.structs.IntegerSet');
+
+
+describe('ol.structs.IntegerSet', function() {
+
+ describe('constructor', function() {
+
+ describe('without an argument', function() {
+
+ it('constructs an empty instance', function() {
+ var is = new ol.structs.IntegerSet();
+ expect(is).to.be.an(ol.structs.IntegerSet);
+ expect(is.pack()).to.be.empty();
+ });
+
+ });
+
+ });
+
+ describe('unpack', function() {
+
+ it('constructs with a valid array', function() {
+ var is = ol.structs.IntegerSet.unpack([0, 2, 4, 6]);
+ expect(is).to.be.an(ol.structs.IntegerSet);
+ expect(is.pack()).to.equalArray([0, 2, 4, 6]);
+ });
+
+ it('throws an exception with an odd number of elements', function() {
+ expect(function() {
+ var is = ol.structs.IntegerSet.unpack([0, 2, 4]);
+ }).to.throwException();
+ });
+
+ it('throws an exception with out-of-order elements', function() {
+ expect(function() {
+ var is = ol.structs.IntegerSet.unpack([0, 2, 2, 4]);
+ }).to.throwException();
+ });
+
+ });
+
+ describe('with an empty instance', function() {
+
+ var is;
+ beforeEach(function() {
+ is = new ol.structs.IntegerSet();
+ });
+
+ describe('addRange', function() {
+
+ it('creates a new element', function() {
+ is.addRange(0, 2);
+ expect(is.pack()).to.equalArray([0, 2]);
+ });
+
+ });
+
+ describe('findRange', function() {
+
+ it('returns -1', function() {
+ expect(is.findRange(2)).to.be(-1);
+ });
+
+ });
+
+ describe('forEachRange', function() {
+
+ it('does not call the callback', function() {
+ var callback = sinon.spy();
+ is.forEachRange(callback);
+ expect(callback).to.not.be.called();
+ });
+
+ });
+
+ describe('forEachRangeInverted', function() {
+
+ it('does call the callback', function() {
+ var callback = sinon.spy();
+ is.forEachRangeInverted(0, 8, callback);
+ expect(callback.calledOnce).to.be(true);
+ expect(callback.args[0]).to.equalArray([0, 8]);
+ });
+
+ });
+
+ describe('getFirst', function() {
+
+ it('returns -1', function() {
+ expect(is.getFirst()).to.be(-1);
+ });
+
+ });
+
+ describe('getLast', function() {
+
+ it('returns -1', function() {
+ expect(is.getLast()).to.be(-1);
+ });
+
+ });
+
+ describe('getSize', function() {
+
+ it('returns 0', function() {
+ expect(is.getSize()).to.be(0);
+ });
+
+ });
+
+ describe('isEmpty', function() {
+
+ it('returns true', function() {
+ expect(is.isEmpty()).to.be(true);
+ });
+
+ });
+
+ });
+
+ describe('with a populated instance', function() {
+
+ var is;
+ beforeEach(function() {
+ is = ol.structs.IntegerSet.unpack([4, 6, 8, 10, 12, 14]);
+ });
+
+ describe('addRange', function() {
+
+ it('inserts before the first element', function() {
+ is.addRange(0, 2);
+ expect(is.pack()).to.equalArray([0, 2, 4, 6, 8, 10, 12, 14]);
+ });
+
+ it('extends the first element to the left', function() {
+ is.addRange(0, 4);
+ expect(is.pack()).to.equalArray([0, 6, 8, 10, 12, 14]);
+ });
+
+ it('extends the first element to the right', function() {
+ is.addRange(6, 7);
+ expect(is.pack()).to.equalArray([4, 7, 8, 10, 12, 14]);
+ });
+
+ it('merges the first two elements', function() {
+ is.addRange(6, 8);
+ expect(is.pack()).to.equalArray([4, 10, 12, 14]);
+ });
+
+ it('extends middle elements to the left', function() {
+ is.addRange(7, 8);
+ expect(is.pack()).to.equalArray([4, 6, 7, 10, 12, 14]);
+ });
+
+ it('extends middle elements to the right', function() {
+ is.addRange(10, 11);
+ expect(is.pack()).to.equalArray([4, 6, 8, 11, 12, 14]);
+ });
+
+ it('merges the last two elements', function() {
+ is.addRange(10, 12);
+ expect(is.pack()).to.equalArray([4, 6, 8, 14]);
+ });
+
+ it('extends the last element to the left', function() {
+ is.addRange(11, 12);
+ expect(is.pack()).to.equalArray([4, 6, 8, 10, 11, 14]);
+ });
+
+ it('extends the last element to the right', function() {
+ is.addRange(14, 15);
+ expect(is.pack()).to.equalArray([4, 6, 8, 10, 12, 15]);
+ });
+
+ it('inserts after the last element', function() {
+ is.addRange(16, 18);
+ expect(is.pack()).to.equalArray([4, 6, 8, 10, 12, 14, 16, 18]);
+ });
+
+ });
+
+ describe('clear', function() {
+
+ it('clears the instance', function() {
+ is.clear();
+ expect(is.pack()).to.be.empty();
+ });
+
+ });
+
+ describe('findRange', function() {
+
+ it('throws an exception when passed a negative size', function() {
+ expect(function() {
+ is.findRange(-1);
+ }).to.throwException();
+ });
+
+ it('throws an exception when passed a zero size', function() {
+ expect(function() {
+ is.findRange(0);
+ }).to.throwException();
+ });
+
+ it('finds the first range of size 1', function() {
+ expect(is.findRange(1)).to.be(4);
+ });
+
+ it('finds the first range of size 2', function() {
+ expect(is.findRange(2)).to.be(4);
+ });
+
+ it('returns -1 when no range can be found', function() {
+ expect(is.findRange(3)).to.be(-1);
+ });
+
+ });
+
+ describe('forEachRange', function() {
+
+ it('calls the callback', function() {
+ var callback = sinon.spy();
+ is.forEachRange(callback);
+ expect(callback).to.be.called();
+ expect(callback.calledThrice).to.be(true);
+ expect(callback.args[0]).to.equalArray([4, 6]);
+ expect(callback.args[1]).to.equalArray([8, 10]);
+ expect(callback.args[2]).to.equalArray([12, 14]);
+ });
+
+ });
+
+ describe('forEachRangeInverted', function() {
+
+ it('does call the callback', function() {
+ var callback = sinon.spy();
+ is.forEachRangeInverted(0, 16, callback);
+ expect(callback.callCount).to.be(4);
+ expect(callback.args[0]).to.equalArray([0, 4]);
+ expect(callback.args[1]).to.equalArray([6, 8]);
+ expect(callback.args[2]).to.equalArray([10, 12]);
+ expect(callback.args[3]).to.equalArray([14, 16]);
+ });
+
+ });
+
+
+ describe('getFirst', function() {
+
+ it('returns the expected value', function() {
+ expect(is.getFirst()).to.be(4);
+ });
+
+ });
+
+ describe('getLast', function() {
+
+ it('returns the expected value', function() {
+ expect(is.getLast()).to.be(14);
+ });
+
+ });
+
+ describe('getSize', function() {
+
+ it('returns the expected value', function() {
+ expect(is.getSize()).to.be(6);
+ });
+
+ });
+
+ describe('isEmpty', function() {
+
+ it('returns false', function() {
+ expect(is.isEmpty()).to.be(false);
+ });
+
+ });
+
+ describe('removeRange', function() {
+
+ it('removes the first part of the first element', function() {
+ is.removeRange(4, 5);
+ expect(is.pack()).to.equalArray([5, 6, 8, 10, 12, 14]);
+ });
+
+ it('removes the last part of the first element', function() {
+ is.removeRange(5, 6);
+ expect(is.pack()).to.equalArray([4, 5, 8, 10, 12, 14]);
+ });
+
+ it('removes the first element', function() {
+ is.removeRange(4, 6);
+ expect(is.pack()).to.equalArray([8, 10, 12, 14]);
+ });
+
+ it('removes the first part of a middle element', function() {
+ is.removeRange(8, 9);
+ expect(is.pack()).to.equalArray([4, 6, 9, 10, 12, 14]);
+ });
+
+ it('removes the last part of a middle element', function() {
+ is.removeRange(9, 10);
+ expect(is.pack()).to.equalArray([4, 6, 8, 9, 12, 14]);
+ });
+
+ it('removes a middle element', function() {
+ is.removeRange(8, 10);
+ expect(is.pack()).to.equalArray([4, 6, 12, 14]);
+ });
+
+ it('removes the first part of the last element', function() {
+ is.removeRange(12, 13);
+ expect(is.pack()).to.equalArray([4, 6, 8, 10, 13, 14]);
+ });
+
+ it('removes the last part of the last element', function() {
+ is.removeRange(13, 14);
+ expect(is.pack()).to.equalArray([4, 6, 8, 10, 12, 13]);
+ });
+
+ it('removes the last element', function() {
+ is.removeRange(12, 14);
+ expect(is.pack()).to.equalArray([4, 6, 8, 10]);
+ });
+
+ it('can remove multiple ranges near the start', function() {
+ is.removeRange(3, 11);
+ expect(is.pack()).to.equalArray([12, 14]);
+ });
+
+ it('can remove multiple ranges near the start', function() {
+ is.removeRange(7, 15);
+ expect(is.pack()).to.equalArray([4, 6]);
+ });
+
+ it('throws an exception when passed an invalid range', function() {
+ expect(function() {
+ is.removeRange(2, 0);
+ }).to.throwException();
+ });
+
+ });
+
+ });
+
+ describe('with fragmentation', function() {
+
+ var is;
+ beforeEach(function() {
+ is = ol.structs.IntegerSet.unpack(
+ [0, 1, 2, 4, 5, 8, 9, 12, 13, 15, 16, 17]);
+ });
+
+ describe('findRange', function() {
+
+ it('finds the first range of size 1', function() {
+ expect(is.findRange(1)).to.be(0);
+ });
+
+ it('finds the first range of size 2', function() {
+ expect(is.findRange(2)).to.be(2);
+ });
+
+ it('finds the first range of size 3', function() {
+ expect(is.findRange(3)).to.be(5);
+ });
+
+ it('returns -1 when no range can be found', function() {
+ expect(is.findRange(4)).to.be(-1);
+ });
+
+ });
+
+ describe('getFirst', function() {
+
+ it('returns the expected value', function() {
+ expect(is.getFirst()).to.be(0);
+ });
+
+ });
+
+ describe('getLast', function() {
+
+ it('returns the expected value', function() {
+ expect(is.getLast()).to.be(17);
+ });
+
+ });
+
+ describe('getSize', function() {
+
+ it('returns the expected value', function() {
+ expect(is.getSize()).to.be(12);
+ });
+
+ });
+
+ describe('removeRange', function() {
+
+ it('removing an empty range has no effect', function() {
+ is.removeRange(0, 0);
+ expect(is.pack()).to.equalArray(
+ [0, 1, 2, 4, 5, 8, 9, 12, 13, 15, 16, 17]);
+ });
+
+ it('can remove elements from the middle of range', function() {
+ is.removeRange(6, 7);
+ expect(is.pack()).to.equalArray(
+ [0, 1, 2, 4, 5, 6, 7, 8, 9, 12, 13, 15, 16, 17]);
+ });
+
+ it('can remove multiple ranges', function() {
+ is.removeRange(2, 12);
+ expect(is.pack()).to.equalArray([0, 1, 13, 15, 16, 17]);
+ });
+
+ it('can remove multiple ranges and reduce others', function() {
+ is.removeRange(0, 10);
+ expect(is.pack()).to.equalArray([10, 12, 13, 15, 16, 17]);
+ });
+
+ it('can remove all ranges', function() {
+ is.removeRange(0, 18);
+ expect(is.pack()).to.equalArray([]);
+ });
+
+ });
+
+ });
+
+ describe('compared to a slow reference implementation', function() {
+
+ var SimpleIntegerSet = function() {
+ this.integers_ = {};
+ };
+
+ SimpleIntegerSet.prototype.addRange = function(addStart, addStop) {
+ var i;
+ for (i = addStart; i < addStop; ++i) {
+ this.integers_[i.toString()] = true;
+ }
+ };
+
+ SimpleIntegerSet.prototype.clear = function() {
+ this.integers_ = {};
+ };
+
+ SimpleIntegerSet.prototype.pack = function() {
+ var integers = goog.array.map(
+ goog.object.getKeys(this.integers_), Number);
+ goog.array.sort(integers);
+ var arr = [];
+ var start = -1, stop;
+ var i;
+ for (i = 0; i < integers.length; ++i) {
+ if (start == -1) {
+ start = stop = integers[i];
+ } else if (integers[i] == stop + 1) {
+ ++stop;
+ } else {
+ arr.push(start, stop + 1);
+ start = stop = integers[i];
+ }
+ }
+ if (start != -1) {
+ arr.push(start, stop + 1);
+ }
+ return arr;
+ };
+
+ SimpleIntegerSet.prototype.removeRange = function(removeStart, removeStop) {
+ var i;
+ for (i = removeStart; i < removeStop; ++i) {
+ delete this.integers_[i.toString()];
+ }
+ };
+
+ var is, sis;
+ beforeEach(function() {
+ is = new ol.structs.IntegerSet();
+ sis = new SimpleIntegerSet();
+ });
+
+ it('behaves identically with random adds', function() {
+ var addStart, addStop, i;
+ for (i = 0; i < 64; ++i) {
+ addStart = goog.math.randomInt(128);
+ addStop = addStart + goog.math.randomInt(16);
+ is.addRange(addStart, addStop);
+ sis.addRange(addStart, addStop);
+ expect(is.pack()).to.equalArray(sis.pack());
+ }
+ });
+
+ it('behaves identically with random removes', function() {
+ is.addRange(0, 128);
+ sis.addRange(0, 128);
+ var i, removeStart, removeStop;
+ for (i = 0; i < 64; ++i) {
+ removeStart = goog.math.randomInt(128);
+ removeStop = removeStart + goog.math.randomInt(16);
+ is.removeRange(removeStart, removeStop);
+ sis.removeRange(removeStart, removeStop);
+ expect(is.pack()).to.equalArray(sis.pack());
+ }
+ });
+
+ it('behaves identically with random adds and removes', function() {
+ var i, start, stop;
+ for (i = 0; i < 64; ++i) {
+ start = goog.math.randomInt(128);
+ stop = start + goog.math.randomInt(16);
+ if (Math.random() < 0.5) {
+ is.addRange(start, stop);
+ sis.addRange(start, stop);
+ } else {
+ is.removeRange(start, stop);
+ sis.removeRange(start, stop);
+ }
+ expect(is.pack()).to.equalArray(sis.pack());
+ }
+ });
+
+ it('behaves identically with random adds, removes, and clears', function() {
+ var i, p, start, stop;
+ for (i = 0; i < 64; ++i) {
+ start = goog.math.randomInt(128);
+ stop = start + goog.math.randomInt(16);
+ p = Math.random();
+ if (p < 0.45) {
+ is.addRange(start, stop);
+ sis.addRange(start, stop);
+ } else if (p < 0.9) {
+ is.removeRange(start, stop);
+ sis.removeRange(start, stop);
+ } else {
+ is.clear();
+ sis.clear();
+ }
+ expect(is.pack()).to.equalArray(sis.pack());
+ }
+ });
+
+ });
+
+});
+
+
+goog.require('goog.array');
+goog.require('goog.object');
+goog.require('ol.structs.IntegerSet');