segmentation.ts 4.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107
  1. /**
  2. * Copyright (c) 2017 molio contributors, licensed under MIT, See LICENSE file for more info.
  3. *
  4. * @author David Sehnal <david.sehnal@gmail.com>
  5. */
  6. import Iterator from '../../iterator'
  7. import OrderedSet from '../ordered-set'
  8. import Interval from '../interval'
  9. import SortedArray from '../sorted-array'
  10. import Segs from '../segmentation'
  11. type Segmentation = { segments: SortedArray, segmentMap: Int32Array, count: number }
  12. export function create(values: ArrayLike<number>): Segmentation {
  13. const segments = SortedArray.ofSortedArray(values);
  14. const max = SortedArray.max(segments);
  15. const segmentMap = new Int32Array(max);
  16. for (let i = 0, _i = values.length - 1; i < _i; i++) {
  17. for (let j = values[i], _j = values[i + 1]; j < _j; j++) {
  18. segmentMap[j] = i;
  19. }
  20. }
  21. return { segments, segmentMap, count: values.length - 1 };
  22. }
  23. export function ofOffsets(offsets: ArrayLike<number>, bounds: Interval): Segmentation {
  24. const s = Interval.start(bounds);
  25. const segments = new Int32Array(offsets.length + 1);
  26. for (let i = 0, _i = offsets.length; i < _i; i++) {
  27. segments[i] = offsets[i] - s;
  28. }
  29. segments[offsets.length] = Interval.end(bounds) - s;
  30. return create(segments);
  31. }
  32. export function count({ count }: Segmentation) { return count; }
  33. export function getSegment({ segmentMap }: Segmentation, value: number) { return segmentMap[value]; }
  34. export function projectValue({ segments }: Segmentation, set: OrderedSet, value: number): Interval {
  35. const last = OrderedSet.max(segments);
  36. const idx = value >= last ? -1 : OrderedSet.findPredecessorIndex(segments, value - 1);
  37. return OrderedSet.findRange(set, OrderedSet.getAt(segments, idx), OrderedSet.getAt(segments, idx + 1) - 1);
  38. }
  39. export class SegmentIterator implements Iterator<Segs.Segment> {
  40. private segmentMin = 0;
  41. private segmentMax = 0;
  42. private setRange = Interval.Empty;
  43. private value: Segs.Segment = { index: 0, start: 0, end: 0 };
  44. private last: number = 0;
  45. hasNext: boolean = false;
  46. move() {
  47. while (this.hasNext) {
  48. if (this.updateValue()) {
  49. this.value.index = this.segmentMin++;
  50. this.hasNext = this.segmentMax >= this.segmentMin && Interval.size(this.setRange) > 0;
  51. break;
  52. } else {
  53. this.updateSegmentRange();
  54. }
  55. }
  56. return this.value;
  57. }
  58. private getSegmentIndex(value: number) {
  59. if (value >= this.last) return -1;
  60. return SortedArray.findPredecessorIndex(this.segments, value + 1) - 1;
  61. }
  62. private updateValue() {
  63. const segmentEnd = this.segments[this.segmentMin + 1];
  64. const setEnd = OrderedSet.findPredecessorIndexInRange(this.set, segmentEnd, this.setRange);
  65. this.value.start = Interval.start(this.setRange);
  66. this.value.end = setEnd;
  67. const rEnd = Interval.end(this.setRange);
  68. this.setRange = Interval.ofBounds(setEnd, rEnd);
  69. return setEnd > this.value.start;
  70. }
  71. private updateSegmentRange() {
  72. const sMin = Interval.min(this.setRange), sMax = Interval.max(this.setRange);
  73. if (sMax < sMin) {
  74. this.hasNext = false;
  75. return;
  76. }
  77. const min = OrderedSet.getAt(this.set, sMin);
  78. const max = OrderedSet.getAt(this.set, sMax);
  79. this.segmentMin = this.getSegmentIndex(min);
  80. this.segmentMax = this.getSegmentIndex(max);
  81. this.hasNext = this.segmentMax >= this.segmentMin && Interval.size(this.setRange) > 0;
  82. }
  83. constructor(private segments: SortedArray, private set: OrderedSet, inputRange: Interval) {
  84. this.last = SortedArray.max(segments);
  85. this.setRange = inputRange;
  86. this.updateSegmentRange();
  87. }
  88. }
  89. export function segments(segs: Segmentation, set: OrderedSet, segment?: Segs.Segment) {
  90. const int = typeof segment !== 'undefined' ? Interval.ofBounds(segment.start, segment.end) : Interval.ofBounds(0, OrderedSet.size(set));
  91. return new SegmentIterator(segs.segments, set, int);
  92. }