sorted-ranges.ts 3.7 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586
  1. /**
  2. * Copyright (c) 2018 mol* contributors, licensed under MIT, See LICENSE file for more info.
  3. *
  4. * @author Alexander Rose <alexander.rose@weirdbyte.de>
  5. */
  6. import { Segmentation, OrderedSet, SortedArray, Interval } from '../int';
  7. import _Iterator from '../iterator';
  8. /** Pairs of min and max indices of sorted, non-overlapping ranges */
  9. type SortedRanges<T extends number = number> = SortedArray<T>
  10. namespace SortedRanges {
  11. export function ofSortedRanges<T extends number = number>(array: ArrayLike<T>) { return SortedArray.ofSortedArray<T>(array) }
  12. export function start<T extends number = number>(ranges: SortedRanges<T>) { return ranges[0] }
  13. export function end<T extends number = number>(ranges: SortedRanges<T>) { return ranges[ranges.length - 1] + 1 }
  14. export function min<T extends number = number>(ranges: SortedRanges<T>) { return ranges[0] }
  15. export function max<T extends number = number>(ranges: SortedRanges<T>) { return ranges[ranges.length - 1] }
  16. export function size<T extends number = number>(ranges: SortedRanges<T>) {
  17. let size = 0
  18. for(let i = 0, il = ranges.length; i < il; i += 2) {
  19. size += ranges[i + 1] - ranges[i] + 1
  20. }
  21. return size
  22. }
  23. export function transientSegments<T extends number = number, I extends number = number>(ranges: SortedRanges<T>, set: OrderedSet<T>) {
  24. return new Iterator<T, I>(ranges, set)
  25. }
  26. export class Iterator<T extends number = number, I extends number = number> implements _Iterator<Segmentation.Segment<I>> {
  27. private value: Segmentation.Segment<I> = { index: 0 as I, start: 0 as T, end: 0 as T }
  28. private curIndex = 0
  29. private maxIndex = 0
  30. private interval: Interval<T>
  31. private curMin: T = 0 as T
  32. hasNext: boolean = false;
  33. updateInterval() {
  34. this.interval = Interval.ofRange(this.ranges[this.curIndex], this.ranges[this.curIndex + 1])
  35. }
  36. updateValue() {
  37. this.value.index = this.curIndex / 2 as I
  38. this.value.start = OrderedSet.findPredecessorIndex(this.set, this.ranges[this.curIndex])
  39. this.value.end = OrderedSet.findPredecessorIndex(this.set, this.ranges[this.curIndex + 1]) + 1
  40. }
  41. move() {
  42. if (this.hasNext) {
  43. this.updateValue()
  44. while (this.curIndex <= this.maxIndex) {
  45. this.curIndex += 2
  46. this.curMin = Interval.end(this.interval)
  47. this.updateInterval()
  48. if (Interval.min(this.interval) >= this.curMin && OrderedSet.areIntersecting(this.interval, this.set)) break
  49. }
  50. this.hasNext = this.curIndex <= this.maxIndex
  51. }
  52. return this.value;
  53. }
  54. getRangeIndex(value: number) {
  55. const index = SortedArray.findPredecessorIndex(this.ranges, value)
  56. return (index % 2 === 1) ? index - 1 : index
  57. }
  58. constructor(private ranges: SortedRanges<T>, private set: OrderedSet<T>) {
  59. // TODO cleanup, refactor to make it clearer
  60. const min = SortedArray.findPredecessorIndex(this.ranges, OrderedSet.min(set))
  61. const max = SortedArray.findPredecessorIndex(this.ranges, OrderedSet.max(set))
  62. if (ranges.length && min !== max) {
  63. this.curIndex = this.getRangeIndex(OrderedSet.min(set))
  64. this.maxIndex = Math.min(ranges.length - 2, this.getRangeIndex(OrderedSet.max(set)))
  65. this.curMin = this.ranges[this.curIndex]
  66. this.updateInterval()
  67. }
  68. this.hasNext = ranges.length > 0 && min !== max && this.curIndex <= this.maxIndex
  69. }
  70. }
  71. }
  72. export default SortedRanges