mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
IDEA-188306 More math operations on DFA (div, shr, ushr)
This commit is contained in:
+12
-35
@@ -602,46 +602,23 @@ public class StandardInstructionVisitor extends InstructionVisitor {
|
||||
}
|
||||
}
|
||||
DfaValue result = null;
|
||||
if (JavaTokenType.AND == opSign) {
|
||||
PsiElement expr = instruction.getPsiAnchor();
|
||||
PsiType type = expr instanceof PsiExpression ? ((PsiExpression)expr).getType() : null;
|
||||
if (PsiType.INT.equals(type) || PsiType.LONG.equals(type)) {
|
||||
LongRangeSet left = memState.getValueFact(dfaLeft, DfaFactType.RANGE);
|
||||
LongRangeSet right = memState.getValueFact(dfaRight, DfaFactType.RANGE);
|
||||
if(left != null && right != null) {
|
||||
result = runner.getFactory().getFactValue(DfaFactType.RANGE, left.bitwiseAnd(right));
|
||||
}
|
||||
}
|
||||
else if (JavaTokenType.PERC == opSign) {
|
||||
LongRangeSet left = memState.getValueFact(dfaLeft, DfaFactType.RANGE);
|
||||
LongRangeSet right = memState.getValueFact(dfaRight, DfaFactType.RANGE);
|
||||
if(left != null && right != null) {
|
||||
result = runner.getFactory().getFactValue(DfaFactType.RANGE, left.mod(right));
|
||||
}
|
||||
}
|
||||
else if (JavaTokenType.PLUS == opSign) {
|
||||
PsiElement expr = instruction.getPsiAnchor();
|
||||
PsiType type = expr instanceof PsiExpression ? ((PsiExpression)expr).getType() : null;
|
||||
if(PsiType.INT.equals(type) || PsiType.LONG.equals(type)) {
|
||||
LongRangeSet left = memState.getValueFact(dfaLeft, DfaFactType.RANGE);
|
||||
LongRangeSet right = memState.getValueFact(dfaRight, DfaFactType.RANGE);
|
||||
if(left != null && right != null) {
|
||||
result = runner.getFactory().getFactValue(DfaFactType.RANGE, left.plus(right, PsiType.LONG.equals(type)));
|
||||
if (left != null && right != null) {
|
||||
LongRangeSet resultRange = left.binOpFromToken(opSign, right, PsiType.LONG.equals(type));
|
||||
if (resultRange != null) {
|
||||
result = runner.getFactory().getFactValue(DfaFactType.RANGE, resultRange);
|
||||
}
|
||||
} else {
|
||||
}
|
||||
}
|
||||
if (result == null) {
|
||||
if (JavaTokenType.PLUS == opSign) {
|
||||
result = instruction.getNonNullStringValue(runner.getFactory());
|
||||
}
|
||||
}
|
||||
else if (JavaTokenType.MINUS == opSign) {
|
||||
PsiElement expr = instruction.getPsiAnchor();
|
||||
PsiType type = expr instanceof PsiExpression ? ((PsiExpression)expr).getType() : null;
|
||||
if (PsiType.INT.equals(type) || PsiType.LONG.equals(type)) {
|
||||
LongRangeSet left = memState.getValueFact(dfaLeft, DfaFactType.RANGE);
|
||||
LongRangeSet right = memState.getValueFact(dfaRight, DfaFactType.RANGE);
|
||||
if (left != null && right != null) {
|
||||
result = runner.getFactory().getFactValue(DfaFactType.RANGE, left.minus(right, PsiType.LONG.equals(type)));
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (instruction instanceof InstanceofInstruction) {
|
||||
else if (instruction instanceof InstanceofInstruction) {
|
||||
handleInstanceof((InstanceofInstruction)instruction, dfaRight, dfaLeft);
|
||||
}
|
||||
}
|
||||
|
||||
+2
-1
@@ -33,7 +33,8 @@ import org.jetbrains.annotations.Nullable;
|
||||
import static com.intellij.psi.JavaTokenType.*;
|
||||
|
||||
public class BinopInstruction extends BranchingInstruction {
|
||||
private static final TokenSet ourSignificantOperations = TokenSet.create(EQEQ, NE, LT, GT, LE, GE, INSTANCEOF_KEYWORD, PLUS, MINUS, AND, PERC);
|
||||
private static final TokenSet ourSignificantOperations =
|
||||
TokenSet.create(EQEQ, NE, LT, GT, LE, GE, INSTANCEOF_KEYWORD, PLUS, MINUS, AND, PERC, DIV, GTGT, GTGTGT);
|
||||
private final IElementType myOperationSign;
|
||||
private final Project myProject;
|
||||
|
||||
|
||||
+161
-5
@@ -4,11 +4,10 @@ package com.intellij.codeInspection.dataFlow.rangeSet;
|
||||
import com.intellij.codeInsight.AnnotationUtil;
|
||||
import com.intellij.codeInspection.dataFlow.DfaFactType;
|
||||
import com.intellij.codeInspection.dataFlow.value.*;
|
||||
import com.intellij.psi.PsiAnnotation;
|
||||
import com.intellij.psi.PsiModifierListOwner;
|
||||
import com.intellij.psi.PsiPrimitiveType;
|
||||
import com.intellij.psi.PsiType;
|
||||
import com.intellij.psi.*;
|
||||
import com.intellij.psi.tree.IElementType;
|
||||
import com.intellij.util.ThreeState;
|
||||
import org.jetbrains.annotations.Contract;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
@@ -139,6 +138,43 @@ public abstract class LongRangeSet {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Performs a supported binary operation from token (defined in {@link JavaTokenType}).
|
||||
*
|
||||
* @param token a token which corresponds to the operation
|
||||
* @param right a right-hand operand
|
||||
* @param isLong true if operation should be performed on long types (otherwise int is assumed)
|
||||
* @return the resulting LongRangeSet which covers possible results of the operation (probably including some more elements);
|
||||
* or null if the supplied token is not supported.
|
||||
*/
|
||||
@Contract("null, _, _ -> null")
|
||||
@Nullable
|
||||
public final LongRangeSet binOpFromToken(IElementType token, LongRangeSet right, boolean isLong) {
|
||||
if (token == null) return null;
|
||||
if (token.equals(JavaTokenType.PLUS)) {
|
||||
return plus(right, isLong);
|
||||
}
|
||||
if (token.equals(JavaTokenType.MINUS)) {
|
||||
return minus(right, isLong);
|
||||
}
|
||||
if (token.equals(JavaTokenType.AND)) {
|
||||
return bitwiseAnd(right);
|
||||
}
|
||||
if (token.equals(JavaTokenType.PERC)) {
|
||||
return mod(right);
|
||||
}
|
||||
if (token.equals(JavaTokenType.DIV)) {
|
||||
return div(right, isLong);
|
||||
}
|
||||
if (token.equals(JavaTokenType.GTGT)) {
|
||||
return shiftRight(right, isLong);
|
||||
}
|
||||
if (token.equals(JavaTokenType.GTGTGT)) {
|
||||
return unsignedShiftRight(right, isLong);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a range which represents all the possible values after applying {@link Math#abs(int)} or {@link Math#abs(long)}
|
||||
* to the values from this set
|
||||
@@ -211,8 +247,128 @@ public abstract class LongRangeSet {
|
||||
return all().subtract(result);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a range which represents all the possible values after applying {@code x / y} operation for
|
||||
* all {@code x} from this set and for all {@code y} from the divisor set. The resulting set may contain
|
||||
* some more values. Division by zero yields an empty set of possible results.
|
||||
*
|
||||
* @param divisor divisor set to divide by
|
||||
* @param isLong whether the operation is performed on long type (if false, the int type is assumed). This only changes the special
|
||||
* treatment of {@code MIN_VALUE/-1} division; other division results do not depend on the resulting type.
|
||||
* @return a new range
|
||||
*/
|
||||
@NotNull
|
||||
abstract public LongRangeSet mod(LongRangeSet other);
|
||||
public LongRangeSet div(LongRangeSet divisor, boolean isLong) {
|
||||
if (divisor.isEmpty() || divisor.equals(new Point(0))) return empty();
|
||||
long[] left = splitAtZero(asRanges());
|
||||
long[] right = splitAtZero(new long[]{divisor.min(), divisor.max()});
|
||||
LongRangeSet result = empty();
|
||||
for (int i = 0; i < left.length; i += 2) {
|
||||
for (int j = 0; j < right.length; j += 2) {
|
||||
result = result.union(divide(left[i], left[i + 1], right[j], right[j + 1], isLong));
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private static LongRangeSet divide(long dividendMin, long dividendMax, long divisorMin, long divisorMax, boolean isLong) {
|
||||
if (divisorMin == 0) {
|
||||
if (divisorMax == 0) return empty();
|
||||
divisorMin = 1;
|
||||
}
|
||||
if (dividendMin >= 0) {
|
||||
return divisorMin > 0
|
||||
? range(dividendMin / divisorMax, dividendMax / divisorMin)
|
||||
: range(dividendMax / divisorMax, dividendMin / divisorMin);
|
||||
}
|
||||
if (divisorMin > 0) {
|
||||
return range(dividendMin / divisorMin, dividendMax / divisorMax);
|
||||
}
|
||||
long minValue = minValue(isLong);
|
||||
if (dividendMin == minValue && divisorMax == -1) {
|
||||
// MIN_VALUE/-1 = MIN_VALUE
|
||||
return point(minValue)
|
||||
.union(divisorMin == -1 ? empty() : range(dividendMin / divisorMin, dividendMin / (divisorMax - 1)))
|
||||
.union(dividendMax == minValue ? empty() : range(dividendMax / divisorMin, (dividendMin + 1) / divisorMax));
|
||||
}
|
||||
return range(dividendMax / divisorMin, dividendMin / divisorMax);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a range which represents all the possible values after applying {@code x >> y} operation for
|
||||
* all {@code x} from this set and for all {@code y} from the shiftSize set. The resulting set may contain
|
||||
* some more values.
|
||||
*
|
||||
* @param shiftSize set of possible shift sizes (number of bits to shift to the right)
|
||||
* @param isLong whether the operation is performed on long type (if false, the int type is assumed).
|
||||
* @return a new range
|
||||
*/
|
||||
@NotNull
|
||||
public LongRangeSet shiftRight(LongRangeSet shiftSize, boolean isLong) {
|
||||
if (isEmpty() || shiftSize.isEmpty()) return empty();
|
||||
int maxShift = (isLong ? Long.SIZE : Integer.SIZE) - 1;
|
||||
if (shiftSize.min() < 0 || shiftSize.max() > maxShift) {
|
||||
shiftSize = shiftSize.bitwiseAnd(point(maxShift));
|
||||
}
|
||||
long min = shiftSize.min();
|
||||
long max = shiftSize.max();
|
||||
LongRangeSet negative = intersect(range(minValue(isLong), -1));
|
||||
LongRangeSet positive = intersect(range(0, maxValue(isLong)));
|
||||
return positive.shrPositive(min, max, isLong)
|
||||
.union(point(-1).minus(point(-1).minus(negative, isLong).shrPositive(min, max, isLong), isLong));
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a range which represents all the possible values after applying {@code x >>> y} operation for
|
||||
* all {@code x} from this set and for all {@code y} from the shiftSize set. The resulting set may contain
|
||||
* some more values.
|
||||
*
|
||||
* @param shiftSize set of possible shift sizes (number of bits to shift to the right)
|
||||
* @param isLong whether the operation is performed on long type (if false, the int type is assumed).
|
||||
* @return a new range
|
||||
*/
|
||||
@NotNull
|
||||
public LongRangeSet unsignedShiftRight(LongRangeSet shiftSize, boolean isLong) {
|
||||
if (isEmpty() || shiftSize.isEmpty()) return empty();
|
||||
int maxShift = (isLong ? Long.SIZE : Integer.SIZE) - 1;
|
||||
if (shiftSize.min() < 0 || shiftSize.max() > maxShift) {
|
||||
shiftSize = shiftSize.bitwiseAnd(point(maxShift));
|
||||
}
|
||||
long min = shiftSize.min();
|
||||
long max = shiftSize.max();
|
||||
LongRangeSet negative = intersect(range(minValue(isLong), -1));
|
||||
LongRangeSet positive = intersect(range(0, maxValue(isLong)));
|
||||
LongRangeSet result = positive.shrPositive(min, max, isLong);
|
||||
if (min == 0) {
|
||||
result = result.union(negative);
|
||||
if (max == 0) return result;
|
||||
min++;
|
||||
}
|
||||
// for x < 0, y > 0, x >>> y = (MAX_VALUE - ((-1-x) >> 1)) >> (y-1)
|
||||
return result.union(point(maxValue(isLong)).minus(point(-1).minus(negative, isLong).shrPositive(1, 1, isLong), isLong)
|
||||
.shrPositive(min - 1, max - 1, isLong));
|
||||
}
|
||||
|
||||
private LongRangeSet shrPositive(long min, long max, boolean isLong) {
|
||||
if (isEmpty()) return empty();
|
||||
int maxShift = (isLong ? Long.SIZE : Integer.SIZE) - 1;
|
||||
if (max == maxShift) {
|
||||
return min == max ? point(0) : point(0).union(div(range(1L << min, 1L << (max - 1)), isLong));
|
||||
}
|
||||
return div(range(1L << min, 1L << max), isLong);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a range which represents all the possible values after applying {@code x % y} operation for
|
||||
* all {@code x} from this set and for all {@code y} from the divisor set. The resulting set may contain
|
||||
* some more values. Division by zero yields an empty set of possible results.
|
||||
*
|
||||
* @param divisor divisor set to divide by
|
||||
* @return a new range
|
||||
*/
|
||||
@NotNull
|
||||
abstract public LongRangeSet mod(LongRangeSet divisor);
|
||||
|
||||
private static long[] splitAtZero(long[] ranges) {
|
||||
for (int i = 0; i < ranges.length; i += 2) {
|
||||
|
||||
@@ -0,0 +1,39 @@
|
||||
import java.util.*;
|
||||
|
||||
public class LongRangeDivShift {
|
||||
void test(int[] arr, int x) {
|
||||
if(<warning descr="Condition 'arr.length / 2 < 0' is always 'false'">arr.length / 2 < 0</warning>) {
|
||||
System.out.println("Impossible");
|
||||
}
|
||||
}
|
||||
|
||||
void signTest(int x, int y) {
|
||||
if(<warning descr="Condition 'x > 0 && y < 0 && x/y > 0' is always 'false'">x > 0 && y < 0 && <warning descr="Condition 'x/y > 0' is always 'false'">x/y > 0</warning></warning>) {
|
||||
System.out.println("Impossible");
|
||||
}
|
||||
}
|
||||
|
||||
void shift(long x) {
|
||||
long a = x >> 32;
|
||||
if(<warning descr="Condition 'a < Integer.MIN_VALUE || a > Integer.MAX_VALUE' is always 'false'"><warning descr="Condition 'a < Integer.MIN_VALUE' is always 'false'">a < Integer.MIN_VALUE</warning> || <warning descr="Condition 'a > Integer.MAX_VALUE' is always 'false' when reached">a > Integer.MAX_VALUE</warning></warning>) {
|
||||
System.out.println("Impossible");
|
||||
}
|
||||
}
|
||||
|
||||
void shiftUnsigned(int x) {
|
||||
x = x >>> 16;
|
||||
if(<warning descr="Condition 'x >= 0 && x <= 0xFFFF' is always 'true'"><warning descr="Condition 'x >= 0' is always 'true'">x >= 0</warning> && <warning descr="Condition 'x <= 0xFFFF' is always 'true' when reached">x <= 0xFFFF</warning></warning>) {
|
||||
char c = (char)x;
|
||||
}
|
||||
}
|
||||
|
||||
static final int RESIZE_STAMP_SHIFT = 16;
|
||||
static final int MAX_RESIZERS = 65535;
|
||||
|
||||
void testCHM(int sc, int rs) {
|
||||
if (sc < 0) {
|
||||
if ((sc >>> RESIZE_STAMP_SHIFT) != rs || <warning descr="Condition 'sc == rs + 1' is always 'false'">sc == rs + 1</warning> ||
|
||||
<warning descr="Condition 'sc == rs + MAX_RESIZERS' is always 'false'">sc == rs + MAX_RESIZERS</warning>) {}
|
||||
}
|
||||
}
|
||||
}
|
||||
+1
@@ -61,6 +61,7 @@ public class DataFlowRangeAnalysisTest extends DataFlowInspectionTestCase {
|
||||
}
|
||||
|
||||
public void testLongRangeMod() { doTest(); }
|
||||
public void testLongRangeDivShift() { doTest(); }
|
||||
|
||||
public void testLongRangePlusMinus() { doTest(); }
|
||||
public void testFebruary31() { doTest(); }
|
||||
|
||||
+85
-5
@@ -341,6 +341,70 @@ public class LongRangeSetTest {
|
||||
checkMod(range(Long.MAX_VALUE - 3, Long.MAX_VALUE), point(Long.MAX_VALUE), "{0..Long.MAX_VALUE-1}");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDiv() {
|
||||
assertEquals(empty(), empty().div(all(), true));
|
||||
assertEquals(empty(), all().div(empty(), true));
|
||||
assertEquals(empty(), point(1).div(empty(), true));
|
||||
assertEquals(empty(), point(1).div(point(3), true).div(empty(), true));
|
||||
assertEquals(all(), all().div(all(), true));
|
||||
assertEquals(empty(), all().div(point(0), true));
|
||||
assertEquals(all(), all().div(point(1), true));
|
||||
assertEquals(all(), all().div(point(-1), true));
|
||||
assertEquals(point(11), point(110).div(point(10), true));
|
||||
|
||||
checkDiv(range(1, 20), range(1, 5), true, "{0..20}");
|
||||
checkDiv(range(1, 20), range(-5, -1), true, "{-20..0}");
|
||||
checkDiv(range(-20, -1), range(1, 5), true, "{-20..0}");
|
||||
checkDiv(range(-20, -1), range(-5, -1), true, "{0..20}");
|
||||
checkDiv(range(-10, 10), range(2, 4), true, "{-5..5}");
|
||||
checkDiv(range(100, 120), range(-2, 2), true, "{-120..-50, 50..120}");
|
||||
checkDiv(range(Integer.MIN_VALUE, Integer.MIN_VALUE + 20), range(-2, 2), true,
|
||||
"{Integer.MIN_VALUE..-1073741814, 1073741814..2147483648}");
|
||||
checkDiv(range(Integer.MIN_VALUE, Integer.MIN_VALUE + 20), range(-2, 2), false,
|
||||
"{Integer.MIN_VALUE..-1073741814, 1073741814..Integer.MAX_VALUE}");
|
||||
checkDiv(range(Integer.MIN_VALUE, Integer.MIN_VALUE + 20), range(-2, -1), true,
|
||||
"{1073741814..2147483648}");
|
||||
checkDiv(range(Integer.MIN_VALUE, Integer.MIN_VALUE + 20), range(-2, -1), false,
|
||||
"{Integer.MIN_VALUE, 1073741814..Integer.MAX_VALUE}");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testShr() {
|
||||
assertEquals(empty(), empty().shiftRight(all(), true));
|
||||
assertEquals(empty(), all().shiftRight(empty(), true));
|
||||
assertEquals(all(), all().shiftRight(all(), true));
|
||||
assertEquals(fromType(PsiType.INT), all().shiftRight(point(32), true));
|
||||
assertEquals(fromType(PsiType.SHORT), fromType(PsiType.INT).shiftRight(point(16), false));
|
||||
assertEquals(fromType(PsiType.BYTE), fromType(PsiType.INT).shiftRight(point(24), false));
|
||||
assertEquals(range(-1, 0), fromType(PsiType.INT).shiftRight(point(31), false));
|
||||
|
||||
checkShr(range(-20, 20), point(31), false, "{-1, 0}");
|
||||
checkShr(range(-20, 20), point(31), true, "{-1, 0}");
|
||||
checkShr(range(-20, 20), range(1, 3), true, "{-10..10}");
|
||||
checkShr(range(-20, 20), range(3, 5), true, "{-3..2}");
|
||||
checkShr(range(1000000, 1000020), range(3, 5), true, "{31250..125002}");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testUShr() {
|
||||
assertEquals(empty(), empty().unsignedShiftRight(all(), true));
|
||||
assertEquals(empty(), all().unsignedShiftRight(empty(), true));
|
||||
assertEquals(all(), all().unsignedShiftRight(all(), true));
|
||||
assertEquals(range(0, 4294967295L), all().unsignedShiftRight(point(32), true));
|
||||
assertEquals(fromType(PsiType.CHAR), fromType(PsiType.INT).unsignedShiftRight(point(16), false));
|
||||
assertEquals(range(0, 255), fromType(PsiType.INT).unsignedShiftRight(point(24), false));
|
||||
assertEquals(range(0, 1), fromType(PsiType.INT).unsignedShiftRight(point(31), false));
|
||||
|
||||
checkUShr(range(-20, 20), point(31), false, "{0, 1}");
|
||||
checkUShr(range(-20, 20), point(31), true, "{0, 8589934591}");
|
||||
checkUShr(range(-20, 20), range(1, 3), true, "{0..10, 2305843009213693949..Long.MAX_VALUE}");
|
||||
checkUShr(range(-20, 20), range(1, 3), false, "{0..10, 536870909..Integer.MAX_VALUE}");
|
||||
checkUShr(range(-20, 20), range(3, 5), true, "{0..2, 576460752303423487..2305843009213693951}");
|
||||
checkUShr(range(-20, 20), range(3, 5), false, "{0..2, 134217727..536870911}");
|
||||
checkUShr(range(1000000, 1000020), range(3, 5), true, "{31250..125002}");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testContains() {
|
||||
assertTrue(range(0, 10).contains(5));
|
||||
@@ -385,18 +449,33 @@ public class LongRangeSetTest {
|
||||
void checkAdd(LongRangeSet addend1, LongRangeSet addend2, boolean isLong, String expected) {
|
||||
LongRangeSet result = addend1.plus(addend2, isLong);
|
||||
assertEquals(result, addend2.plus(addend1, isLong)); // commutative
|
||||
checkBinOp(addend1, addend2, result, x -> true, isLong ? Long::sum : (a, b) -> (int)(a + b), expected);
|
||||
checkBinOp(addend1, addend2, result, x -> true, isLong ? Long::sum : (a, b) -> (int)(a + b), expected, "+");
|
||||
}
|
||||
|
||||
void checkMod(LongRangeSet dividendRange, LongRangeSet divisorRange, String expected) {
|
||||
LongRangeSet result = dividendRange.mod(divisorRange);
|
||||
checkBinOp(dividendRange, divisorRange, result, divisor -> divisor != 0, (a, b) -> a % b, expected);
|
||||
checkBinOp(dividendRange, divisorRange, result, divisor -> divisor != 0, (a, b) -> a % b, expected, "%");
|
||||
}
|
||||
|
||||
void checkDiv(LongRangeSet dividendRange, LongRangeSet divisorRange, boolean isLong, String expected) {
|
||||
LongRangeSet result = dividendRange.div(divisorRange, isLong);
|
||||
checkBinOp(dividendRange, divisorRange, result, divisor -> divisor != 0, (a, b) -> isLong ? a / b : ((int)a / (int)b), expected, "/");
|
||||
}
|
||||
|
||||
void checkShr(LongRangeSet arg, LongRangeSet shiftSize, boolean isLong, String expected) {
|
||||
LongRangeSet result = arg.shiftRight(shiftSize, isLong);
|
||||
checkBinOp(arg, shiftSize, result, x -> true, (a, b) -> isLong ? a >> b : ((int)a >> (int)b), expected, ">>");
|
||||
}
|
||||
|
||||
void checkUShr(LongRangeSet arg, LongRangeSet shiftSize, boolean isLong, String expected) {
|
||||
LongRangeSet result = arg.unsignedShiftRight(shiftSize, isLong);
|
||||
checkBinOp(arg, shiftSize, result, x -> true, (a, b) -> isLong ? a >>> b : ((int)a >>> (int)b), expected, ">>>");
|
||||
}
|
||||
|
||||
void checkBitwiseAnd(LongRangeSet range1, LongRangeSet range2, String expected) {
|
||||
LongRangeSet result = range1.bitwiseAnd(range2);
|
||||
assertEquals(result, range2.bitwiseAnd(range1)); // commutative
|
||||
checkBinOp(range1, range2, result, x -> true, (a, b) -> a & b, expected);
|
||||
checkBinOp(range1, range2, result, x -> true, (a, b) -> a & b, expected, "&");
|
||||
}
|
||||
|
||||
void checkBinOp(LongRangeSet op1,
|
||||
@@ -404,13 +483,14 @@ public class LongRangeSetTest {
|
||||
LongRangeSet result,
|
||||
LongPredicate filter,
|
||||
LongBinaryOperator operator,
|
||||
String expected) {
|
||||
String expected,
|
||||
String sign) {
|
||||
assertEquals(expected, result.toString());
|
||||
String errors = op1.stream()
|
||||
.mapToObj(a -> op2.stream()
|
||||
.filter(filter)
|
||||
.filter(b -> !result.contains(operator.applyAsLong(a, b)))
|
||||
.mapToObj(b -> a + " + " + b + " = " + operator.applyAsLong(a, b)))
|
||||
.mapToObj(b -> a + " " + sign + " " + b + " = " + operator.applyAsLong(a, b)))
|
||||
.flatMap(Function.identity())
|
||||
.collect(Collectors.joining("\n"));
|
||||
if (!errors.isEmpty()) {
|
||||
|
||||
Reference in New Issue
Block a user