package org.checkerframework.checker.index.lowerbound;

import com.sun.source.tree.MemberSelectTree;
import com.sun.source.tree.MethodInvocationTree;
import com.sun.source.tree.MethodTree;
import com.sun.source.tree.Tree;
import com.sun.source.tree.VariableTree;
import java.util.List;
import javax.lang.model.element.AnnotationMirror;
import javax.lang.model.element.ExecutableElement;
import javax.lang.model.type.TypeKind;
import org.checkerframework.checker.index.IndexAbstractTransfer;
import org.checkerframework.checker.index.IndexRefinementInfo;
import org.checkerframework.checker.index.qual.GTENegativeOne;
import org.checkerframework.checker.index.qual.LowerBoundUnknown;
import org.checkerframework.checker.index.qual.NonNegative;
import org.checkerframework.checker.index.qual.Positive;
import org.checkerframework.checker.index.upperbound.OffsetEquation;
import org.checkerframework.common.value.ValueCheckerUtils;
import org.checkerframework.dataflow.analysis.TransferInput;
import org.checkerframework.dataflow.analysis.TransferResult;
import org.checkerframework.dataflow.cfg.UnderlyingAST;
import org.checkerframework.dataflow.cfg.node.BinaryOperationNode;
import org.checkerframework.dataflow.cfg.node.BitwiseAndNode;
import org.checkerframework.dataflow.cfg.node.IntegerDivisionNode;
import org.checkerframework.dataflow.cfg.node.IntegerRemainderNode;
import org.checkerframework.dataflow.cfg.node.Node;
import org.checkerframework.dataflow.cfg.node.NumericalAdditionNode;
import org.checkerframework.dataflow.cfg.node.NumericalMultiplicationNode;
import org.checkerframework.dataflow.cfg.node.NumericalSubtractionNode;
import org.checkerframework.dataflow.cfg.node.SignedRightShiftNode;
import org.checkerframework.dataflow.cfg.node.UnsignedRightShiftNode;
import org.checkerframework.dataflow.expression.JavaExpression;
import org.checkerframework.framework.flow.CFAnalysis;
import org.checkerframework.framework.flow.CFStore;
import org.checkerframework.framework.flow.CFValue;
import org.checkerframework.framework.type.AnnotatedTypeFactory;
import org.checkerframework.javacutil.AnnotationUtils;
import org.checkerframework.javacutil.BugInCF;
import org.checkerframework.javacutil.TreeUtils;

/**
 * Implements dataflow refinement rules based on tests: &lt;, &gt;, ==, and their derivatives.
 *
 * <p>Also implements the logic for binary operations: +, -, *, /, and %.
 *
 * <p>&gt;, &lt;, &ge;, &le;, ==, and != nodes are represented as combinations of &gt; and &ge;
 * (e.g. == is &ge; in both directions in the then branch), and implement refinements based on these
 * decompositions.
 *
 * <pre>
 * Refinement/transfer rules for conditionals:
 *
 * There are two "primitives":
 *
 * x &gt; y, which implies things about x based on y's type:
 *
 * y has type:    implies x has type:
 *  gte-1                nn
 *  nn                   pos
 *  pos                  pos
 *
 * and x &ge; y:
 *
 * y has type:    implies x has type:
 *  gte-1                gte-1
 *  nn                   nn
 *  pos                  pos
 *
 * These two "building blocks" can be combined to make all
 * other conditional expressions:
 *
 * EXPR             THEN          ELSE
 * x &gt; y            x &gt; y         y &ge; x
 * x &ge; y           x &ge; y        y &gt; x
 * x &lt; y            y &gt; x         x &ge; y
 * x &le; y           y &ge; x        x &gt; y
 *
 * Or, more formally:
 *
 * EXPR        THEN                                        ELSE
 * x &gt; y       x_refined = GLB(x_orig, promote(y))         y_refined = GLB(y_orig, x)
 * x &ge; y      x_refined = GLB(x_orig, y)                  y_refined = GLB(y_orig, promote(x))
 * x &lt; y       y_refined = GLB(y_orig, promote(x))         x_refined = GLB(x_orig, y)
 * x &le; y      y_refined = GLB(y_orig, x)                  x_refined = GLB(x_orig, promote(y))
 *
 * where GLB is the greatest lower bound and promote is the increment
 * function on types (or, equivalently, the function specified by the "x
 * &gt; y" information above).
 *
 * There's also ==, which is a special case. Only the THEN
 * branch is refined:
 *
 * EXPR             THEN                   ELSE
 * x == y           x &ge; y &amp;&amp; y &ge; x       nothing known
 *
 * or, more formally:
 *
 * EXPR            THEN                                    ELSE
 * x == y          x_refined = GLB(x_orig, y_orig)         nothing known
 *                y_refined = GLB(x_orig, y_orig)
 *
 * finally, not equal:
 *
 * EXPR             THEN                   ELSE
 * x != y           nothing known          x &ge; y &amp;&amp; y &ge; x
 *
 * more formally:
 *
 * EXPR            THEN               ELSE
 * x != y          nothing known      x_refined = GLB(x_orig, y_orig)
 *                                   y_refined = GLB(x_orig, y_orig)
 *
 * </pre>
 *
 * Dividing these rules up by cases, this class implements:
 *
 * <ul>
 *   <li>1. The rule described above for &gt;
 *   <li>2. The rule described above for &ge;
 *   <li>3. The rule described above for &lt;
 *   <li>4. The rule described above for &le;
 *   <li>5. The rule described above for ==
 *   <li>6. The rule described above for !=
 *   <li>7. A special refinement for != when the value being compared to is a compile-time constant
 *       with a value exactly equal to -1 or 0 (i.e. {@code x != -1} and x is GTEN1 implies x is
 *       non-negative). Maybe two rules?
 *   <li>8. When a compile-time constant -2 is added to a positive, the result is GTEN1
 *   <li>9. When a compile-time constant 2 is added to a GTEN1, the result is positive
 *   <li>10. When a positive is added to a positive, the result is positive
 *   <li>11. When a non-negative is added to any other type, the result is that other type
 *   <li>12. When a GTEN1 is added to a positive, the result is non-negative
 *   <li>13. When the left side of a subtraction expression is &gt; the right side according to the
 *       LessThanChecker, the result of the subtraction expression is positive
 *   <li>14. When the left side of a subtraction expression is &ge; the right side according to the
 *       LessThanChecker, the result of the subtraction expression is non-negative
 *   <li>15. special handling for when the left side is the length of an array or String that's
 *       stored as a field, and the right side is a compile time constant. Do we need this?
 *   <li>16. Multiplying any value by a compile time constant of 1 preserves its type
 *   <li>17. Multiplying two positives produces a positive
 *   <li>18. Multiplying a positive and a non-negative produces a non-negative
 *   <li>19. Multiplying two non-negatives produces a non-negative
 *   <li>20. When the result of Math.random is multiplied by an array length, the result is
 *       NonNegative.
 *   <li>21. literal 0 divided by anything is non-negative
 *   <li>22. anything divided by literal zero is bottom
 *   <li>23. literal 1 divided by a positive or non-negative is non-negative
 *   <li>24. literal 1 divided by anything else is GTEN1
 *   <li>25. anything divided by literal 1 is itself
 *   <li>26. a positive or non-negative divided by a positive or non-negative is non-negative
 *   <li>27. anything modded by literal 1 or -1 is non-negative
 *   <li>28. a positive or non-negative modded by anything is non-negative
 *   <li>29. a GTEN1 modded by anything is GTEN1
 *   <li>30. anything right-shifted by a non-negative is non-negative
 *   <li>31. anything bitwise-anded by a non-negative is non-negative
 *   <li>32. If a and b are non-negative and {@code a <= b} and {@code a != b}, then b is pos.
 *   <li>33. A char is always non-negative
 * </ul>
 */
public class LowerBoundTransfer extends IndexAbstractTransfer {

  /** The canonical {@link GTENegativeOne} annotation. */
  public final AnnotationMirror GTEN1;

  /** The canonical {@link NonNegative} annotation. */
  public final AnnotationMirror NN;

  /** The canonical {@link Positive} annotation. */
  public final AnnotationMirror POS;

  /** The canonical {@link LowerBoundUnknown} annotation. */
  public final AnnotationMirror UNKNOWN;

  /** The annotated type factory. */
  private final LowerBoundAnnotatedTypeFactory atypeFactory;

  /**
   * Create a new LowerBoundTransfer.
   *
   * @param analysis the CFAnalysis
   */
  public LowerBoundTransfer(CFAnalysis analysis) {
    super(analysis);
    atypeFactory = (LowerBoundAnnotatedTypeFactory) analysis.getTypeFactory();
    // Initialize qualifiers.
    GTEN1 = atypeFactory.GTEN1;
    NN = atypeFactory.NN;
    POS = atypeFactory.POS;
    UNKNOWN = atypeFactory.UNKNOWN;
  }

  /**
   * Refines GTEN1 to NN if it is not equal to -1, and NN to Pos if it is not equal to 0. Implements
   * case 7.
   *
   * @param mLiteral a potential literal
   * @param otherNode the node on the other side of the ==/!=
   * @param otherAnno the annotation of the other side of the ==/!=
   */
  private void notEqualToValue(
      Node mLiteral, Node otherNode, AnnotationMirror otherAnno, CFStore store) {

    Long integerLiteral =
        ValueCheckerUtils.getExactValue(
            mLiteral.getTree(), atypeFactory.getValueAnnotatedTypeFactory());

    if (integerLiteral == null) {
      return;
    }
    long intLiteral = integerLiteral.longValue();

    if (intLiteral == 0) {
      if (atypeFactory.areSameByClass(otherAnno, NonNegative.class)) {
        List<Node> internals = splitAssignments(otherNode);
        for (Node internal : internals) {
          JavaExpression je = JavaExpression.fromNode(internal);
          store.insertValue(je, POS);
        }
      }
    } else if (intLiteral == -1) {
      if (atypeFactory.areSameByClass(otherAnno, GTENegativeOne.class)) {
        List<Node> internals = splitAssignments(otherNode);
        for (Node internal : internals) {
          JavaExpression je = JavaExpression.fromNode(internal);
          store.insertValue(je, NN);
        }
      }
    }
  }

  /**
   * Implements the transfer rules for both equal nodes and not-equals nodes (i.e. cases 5, 6, 32).
   */
  @Override
  protected TransferResult<CFValue, CFStore> strengthenAnnotationOfEqualTo(
      TransferResult<CFValue, CFStore> result,
      Node firstNode,
      Node secondNode,
      CFValue firstValue,
      CFValue secondValue,
      boolean notEqualTo) {
    result =
        super.strengthenAnnotationOfEqualTo(
            result, firstNode, secondNode, firstValue, secondValue, notEqualTo);

    IndexRefinementInfo rfi = new IndexRefinementInfo(result, analysis, secondNode, firstNode);
    if (rfi.leftAnno == null || rfi.rightAnno == null) {
      return result;
    }

    // There is also special processing to look for literals on one side of the equals and a
    // GTEN1 or NN on the other, so that those types can be promoted in the branch where their
    // values are not equal to certain literals.
    CFStore notEqualsStore = notEqualTo ? rfi.thenStore : rfi.elseStore;
    notEqualToValue(rfi.left, rfi.right, rfi.rightAnno, notEqualsStore);
    notEqualToValue(rfi.right, rfi.left, rfi.leftAnno, notEqualsStore);

    notEqualsLessThan(rfi.left, rfi.leftAnno, rfi.right, rfi.rightAnno, notEqualsStore);
    notEqualsLessThan(rfi.right, rfi.rightAnno, rfi.left, rfi.leftAnno, notEqualsStore);

    return rfi.newResult;
  }

  /** Implements case 32. */
  private void notEqualsLessThan(
      Node leftNode,
      AnnotationMirror leftAnno,
      Node otherNode,
      AnnotationMirror otherAnno,
      CFStore store) {
    if (!isNonNegative(leftAnno) || !isNonNegative(otherAnno)) {
      return;
    }
    JavaExpression otherJe = JavaExpression.fromNode(otherNode);
    if (atypeFactory
        .getLessThanAnnotatedTypeFactory()
        .isLessThanOrEqual(leftNode.getTree(), otherJe.toString())) {
      store.insertValue(otherJe, POS);
    }
  }

  /**
   * The implementation of the algorithm for refining a &gt; test. Changes the type of left (the
   * greater one) to one closer to bottom than the type of right. Can't call the promote function
   * from the ATF directly because a new expression isn't introduced here - the modifications have
   * to be made to an existing one.
   *
   * <p>This implements parts of cases 1, 2, 3, and 4 using the decomposition strategy described in
   * the Javadoc of this class.
   */
  @Override
  protected void refineGT(
      Node left,
      AnnotationMirror leftAnno,
      Node right,
      AnnotationMirror rightAnno,
      CFStore store,
      TransferInput<CFValue, CFStore> in) {

    if (rightAnno == null || leftAnno == null) {
      return;
    }

    JavaExpression leftJe = JavaExpression.fromNode(left);

    if (AnnotationUtils.areSame(rightAnno, GTEN1)) {
      store.insertValue(leftJe, NN);
      return;
    }
    if (AnnotationUtils.areSame(rightAnno, NN)) {
      store.insertValue(leftJe, POS);
      return;
    }
    if (AnnotationUtils.areSame(rightAnno, POS)) {
      store.insertValue(leftJe, POS);
      return;
    }
  }

  /**
   * Refines left to exactly the level of right, since in the worst case they're equal. Modifies an
   * existing type in the store, but has to be careful not to overwrite a more precise existing
   * type.
   *
   * <p>This implements parts of cases 1, 2, 3, and 4 using the decomposition strategy described in
   * this class's Javadoc.
   */
  @Override
  protected void refineGTE(
      Node left,
      AnnotationMirror leftAnno,
      Node right,
      AnnotationMirror rightAnno,
      CFStore store,
      TransferInput<CFValue, CFStore> in) {

    if (rightAnno == null || leftAnno == null) {
      return;
    }

    JavaExpression leftJe = JavaExpression.fromNode(left);

    AnnotationMirror newLBType =
        atypeFactory
            .getQualifierHierarchy()
            .greatestLowerBoundShallow(rightAnno, left.getType(), leftAnno, right.getType());

    store.insertValue(leftJe, newLBType);
  }

  /**
   * Returns an annotation mirror representing the result of subtracting one from {@code oldAnm}.
   */
  private AnnotationMirror anmAfterSubtractingOne(AnnotationMirror oldAnm) {
    if (isPositive(oldAnm)) {
      return NN;
    } else if (isNonNegative(oldAnm)) {
      return GTEN1;
    } else {
      return UNKNOWN;
    }
  }

  /** Returns an annotation mirror representing the result of adding one to {@code oldAnm}. */
  private AnnotationMirror anmAfterAddingOne(AnnotationMirror oldAnm) {
    if (isNonNegative(oldAnm)) {
      return POS;
    } else if (isGTEN1(oldAnm)) {
      return NN;
    } else {
      return UNKNOWN;
    }
  }

  /**
   * Helper method for getAnnotationForPlus. Handles addition of constants (cases 8 and 9).
   *
   * @param val the integer value of the constant
   * @param nonLiteralType the type of the side of the expression that isn't a constant
   */
  private AnnotationMirror getAnnotationForLiteralPlus(int val, AnnotationMirror nonLiteralType) {
    if (val == -2) {
      if (isPositive(nonLiteralType)) {
        return GTEN1;
      }
    } else if (val == -1) {
      return anmAfterSubtractingOne(nonLiteralType);
    } else if (val == 0) {
      return nonLiteralType;
    } else if (val == 1) {
      return anmAfterAddingOne(nonLiteralType);
    } else if (val >= 2) {
      if (isGTEN1(nonLiteralType)) {
        // 2 + a positive, or a non-negative, or a non-negative-1 is a positive
        return POS;
      }
    }
    return UNKNOWN;
  }

  /**
   * getAnnotationForPlus handles the following cases (cases 10-12 above):
   *
   * <pre>
   *      8. lit -2 + pos &rarr; gte-1
   *      lit -1 + * &rarr; call demote
   *      lit 0 + * &rarr; *
   *      lit 1 + * &rarr; call promote
   *      9. lit &ge; 2 + {gte-1, nn, or pos} &rarr; pos
   *      let all other lits, including sets, fall through:
   *      10. pos + pos &rarr; pos
   *      11. nn + * &rarr; *
   *      12. pos + gte-1 &rarr; nn
   *      * + * &rarr; lbu
   *  </pre>
   */
  private AnnotationMirror getAnnotationForPlus(
      BinaryOperationNode binaryOpNode, TransferInput<CFValue, CFStore> p) {

    Node leftExprNode = binaryOpNode.getLeftOperand();
    Node rightExprNode = binaryOpNode.getRightOperand();

    AnnotationMirror leftAnno = getLowerBoundAnnotation(leftExprNode, p);

    // Check if the right side's value is known at compile time.
    Long valRight =
        ValueCheckerUtils.getExactValue(
            rightExprNode.getTree(), atypeFactory.getValueAnnotatedTypeFactory());
    if (valRight != null) {
      return getAnnotationForLiteralPlus(valRight.intValue(), leftAnno);
    }

    AnnotationMirror rightAnno = getLowerBoundAnnotation(rightExprNode, p);

    // Check if the left side's value is known at compile time.
    Long valLeft =
        ValueCheckerUtils.getExactValue(
            leftExprNode.getTree(), atypeFactory.getValueAnnotatedTypeFactory());
    if (valLeft != null) {
      return getAnnotationForLiteralPlus(valLeft.intValue(), rightAnno);
    }

    /* This section is handling the generic cases:
     *      pos + pos -> pos
     *      nn + * -> *
     *      pos + gte-1 -> nn
     */
    if (atypeFactory.areSameByClass(leftAnno, Positive.class)
        && atypeFactory.areSameByClass(rightAnno, Positive.class)) {
      return POS;
    }

    if (atypeFactory.areSameByClass(leftAnno, NonNegative.class)) {
      return rightAnno;
    }

    if (atypeFactory.areSameByClass(rightAnno, NonNegative.class)) {
      return leftAnno;
    }

    if ((isPositive(leftAnno) && isGTEN1(rightAnno))
        || (isGTEN1(leftAnno) && isPositive(rightAnno))) {
      return NN;
    }
    return UNKNOWN;
  }

  /**
   * getAnnotationForMinus handles the following cases:
   *
   * <pre>
   *      * - lit &rarr; call plus(*, -1 * the value of the lit)
   *      * - * &rarr; lbu
   *      13. if the LessThan type checker can establish that the left side of the expression is &gt; the right side,
   *      returns POS.
   *      14. if the LessThan type checker can establish that the left side of the expression is &ge; the right side,
   *      returns NN.
   *      15. special handling for when the left side is the length of an array or String that's stored as a field,
   *      and the right side is a compile time constant. Do we need this?
   *  </pre>
   */
  private AnnotationMirror getAnnotationForMinus(
      BinaryOperationNode minusNode, TransferInput<CFValue, CFStore> p) {

    // Check if the right side's value is known at compile time.
    Long valRight =
        ValueCheckerUtils.getExactValue(
            minusNode.getRightOperand().getTree(), atypeFactory.getValueAnnotatedTypeFactory());
    if (valRight != null) {
      AnnotationMirror leftAnno = getLowerBoundAnnotation(minusNode.getLeftOperand(), p);
      // Instead of a separate method for subtraction, add the negative of a constant.
      AnnotationMirror result = getAnnotationForLiteralPlus(-1 * valRight.intValue(), leftAnno);

      Tree leftExpr = minusNode.getLeftOperand().getTree();
      Integer minLen = null;
      // Check if the left side is a field access of an array's length, or invocation of
      // String.length. If so, try to look up the MinLen of the array, and potentially keep
      // this either NN or POS instead of GTEN1 or LBU.
      if (leftExpr.getKind() == Tree.Kind.MEMBER_SELECT) {
        MemberSelectTree mstree = (MemberSelectTree) leftExpr;
        minLen = atypeFactory.getMinLenFromMemberSelectTree(mstree);
      } else if (leftExpr.getKind() == Tree.Kind.METHOD_INVOCATION) {
        MethodInvocationTree mitree = (MethodInvocationTree) leftExpr;
        minLen = atypeFactory.getMinLenFromMethodInvocationTree(mitree);
      }

      if (minLen != null) {
        result = atypeFactory.anmFromVal(minLen - valRight);
      }
      return result;
    }

    OffsetEquation leftExpression =
        OffsetEquation.createOffsetFromNode(minusNode.getLeftOperand(), atypeFactory, '+');
    if (leftExpression != null) {
      if (atypeFactory
          .getLessThanAnnotatedTypeFactory()
          .isLessThan(minusNode.getRightOperand().getTree(), leftExpression.toString())) {
        return POS;
      }

      if (atypeFactory
          .getLessThanAnnotatedTypeFactory()
          .isLessThanOrEqual(minusNode.getRightOperand().getTree(), leftExpression.toString())) {
        return NN;
      }
    }

    // The checker can't reason about arbitrary (i.e. non-literal)
    // things that are being subtracted, so it gives up.
    return UNKNOWN;
  }

  /**
   * Helper function for getAnnotationForMultiply. Handles compile-time known constants.
   *
   * @param val the integer value of the constant
   * @param nonLiteralType the type of the side of the expression that isn't a constant
   */
  private AnnotationMirror getAnnotationForLiteralMultiply(
      int val, AnnotationMirror nonLiteralType) {
    if (val == 0) {
      return NN;
    } else if (val == 1) {
      return nonLiteralType;
    } else if (val > 1) {
      if (isNonNegative(nonLiteralType)) {
        return nonLiteralType;
      }
    }
    return UNKNOWN;
  }

  /**
   * getAnnotationForMultiply handles the following cases:
   *
   * <pre>
   *        * * lit 0 &rarr; nn (=0)
   *        16. * * lit 1 &rarr; *
   *        17. pos * pos &rarr; pos
   *        18. pos * nn &rarr; nn
   *        19. nn * nn &rarr; nn
   *        * * * &rarr; lbu
   *  </pre>
   *
   * Also handles a special case involving Math.random (case 20).
   */
  private AnnotationMirror getAnnotationForMultiply(
      NumericalMultiplicationNode node, TransferInput<CFValue, CFStore> p) {

    // Special handling for multiplying an array length by a Math.random().
    AnnotationMirror randomSpecialCaseResult = atypeFactory.checkForMathRandomSpecialCase(node);
    if (randomSpecialCaseResult != null) {
      return randomSpecialCaseResult;
    }

    AnnotationMirror leftAnno = getLowerBoundAnnotation(node.getLeftOperand(), p);

    // Check if the right side's value is known at compile time.
    Long valRight =
        ValueCheckerUtils.getExactValue(
            node.getRightOperand().getTree(), atypeFactory.getValueAnnotatedTypeFactory());
    if (valRight != null) {
      return getAnnotationForLiteralMultiply(valRight.intValue(), leftAnno);
    }

    AnnotationMirror rightAnno = getLowerBoundAnnotation(node.getRightOperand(), p);
    // Check if the left side's value is known at compile time.
    Long valLeft =
        ValueCheckerUtils.getExactValue(
            node.getLeftOperand().getTree(), atypeFactory.getValueAnnotatedTypeFactory());
    if (valLeft != null) {
      return getAnnotationForLiteralMultiply(valLeft.intValue(), rightAnno);
    }

    /* This section handles generic annotations:
     *   pos * pos -> pos
     *   nn * pos -> nn (elided, since positives are also non-negative)
     *   nn * nn -> nn
     */
    if (isPositive(leftAnno) && isPositive(rightAnno)) {
      return POS;
    }
    if (isNonNegative(leftAnno) && isNonNegative(rightAnno)) {
      return NN;
    }
    return UNKNOWN;
  }

  /** When the value on the left is known at compile time. */
  private AnnotationMirror addAnnotationForLiteralDivideLeft(int val, AnnotationMirror rightAnno) {
    if (val == 0) {
      return NN;
    } else if (val == 1) {
      if (isNonNegative(rightAnno)) {
        return NN;
      } else {
        // (1 / x) can't be outside the range [-1, 1] when x is an integer.
        return GTEN1;
      }
    }
    return UNKNOWN;
  }

  /** When the value on the right is known at compile time. */
  private AnnotationMirror addAnnotationForLiteralDivideRight(int val, AnnotationMirror leftAnno) {
    if (val == 0) {
      // Reaching this indicates a divide by zero error. If the value is zero, then this is
      // division by zero. Division by zero is treated as bottom so that users aren't warned
      // about dead code that's dividing by zero. This code assumes that non-dead code won't
      // include literal divide by zeros...
      return atypeFactory.BOTTOM;
    } else if (val == 1) {
      return leftAnno;
    } else if (val >= 2) {
      if (isNonNegative(leftAnno)) {
        return NN;
      }
    }
    return UNKNOWN;
  }

  /**
   * getAnnotationForDivide handles the following cases (21-26).
   *
   * <pre>
   *      lit 0 / * &rarr; nn (=0)
   *      * / lit 0 &rarr; pos
   *      lit 1 / {pos, nn} &rarr; nn
   *      lit 1 / * &rarr; gten1
   *      * / lit 1 &rarr; *
   *      {pos, nn} / lit &gt;1 &rarr; nn
   *      pos / {pos, nn} &rarr; nn (can round to zero)
   *      * / {pos, nn} &rarr; *
   *      * / * &rarr; lbu
   *  </pre>
   */
  private AnnotationMirror getAnnotationForDivide(
      IntegerDivisionNode node, TransferInput<CFValue, CFStore> p) {

    AnnotationMirror leftAnno = getLowerBoundAnnotation(node.getLeftOperand(), p);

    // Check if the right side's value is known at compile time.
    Long valRight =
        ValueCheckerUtils.getExactValue(
            node.getRightOperand().getTree(), atypeFactory.getValueAnnotatedTypeFactory());
    if (valRight != null) {
      return addAnnotationForLiteralDivideRight(valRight.intValue(), leftAnno);
    }

    AnnotationMirror rightAnno = getLowerBoundAnnotation(node.getRightOperand(), p);

    // Check if the left side's value is known at compile time.
    Long valLeft =
        ValueCheckerUtils.getExactValue(
            node.getLeftOperand().getTree(), atypeFactory.getValueAnnotatedTypeFactory());
    if (valLeft != null) {
      return addAnnotationForLiteralDivideLeft(valLeft.intValue(), leftAnno);
    }

    /* This section handles generic annotations:
     *    pos / {pos, nn} -> nn (can round to zero)
     *    * / {pos, nn} -> *
     */
    if (isPositive(leftAnno) && isNonNegative(rightAnno)) {
      return NN;
    }
    if (isNonNegative(rightAnno)) {
      return leftAnno;
    }
    // Everything else is unknown.
    return UNKNOWN;
  }

  /** A remainder with 1 or -1 as the divisor always results in zero. */
  private AnnotationMirror addAnnotationForLiteralRemainder(int val) {
    if (val == 1 || val == -1) {
      return NN;
    }
    return UNKNOWN;
  }

  /** Adds a default NonNegative annotation to every character. Implements case 33. */
  @Override
  protected void addInformationFromPreconditions(
      CFStore info,
      AnnotatedTypeFactory factory,
      UnderlyingAST.CFGMethod method,
      MethodTree methodTree,
      ExecutableElement methodElement) {
    super.addInformationFromPreconditions(info, factory, method, methodTree, methodElement);

    List<? extends VariableTree> paramTrees = methodTree.getParameters();

    for (VariableTree variableTree : paramTrees) {
      if (TreeUtils.typeOf(variableTree).getKind() == TypeKind.CHAR) {
        JavaExpression je = JavaExpression.fromVariableTree(variableTree);
        info.insertValuePermitNondeterministic(je, atypeFactory.NN);
      }
    }
  }

  /**
   * getAnnotationForRemainder handles these cases:
   *
   * <pre>
   *      27. * % 1/-1 &rarr; nn
   *      28. pos/nn % * &rarr; nn
   *      29. gten1 % * &rarr; gten1
   *      * % * &rarr; lbu
   * </pre>
   */
  public AnnotationMirror getAnnotationForRemainder(
      IntegerRemainderNode node, TransferInput<CFValue, CFStore> p) {

    AnnotationMirror leftAnno = getLowerBoundAnnotation(node.getLeftOperand(), p);

    // Check if the right side's value is known at compile time.
    Long valRight =
        ValueCheckerUtils.getExactValue(
            node.getRightOperand().getTree(), atypeFactory.getValueAnnotatedTypeFactory());
    if (valRight != null) {
      return addAnnotationForLiteralRemainder(valRight.intValue());
    }

    /* This section handles generic annotations:
          pos/nn % * -> nn
          gten1 % * -> gten1
    */
    if (isNonNegative(leftAnno)) {
      return NN;
    }
    if (isGTEN1(leftAnno)) {
      return GTEN1;
    }

    // Everything else is unknown.
    return UNKNOWN;
  }

  /** Handles shifts (case 30). * &gt;&gt; NonNegative &rarr; NonNegative */
  private AnnotationMirror getAnnotationForRightShift(
      BinaryOperationNode node, TransferInput<CFValue, CFStore> p) {
    AnnotationMirror leftAnno = getLowerBoundAnnotation(node.getLeftOperand(), p);
    AnnotationMirror rightAnno = getLowerBoundAnnotation(node.getRightOperand(), p);

    if (isNonNegative(leftAnno)) {
      if (isNonNegative(rightAnno)) {
        return NN;
      }
    }
    return UNKNOWN;
  }

  /**
   * Handles masking (case 31). Particularly, handles the following cases: * &amp; NonNegative
   * &rarr; NonNegative
   */
  private AnnotationMirror getAnnotationForAnd(
      BitwiseAndNode node, TransferInput<CFValue, CFStore> p) {

    AnnotationMirror rightAnno = getLowerBoundAnnotation(node.getRightOperand(), p);
    if (isNonNegative(rightAnno)) {
      return NN;
    }

    AnnotationMirror leftAnno = getLowerBoundAnnotation(node.getLeftOperand(), p);
    if (isNonNegative(leftAnno)) {
      return NN;
    }
    return UNKNOWN;
  }

  /**
   * Returns true if the argument is the @Positive type annotation.
   *
   * @param anm the annotation to test
   * @return true if the argument is the @Positive type annotation
   */
  private boolean isPositive(AnnotationMirror anm) {
    return atypeFactory.areSameByClass(anm, Positive.class);
  }

  /**
   * Returns true if the argument is the @NonNegative type annotation (or a stronger one).
   *
   * @param anm the annotation to test
   * @return true if the argument is the @NonNegative type annotation
   */
  private boolean isNonNegative(AnnotationMirror anm) {
    return atypeFactory.areSameByClass(anm, NonNegative.class) || isPositive(anm);
  }

  /**
   * Returns true if the argument is the @GTENegativeOne type annotation (or a stronger one).
   *
   * @param anm the annotation to test
   * @return true if the argument is the @GTENegativeOne type annotation
   */
  private boolean isGTEN1(AnnotationMirror anm) {
    return atypeFactory.areSameByClass(anm, GTENegativeOne.class) || isNonNegative(anm);
  }

  private AnnotationMirror getLowerBoundAnnotation(
      Node subNode, TransferInput<CFValue, CFStore> p) {
    CFValue value = p.getValueOfSubNode(subNode);
    if (value == null) {
      throw new BugInCF("value==null for getLowerBoundAnnotation(%s, %s)%n", subNode, p);
    }
    return getLowerBoundAnnotation(value);
  }

  /**
   * Returns the lower bound annotation for the given value.
   *
   * @param cfValue a value
   * @return the lower bound annotation for the given value
   */
  private AnnotationMirror getLowerBoundAnnotation(CFValue cfValue) {
    return atypeFactory
        .getQualifierHierarchy()
        .findAnnotationInHierarchy(cfValue.getAnnotations(), atypeFactory.UNKNOWN);
  }

  @Override
  public TransferResult<CFValue, CFStore> visitNumericalAddition(
      NumericalAdditionNode n, TransferInput<CFValue, CFStore> p) {
    TransferResult<CFValue, CFStore> result = super.visitNumericalAddition(n, p);
    AnnotationMirror newAnno = getAnnotationForPlus(n, p);
    return recreateTransferResult(newAnno, result);
  }

  @Override
  public TransferResult<CFValue, CFStore> visitNumericalSubtraction(
      NumericalSubtractionNode n, TransferInput<CFValue, CFStore> p) {
    TransferResult<CFValue, CFStore> result = super.visitNumericalSubtraction(n, p);
    AnnotationMirror newAnno = getAnnotationForMinus(n, p);
    return recreateTransferResult(newAnno, result);
  }

  @Override
  public TransferResult<CFValue, CFStore> visitNumericalMultiplication(
      NumericalMultiplicationNode n, TransferInput<CFValue, CFStore> p) {
    TransferResult<CFValue, CFStore> result = super.visitNumericalMultiplication(n, p);
    AnnotationMirror newAnno = getAnnotationForMultiply(n, p);
    return recreateTransferResult(newAnno, result);
  }

  @Override
  public TransferResult<CFValue, CFStore> visitIntegerDivision(
      IntegerDivisionNode n, TransferInput<CFValue, CFStore> p) {
    TransferResult<CFValue, CFStore> result = super.visitIntegerDivision(n, p);
    AnnotationMirror newAnno = getAnnotationForDivide(n, p);
    return recreateTransferResult(newAnno, result);
  }

  @Override
  public TransferResult<CFValue, CFStore> visitIntegerRemainder(
      IntegerRemainderNode n, TransferInput<CFValue, CFStore> p) {
    TransferResult<CFValue, CFStore> transferResult = super.visitIntegerRemainder(n, p);
    AnnotationMirror resultAnno = getAnnotationForRemainder(n, p);
    return recreateTransferResult(resultAnno, transferResult);
  }

  @Override
  public TransferResult<CFValue, CFStore> visitSignedRightShift(
      SignedRightShiftNode n, TransferInput<CFValue, CFStore> p) {
    TransferResult<CFValue, CFStore> transferResult = super.visitSignedRightShift(n, p);
    AnnotationMirror resultAnno = getAnnotationForRightShift(n, p);
    return recreateTransferResult(resultAnno, transferResult);
  }

  @Override
  public TransferResult<CFValue, CFStore> visitUnsignedRightShift(
      UnsignedRightShiftNode n, TransferInput<CFValue, CFStore> p) {
    TransferResult<CFValue, CFStore> transferResult = super.visitUnsignedRightShift(n, p);
    AnnotationMirror resultAnno = getAnnotationForRightShift(n, p);
    return recreateTransferResult(resultAnno, transferResult);
  }

  @Override
  public TransferResult<CFValue, CFStore> visitBitwiseAnd(
      BitwiseAndNode n, TransferInput<CFValue, CFStore> p) {
    TransferResult<CFValue, CFStore> transferResult = super.visitBitwiseAnd(n, p);
    AnnotationMirror resultAnno = getAnnotationForAnd(n, p);
    return recreateTransferResult(resultAnno, transferResult);
  }
}
