import org.checkerframework.common.value.qual.*;

class Refinement2 {

    void eq_test(int x, @IntVal({1, 5, 6}) int w) {
        if (x == 1) {
            // :: error: (assignment.type.incompatible)
            @BottomVal int q = x;

        } else if (x == 2) {
        } else {
            return;
        }

        if (x != 1) {
            // :: error: (assignment.type.incompatible)
            @IntVal({1}) int y = x;
            @IntVal({2}) int z = x;
        }

        if (x == 1) {

            @IntVal({1}) int y = x;

            // :: error: (assignment.type.incompatible)
            @IntVal({2}) int z = x;
        } else {
            @IntVal({2}) int y = x;

            // :: error: (assignment.type.incompatible)
            @IntVal({1}) int z = x;
        }

        if (x == w) {
            @IntVal({1}) int y = x;
            @IntVal({1}) int z = w;
        } else {
            // These two assignments are illegal because one of x or w could be 1,
            // while the other is not. So no refinement can be applied.

            // :: error: (assignment.type.incompatible)
            @IntVal({2}) int y = x;
            // :: error: (assignment.type.incompatible)
            @IntVal({5, 6}) int z = w;
        }
    }

    void testDeadCode(int x) {
        if (x == 4 || x == 5) {
            @IntVal({4, 5}) int a = x;
            // :: error: (assignment.type.incompatible)
            @BottomVal int a2 = x;
        }
        if (x == 4 && x == 5) {
            // This is dead, so x should be bottom.
            @IntVal({4, 5}) int a = x;
            @BottomVal int a2 = x;
        }
        if (x != 1 || x != 2) {
            return;
        }
        if (x != 2) {
            @IntVal(1) int a = x;
        }

        if (x == 3) {
            // This is actually dead code, so x is treated as bottom.
            @IntVal(3) int y = x;
            @IntVal(33) int v = x;
            @BottomVal int z = x;
        }
    }

    void simpleNull(@IntVal({1, 2, 3}) Integer x) {
        if (x == null) {
            @BottomVal int y = x;
        }
    }

    void moreTests(@IntVal({1, 2, 3}) Integer x, Integer y) {
        if (x == null) {
            if (y == x) {
                // x and y should be @BottomVal
                @BottomVal int z1 = x;
                @BottomVal int z2 = y;
            } else {
                // y should be @UnknownVal here.
                // :: error: (assignment.type.incompatible)
                @IntVal({1, 2, 3}) int z = y;
            }
        }

        if (x == null) {
            if (y < x) {
                // x should be @BottomVal
                @BottomVal int z1 = x;
                // This is dead code since the unboxing of x in the if statement
                // will throw a NPE, so the type of y doesn't matter.
                // @BottomVal int z2 = y;
            } else {
                // This is dead code, too.
            }
        }
    }

    void testArrayLen(boolean flag) {
        int[] a;
        if (flag) {
            a = new int[] {1};
        } else {
            a = new int[] {1, 2};
        }

        if (a.length != 1) {
            int @ArrayLen(2) [] b = a;
        }

        if (a.length == 3) {
            // Dead code
            int @ArrayLen(3) [] b = a;
            int @ArrayLen(5) [] c = a;
            int @BottomVal [] bot = a;
        }
    }
}
