f7
f7 is a spreadsheet formula execution library
git clone https://git.vogt.world/f7.git
Log | Files | README.md | LICENSE.md
← All files
name: src/main/js/models/common/Range.ts
-rw-r--r--
2979
  1import { Compare } from "../../utils/Compare";
  2
  3/**
  4 * Range is essentially Google Guava's Range: https://github.com/google/guava/wiki/RangesExplained.
  5 */
  6export class Range {
  7  readonly lower: number | null;
  8  readonly upper: number | null;
  9
 10  constructor(lower: number | null, upper: number | null) {
 11    this.lower = lower;
 12    this.upper = upper;
 13  }
 14
 15  /**
 16   * Create a closed range.
 17   * @param lower - lower bound.
 18   * @param upper - upper bound.
 19   */
 20  static closed(lower: number, upper: number): Range {
 21    return new Range(lower, upper);
 22  }
 23
 24  /**
 25   * Create a range with a lower bound, but no upper bound.
 26   * @param lower - lower bound.
 27   */
 28  static atLeast(lower: number): Range {
 29    return new Range(lower, null);
 30  }
 31
 32  /**
 33   * Returns the minimal range that encloses both this range and other. If the ranges are both connected, this is their
 34   * union.
 35   * @param other - other range
 36   */
 37  span(other: Range): Range {
 38    const lowerCmp = Compare.numberComparison(this.lower, other.lower);
 39    const upperCmp = Compare.numberComparison(this.upper, other.upper);
 40    if (lowerCmp <= 0 && upperCmp >= 0) {
 41      return this;
 42    } else if (lowerCmp >= 0 && upperCmp <= 0) {
 43      return other;
 44    } else {
 45      const newLower = lowerCmp <= 0 ? this.lower : other.lower;
 46      const newUpper = upperCmp >= 0 ? this.upper : other.upper;
 47      return new Range(newLower, newUpper);
 48    }
 49  }
 50
 51  /**
 52   * Tests if these ranges are connected
 53   * @param other - other range.
 54   */
 55  isConnected(other: Range): boolean {
 56    return (
 57      Compare.numberComparison(this.lower, other.upper === null ? Infinity : other.upper) <= 0 &&
 58      Compare.numberComparison(other.lower, this.upper === null ? Infinity : this.upper) <= 0
 59    );
 60  }
 61
 62  /**
 63   * Returns the maximal range enclosed by both this range and other (which exists iff these ranges are connected).
 64   * @param connectedRange - connected range.
 65   */
 66  intersection(connectedRange: Range): Range {
 67    const lowerCmp = Compare.numberComparison(this.lower, connectedRange.lower);
 68    const upperCmp = Compare.numberComparison(this.upper, connectedRange.upper);
 69    if (lowerCmp >= 0 && upperCmp <= 0) {
 70      return this;
 71    } else if (lowerCmp <= 0 && upperCmp >= 0) {
 72      return connectedRange;
 73    } else {
 74      const newLower = lowerCmp >= 0 ? this.lower : connectedRange.lower;
 75      const newUpper = upperCmp <= 0 ? this.upper : connectedRange.upper;
 76      return new Range(newLower, newUpper);
 77    }
 78  }
 79
 80  /**
 81   * Returns the lower endpoint of this range.
 82   */
 83  lowerEndpoint(): number {
 84    return this.lower;
 85  }
 86
 87  /**
 88   * Returns the upper endpoint of this range.
 89   */
 90  upperEndpoint(): number {
 91    return this.upper;
 92  }
 93
 94  /**
 95   * Tests if this range uas an upper bound.
 96   */
 97  hasUpperBound(): boolean {
 98    return this.upper !== null;
 99  }
100
101  /**
102   * Tests if this range has a lower bound.
103   */
104  hasLowerBound(): boolean {
105    return this.lower !== null;
106  }
107}