/** * Copyright (c) 2018-2019 mol* contributors, licensed under MIT, See LICENSE file for more info. * * @author Alexander Rose */ import { Segmentation, OrderedSet, SortedArray, Interval } from '../int'; import _Iterator from '../iterator'; /** Pairs of min and max indices of sorted, non-overlapping ranges */ type SortedRanges = SortedArray namespace SortedRanges { export function ofSortedRanges(array: ArrayLike) { return SortedArray.ofSortedArray(array); } export function start(ranges: SortedRanges) { return ranges[0]; } export function end(ranges: SortedRanges) { return ranges[ranges.length - 1] + 1; } export function min(ranges: SortedRanges) { return ranges[0]; } export function max(ranges: SortedRanges) { return ranges[ranges.length - 1]; } export function size(ranges: SortedRanges) { let size = 0; for (let i = 0, il = ranges.length; i < il; i += 2) { size += ranges[i + 1] - ranges[i] + 1; } return size; } export function count(ranges: SortedRanges) { return ranges.length / 2; } export function startAt(ranges: SortedRanges, index: number) { return ranges[index * 2]; } export function endAt(ranges: SortedRanges, index: number) { return ranges[index * 2 + 1] + 1; } export function minAt(ranges: SortedRanges, index: number) { return ranges[index * 2]; } export function maxAt(ranges: SortedRanges, index: number) { return ranges[index * 2 + 1]; } export function areEqual(a: SortedRanges, b: SortedRanges) { if (a.length !== b.length) return false; for (let i = 0, il = a.length; i < il; ++i) { if (a[i] !== b[i]) return false; } return true; } export function forEach(ranges: SortedRanges, f: (value: T, i: number) => void) { let k = 0; for (let i = 0, il = ranges.length; i < il; i += 2) { for (let j = ranges[i], jl = ranges[i + 1]; j <= jl; ++j) { f(j, k); ++k; } } } /** Returns if a value of `set` is included in `ranges` */ export function has(ranges: SortedRanges, set: OrderedSet) { return firstIntersectionIndex(ranges, set) !== -1; } /** Returns if a value of `set` is included in `ranges` from given index */ export function hasFrom(ranges: SortedRanges, set: OrderedSet, from: number) { return firstIntersectionIndexFrom(ranges, set, from) !== -1; } export function firstIntersectionIndex(ranges: SortedRanges, set: OrderedSet): number { return firstIntersectionIndexFrom(ranges, set, 0); } export function firstIntersectionIndexFrom(ranges: SortedRanges, set: OrderedSet, from: number): number { if (minAt(ranges, from) > OrderedSet.max(set) || max(ranges) < OrderedSet.min(set)) return -1; for (let i = from, il = count(ranges); i < il; ++i) { const interval = Interval.ofRange(minAt(ranges, i), maxAt(ranges, i)); if (OrderedSet.areIntersecting(interval, set)) return i; } return -1; } export function transientSegments(ranges: SortedRanges, set: OrderedSet) { return new Iterator(ranges, set); } export class Iterator implements _Iterator> { private value: Segmentation.Segment = { index: 0 as I, start: 0 as T, end: 0 as T } private curIndex = 0 hasNext: boolean = false; private updateValue() { this.value.index = this.curIndex as I; this.value.start = OrderedSet.findPredecessorIndex(this.set, startAt(this.ranges, this.curIndex)); this.value.end = OrderedSet.findPredecessorIndex(this.set, endAt(this.ranges, this.curIndex)); } move() { if (this.hasNext) { this.updateValue(); this.curIndex = firstIntersectionIndexFrom(this.ranges, this.set, this.curIndex + 1); this.hasNext = this.curIndex !== -1; } return this.value; } constructor(private ranges: SortedRanges, private set: OrderedSet) { this.curIndex = firstIntersectionIndex(ranges, set); this.hasNext = this.curIndex !== -1; } } } export default SortedRanges;