import java.util.List;
import org.checkerframework.common.value.qual.IntRange;

// Because the analysis of loops isn't precise enough, the Value Checker issues
// warnings on this test case. So, suppress those warnings, but run the tests
// to make sure that dataflow reaches a fixed point.
// The expected errors in comments are the errors that should be issued if dataflow were precise
// enough.
@SuppressWarnings("value")
public class WidenedUpperBound {

  void increment() {
    int forIndex;
    for (forIndex = 0; forIndex < 4323; forIndex++) {
      @IntRange(from = 0, to = 4322) int x = forIndex;
    }
    // // ::error: (assignment)
    @IntRange(from = 0, to = 4322) int x = forIndex;
    @IntRange(from = 4323) int y = forIndex;

    int whileIndex = 0;
    while (whileIndex < 1234) {
      @IntRange(from = 0, to = 1233) int z = whileIndex;
      whileIndex++;
    }
    // // ::error: (assignment)
    @IntRange(from = 0, to = 1233) int a = whileIndex;
    @IntRange(from = 1234) int b = whileIndex;

    int doWhileIndex = 0;
    do {
      @IntRange(from = 0, to = 2344) int c = doWhileIndex;
      doWhileIndex++;
    } while (doWhileIndex < 2345);
    // // ::error: (assignment)
    @IntRange(from = 0, to = 2344) int d = doWhileIndex;
    @IntRange(from = 2345) int e = doWhileIndex;
  }

  void decrement() {
    int forIndex;
    for (forIndex = 4323; forIndex > 0; forIndex--) {
      @IntRange(from = 1, to = 4323) int x = forIndex;
    }
    // // ::error: (assignment)
    @IntRange(from = 1, to = 4323) int x = forIndex;
    @IntRange(to = 0) int y = forIndex;

    int whileIndex = 1234;
    while (whileIndex > 0) {
      @IntRange(from = 1, to = 1234) int z = whileIndex;
      whileIndex--;
    }
    // // ::error: (assignment)
    @IntRange(from = 1, to = 1234) int a = whileIndex;
    @IntRange(to = 0) int b = whileIndex;

    int doWhileIndex = 2344;
    do {
      @IntRange(from = 1, to = 2344) int c = doWhileIndex;
      doWhileIndex--;
    } while (doWhileIndex > 0);
    // // ::error: (assignment)
    @IntRange(from = 1, to = 2344) int d = doWhileIndex;
    @IntRange(to = 0) int e = doWhileIndex;
  }

  static void test1() {
    for (int i = 40; i > 0; i--) {
      @IntRange(from = 1, to = 40) int x = i;
    }
  }

  static void test1Explicit() {
    for (@IntRange(from = 1, to = 40) int i = 40; i > 0; i--) {
      @IntRange(from = 1, to = 40) int x = i;
    }
  }

  static void test2() {
    for (int i = 0; i < 18; i++) {
      @IntRange(from = 0, to = 17) int x = i;
    }
  }

  static void test2Explicit() {
    for (@IntRange(from = 0, to = 17) int i = 0; i < 18; i++) {
      @IntRange(from = 0, to = 17) int x = i;
    }
  }

  static void test3() {
    for (int i = 0; i < 18; i++) {
      @IntRange(from = 0, to = 17) int x = i;
    }
  }

  static void evenOdd(int param) {
    for (int i = 0; i < 12; i++) {
      if (i % 2 == 0) {
        @IntRange(from = 0, to = 11) int z = i;
      }
      @IntRange(from = 0, to = 11) int x = i;
    }

    for (int i = 0; i < 12; i++) {
      if (param == 0) {
        @IntRange(from = 0, to = 11) int z = i;
      }
      @IntRange(from = 0, to = 11) int x = i;
    }
  }

  static void evenOdd2(int param) {
    for (int i = 0; i < 300; i++) {
      if (i % 2 == 0) {
        @IntRange(from = 0, to = 299) int z = i;
      }
      @IntRange(from = 0, to = 299) int x = i;
    }

    for (int i = 0; i < 399; i++) {
      if (param == 0) {
        @IntRange(from = 0, to = 398) int z = i;
      }
      @IntRange(from = 0, to = 398) int x = i;
    }
  }

  void ifBlock(int param) {
    int x = 10;
    if (x < param) {
      x = param;
    }
    int z = 40;
    if (z < param) {
      param = 40;
    }
  }

  void doWhile() {
    int d = 0;
    do {
      @IntRange(from = 0, to = 399) int x = d;
      if (d % 2 == 0) {
        @IntRange(from = 0, to = 399) int y = d;
      }

      d++;
    } while (d < 399);

    @IntRange(from = 399) int z = d;
  }

  void doWhileMax() {
    int d = 0;
    do {
      @IntRange(from = 0) int x = d;
      if (d % 2 == 0) {
        @IntRange(from = 0) int y = d;
      }

      d++;
    } while (d < Integer.MAX_VALUE);

    @IntRange(from = 399) int z = d;
  }

  void whileLoop() {
    int i = 0;
    while (i < 399) {
      @IntRange(from = 0, to = 399) int x = i;
      if (i % 2 == 0) {
        @IntRange(from = 0, to = 399) int y = i;
      }

      i++;
    }

    @IntRange(from = 399) int z = i;
  }

  static void testMax() {
    for (int i = 0; i < Integer.MAX_VALUE; i++) {
      @IntRange(from = 0, to = Integer.MAX_VALUE - 1) int x = i;
    }
  }

  void exceptionLoop(List<Object> list) {
    int x = 0;
    for (short z = 0; z < list.size(); z++) {
      x = z;
      if (z == 100) {
        break;
      }
    }
    @IntRange(from = 0, to = 127) int result = x;
  }
}
