All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.checkerframework.common.value.util.Range Maven / Gradle / Ivy

Go to download

The Checker Framework enhances Java's type system to make it more powerful and useful. This lets software developers detect and prevent errors in their Java programs. The Checker Framework includes compiler plug-ins ("checkers") that find bugs or verify their absence. It also permits you to write your own compiler plug-ins.

There is a newer version: 3.0.0-b2
Show newest version
package org.checkerframework.common.value.util;

import java.math.BigInteger;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Objects;

/**
 * The Range class models a 64-bit two's-complement integral interval, such as all integers between
 * 1 and 10, inclusive. Ranges are immutable.
 */
public class Range {

    /** The lower bound of the interval, inclusive. */
    public final long from;

    /** The upper bound of the interval, inclusive. */
    public final long to;

    /**
     * Should ranges take overflow into account or ignore it?
     *
     * 
    *
  • If {@code ignoreOverflow} is true, then operations that would result in more than the * max value are clipped to the max value (and similarly for the min). *
  • If {@code ignoreOverflow} is false, then operations that would result in more than the * max wrap around according to the rules of twos-complement arithmetic and produce a * smaller value (and similarly for the min). *
* *

Any checker that uses this library should set this field. By default, this field is set to * false (meaning overflow is taken into account), but a previous checker might have set it to * true. * *

A static field is used because passing an instance field throughout the class bloats the * code. */ public static boolean ignoreOverflow = false; /** A range containing all possible 64-bit values. */ public static final Range EVERYTHING = new Range(Long.MIN_VALUE, Long.MAX_VALUE); /** A range containing all possible 32-bit values. */ public static final Range INT_EVERYTHING = new Range(Integer.MIN_VALUE, Integer.MAX_VALUE); /** A range containing all possible 16-bit values. */ public static final Range SHORT_EVERYTHING = new Range(Short.MIN_VALUE, Short.MAX_VALUE); /** A range containing all possible char values. */ public static final Range CHAR_EVERYTHING = new Range(Character.MIN_VALUE, Character.MAX_VALUE); /** A range containing all possible 8-bit values. */ public static final Range BYTE_EVERYTHING = new Range(Byte.MIN_VALUE, Byte.MAX_VALUE); /** The empty range. */ public static final Range NOTHING = new Range(); /** * Constructs a range with its bounds specified by two parameters, {@code from} and {@code to}. * * @param from the lower bound (inclusive) * @param to the upper bound (inclusive) */ public Range(long from, long to) { if (!(from <= to)) { throw new IllegalArgumentException(String.format("Invalid Range: %s %s", from, to)); } this.from = from; this.to = to; } /** Creates the singleton empty range. */ private Range() { this.from = Long.MAX_VALUE; this.to = Long.MIN_VALUE; } /** * Returns a range with its bounds specified by two parameters, {@code from} and {@code to}. If * {@code from} is greater than {@code to}, returns {@link #NOTHING}. * * @param from the lower bound (inclusive) * @param to the upper bound (inclusive) */ private Range createRangeOrNothing(long from, long to) { if (from <= to) { return new Range(from, to); } else { return NOTHING; } } @Override public String toString() { if (this.isNothing()) { return "[]"; } else { return String.format("[%s..%s]", from, to); } } @Override public boolean equals(Object obj) { if (obj instanceof Range) { Range range = (Range) obj; return from == range.from && to == range.to; } return false; } @Override public int hashCode() { return Objects.hash(from, to); } /** Return true if this range contains every {@code long} value. */ public boolean isLongEverything() { return from == Long.MIN_VALUE && to == Long.MAX_VALUE; } /** Return true if this range contains every {@code int} value. */ public boolean isIntEverything() { return from == Integer.MIN_VALUE && to == Integer.MAX_VALUE; } /** Return true if this range contains every {@code short} value. */ public boolean isShortEverything() { return from == Short.MIN_VALUE && to == Short.MAX_VALUE; } /** Return true if this range contains every {@code char} value. */ public boolean isCharEverything() { return from == Character.MIN_VALUE && to == Character.MAX_VALUE; } /** Return true if this range contains every {@code byte} value. */ public boolean isByteEverything() { return from == Byte.MIN_VALUE && to == Byte.MAX_VALUE; } /** Return true if this range contains no values. */ public boolean isNothing() { return this == NOTHING; } /** The number of values representable in 32 bits: 2^32 or 1<<32. */ private static long integerWidth = (long) Integer.MAX_VALUE - (long) Integer.MIN_VALUE + 1; /** * Converts this range to a 32-bit integral range. * *

If {@link #ignoreOverflow} is true and one of the bounds is outside the Integer range, * then that bound is set to the bound of the Integer range. * *

If {@link #ignoreOverflow} is false and this range is too wide, i.e., wider than the full * range of the Integer class, return INT_EVERYTHING. * *

If {@link #ignoreOverflow} is false and the bounds of this range are not representable as * 32-bit integers, convert the bounds to Integer type in accordance with Java twos-complement * overflow rules, e.g., Integer.MAX_VALUE + 1 is converted to Integer.MIN_VALUE. */ public Range intRange() { if (this.isNothing()) { return this; } if (ignoreOverflow) { return new Range(Math.max(from, Integer.MIN_VALUE), Math.min(to, Integer.MAX_VALUE)); } if (this.isWiderThan(integerWidth)) { return INT_EVERYTHING; } int intFrom = (int) this.from; int intTo = (int) this.to; if (intFrom <= intTo) { return new Range(intFrom, intTo); } return INT_EVERYTHING; } /** The number of values representable in 16 bits: 2^16 or 1<<16. */ private static long shortWidth = Short.MAX_VALUE - Short.MIN_VALUE + 1; /** * Converts a this range to a 16-bit short range. * *

If {@link #ignoreOverflow} is true and one of the bounds is outside the Short range, then * that bound is set to the bound of the Short range. * *

If {@link #ignoreOverflow} is false and this range is too wide, i.e., wider than the full * range of the Short class, return SHORT_EVERYTHING. * *

If {@link #ignoreOverflow} is false and the bounds of this range are not representable as * 16-bit integers, convert the bounds to Short type in accordance with Java twos-complement * overflow rules, e.g., Short.MAX_VALUE + 1 is converted to Short.MIN_VALUE. */ public Range shortRange() { if (this.isNothing()) { return this; } if (ignoreOverflow) { return new Range(Math.max(from, Short.MIN_VALUE), Math.min(to, Short.MAX_VALUE)); } if (this.isWiderThan(shortWidth)) { // short is promoted to int before the operation so no need for explicit casting return SHORT_EVERYTHING; } short shortFrom = (short) this.from; short shortTo = (short) this.to; if (shortFrom <= shortTo) { return new Range(shortFrom, shortTo); } return SHORT_EVERYTHING; } /** The number of values representable in char: */ private static long charWidth = Character.MAX_VALUE - Character.MIN_VALUE + 1; /** * Converts this range to a char range. * *

If {@link #ignoreOverflow} is true and one of the bounds is outside the Character range, * then that bound is set to the bound of the Character range. * *

If {@link #ignoreOverflow} is false and this range is too wide, i.e., wider than the full * range of the Character class, return CHAR_EVERYTHING. * *

If {@link #ignoreOverflow} is false and the bounds of this range are not representable as * 8-bit integers, convert the bounds to Character type in accordance with Java overflow rules * (twos-complement), e.g., Character.MAX_VALUE + 1 is converted to Character.MIN_VALUE. */ public Range charRange() { if (this.isNothing()) { return this; } if (ignoreOverflow) { return new Range( Math.max(from, Character.MIN_VALUE), Math.min(to, Character.MAX_VALUE)); } if (this.isWiderThan(charWidth)) { // char is promoted to int before the operation so no need for explicit casting return CHAR_EVERYTHING; } char charFrom = (char) this.from; char charTo = (char) this.to; if (charFrom <= charTo) { return new Range(charFrom, charTo); } return CHAR_EVERYTHING; } /** The number of values representable in 8 bits: 2^8 or 1<<8. */ private static long byteWidth = Byte.MAX_VALUE - Byte.MIN_VALUE + 1; /** * Converts a this range to a 8-bit byte range. * *

If {@link #ignoreOverflow} is true and one of the bounds is outside the Byte range, then * that bound is set to the bound of the Byte range. * *

If {@link #ignoreOverflow} is false and this range is too wide, i.e., wider than the full * range of the Byte class, return BYTE_EVERYTHING. * *

If {@link #ignoreOverflow} is false and the bounds of this range are not representable as * 8-bit integers, convert the bounds to Byte type in accordance with Java twos-complement * overflow rules, e.g., Byte.MAX_VALUE + 1 is converted to Byte.MIN_VALUE. */ public Range byteRange() { if (this.isNothing()) { return this; } if (ignoreOverflow) { return new Range(Math.max(from, Byte.MIN_VALUE), Math.min(to, Byte.MAX_VALUE)); } if (this.isWiderThan(byteWidth)) { // byte is promoted to int before the operation so no need for explicit casting return BYTE_EVERYTHING; } byte byteFrom = (byte) this.from; byte byteTo = (byte) this.to; if (byteFrom <= byteTo) { return new Range(byteFrom, byteTo); } return BYTE_EVERYTHING; } /** Returns true if the element is contained in this range. */ public boolean contains(long element) { return from <= element && element <= to; } /** Returns true if the element is contained in this range. */ public boolean contains(Range other) { return from <= other.from && other.to <= to; } /** * Returns the smallest range that includes all values contained in either of the two ranges. We * call this the union of two ranges. * * @param right a range to union with this range * @return a range resulting from the union of the specified range and this range */ public Range union(Range right) { if (this.isNothing()) { return right; } else if (right.isNothing()) { return this; } long resultFrom = Math.min(from, right.from); long resultTo = Math.max(to, right.to); return new Range(resultFrom, resultTo); } /** * Returns the smallest range that includes all values contained in both of the two ranges. We * call this the intersection of two ranges. If there is no overlap between the two ranges, * returns an empty range. * * @param right the range to intersect with this range * @return a range resulting from the intersection of the specified range and this range */ public Range intersect(Range right) { if (this.isNothing() || right.isNothing()) { return NOTHING; } long resultFrom = Math.max(from, right.from); long resultTo = Math.min(to, right.to); return createRangeOrNothing(resultFrom, resultTo); } /** * Returns the smallest range that includes all possible values resulting from adding an * arbitrary value in the specified range to an arbitrary value in this range. We call this the * addition of two ranges. * * @param right a range to be added to this range * @return the range resulting from the addition of the specified range and this range */ public Range plus(Range right) { if (this.isNothing() || right.isNothing()) { return NOTHING; } if (this.isWithinHalfLong() && right.isWithinHalfLong()) { // This bound is adequate to guarantee no overflow when using long to evaluate long resultFrom = from + right.from; long resultTo = to + right.to; if (from > to) { return Range.EVERYTHING; } else { return new Range(resultFrom, resultTo); } } else { BigInteger bigFrom = BigInteger.valueOf(from).add(BigInteger.valueOf(right.from)); BigInteger bigTo = BigInteger.valueOf(to).add(BigInteger.valueOf(right.to)); return bigRangeToLongRange(bigFrom, bigTo); } } /** * Returns the smallest range that includes all possible values resulting from subtracting an * arbitrary value in the specified range from an arbitrary value in this range. We call this * the subtraction of two ranges. * * @param right the range to be subtracted from this range * @return the range resulting from subtracting the specified range from this range */ public Range minus(Range right) { if (this.isNothing() || right.isNothing()) { return NOTHING; } if (this.isWithinHalfLong() && right.isWithinHalfLong()) { // This bound is adequate to guarantee no overflow when using long to evaluate long resultFrom = from - right.to; long resultTo = to - right.from; return new Range(resultFrom, resultTo); } else { BigInteger bigFrom = BigInteger.valueOf(from).subtract(BigInteger.valueOf(right.to)); BigInteger bigTo = BigInteger.valueOf(to).subtract(BigInteger.valueOf(right.from)); return bigRangeToLongRange(bigFrom, bigTo); } } /** * Returns the smallest range that includes all possible values resulting from multiplying an * arbitrary value in the specified range by an arbitrary value in this range. We call this the * multiplication of two ranges. * * @param right the specified range to be multiplied by this range * @return the range resulting from multiplying the specified range by this range */ public Range times(Range right) { if (this.isNothing() || right.isNothing()) { return NOTHING; } // These bounds are adequate: Integer.MAX_VALUE^2 is still a bit less than Long.MAX_VALUE. if (this.isWithinInteger() && right.isWithinInteger()) { List possibleValues = Arrays.asList( from * right.from, from * right.to, to * right.from, to * right.to); return new Range(Collections.min(possibleValues), Collections.max(possibleValues)); } else { List bigPossibleValues = Arrays.asList( BigInteger.valueOf(from).multiply(BigInteger.valueOf(right.from)), BigInteger.valueOf(from).multiply(BigInteger.valueOf(right.to)), BigInteger.valueOf(to).multiply(BigInteger.valueOf(right.from)), BigInteger.valueOf(to).multiply(BigInteger.valueOf(right.to))); BigInteger bigFrom = Collections.min(bigPossibleValues); BigInteger bigTo = Collections.max(bigPossibleValues); return bigRangeToLongRange(bigFrom, bigTo); } } /** * Returns the smallest range that includes all possible values resulting from dividing (integer * division) an arbitrary value in this range by an arbitrary value in the specified range. We * call this the division of two ranges. * * @param right the specified range by which this range is divided * @return the range resulting from dividing this range by the specified range */ public Range divide(Range right) { if (this.isNothing() || right.isNothing()) { return NOTHING; } if (right.from == 0 && right.to == 0) { return NOTHING; } // Special cases that involve overflow. // The only overflow in integer division is Long.MIN_VALUE / -1 == Long.MIN_VALUE. if (from == Long.MIN_VALUE && right.contains(-1)) { // The values in the right range are all negative because right does not contain 0 but // does contain 1. if (from != to) { // Special case 1: // This range contains Long.MIN_VALUE and Long.MIN_VALUE + 1, which makes the // result range EVERYTHING. return EVERYTHING; } else if (right.from != right.to) { // Special case 2: // This range contains only Long.MIN_VALUE, and the right range contains at least -1 // and -2. The result range is from Long.MIN_VALUE to Long.MIN_VALUE / -2. return new Range(Long.MIN_VALUE, Long.MIN_VALUE / -2); } else { // Special case 3: // This range contains only Long.MIN_VALUE, and right contains only -1. return new Range(Long.MIN_VALUE, Long.MIN_VALUE); } } // We needn't worry about the overflow issue starting from here. // There are 9 different cases: // (note: pos=positive, neg=negative, unk=unknown sign, np=non-positive, nn=non-negative) long resultFrom; long resultTo; if (from > 0) { // this range is positive if (right.from >= 0) { // 1. right: nn resultFrom = from / Math.max(right.to, 1); resultTo = to / Math.max(right.from, 1); } else if (right.to <= 0) { // 2. right: np resultFrom = to / Math.min(right.to, -1); resultTo = from / Math.min(right.from, -1); } else { // 3. right: unk; values include -1 and 1 resultFrom = -to; resultTo = to; } } else if (to < 0) { // this range is negative if (right.from >= 0) { // 4. right: nn resultFrom = from / Math.max(right.from, 1); resultTo = to / Math.max(right.to, 1); } else if (right.to <= 0) { // 5. right: np resultFrom = to / Math.min(right.from, -1); resultTo = from / Math.min(right.to, -1); } else { // 6. right: unk; values include -1 and 1 resultFrom = from; resultTo = -from; } } else { // this range spans both signs if (right.from >= 0) { // 7. right: nn resultFrom = from / Math.max(right.from, 1); resultTo = to / Math.max(right.from, 1); } else if (right.to <= 0) { // 8. right: np resultFrom = to / Math.min(right.to, -1); resultTo = from / Math.min(right.to, -1); } else { // 9. right: unk; values include -1 and 1 resultFrom = Math.min(from, -to); resultTo = Math.max(-from, to); } } return new Range(resultFrom, resultTo); } /** * Returns a range that includes all possible values of the remainder of dividing an arbitrary * value in this range by an arbitrary value in the specified range. * *

In the current implementation, the result might not be the smallest range that includes * all the possible values. * * @param right the specified range by which this range is divided * @return the range of the remainder of dividing this range by the specified range */ public Range remainder(Range right) { if (this.isNothing() || right.isNothing()) { return NOTHING; } if (right.from == 0 && right.to == 0) { return NOTHING; } // Special cases that would cause overflow if we use the general method below if (right.from == Long.MIN_VALUE) { Range range; // The value Long.MIN_VALUE as a divisor needs special handling as follows: if (from == Long.MIN_VALUE) { if (to == Long.MIN_VALUE) { // This range only contains Long.MIN_VALUE, so the result range is {0}. range = new Range(0, 0); } else { // (to > Long.MIN_VALUE) // When this range contains Long.MIN_VALUE, which would have a remainder of 0 if // divided by Long.MIN_VALUE, the result range is {0} unioned with [from + 1, // to] range = new Range(from + 1, to).union(new Range(0, 0)); } } else { // (from > Long.MIN_VALUE) // When this range doesn't contain Long.MIN_VALUE, the remainder of each value // in this range divided by Long.MIN_VALUE is this value itself. Therefore the // result range is this range itself. range = this; } // If right.to > Long.MIN_VALUE, union the previous result with the result of range // [right.from + 1, right.to] divided by this range, which can be calculated using // the general method (see below) if (right.to > Long.MIN_VALUE) { Range rangeAdditional = this.remainder(new Range(right.from + 1, right.to)); range = range.union(rangeAdditional); } return range; } // General method: // Calculate range1: the result range of this range divided by EVERYTHING. For example, // if this range is [3, 5], then the result range would be [0, 5]. If this range is [-3, 4], // then the result range would be [-3, 4]. In general, the result range is {0} union with // this range excluding the value Long.MIN_VALUE. Range range1 = new Range(Math.max(Long.MIN_VALUE + 1, from), Math.max(Long.MIN_VALUE + 1, to)) .union(new Range(0, 0)); // Calculate range2: the result range of range EVERYTHING divided by the right range. For // example, if the right range is [-5, 3], then the result range would be [-4, 4]. If the // right range is [3, 6], then the result range would be [-5, 5]. In general, the result // range is calculated as following: long maxAbsolute = Math.max(Math.abs(right.from), Math.abs(right.to)); Range range2 = new Range(-maxAbsolute + 1, maxAbsolute - 1); // Since range1 and range2 are both super sets of the minimal result range, we return the // intersection of range1 and range2, which is correct (super set) and precise enough. return range1.intersect(range2); } /** * Returns a range that includes all possible values resulting from left shifting an arbitrary * value in this range by an arbitrary number of bits in the specified range. We call this the * left shift of a range. * * @param right the range of bits by which this range is left shifted * @return the range resulting from left shifting this range by the specified range */ public Range shiftLeft(Range right) { if (this.isNothing() || right.isNothing()) { return NOTHING; } // Shifting operations in Java are depending on the type of the left-hand operand: // If the left-hand operand is int type, only the 5 lowest-order bits of the right-hand // operand are used. // If the left-hand operand is long type, only the 6 lowest-order bits of the right-hand // operand are used. // For example, while 1 << -1== 1 << 31, 1L << -1 == 1L << 63. // For ths reason, we restrict the shift-bits to analyze in [0. 31] and give up the analysis // when out of this range. // // Other possible solutions: // 1. create different methods for int type and long type and use them accordingly // 2. add an additional boolean parameter to indicate the type of the left-hand operand // // see https://docs.oracle.com/javase/specs/jls/se10/html/jls-15.html#jls-15.19 for more // detail. if (right.isWithin(0, 31)) { if (this.isWithinInteger()) { // This bound is adequate to guarantee no overflow when using long to evaluate long resultFrom = from << (from >= 0 ? right.from : right.to); long resultTo = to << (to >= 0 ? right.to : right.from); return new Range(resultFrom, resultTo); } else { BigInteger bigFrom = BigInteger.valueOf(from) .shiftLeft(from >= 0 ? (int) right.from : (int) right.to); BigInteger bigTo = BigInteger.valueOf(to) .shiftLeft(to >= 0 ? (int) right.to : (int) right.from); return bigRangeToLongRange(bigFrom, bigTo); } } else { // In other cases, we give up on the calculation and return EVERYTHING (rare in // practice). return EVERYTHING; } } /** * Returns a range that includes all possible values resulting from signed right shifting an * arbitrary value in this range by an arbitrary number of bits in the specified range. We call * this the signed right shift operation of a range. * * @param right the range of bits by which this range is signed right shifted * @return the range resulting from signed right shifting this range by the specified range */ public Range signedShiftRight(Range right) { if (this.isNothing() || right.isNothing()) { return NOTHING; } if (this.isWithinInteger() && right.isWithin(0, 31)) { // This bound is adequate to guarantee no overflow when using long to evaluate long resultFrom = from >> (from >= 0 ? right.to : right.from); long resultTo = to >> (to >= 0 ? right.from : right.to); return new Range(resultFrom, resultTo); } else { // Signed shift right operation for long type cannot be simulated with BigInteger. // Give up on the calculation and return EVERYTHING instead. return EVERYTHING; } } /** * When this range only contains non-negative values, the refined result should be the same as * {@link #signedShiftRight(Range)}. We give up the analysis when this range contains negative * value(s). */ public Range unsignedShiftRight(Range right) { if (this.from >= 0) { return signedShiftRight(right); } if (this.isNothing() || right.isNothing()) { return NOTHING; } return EVERYTHING; } /** * Returns a range that includes all possible values resulting from performing the bitwise and * operation on a value in this range by a mask in the specified range. We call this the bitwise * and operation of a range. * *

The current implementation is conservative: it only refines the cases where the range of * mask represents a constant. In other cases, it gives up on the refinement and returns {@code * EVERYTHING} instead. * * @param right the range of mask of the bitwise and operation * @return the range resulting from the bitwise and operation of this range and the specified * range of mask */ public Range bitwiseAnd(Range right) { if (this.isNothing() || right.isNothing()) { return NOTHING; } // We only refine the cases where the range of mask represent a constant. // Recall these two's-complement facts: // 11111111 represents -1 // 10000000 represents MIN_VALUE Range constant = null; Range variable = null; if (right.isConstant()) { constant = right; variable = this; } else if (this.isConstant()) { constant = this; variable = right; } if (constant != null) { long mask = constant.from; if (mask >= 0) { // Sign bit of mask is 0. The elements in the result range must be positive, and // the result range is upper-bounded by the mask. if (variable.from >= 0) { // Case 1.1: The result range is upper-bounded by the upper bound of this range. return new Range(0, Math.min(mask, variable.to)); } else if (variable.to < 0) { // Case 1.2: The result range is upper-bounded by the upper bound of this range // after ignoring the sign bit. The upper bound of this range has the most bits // (of the highest place values) set to 1. return new Range(0, Math.min(mask, noSignBit(variable.to))); } else { // Case 1.3: Since this range contains -1, the upper bound of this range after // ignoring the sign bit is Long.MAX_VALUE and thus doesn't contribute to // further refinement. return new Range(0, mask); } } else { // Sign bit of mask is 1. if (variable.from >= 0) { // Case 2.1: Similar to case 1.1 except that the sign bit of the mask can be // ignored. return new Range(0, Math.min(noSignBit(mask), variable.to)); } else if (variable.to < 0) { // Case 2.2: The sign bit of the elements in the result range must be 1. // Therefore the lower bound of the result range is Long.MIN_VALUE (when all // 1-bits are mismatched between the mask and the element in this range). The // result range is also upper-bounded by this mask itself and the upper bound of // this range. (Because more set bits means a larger number -- still negative, // but closer to 0.) return new Range(Long.MIN_VALUE, Math.min(mask, variable.to)); } else { // Case 2.3: Similar to case 2.2 except that the elements in this range could // be positive, and thus the result range is upper-bounded by the upper bound // of this range and the mask after ignoring the sign bit. return new Range(Long.MIN_VALUE, Math.min(noSignBit(mask), variable.to)); } } } return EVERYTHING; } /** Return the argument, with its sign bit zeroed out. */ private long noSignBit(Long mask) { return mask & (-1L >>> 1); } /** We give up the analysis for bitwise OR operation. */ public Range bitwiseOr(Range right) { if (this.isNothing() || right.isNothing()) { return NOTHING; } return EVERYTHING; } /** We give up the analysis for bitwise XOR operation. */ public Range bitwiseXor(Range right) { if (this.isNothing() || right.isNothing()) { return NOTHING; } return EVERYTHING; } /** * Returns the range of a variable that falls within this range after applying the unary plus * operation (which is a no-op). * * @return this range */ public Range unaryPlus() { return this; } /** * Returns the range of a variable that falls within this range after applying the unary minus * operation. * * @return the resulted range of applying unary minus on an arbitrary value in this range */ public Range unaryMinus() { if (this.isNothing()) { return NOTHING; } if (from == Long.MIN_VALUE && from != to) { // the only case that needs special handling because of overflow return EVERYTHING; } return new Range(-to, -from); } /** * Returns the range of a variable that falls within this range after applying the bitwise * complement operation. * * @return the resulting range of applying bitwise complement on an arbitrary value in this * range */ public Range bitwiseComplement() { if (this.isNothing()) { return NOTHING; } return new Range(~to, ~from); } /** * Refines this range to reflect that some value in it can be less than a value in the given * range. This is used for calculating the control-flow-refined result of the < operator. For * example: * *

     * 
     *    {@literal @}IntRange(from = 0, to = 10) int a;
     *    {@literal @}IntRange(from = 3, to = 7) int b;
     *     ...
     *     if (a < b) {
     *         // range of a is now refined to [0, 6] because a value in range [7, 10]
     *         // cannot be smaller than variable b with range [3, 7].
     *         ...
     *     }
     * 
     * 
* * Use the {@link #refineGreaterThanEq(Range)} method if you are also interested in refining the * range of {@code b} in the code above. * * @param right the specified {@code Range} to compare with * @return the refined {@code Range} */ public Range refineLessThan(Range right) { if (this.isNothing() || right.isNothing()) { return NOTHING; } if (right.to == Long.MIN_VALUE) { return NOTHING; } long resultTo = Math.min(to, right.to - 1); return createRangeOrNothing(from, resultTo); } /** * Refines this range to reflect that some value in it can be less than or equal to a value in * the given range. This is used for calculating the control-flow-refined result of the <= * operator. For example: * *
     * 
     *    {@literal @}IntRange(from = 0, to = 10) int a;
     *    {@literal @}IntRange(from = 3, to = 7) int b;
     *     ...
     *     if (a <= b) {
     *         // range of a is now refined to [0, 7] because a value in range [8, 10]
     *         // cannot be less than or equal to variable b with range [3, 7].
     *         ...
     *     }
     * 
     * 
* * Use the {@link #refineGreaterThan(Range)} method if you are also interested in refining the * range of {@code b} in the code above. * * @param right the specified {@code Range} to compare with * @return the refined {@code Range} */ public Range refineLessThanEq(Range right) { if (this.isNothing() || right.isNothing()) { return NOTHING; } long resultTo = Math.min(to, right.to); return createRangeOrNothing(from, resultTo); } /** * Refines this range to reflect that some value in it can be greater than a value in the given * range. This is used for calculating the control-flow-refined result of the > operator. For * example: * *
     * 
     *    {@literal @}IntRange(from = 0, to = 10) int a;
     *    {@literal @}IntRange(from = 3, to = 7) int b;
     *     ...
     *     if (a > b) {
     *         // range of a is now refined to [4, 10] because a value in range [0, 3]
     *         // cannot be greater than variable b with range [3, 7].
     *         ...
     *     }
     * 
     * 
* * Use the {@link #refineLessThanEq(Range)} method if you are also interested in refining the * range of {@code b} in the code above. * * @param right the specified {@code Range} to compare with * @return the refined {@code Range} */ public Range refineGreaterThan(Range right) { if (this.isNothing() || right.isNothing()) { return NOTHING; } if (right.from == Long.MAX_VALUE) { return NOTHING; } long resultFrom = Math.max(from, right.from + 1); return createRangeOrNothing(resultFrom, to); } /** * Refines this range to reflect that some value in it can be greater than or equal to a value * in the given range. This is used for calculating the control-flow-refined result of the >= * operator. For example: * *
     * 
     *    {@literal @}IntRange(from = 0, to = 10) int a;
     *    {@literal @}IntRange(from = 3, to = 7) int b;
     *     ...
     *     if (a >= b) {
     *         // range of a is now refined to [3, 10] because a value in range [0, 2]
     *         // cannot be greater than or equal to variable b with range [3, 7].
     *         ...
     *     }
     * 
     * 
* * Use the {@link #refineLessThan(Range)} method if you are also interested in refining the * range of {@code b} in the code above. * * @param right the specified {@code Range} to compare with * @return the refined {@code Range} */ public Range refineGreaterThanEq(Range right) { if (this.isNothing() || right.isNothing()) { return NOTHING; } long resultFrom = Math.max(from, right.from); return createRangeOrNothing(resultFrom, to); } /** * Refines this range to reflect that some value in it can be equal to a value in the given * range. This is used for calculating the control-flow-refined result of the == operator. For * example: * *
     * 
     *    {@literal @}IntRange(from = 0, to = 10) int a;
     *    {@literal @}IntRange(from = 3, to = 15) int b;
     *     ...
     *     if (a == b) {
     *         // range of a is now refined to [3, 10] because a value in range [0, 2]
     *         // cannot be equal to variable b with range [3, 15].
     *         ...
     *     }
     * 
     * 
* * @param right the specified {@code Range} to compare with * @return the refined {@code Range} */ public Range refineEqualTo(Range right) { return this.intersect(right); } /** * Refines this range to reflect that some value in it must not be equal to a value in the given * range. This only changes the range if the given range (right) contains exactly one integer, * and that integer is one of the bounds of this range. This is used for calculating the * control-flow-refined result of the != operator. For example: * *
     * 
     *    {@literal @}IntRange(from = 0, to = 10) int a;
     *    {@literal @}IntRange(from = 0, to = 0) int b;
     *     ...
     *     if (a != b) {
     *         // range of a is now refined to [1, 10] because it cannot
     *         // be zero.
     *         ...
     *     }
     * 
     * 
* * @param right the specified {@code Range} to compare with * @return the refined {@code Range} */ public Range refineNotEqualTo(Range right) { if (right.isConstant()) { if (this.to == right.to) { return new Range(this.from, this.to - 1); } else if (this.from == right.from) { return new Range(this.from + 1, this.to); } } return this; } /** * Determines if the range is wider than a given value, i.e., if the number of possible values * enclosed by this range is more than the given value. * * @param value the value to compare with * @return true if wider than the given value */ public boolean isWiderThan(long value) { if (this.isWithin((Long.MIN_VALUE >> 1) + 1, Long.MAX_VALUE >> 1)) { // This bound is adequate to guarantee no overflow when using long to evaluate. // Long.MIN_VALUE >> 1 + 1 = -4611686018427387903 // Long.MAX_VALUE >> 1 = 4611686018427387903 return to - from + 1 > value; } else { return BigInteger.valueOf(to) .subtract(BigInteger.valueOf(from)) .add(BigInteger.ONE) .compareTo(BigInteger.valueOf(value)) > 0; } } /** Determines if this range represents a constant value. */ public boolean isConstant() { return from == to; } /** * Determines if this range is completely contained in the range specified by the given lower * bound and upper bound. */ public boolean isWithin(long lb, long ub) { return from >= lb && to <= ub; } /** * Determines if this range is completely contained in the range that is of half length of the * Long type and centered with 0. */ private boolean isWithinHalfLong() { return isWithin(Long.MIN_VALUE >> 1, Long.MAX_VALUE >> 1); } /** Determines if this range is completely contained in the scope of the Integer type. */ private boolean isWithinInteger() { return isWithin(Integer.MIN_VALUE, Integer.MAX_VALUE); } private static final BigInteger longWidth = BigInteger.valueOf(Long.MAX_VALUE) .subtract(BigInteger.valueOf(Long.MIN_VALUE)) .add(BigInteger.ONE); /** * Converts a range with BigInteger type bounds to a range with Long type bounds. * *

If the BigInteger range is too wide, i.e., wider than the full range of the Long class, * return EVERYTHING. * *

If one of the BigInteger bounds is out of Long's range and {@link #ignoreOverflow} is * false, convert the bounds to Long type in accordance with Java twos-complement overflow * rules, e.g., Long.MAX_VALUE + 1 is converted to Long.MIN_VALUE. * *

If one of the BigInteger bounds is out of Long's range and {@link #ignoreOverflow} is * true, convert the bound that is outside Long's range to max/min value of a Long. * * @param bigFrom the lower bound of the BigInteger range * @param bigTo the upper bound of the BigInteger range * @return a range with Long type bounds converted from the BigInteger range */ private Range bigRangeToLongRange(BigInteger bigFrom, BigInteger bigTo) { BigInteger numValues = bigTo.subtract(bigFrom).add(BigInteger.ONE); long resultFrom; long resultTo; if (ignoreOverflow) { BigInteger longMin = BigInteger.valueOf(Long.MIN_VALUE); resultFrom = bigFrom.max(longMin).longValue(); BigInteger longMax = BigInteger.valueOf(Long.MAX_VALUE); resultTo = bigTo.min(longMax).longValue(); } else { if (numValues.compareTo(longWidth) > 0) { return EVERYTHING; } else { resultFrom = bigFrom.longValue(); resultTo = bigTo.longValue(); } } if (resultFrom <= resultTo) { return new Range(resultFrom, resultTo); } else { return EVERYTHING; } } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy