export type Interval = [number, number]; /** * Uses a List of intervals to determine whether a number is in a set. * Intervals are [X, Y) (Start-inclusive, End-exclusive). */ export class Intervals { private _intervals: Interval[] = []; insert(n: Interval) { const newIntervals: Interval[] = []; for (let i of this._intervals) { if (!n || i[1] < n[0]) { newIntervals.push(i); } else if (i[0] > n[1]) { newIntervals.push(n, i); n = null; } else { // merge n[0] = Math.min(n[0], i[0]); n[1] = Math.max(n[1], i[1]); } } if (n && n[1] > n[0]) newIntervals.push(n); this._intervals = newIntervals; return this; } remove(n: Interval) { const newIntervals: Interval[] = []; for (let i of this._intervals) { if (i[0] >= n[0] && i[1] <= n[1]) { continue; } else { const trimEnd = (i[0] < n[0] && i[1] > n[0]); const trimStart = (i[1] > n[1] && i[0] < n[1]); if (trimEnd) { newIntervals.push([i[0], n[0]]); } if (trimStart) { newIntervals.push([n[1], i[1]]); } if (!trimEnd && !trimStart) newIntervals.push(i); } } this._intervals = newIntervals; return this; } contains(n: number | Interval) { for (let i of this._intervals) { if (typeof n == 'number') { if (i[0] <= n && n < i[1]) return true; } else { if (i[0] <= n[0] && n[1] <= i[1]) return true; } } return false; } breakup(n: Interval): Interval[] { const intervals: Interval[] = []; for (let i of this._intervals) { if (n[0] >= i[0] && n[1] <= i[1]) { return intervals; } else if (n[1] < n[0]) { return intervals; } else if (i[1] <= n[1] && i[1] >= n[0]) { if (i[0] > n[0]) { intervals.push([n[0], i[0]]); } n[0] = i[1]; } else if (i[0] >= n[0] && i[0] <= n[1]) { if (i[1] < n[1]) { intervals.push([n[1], i[1]]); } n[1] = i[0]; } } intervals.push(n); return intervals } }