UNPKG

int-interval-set

Version:

A simple interval set (range set) for integers.

407 lines (334 loc) 12.9 kB
var chai = require('chai'); var expect = chai.expect; const IntIntervalSet = require('../src/int-interval-set'); describe('Int Interval Set', function () { it('should add non overlapping intervals', function () { let set = new IntIntervalSet(); set.union(2, 4).union(8, 9); expect(set.intervals.length).to.equal(2); expect(set.intervals[0].lower).to.equal(2); expect(set.intervals[0].upper).to.equal(4); expect(set.intervals[1].lower).to.equal(8); expect(set.intervals[1].upper).to.equal(9); }); it('should iterate over all ints in the set, across non overlapping intervals (values)', function () { const set = new IntIntervalSet(); set.union(2, 4).union(8, 9).union(11); const actual = []; for (let x of set.values()) { actual.push(x); } expect(actual).to.eql([2, 3, 4, 8, 9, 11]); }); it('should iterate over all ints in the set, across non overlapping intervals (points)', function () { const set = new IntIntervalSet(); set.union(2, 4).union(8, 9).union(11); const actual = []; for (let x of set.points()) { actual.push(x); } expect(actual).to.eql([2, 3, 4, 8, 9, 11]); }); it('should add overlapping intervals', function () { let set = new IntIntervalSet(); set.union(2, 4); set.union(3, 9); expect(set.intervals.length).to.equal(1); expect(set.intervals[0].lower).to.equal(2); expect(set.intervals[0].upper).to.equal(9); }); it('should merge overlapping intervals', function () { let set = new IntIntervalSet(); set.intervals = [ {lower: 1, upper: 2}, {lower: 4, upper: 5}, {lower: 8, upper: 9} ]; set.union(2, 10); expect(set.intervals.length).to.equal(1); expect(set.intervals[0].lower).to.equal(1); expect(set.intervals[0].upper).to.equal(10); }); it('should merge intervals on a point', function () { let set = new IntIntervalSet(); set.intervals = [ {lower: 1, upper: 2}, {lower: 4, upper: 5} ]; set.union(3); expect(set.intervals.length).to.equal(1); expect(set.intervals[0].lower).to.equal(1); expect(set.intervals[0].upper).to.equal(5); }); it('should not intersect adjacent interval (higher)', function () { let set = new IntIntervalSet([{ lower: 15, upper: 25 }]); const intersection = set.intersection(26, 30) expect(intersection.isEmpty()).to.equal(true) }); it('should not intersect adjacent interval (lower)', function () { let set = new IntIntervalSet([{ lower: 15, upper: 25 }]); const intersection = set.intersection(10, 14) expect(intersection.isEmpty()).to.equal(true) }); it('should intersect lower and higher intervals', function () { let set = new IntIntervalSet([{ lower: 15, upper: 25 }, { lower: 30, upper: 40 }]); const intersection = set.intersection(20, 33); expect(intersection.intervals).to.deep.equal([ { lower: 20, upper: 25 }, { lower: 30, upper: 33 } ]); }); it('should intersect middle and higher intervals, adjacent to a low interval', function () { let set = new IntIntervalSet([{ lower: 15, upper: 25 }, { lower: 30, upper: 40 }, { lower: 45, upper: 50 }]); const intersection = set.intersection(26, 47); expect(intersection.intervals).to.deep.equal([ { lower: 30, upper: 40 }, { lower: 45, upper: 47 } ]); }); it('should intersect middle and lower intervals, adjacent to a high interval', function () { let set = new IntIntervalSet([{ lower: 15, upper: 25 }, { lower: 30, upper: 40 }, { lower: 45, upper: 50 }]); const intersection = set.intersection(20, 44); expect(intersection.intervals).to.deep.equal([ { lower: 20, upper: 25 }, { lower: 30, upper: 40 } ]); }); it('should intersect a subset', function () { let set = new IntIntervalSet([{ lower: 15, upper: 25 }, { lower: 30, upper: 40 }]); const intersection = set.intersection(0, 100); expect(intersection.intervals).to.deep.equal([ { lower: 15, upper: 25 }, { lower: 30, upper: 40 } ]); }); it('should intersect a superset', function () { let set = new IntIntervalSet([{ lower: 1, upper: 100 }]); const intersection = set.intersection(40, 50); expect(intersection.intervals).to.deep.equal([ { lower: 40, upper: 50 } ]); }); it('should find cut points', function () { let set = new IntIntervalSet(); set.intervals = [ {lower: 1, upper: 3}, {lower: 5, upper: 7}, {lower: 11, upper: 12} ]; let cut = set._findCutPoint(-1); expect(!!cut.connected).to.equal(false); expect(!!cut.contained).to.equal(false); expect(cut.index).to.equal(0); cut = set._findCutPoint(0); expect(!!cut.connected).to.equal(true); expect(!!cut.contained).to.equal(false); expect(cut.index).to.equal(0); cut = set._findCutPoint(1); expect(!!cut.connected).to.equal(true); expect(!!cut.contained).to.equal(true); expect(cut.index).to.equal(0); cut = set._findCutPoint(2); expect(!!cut.connected).to.equal(true); expect(!!cut.contained).to.equal(true); expect(cut.index).to.equal(0); cut = set._findCutPoint(3); expect(!!cut.connected).to.equal(true); expect(!!cut.contained).to.equal(true); expect(cut.index).to.equal(0); cut = set._findCutPoint(4); expect(!!cut.connected).to.equal(true); expect(!!cut.contained).to.equal(false); expect(cut.index).to.equal(1); cut = set._findCutPoint(5); expect(!!cut.connected).to.equal(true); expect(!!cut.contained).to.equal(true); expect(cut.index).to.equal(1); cut = set._findCutPoint(6); expect(!!cut.connected).to.equal(true); expect(!!cut.contained).to.equal(true); expect(cut.index).to.equal(1); cut = set._findCutPoint(7); expect(!!cut.connected).to.equal(true); expect(!!cut.contained).to.equal(true); expect(cut.index).to.equal(1); cut = set._findCutPoint(8); expect(!!cut.connected).to.equal(true); expect(!!cut.contained).to.equal(false); expect(cut.index).to.equal(1); cut = set._findCutPoint(9); expect(!!cut.connected).to.equal(false); expect(!!cut.contained).to.equal(false); expect(cut.index).to.equal(2); cut = set._findCutPoint(10); expect(!!cut.connected).to.equal(true); expect(!!cut.contained).to.equal(false); expect(cut.index).to.equal(2); cut = set._findCutPoint(11); expect(!!cut.connected).to.equal(true); expect(!!cut.contained).to.equal(true); expect(cut.index).to.equal(2); cut = set._findCutPoint(12); expect(!!cut.connected).to.equal(true); expect(!!cut.contained).to.equal(true); expect(cut.index).to.equal(2); cut = set._findCutPoint(13); expect(!!cut.connected).to.equal(true); expect(!!cut.contained).to.equal(false); expect(cut.index).to.equal(2); cut = set._findCutPoint(14); expect(!!cut.connected).to.equal(false); expect(!!cut.contained).to.equal(false); expect(cut.index).to.equal(3); }); it('should know if a point is contained', function () { let set = new IntIntervalSet(); set.intervals = [ {lower: 1, upper: 2}, {lower: 4, upper: 5}, {lower: 8, upper: 9} ]; expect(set.contains(0)).to.equal(false); expect(set.contains(1)).to.equal(true); expect(set.contains(2)).to.equal(true); expect(set.contains(3)).to.equal(false); expect(set.contains(4)).to.equal(true); expect(set.contains(5)).to.equal(true); expect(set.contains(6)).to.equal(false); expect(set.contains(7)).to.equal(false); expect(set.contains(8)).to.equal(true); expect(set.contains(9)).to.equal(true); expect(set.contains(10)).to.equal(false); }); it('should return the span as an IntIntervalSet', function () { let set = new IntIntervalSet(); set.intervals = [ {lower: 1, upper: 2}, {lower: 8, upper: 9} ]; const spanSet = set.spanSet(); const expectedSpan = { lower: 1, upper: 9 }; expect(spanSet instanceof IntIntervalSet).to.equal(true); expect(spanSet.intervals).to.deep.equal([expectedSpan]); expect(spanSet.span()).to.deep.equal(expectedSpan); }); it('should generate the complement of this interval set', function () { let set = new IntIntervalSet(); set.intervals = [ {lower: 1, upper: 2}, {lower: 4, upper: 5}, {lower: 8, upper: 9} ]; let actual = set.complement(); let expected = [ {lower: -Number.MAX_SAFE_INTEGER, upper: 0}, {lower: 3, upper: 3}, {lower: 6, upper: 7}, {lower: 10, upper: Number.MAX_SAFE_INTEGER} ]; expect(JSON.stringify(actual.intervals)).to.equal(JSON.stringify(expected)); }); it('should generate an empty set as the complement of the full set', function () { let set = new IntIntervalSet(); set.intervals = [ {lower: 1, upper: 2}, {lower: 4, upper: 5}, {lower: 8, upper: 9} ]; let actual = set.complement(); let expected = [ {lower: -Number.MAX_SAFE_INTEGER, upper: 0}, {lower: 3, upper: 3}, {lower: 6, upper: 7}, {lower: 10, upper: Number.MAX_SAFE_INTEGER} ]; expect(JSON.stringify(actual.intervals)).to.equal(JSON.stringify(expected)); }); it('should generate a bounded clone of the interval set', function () { let set = new IntIntervalSet(); set.intervals = [ {lower: 1, upper: 3}, {lower: 5, upper: 7}, {lower: 9, upper: 10} ]; let actual = set.intersection(-5, 0); let expected = []; expect(JSON.stringify(actual.intervals)).to.equal(JSON.stringify(expected)); actual = set.intersection(0, 1); expected = [{lower: 1, upper: 1}]; expect(JSON.stringify(actual.intervals)).to.equal(JSON.stringify(expected)); actual = set.intersection(1, 1); expected = [{lower: 1, upper: 1}]; expect(JSON.stringify(actual.intervals)).to.equal(JSON.stringify(expected)); actual = set.intersection(0, 2); expected = [{lower: 1, upper: 2}]; expect(JSON.stringify(actual.intervals)).to.equal(JSON.stringify(expected)); actual = set.intersection(2, 3); expected = [{lower: 2, upper: 3}]; expect(JSON.stringify(actual.intervals)).to.equal(JSON.stringify(expected)); actual = set.intersection(2, 6); expected = [ {lower: 2, upper: 3}, {lower: 5, upper: 6}]; expect(JSON.stringify(actual.intervals)).to.equal(JSON.stringify(expected)); actual = set.intersection(2, 15); expected = [ {lower: 2, upper: 3}, {lower: 5, upper: 7}, {lower: 9, upper: 10} ]; expect(JSON.stringify(actual.intervals)).to.equal(JSON.stringify(expected)); actual = set.intersection(5, 7); expected = [{lower: 5, upper: 7}]; expect(JSON.stringify(actual.intervals)).to.equal(JSON.stringify(expected)); actual = set.intersection(7, 9); expected = [ {lower: 7, upper: 7}, {lower: 9, upper: 9} ]; expect(JSON.stringify(actual.intervals)).to.equal(JSON.stringify(expected)); actual = set.intersection(10, 12); expected = [ {lower: 10, upper: 10} ]; expect(JSON.stringify(actual.intervals)).to.equal(JSON.stringify(expected)); }); it('should find the cut point with a hint', function () { let set = new IntIntervalSet(); set.intervals = [ {lower: -9007199254740991, upper: 1549067879}, {lower: 1550623081, upper: 1552865879}, {lower:1558543081, upper: 9007199254740991} ]; const lower = 1549756800; const upper = 1550448000; const lowerCutpoint = set._findCutPoint(lower); expect(lowerCutpoint.index).to.equal(1); expect(!!lowerCutpoint.connected).to.be.false; expect(!!lowerCutpoint.contained).to.be.false; const upperCutpoint = set._findCutPoint(upper); expect(upperCutpoint.index).to.equal(1); expect(!!upperCutpoint.connected).to.be.false; expect(!!upperCutpoint.contained).to.be.false; const upperCutpointHint = set._findCutPoint(upper, 1); expect(upperCutpointHint.index).to.equal(1); expect(!!upperCutpointHint.connected).to.be.false; expect(!!upperCutpointHint.contained).to.be.false; const intersection = set.intersection(lower, upper); expect(intersection.isEmpty()).to.be.true; }); it('should find the cut point in an internal gap', function () { let set = new IntIntervalSet(); set.intervals = [ {lower: -9007199254740991, upper: 1541378279}, {lower: 1542818281, upper: 1549067879}, {lower: 1550623081, upper: 1551832679}, {lower: 1558543081, upper: 9007199254740991} ]; const lower = 1549756800; const lowerCutpoint = set._findCutPoint(lower); expect(lowerCutpoint.index).to.equal(2); expect(!!lowerCutpoint.connected).to.be.false; expect(!!lowerCutpoint.contained).to.be.false; }); });