/**
   * Clamps {@code value} to this range.
   *
   * <p>If the value is within this range, it is returned. Otherwise, if it is {@code <} than the
   * lower endpoint, the lower endpoint is returned, else the upper endpoint is returned.
   * Comparisons are performed using the {@link Comparable} interface.
   *
   * @param value a non-{@code null} {@code T} reference
   * @return {@code value} clamped to this range.
   */
  public T clamp(T value) {
    checkNotNull(value, "value must not be null");

    if (value.compareTo(mLower) < 0) {
      return mLower;
    } else if (value.compareTo(mUpper) > 0) {
      return mUpper;
    } else {
      return value;
    }
  }
  /**
   * Returns the smallest range that includes this range and the inclusive range specified by {@code
   * [lower, upper]}.
   *
   * <p>See {@link #extend(Range)} for more details.
   *
   * @param lower a non-{@code null} {@code T} reference
   * @param upper a non-{@code null} {@code T} reference
   * @return the extension of this range and the other range.
   * @throws NullPointerException if {@code lower} or {@code upper} was {@code null}
   */
  public Range<T> extend(T lower, T upper) {
    checkNotNull(lower, "lower must not be null");
    checkNotNull(upper, "upper must not be null");

    int cmpLower = lower.compareTo(mLower);
    int cmpUpper = upper.compareTo(mUpper);

    if (cmpLower >= 0 && cmpUpper <= 0) {
      // this inludes other
      return this;
    } else {
      return Range.create(cmpLower >= 0 ? mLower : lower, cmpUpper <= 0 ? mUpper : upper);
    }
  }
  /**
   * Returns the intersection of this range and the inclusive range specified by {@code [lower,
   * upper]}.
   *
   * <p>See {@link #intersect(Range)} for more details.
   *
   * @param lower a non-{@code null} {@code T} reference
   * @param upper a non-{@code null} {@code T} reference
   * @return the intersection of this range and the other range
   * @throws NullPointerException if {@code lower} or {@code upper} was {@code null}
   * @throws IllegalArgumentException if the ranges are disjoint.
   */
  public Range<T> intersect(T lower, T upper) {
    checkNotNull(lower, "lower must not be null");
    checkNotNull(upper, "upper must not be null");

    int cmpLower = lower.compareTo(mLower);
    int cmpUpper = upper.compareTo(mUpper);

    if (cmpLower <= 0 && cmpUpper >= 0) {
      // [lower, upper] includes this
      return this;
    } else {
      return Range.create(cmpLower <= 0 ? mLower : lower, cmpUpper >= 0 ? mUpper : upper);
    }
  }
  /**
   * Checks if the {@code value} is within the bounds of this range.
   *
   * <p>A value is considered to be within this range if it's {@code >=} the lower endpoint
   * <i>and</i> {@code <=} the upper endpoint (using the {@link Comparable} interface.)
   *
   * @param value a non-{@code null} {@code T} reference
   * @return {@code true} if the value is within this inclusive range, {@code false} otherwise
   * @throws NullPointerException if {@code value} was {@code null}
   */
  public boolean contains(T value) {
    checkNotNull(value, "value must not be null");

    boolean gteLower = value.compareTo(mLower) >= 0;
    boolean lteUpper = value.compareTo(mUpper) <= 0;

    return gteLower && lteUpper;
  }
  /**
   * Create a new immutable range.
   *
   * <p>The endpoints are {@code [lower, upper]}; that is the range is bounded. {@code lower} must
   * be {@link Comparable#compareTo lesser or equal} to {@code upper}.
   *
   * @param lower The lower endpoint (inclusive)
   * @param upper The upper endpoint (inclusive)
   * @throws NullPointerException if {@code lower} or {@code upper} is {@code null}
   */
  public Range(final T lower, final T upper) {
    mLower = checkNotNull(lower, "lower must not be null");
    mUpper = checkNotNull(upper, "upper must not be null");

    if (lower.compareTo(upper) > 0) {
      throw new IllegalArgumentException("lower must be less than or equal to upper");
    }
  }