/**
   * This method returns the input-value, casted to match the type. If the value matches the type,
   * it is returned unchanged. This method handles overflows and print warnings for the user.
   * Example: This method is called, when an value of type 'integer' is assigned to a variable of
   * type 'char'.
   *
   * @param value will be casted. If value is null, null is returned.
   * @param targetType value will be casted to targetType.
   * @param machineModel contains information about types
   */
  public static Region[] castCValue(
      @Nullable final Region[] value,
      final CType targetType,
      final BitvectorManager bvmgr,
      final MachineModel machineModel) {
    if (value == null) {
      return null;
    }

    final CType type = targetType.getCanonicalType();
    if (type instanceof CSimpleType) {
      final CSimpleType st = (CSimpleType) type;
      if (st.getType() == CBasicType.INT || st.getType() == CBasicType.CHAR) {
        final int bitPerByte = machineModel.getSizeofCharInBits();
        final int numBytes = machineModel.getSizeof(st);
        final int size = bitPerByte * numBytes;
        final Region[] result = bvmgr.toBitsize(size, st.isSigned(), value);
        return result;
      }
    }
    // currently we do not handle floats, doubles or voids, pointers, so lets ignore this case.
    return value;
  }
  /**
   * This method calculates the exact result for a binary operation.
   *
   * @param lVal first operand
   * @param rVal second operand
   * @param machineModel information about types
   */
  public static Region[] calculateBinaryOperation(
      Region[] lVal,
      Region[] rVal,
      final BitvectorManager bvmgr,
      final CBinaryExpression binaryExpr,
      final MachineModel machineModel) {

    final BinaryOperator binaryOperator = binaryExpr.getOperator();
    final CType calculationType = binaryExpr.getCalculationType();

    lVal = castCValue(lVal, calculationType, bvmgr, machineModel);
    if (binaryOperator != BinaryOperator.SHIFT_LEFT
        && binaryOperator != BinaryOperator.SHIFT_RIGHT) {
      /* For SHIFT-operations we do not cast the second operator.
       * We even do not need integer-promotion,
       * because the maximum SHIFT of 64 is lower than MAX_CHAR.
       *
       * ISO-C99 (6.5.7 #3): Bitwise shift operators
       * The integer promotions are performed on each of the operands.
       * The type of the result is that of the promoted left operand.
       * If the value of the right operand is negative or is greater than
       * or equal to the width of the promoted left operand,
       * the behavior is undefined.
       */
      rVal = castCValue(rVal, calculationType, bvmgr, machineModel);
    }

    Region[] result;
    switch (binaryOperator) {
      case PLUS:
      case MINUS:
      case DIVIDE:
      case MODULO:
      case MULTIPLY:
      case SHIFT_LEFT:
      case SHIFT_RIGHT:
      case BINARY_AND:
      case BINARY_OR:
      case BINARY_XOR:
        {
          result =
              arithmeticOperation(lVal, rVal, bvmgr, binaryOperator, calculationType, machineModel);
          result = castCValue(result, binaryExpr.getExpressionType(), bvmgr, machineModel);

          break;
        }

      case EQUALS:
      case NOT_EQUALS:
      case GREATER_THAN:
      case GREATER_EQUAL:
      case LESS_THAN:
      case LESS_EQUAL:
        {
          final Region tmp =
              booleanOperation(lVal, rVal, bvmgr, binaryOperator, calculationType, machineModel);
          // return 1 if expression holds, 0 otherwise

          int size = 32;
          if (calculationType instanceof CSimpleType) {
            final CSimpleType st = (CSimpleType) calculationType;
            if (st.getType() == CBasicType.INT || st.getType() == CBasicType.CHAR) {
              final int bitPerByte = machineModel.getSizeofCharInBits();
              final int numBytes = machineModel.getSizeof(st);
              size = bitPerByte * numBytes;
            }
          }

          result = bvmgr.wrapLast(tmp, size);
          // we do not cast here, because 0 and 1 should be small enough for every type.

          break;
        }

      default:
        throw new AssertionError("unhandled binary operator");
    }

    return result;
  }