@Override
  public Region[] visit(final CUnaryExpression unaryExpression) {
    final UnaryOperator unaryOperator = unaryExpression.getOperator();
    final CExpression unaryOperand = unaryExpression.getOperand();

    if (unaryOperator == UnaryOperator.SIZEOF) {
      return getSizeof(unaryOperand.getExpressionType());
    }

    final Region[] value = unaryOperand.accept(this);

    if (value == null) {
      return null;
    }

    switch (unaryOperator) {
      case MINUS: // -X == (0-X)
        return bvmgr.makeSub(bvmgr.makeNumber(BigInteger.ZERO, value.length), value);

      case SIZEOF:
        throw new AssertionError("SIZEOF should be handled before!");

      case AMPER: // valid expression, but it's a pointer value
        // TODO Not precise enough
        return getSizeof(unaryOperand.getExpressionType());
      case TILDE:
      default:
        // TODO handle unimplemented operators
        return null;
    }
  }
  protected static Region booleanOperation(
      final Region[] l,
      final Region[] r,
      final BitvectorManager bvmgr,
      final BinaryOperator op,
      final CType calculationType,
      final MachineModel machineModel) {

    boolean signed = true;
    if (calculationType instanceof CSimpleType) {
      signed = !((CSimpleType) calculationType).isUnsigned();
    }

    switch (op) {
      case EQUALS:
        return bvmgr.makeLogicalEqual(l, r);
      case NOT_EQUALS:
        return bvmgr.makeNot(bvmgr.makeLogicalEqual(l, r));
      case GREATER_THAN: // A>B <--> B<A
        return bvmgr.makeLess(r, l, signed);
      case GREATER_EQUAL: // A>=B <--> B<=A
        return bvmgr.makeLessOrEqual(r, l, signed);
      case LESS_THAN:
        return bvmgr.makeLess(l, r, signed);
      case LESS_EQUAL:
        return bvmgr.makeLessOrEqual(l, r, signed);

      default:
        throw new AssertionError("unknown binary operation: " + op);
    }
  }
  private static Region[] arithmeticOperation(
      final Region[] l,
      final Region[] r,
      final BitvectorManager bvmgr,
      final BinaryOperator op,
      final CType calculationType,
      final MachineModel machineModel) {

    switch (op) {
      case PLUS:
        return bvmgr.makeAdd(l, r);
      case MINUS:
        return bvmgr.makeSub(l, r);
      case DIVIDE:
      case MODULO:
      case MULTIPLY:
        // TODO implement multiplier circuit for Regions/BDDs?
        // this would be working for constant numbers (2*3), however timeout for variables (a*b ->
        // exponential bdd-size).
        return null;
      case SHIFT_LEFT:
      case SHIFT_RIGHT:
        // TODO implement shift circuit? this should be easier than multiplier,
        // because 'r' is smaller (max 64 for longlong), so many positions can be ignored
        return null;
      case BINARY_AND:
        return bvmgr.makeBinaryAnd(l, r);
      case BINARY_OR:
        return bvmgr.makeBinaryOr(l, r);
      case BINARY_XOR:
        return bvmgr.makeXor(l, r);

      default:
        throw new AssertionError("unknown binary operation: " + op);
    }
  }
  @Override
  public Region[] visit(CIdExpression idExp) {
    if (idExp.getDeclaration() instanceof CEnumerator) {
      CEnumerator enumerator = (CEnumerator) idExp.getDeclaration();
      if (enumerator.hasValue()) {
        return bvmgr.makeNumber(enumerator.getValue(), getSize(idExp.getExpressionType()));
      } else {
        return null;
      }
    }

    return predMgr.createPredicate(
        idExp.getDeclaration().getQualifiedName(),
        idExp.getExpressionType(),
        location,
        getSize(idExp.getExpressionType()),
        precision);
  }
  /**
   * 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;
  }
 private Region[] getSizeof(CType pType) {
   return bvmgr.makeNumber(machineModel.getSizeof(pType), machineModel.getSizeofInt());
 }
 @Override
 public Region[] visit(CIntegerLiteralExpression pE) {
   return bvmgr.makeNumber(pE.asLong(), getSize(pE.getExpressionType()));
 }
 @Override
 public Region[] visit(CCharLiteralExpression pE) {
   return bvmgr.makeNumber(pE.getCharacter(), getSize(pE.getExpressionType()));
 }
  /**
   * 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;
  }