segmentation.ts 4.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110
  1. /**
  2. * Copyright (c) 2017 mol* 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. interface Segmentation {
  12. /** Segments stored as a sorted array */
  13. segments: SortedArray,
  14. /** Mapping of values to segments */
  15. segmentMap: Int32Array,
  16. /** Number of segments */
  17. count: number
  18. }
  19. export function create(values: ArrayLike<number>): Segmentation {
  20. const segments = SortedArray.ofSortedArray(values);
  21. const max = SortedArray.max(segments);
  22. const segmentMap = new Int32Array(max);
  23. for (let i = 0, _i = values.length - 1; i < _i; i++) {
  24. for (let j = values[i], _j = values[i + 1]; j < _j; j++) {
  25. segmentMap[j] = i;
  26. }
  27. }
  28. return { segments, segmentMap, count: values.length - 1 };
  29. }
  30. export function ofOffsets(offsets: ArrayLike<number>, bounds: Interval): Segmentation {
  31. const s = Interval.start(bounds);
  32. const segments = new Int32Array(offsets.length + 1);
  33. for (let i = 0, _i = offsets.length; i < _i; i++) {
  34. segments[i] = offsets[i] - s;
  35. }
  36. segments[offsets.length] = Interval.end(bounds) - s;
  37. return create(segments);
  38. }
  39. /** Get number of segments in a segmentation */
  40. export function count({ count }: Segmentation) { return count; }
  41. export function getSegment({ segmentMap }: Segmentation, value: number) { return segmentMap[value]; }
  42. export function projectValue({ segments }: Segmentation, set: OrderedSet, value: number): Interval {
  43. const last = OrderedSet.max(segments);
  44. const idx = value >= last ? -1 : OrderedSet.findPredecessorIndex(segments, value - 1);
  45. return OrderedSet.findRange(set, OrderedSet.getAt(segments, idx), OrderedSet.getAt(segments, idx + 1) - 1);
  46. }
  47. export class SegmentIterator<T extends number = number> implements Iterator<Segs.Segment<T>> {
  48. private segmentMax = 0;
  49. private segmentMin = 0;
  50. private setRange = Interval.Empty;
  51. private value: Segs.Segment<T> = { index: 0, start: 0 as T, end: 0 as T };
  52. hasNext: boolean = false;
  53. move() {
  54. while (this.hasNext) {
  55. if (this.updateValue()) {
  56. this.value.index = this.segmentMin++;
  57. this.hasNext = this.segmentMax >= this.segmentMin && Interval.size(this.setRange) > 0;
  58. break;
  59. } else {
  60. this.updateSegmentRange();
  61. }
  62. }
  63. return this.value;
  64. }
  65. private updateValue() {
  66. const segmentEnd = this.segments[this.segmentMin + 1];
  67. // TODO: add optimized version for interval and array?
  68. const setEnd = OrderedSet.findPredecessorIndexInInterval(this.set, segmentEnd, this.setRange);
  69. this.value.start = Interval.start(this.setRange) as T;
  70. this.value.end = setEnd as T;
  71. this.setRange = Interval.ofBounds(setEnd, Interval.end(this.setRange));
  72. return setEnd > this.value.start;
  73. }
  74. private updateSegmentRange() {
  75. const sMin = Interval.min(this.setRange), sMax = Interval.max(this.setRange);
  76. if (sMax < sMin) {
  77. this.hasNext = false;
  78. return;
  79. }
  80. this.segmentMin = this.segmentMap[OrderedSet.getAt(this.set, sMin)];
  81. this.segmentMax = this.segmentMap[OrderedSet.getAt(this.set, sMax)];
  82. this.hasNext = this.segmentMax >= this.segmentMin;
  83. }
  84. setSegment(segment: Segs.Segment<T>) {
  85. this.setRange = Interval.ofBounds(segment.start, segment.end);
  86. this.updateSegmentRange();
  87. }
  88. constructor(private segments: SortedArray, private segmentMap: Int32Array, private set: OrderedSet, inputRange: Interval) {
  89. this.setRange = inputRange;
  90. this.updateSegmentRange();
  91. }
  92. }
  93. export function segments(segs: Segmentation, set: OrderedSet, segment?: Segs.Segment) {
  94. const int = typeof segment !== 'undefined' ? Interval.ofBounds(segment.start, segment.end) : Interval.ofBounds(0, OrderedSet.size(set));
  95. return new SegmentIterator(segs.segments, segs.segmentMap, set, int);
  96. }