import org.checkerframework.checker.nullness.qual.*;

public class BoxingNullness {
  void withinOperation() {
    Integer i1 = 3;
    int i1u = i1 + 2; // valid
    Integer i2 = null;
    // :: error: (unboxing.of.nullable)
    int i2u = i2 + 2; // invalid
    Integer i3 = i1;
    i3.toString();
  }

  void withinAssignment() {
    Integer i1 = 5;
    int i1u = i1;
    Integer i2 = null;
    // :: error: (unboxing.of.nullable)
    int i2u = i2; // invalid
  }

  void validWithinUnary() {
    // within blocks to stop flow
    Integer i1 = 1, i2 = 1, i3 = 1, i4 = 1;
    ++i1;
    i2++;
  }

  void invalidWithinUnary() {
    // within blocks to stop flow
    Integer i1 = null, i2 = null, i3 = null, i4 = null;
    // :: error: (unboxing.of.nullable)
    ++i1; // invalid
    // :: error: (unboxing.of.nullable)
    i2++; // invalid
  }

  void validCompoundAssignmentsAsVariable() {
    @NonNull Integer i = 0; // nonnull is needed because flow is buggy
    i += 1;
    i -= 1;
    @NonNull Boolean b = true;
    b &= true;
  }

  void invalidCompoundAssignmentsAsVariable() {
    Integer i = null;
    // :: error: (unboxing.of.nullable)
    i += 1; // invalid
    Boolean b = null;
    // :: error: (unboxing.of.nullable)
    b &= true; // invalid
  }

  void invalidCompoundAssignmentAsValue() {
    @NonNull Integer var = 3;
    Integer val = null;
    // :: error: (unboxing.of.nullable)
    var += val;
    Boolean b1 = null;
    boolean b2 = true;
    // :: error: (unboxing.of.nullable)
    b2 &= b1; // invalid
  }

  void randomValidStringOperations() {
    String s = null;
    s += null;
  }

  void equalityTest() {
    Integer bN = null;
    Integer bN1 = null;
    Integer bN2 = null;
    Integer bN3 = null;
    Integer bN4 = null;
    Integer bN5 = null;
    Integer bN6 = null;
    Integer bN7 = null;
    Integer b1 = 1;
    int u1 = 1;
    System.out.println(bN == bN1); // valid
    System.out.println(bN2 == b1); // valid
    System.out.println(bN3 != bN4); // valid
    System.out.println(bN5 != b1); // valid

    System.out.println(u1 == b1);
    System.out.println(u1 != b1);
    System.out.println(u1 == u1);
    System.out.println(u1 != u1);

    // :: error: (unboxing.of.nullable)
    System.out.println(bN6 == u1); // invalid
    // :: error: (unboxing.of.nullable)
    System.out.println(bN7 != u1); // invalid
  }

  void addition() {
    Integer bN = null;
    Integer bN1 = null;
    Integer bN2 = null;
    Integer bN3 = null;
    Integer b1 = 1;
    int u1 = 1;
    // :: error: (unboxing.of.nullable)
    System.out.println(bN + bN1); // invalid
    // :: error: (unboxing.of.nullable)
    System.out.println(bN2 + b1); // invalid

    System.out.println(u1 + b1);
    System.out.println(u1 + u1);

    // :: error: (unboxing.of.nullable)
    System.out.println(bN3 + u1); // invalid
  }

  void visitCast() {
    Integer bN = null;
    Integer bN2 = null;
    Integer b1 = 1;
    int u1 = 1;

    println(bN);
    // :: error: (unboxing.of.nullable)
    println((int) bN2); // invalid

    println(b1);
    println((int) b1);

    println(u1);
    println((int) u1);
  }

  void println(@Nullable Object o) {}

  void testObjectString() {
    Object o = null;
    o += "m";
  }

  void testCharString() {
    CharSequence cs = null;
    cs += "m";
  }
}
