segmentation.ts 3.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990
  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: ArrayLike<number>, offset: number, count: number }
  12. export function create(values: ArrayLike<number>): Segmentation {
  13. const segments = SortedArray.ofSortedArray(values);
  14. const min = SortedArray.min(segments), max = SortedArray.max(segments);
  15. const segmentMap = new Int32Array(max - min);
  16. for (let i = 0, _i = values.length - 1; i < _i; i++) {
  17. for (let j = values[i] - min, _j = values[i + 1] - min; j < _j; j++) {
  18. segmentMap[j] = i;
  19. }
  20. }
  21. return { segments, segmentMap, offset: min, count: values.length - 1 };
  22. }
  23. export function count({ count }: Segmentation) { return count; }
  24. export function getSegment({ segmentMap, offset }: Segmentation, value: number) { return segmentMap[value - offset]; }
  25. export function projectValue({ segments }: Segmentation, set: OrderedSet, value: number): Interval {
  26. const last = OrderedSet.max(segments);
  27. const idx = value >= last ? -1 : OrderedSet.findPredecessorIndex(segments, value - 1);
  28. return OrderedSet.findRange(set, OrderedSet.getAt(segments, idx), OrderedSet.getAt(segments, idx + 1) - 1);
  29. }
  30. class SegmentIterator implements Iterator<Segs.Segment> {
  31. private segmentStart = 0;
  32. private segmentEnd = 0;
  33. private setRange = Interval.Empty;
  34. private value: Segs.Segment = { index: 0, start: 0, end: 0 };
  35. private last: number = 0;
  36. hasNext: boolean = false;
  37. move() {
  38. while (this.hasNext) {
  39. if (!this.updateValue()) {
  40. this.updateSegmentRange();
  41. } else {
  42. this.value.index = this.segmentStart++;
  43. this.hasNext = this.segmentEnd > this.segmentStart;
  44. break;
  45. }
  46. }
  47. return this.value;
  48. }
  49. private getSegmentIndex(value: number) {
  50. if (value >= this.last) return -1;
  51. return OrderedSet.findPredecessorIndex(this.segments, value - 1);
  52. }
  53. private updateValue() {
  54. const segmentEnd = OrderedSet.getAt(this.segments, this.segmentStart + 1);
  55. const setEnd = OrderedSet.findPredecessorIndexInRange(this.set, segmentEnd, this.setRange);
  56. this.value.start = Interval.start(this.setRange);
  57. this.value.end = setEnd;
  58. this.setRange = Interval.ofBounds(setEnd, Interval.end(this.setRange))
  59. return setEnd > this.value.start;
  60. }
  61. private updateSegmentRange() {
  62. const min = OrderedSet.getAt(this.set, Interval.min(this.setRange));
  63. const max = OrderedSet.getAt(this.set, Interval.max(this.setRange));
  64. this.segmentStart = this.getSegmentIndex(min);
  65. this.segmentEnd = this.getSegmentIndex(max) + 1;
  66. this.hasNext = this.segmentEnd > this.segmentStart;
  67. }
  68. constructor(private segments: OrderedSet, private set: OrderedSet, inputRange: Interval) {
  69. this.last = OrderedSet.max(segments);
  70. this.setRange = inputRange;
  71. this.updateSegmentRange();
  72. }
  73. }
  74. export function segments(segs: Segmentation, set: OrderedSet, range?: Interval) {
  75. const int = typeof range !== 'undefined' ? range : Interval.ofBounds(0, OrderedSet.size(set));
  76. return new SegmentIterator(segs.segments, set, int);
  77. }