edu.buaa.satla.analysis.util.VariableClassification.java Source code

Java tutorial

Introduction

Here is the source code for edu.buaa.satla.analysis.util.VariableClassification.java

Source

/*
 *  CPAchecker is a tool for configurable software verification.
 *  This file is part of CPAchecker.
 *
 *  Copyright (C) 2007-2014  Dirk Beyer
 *  All rights reserved.
 *
 *  Licensed under the Apache License, Version 2.0 (the "License");
 *  you may not use this file except in compliance with the License.
 *  You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 *
 *
 *  CPAchecker web page:
 *    http://cpachecker.sosy-lab.org
 */
package edu.buaa.satla.analysis.util;

import static com.google.common.base.Preconditions.checkArgument;
import static edu.buaa.satla.analysis.util.CFAUtils.leavingEdges;

import java.io.IOException;
import java.io.PrintWriter;
import java.io.Writer;
import java.math.BigInteger;
import java.util.ArrayDeque;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Queue;
import java.util.Set;
import java.util.TreeSet;
import java.util.logging.Level;

import javax.annotation.Nonnull;
import javax.annotation.Nullable;

import org.sosy_lab.common.Pair;
import org.sosy_lab.common.configuration.Configuration;
import org.sosy_lab.common.configuration.FileOption;
import org.sosy_lab.common.configuration.InvalidConfigurationException;
import org.sosy_lab.common.configuration.Option;
import org.sosy_lab.common.configuration.Options;
import org.sosy_lab.common.io.Files;
import org.sosy_lab.common.io.Path;
import org.sosy_lab.common.io.Paths;
import org.sosy_lab.common.log.LogManager;
import org.sosy_lab.common.time.Timer;
import org.sosy_lab.cpachecker.cfa.CFA;
import org.sosy_lab.cpachecker.cfa.Language;
import org.sosy_lab.cpachecker.cfa.ast.c.CArraySubscriptExpression;
import org.sosy_lab.cpachecker.cfa.ast.c.CAssignment;
import org.sosy_lab.cpachecker.cfa.ast.c.CBinaryExpression;
import org.sosy_lab.cpachecker.cfa.ast.c.CCastExpression;
import org.sosy_lab.cpachecker.cfa.ast.c.CCharLiteralExpression;
import org.sosy_lab.cpachecker.cfa.ast.c.CComplexCastExpression;
import org.sosy_lab.cpachecker.cfa.ast.c.CDeclaration;
import org.sosy_lab.cpachecker.cfa.ast.c.CExpression;
import org.sosy_lab.cpachecker.cfa.ast.c.CExpressionAssignmentStatement;
import org.sosy_lab.cpachecker.cfa.ast.c.CExpressionVisitor;
import org.sosy_lab.cpachecker.cfa.ast.c.CFieldReference;
import org.sosy_lab.cpachecker.cfa.ast.c.CFloatLiteralExpression;
import org.sosy_lab.cpachecker.cfa.ast.c.CFunctionCall;
import org.sosy_lab.cpachecker.cfa.ast.c.CFunctionCallAssignmentStatement;
import org.sosy_lab.cpachecker.cfa.ast.c.CFunctionCallExpression;
import org.sosy_lab.cpachecker.cfa.ast.c.CFunctionCallStatement;
import org.sosy_lab.cpachecker.cfa.ast.c.CIdExpression;
import org.sosy_lab.cpachecker.cfa.ast.c.CIdExpressionCollectorVisitor;
import org.sosy_lab.cpachecker.cfa.ast.c.CImaginaryLiteralExpression;
import org.sosy_lab.cpachecker.cfa.ast.c.CInitializer;
import org.sosy_lab.cpachecker.cfa.ast.c.CInitializerExpression;
import org.sosy_lab.cpachecker.cfa.ast.c.CInitializers;
import org.sosy_lab.cpachecker.cfa.ast.c.CIntegerLiteralExpression;
import org.sosy_lab.cpachecker.cfa.ast.c.CLeftHandSide;
import org.sosy_lab.cpachecker.cfa.ast.c.CParameterDeclaration;
import org.sosy_lab.cpachecker.cfa.ast.c.CPointerExpression;
import org.sosy_lab.cpachecker.cfa.ast.c.CRightHandSide;
import org.sosy_lab.cpachecker.cfa.ast.c.CRightHandSideVisitor;
import org.sosy_lab.cpachecker.cfa.ast.c.CSimpleDeclaration;
import org.sosy_lab.cpachecker.cfa.ast.c.CStatement;
import org.sosy_lab.cpachecker.cfa.ast.c.CStringLiteralExpression;
import org.sosy_lab.cpachecker.cfa.ast.c.CTypeIdExpression;
import org.sosy_lab.cpachecker.cfa.ast.c.CUnaryExpression;
import org.sosy_lab.cpachecker.cfa.ast.c.CUnaryExpression.UnaryOperator;
import org.sosy_lab.cpachecker.cfa.ast.c.CVariableDeclaration;
import org.sosy_lab.cpachecker.cfa.ast.c.DefaultCExpressionVisitor;
import org.sosy_lab.cpachecker.cfa.model.CFAEdge;
import org.sosy_lab.cpachecker.cfa.model.CFANode;
import org.sosy_lab.cpachecker.cfa.model.MultiEdge;
import org.sosy_lab.cpachecker.cfa.model.c.CAssumeEdge;
import org.sosy_lab.cpachecker.cfa.model.c.CDeclarationEdge;
import org.sosy_lab.cpachecker.cfa.model.c.CFunctionCallEdge;
import org.sosy_lab.cpachecker.cfa.model.c.CFunctionSummaryEdge;
import org.sosy_lab.cpachecker.cfa.model.c.CReturnStatementEdge;
import org.sosy_lab.cpachecker.cfa.model.c.CStatementEdge;
import org.sosy_lab.cpachecker.cfa.types.c.CCompositeType;
import org.sosy_lab.cpachecker.cfa.types.c.CPointerType;
import org.sosy_lab.cpachecker.cfa.types.c.CSimpleType;
import org.sosy_lab.cpachecker.cfa.types.c.CType;
import org.sosy_lab.cpachecker.exceptions.UnrecognizedCCodeException;

import com.google.common.base.Joiner;
import com.google.common.collect.HashMultimap;
import com.google.common.collect.LinkedHashMultimap;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.google.common.collect.Multimap;
import com.google.common.collect.Sets;

@Options(prefix = "cfa.variableClassification")
public class VariableClassification {

    @Option(secure = true, name = "logfile", description = "Dump variable classification to a file.")
    @FileOption(FileOption.Type.OUTPUT_FILE)
    private Path dumpfile = Paths.get("VariableClassification.log");

    @Option(secure = true, description = "Dump variable type mapping to a file.")
    @FileOption(FileOption.Type.OUTPUT_FILE)
    private Path typeMapFile = Paths.get("VariableTypeMapping.txt");

    @Option(secure = true, description = "Dump domain type statistics to a CSV file.")
    @FileOption(FileOption.Type.OUTPUT_FILE)
    private Path domainTypeStatisticsFile = null;

    @Option(secure = true, description = "Print some information about the variable classification.")
    private boolean printStatsOnStartup = false;

    /** name for return-variables, it is used for function-returns. */
    public static final String FUNCTION_RETURN_VARIABLE = "__retval__";
    private static final String SCOPE_SEPARATOR = "::";

    /** normally a boolean value would be 0 or 1,
     * however there are cases, where the values are only 0 and 1,
     * but the variable is not boolean at all: "int x; if(x!=0 && x!= 1){}".
     * so we allow only 0 as boolean value, and not 1. */
    private boolean allowOneAsBooleanValue = false;
    private Timer buildTimer = new Timer();

    private Set<String> allVars = null;

    private Set<String> nonIntBoolVars;
    private Set<String> nonIntEqVars;
    private Set<String> nonIntAddVars;

    private Dependencies dependencies;

    private Set<String> intBoolVars;
    private Set<String> intEqualVars;
    private Set<String> intAddVars;

    /** These sets contain all variables even ones of array, pointer or structure types.
     *  Such variables cannot be classified even as Int, so they are only kept in these sets in order
     *  not to break the classification of Int variables.*/
    private Set<String> assignedVariables; // Variables used in the left hand side
    // Initially contains variables used in assumes and assigned to pointer dereferences,
    // then all essential variables (by propagation)
    private Set<String> relevantVariables;
    private Set<String> irrelevantVariables; // leftVariables without rightVariables
    private Set<String> addressedVariables;

    /** Fields information doesn't take any aliasing information into account,
     *  fields are considered per type, not per composite instance */
    private Multimap<CCompositeType, String> assignedFields; // Fields used in the left hand side
    // Initially contains fields used in assumes and assigned to pointer dereferences,
    // then all essential fields (by propagation)
    private Multimap<CCompositeType, String> relevantFields;
    private Multimap<CCompositeType, String> irrelevantFields; // leftFields without rightFields

    private Multimap<VariableOrField, VariableOrField> assignments; // Variables and fields used in the right hand side

    private Set<Partition> intBoolPartitions;
    private Set<Partition> intEqualPartitions;
    private Set<Partition> intAddPartitions;

    private final CollectingLHSVisitor collectingLHSVisitor = new CollectingLHSVisitor();

    private final CFA cfa;
    private final LogManager logger;

    public VariableClassification(CFA cfa, Configuration config, LogManager pLogger)
            throws InvalidConfigurationException {
        checkArgument(cfa.getLanguage() == Language.C, "VariableClassification currently only supports C");
        config.inject(this);
        this.cfa = cfa;
        this.logger = pLogger;

        if (printStatsOnStartup) {
            build();
            printStats();
        }

    }

    private void printStats() {
        final Set<Partition> intBool = getIntBoolPartitions();
        int numOfBooleans = getIntBoolVars().size();

        int numOfIntEquals = 0;
        final Set<Partition> intEq = getIntEqualPartitions();
        for (Partition p : intEq) {
            numOfIntEquals += p.getVars().size();
        }

        int numOfIntAdds = 0;
        final Set<Partition> intAdd = getIntAddPartitions();
        for (Partition p : intAdd) {
            numOfIntAdds += p.getVars().size();
        }

        final String prefix = "\nVC ";
        StringBuilder str = new StringBuilder("VariableClassification Statistics\n");
        Joiner.on(prefix).appendTo(str, new String[] { "---------------------------------",
                "number of boolean vars:  " + numOfBooleans, "number of intEq vars:    " + numOfIntEquals,
                "number of intAdd vars:   " + numOfIntAdds, "number of all vars:      " + allVars.size(),
                "number of irrel. vars:   " + irrelevantVariables.size(),
                "number of addr. vars:    " + addressedVariables.size(),
                "number of irrel. fields: " + irrelevantFields.size(),
                "number of intBool partitions:  " + intBool.size(),
                "number of intEq partitions:    " + intEq.size(), "number of intAdd partitions:   " + intAdd.size(),
                "number of all partitions:      " + getPartitions().size(),
                "time for building classification: " + buildTimer });
        str.append("\n---------------------------------\n");

        logger.log(Level.INFO, str.toString());
    }

    /** This function does the whole work:
     * creating all maps, collecting vars, solving dependencies.
     * The function runs only once, after that it does nothing. */
    private void build() {
        if (allVars == null) {

            buildTimer.start();

            // init maps
            allVars = new HashSet<>();
            nonIntBoolVars = new HashSet<>();
            nonIntEqVars = new HashSet<>();
            nonIntAddVars = new HashSet<>();

            dependencies = new Dependencies();

            intBoolVars = new HashSet<>();
            intEqualVars = new HashSet<>();
            intAddVars = new HashSet<>();

            assignedVariables = new HashSet<>();
            relevantVariables = new HashSet<>();
            irrelevantVariables = new HashSet<>();
            addressedVariables = new HashSet<>();

            assignedFields = LinkedHashMultimap.create();
            relevantFields = LinkedHashMultimap.create();
            irrelevantFields = LinkedHashMultimap.create();

            assignments = LinkedHashMultimap.create();

            intBoolPartitions = new HashSet<>();
            intEqualPartitions = new HashSet<>();
            intAddPartitions = new HashSet<>();

            // fill maps
            collectVars();

            // we have collected the nonBooleanVars, lets build the needed booleanVars.
            buildOpposites();

            // add last vars to dependencies,
            // this allows to get partitions for all vars,
            // otherwise only dependent vars are in the partitions
            for (String var : allVars) {
                dependencies.addVar(var);
            }

            buildTimer.stop();

            if (dumpfile != null) { // option -noout
                try (Writer w = Files.openOutputFile(dumpfile)) {
                    w.append("IntBool\n\n");
                    w.append(intBoolVars.toString());
                    w.append("\n\nIntEq\n\n");
                    w.append(intEqualVars.toString());
                    w.append("\n\nIntAdd\n\n");
                    w.append(intAddVars.toString());
                    w.append("\n\nALL\n\n");
                    w.append(allVars.toString());
                    w.append("\n\nIRRELEVANT FIELDS\n\n");
                    w.append(irrelevantFields.toString());
                } catch (IOException e) {
                    logger.logUserException(Level.WARNING, e, "Could not write variable classification to file");
                }
            }

            if (typeMapFile != null) {
                dumpVariableTypeMapping(typeMapFile);
            }

            if (domainTypeStatisticsFile != null) {
                dumpDomainTypeStatistics(domainTypeStatisticsFile);
            }
        }
    }

    private void dumpDomainTypeStatistics(Path pDomainTypeStatisticsFile) {
        try (Writer w = Files.openOutputFile(pDomainTypeStatisticsFile)) {
            try (PrintWriter p = new PrintWriter(w)) {
                Object[][] statMapping = { { "intBoolVars", intBoolVars.size() },
                        { "intEqualVars", intEqualVars.size() }, { "intAddVars", intAddVars.size() },
                        { "allVars", allVars.size() }, { "irrelevantFields", irrelevantFields.size() },
                        { "intBoolVarsRelevant", countNumberOfRelevantVars(intBoolVars) },
                        { "intEqualVarsRelevant", countNumberOfRelevantVars(intEqualVars) },
                        { "intAddVarsRelevant", countNumberOfRelevantVars(intAddVars) },
                        { "allVarsRelevant", countNumberOfRelevantVars(allVars) } };
                // Write header
                for (int col = 0; col < statMapping.length; col++) {
                    p.print(statMapping[col][0]);
                    if (col != statMapping.length - 1) {
                        p.print("\t");
                    }
                }
                p.print("\n");
                // Write data
                for (int col = 0; col < statMapping.length; col++) {
                    p.print(statMapping[col][1]);
                    if (col != statMapping.length - 1) {
                        p.print("\t");
                    }
                }
                p.print("\n");
            }
        } catch (IOException e) {
            logger.logUserException(Level.WARNING, e, "Could not write variable classification statistics to file");
        }
    }

    public static Set<String> getVariablesOfExpression(CExpression expr) {
        Set<String> result = new HashSet<>();
        CIdExpressionCollectorVisitor collector = new CIdExpressionCollectorVisitor();

        expr.accept(collector);

        for (CIdExpression id : collector.getReferencedIdExpressions()) {
            String assignToVar = scopeVar(id);
            result.add(assignToVar);
        }

        return result;
    }

    public void dumpVariableTypeMapping(Path target) {
        try (Writer w = Files.openOutputFile(target)) {
            for (String var : getAllVars()) {
                byte type = 0;
                if (getIntBoolVars().contains(var)) {
                    type += 1 + 2 + 4; // IntBool is subset of IntEqualBool and IntAddEqBool
                } else if (getIntEqualVars().contains(var)) {
                    type += 2 + 4; // IntEqual is subset of IntAddEqBool
                } else if (getIntAddVars().contains(var)) {
                    type += 4;
                }
                w.append(String.format("%s\t%d%n", var, type));
            }
        } catch (IOException e) {
            logger.logUserException(Level.WARNING, e, "Could not write variable type mapping to file");
        }
    }

    /**
     * All variables, that may be assigned, but are not essential for reachability properties.
     * The variables are returned as a collection of scopedNames.
     * <p>
     * <strong>
     * Note: the collection includes all variables, including pointers, arrays and structures, i.e.
     *       non-Int variables.
     * </strong>
     * </p>
     */
    public Set<String> getIrrelevantVariables() {
        build();
        return irrelevantVariables;
    }

    private int countNumberOfRelevantVars(Collection<String> ofVars) {
        build();

        int result = 0;
        for (String var : ofVars) {
            if (relevantVariables.contains(var)) {
                result++;
            }
        }

        return result;
    }

    public boolean hasRelevantNonIntAddVars() {
        build();

        for (String var : nonIntAddVars) {
            if (relevantVariables.contains(var)) {
                return true;
            }
        }
        return false;
    }

    /**
     * All variables that may be essential for reachability properties.
     * The variables are returned as a collection of scopedNames.
     * <p>
     * <strong>
     * Note: the collection includes all variables, including pointers, arrays and structures, i.e.
     *       non-Int variables.
     * </strong>
     * </p>
     */
    public Set<String> getRelevantVariables() {
        build();
        return relevantVariables;
    }

    /**
     * All variables that have their addresses taken somewhere in the source code.
     * The variables are returned as a collection of scopedNames.
     * <p>
     * <strong>
     * Note: the collection includes all variables, including pointers, arrays and structures, i.e.
     *       non-Int variables.
     * </strong>
     * </p>
     */
    public Set<String> getAddressedVariables() {
        build();
        return addressedVariables;
    }

    /**
     * All fields that may be essential for reachability properties
     * (only fields accessed explicitly through either dot (.) or arrow (->) operator count).
     *
     * @return A collection of (CCompositeType, fieldName) mappings.
     */
    public Multimap<CCompositeType, String> getRelevantFields() {
        build();
        return relevantFields;
    }

    /**
     * All fields that are written somewhere
     * (explicitly with dot (.), arrow (->) operators or designated initializers), but
     * are not essential for reachability properties.
     *
     * @return A collection of (CCompositeType, fieldName) mappings.
     */
    public Multimap<CCompositeType, String> getIrrelevantFields() {
        build();
        return irrelevantFields;
    }

    /** This function returns a collection of scoped names.
     * This collection contains all vars. */
    public Set<String> getAllVars() {
        build();
        return allVars;
    }

    /** This function returns a collection of scoped names.
     * This collection contains all vars, that are boolean,
     * i.e. the value is 0 or 1. */
    public Set<String> getIntBoolVars() {
        build();
        return intBoolVars;
    }

    /** This function returns a collection of partitions.
     * Each partition contains only boolean vars. */
    public Set<Partition> getIntBoolPartitions() {
        build();
        return intBoolPartitions;
    }

    /** This function returns a collection of scoped names.
     * This collection contains all vars, that are only assigned or compared
     * for equality with integer values.
     * There are NO mathematical calculations (add, sub, mult) with these vars.
     * This collection does not contain any variable from "IntBool" or "IntAdd". */
    public Set<String> getIntEqualVars() {
        build();
        return intEqualVars;
    }

    /** This function returns a collection of partitions.
     * Each partition contains only vars,
     * that are only assigned or compared for equality with integer values.
     * This collection does not contains anypartition from "IntBool" or "IntAdd". */
    public Set<Partition> getIntEqualPartitions() {
        build();
        return intEqualPartitions;
    }

    /** This function returns a collection of scoped names.
     * This collection contains all vars, that are only used in simple calculations
     * (+, -, <, >, <=, >=, ==, !=, &, &&, |, ||, ^).
     * This collection does not contain any variable from "IntBool" or "IntEq". */
    public Set<String> getIntAddVars() {
        build();
        return intAddVars;
    }

    /** This function returns a collection of partitions.
     * Each partition contains only vars, that are used in simple calculations.
     * This collection does not contains anypartition from "IntBool" or "IntEq". */
    public Set<Partition> getIntAddPartitions() {
        build();
        return intAddPartitions;
    }

    /** This function returns a collection of partitions.
     * A partition contains all vars, that are dependent from each other. */
    public List<Partition> getPartitions() {
        build();
        return dependencies.getPartitions();
    }

    /** This function returns a partition containing all vars,
     * that are dependent with the given variable. */
    public Partition getPartitionForVar(String var) {
        build();
        return dependencies.getPartitionForVar(var);
    }

    /** This function returns a partition containing all vars,
     * that are dependent from a given CFAedge. */
    public Partition getPartitionForEdge(CFAEdge edge) {
        return getPartitionForEdge(edge, 0);
    }

    /** This function returns a partition containing all vars,
     * that are dependent from a given CFAedge.
     * The index is 0 for all edges, except functionCalls,
     * where it is the position of the param.
     * For the left-hand-side of the assignment of external functionCalls use -1. */
    public Partition getPartitionForEdge(CFAEdge edge, int index) {
        build();
        return dependencies.getPartitionForEdge(edge, index);
    }

    /** This function iterates over all edges of the cfa, collects all variables
     * and orders them into different sets, i.e. nonBoolean and nonIntEuqalNumber. */
    private void collectVars() {
        Collection<CFANode> nodes = cfa.getAllNodes();
        for (CFANode node : nodes) {
            for (CFAEdge edge : leavingEdges(node)) {
                handleEdge(edge);
            }
        }

        // if a value is not boolean, all dependent vars are not boolean and viceversa
        dependencies.solve(nonIntBoolVars);
        dependencies.solve(nonIntEqVars);
        dependencies.solve(nonIntAddVars);
    }

    /** This function builds the opposites of each non-x-vars-collection.
     * This method is responsible for the hierarchy of the variables. */
    private void buildOpposites() {
        for (final String var : allVars) {
            // we have this hierarchy of classes for variables:
            //        IntBool < IntEqBool < IntAddEqBool < AllInt
            // we define and build:
            //        IntBool = IntBool
            //        IntEq   = IntEqBool - IntBool
            //        IntAdd  = IntAddEqBool - IntEqBool
            //        Other   = IntAll - IntAddEqBool

            if (!nonIntBoolVars.contains(var)) {
                intBoolVars.add(var);
                intBoolPartitions.add(getPartitionForVar(var));

            } else if (!nonIntEqVars.contains(var)) {
                intEqualVars.add(var);
                intEqualPartitions.add(getPartitionForVar(var));

            } else if (!nonIntAddVars.contains(var)) {
                intAddVars.add(var);
                intAddPartitions.add(getPartitionForVar(var));
            }
        }

        // Propagate relevant variables from assumes and assignments to pointer dereferences to
        // other variables up to a fix-point (actually as the direction of dependency doesn't matter
        // it's just a BFS)
        Queue<VariableOrField> queue = new ArrayDeque<>(relevantVariables.size() + relevantFields.size());
        for (final String relevantVariable : relevantVariables) {
            queue.add(VariableOrField.newVariable(relevantVariable));
        }
        for (final Map.Entry<CCompositeType, String> relevantField : relevantFields.entries()) {
            queue.add(VariableOrField.newField(relevantField.getKey(), relevantField.getValue()));
        }
        while (!queue.isEmpty()) {
            final VariableOrField relevantVariableOrField = queue.poll();
            for (VariableOrField variableOrField : assignments.get(relevantVariableOrField)) {
                final VariableOrField.Variable variable = variableOrField.asVariable();
                final VariableOrField.Field field = variableOrField.asField();
                assert variable != null || field != null : "Sum type match failure: neither variable nor field!";
                if (variable != null && !relevantVariables.contains(variable.getScopedName())) {
                    relevantVariables.add(variable.getScopedName());
                    queue.add(variable);
                } else if (field != null
                        && !relevantFields.containsEntry(field.getCompositeType(), field.getName())) {
                    relevantFields.put(field.getCompositeType(), field.getName());
                    queue.add(field);
                }
            }
        }

        // assignedFields without relevantFields
        for (final CCompositeType t : assignedFields.keySet()) {
            for (final String field : assignedFields.get(t)) {
                if (!relevantFields.containsEntry(t, field)) {
                    irrelevantFields.put(t, field);
                }
            }
        }

        // we define: irrelevantVars == assignedVars without relevantVars
        for (final String var : assignedVariables) {
            if (!relevantVariables.contains(var)) {
                irrelevantVariables.add(var);
            }
        }
    }

    private static CCompositeType getCanonicalFieldOwnerType(CFieldReference fieldReference) {
        CType fieldOwnerType = fieldReference.getFieldOwner().getExpressionType().getCanonicalType();

        if (fieldOwnerType instanceof CPointerType) {
            fieldOwnerType = ((CPointerType) fieldOwnerType).getType();
        }
        assert fieldOwnerType instanceof CCompositeType : "Field owner should have composite type, but the field-owner type of expression "
                + fieldReference + " in " + fieldReference.getFileLocation() + " is " + fieldOwnerType
                + ", which is a " + fieldOwnerType.getClass().getSimpleName() + ".";
        final CCompositeType compositeType = (CCompositeType) fieldOwnerType;
        // Currently we don't pay attention to possible const and volatile modifiers
        if (compositeType.isConst() || compositeType.isVolatile()) {
            return new CCompositeType(false, false, compositeType.getKind(), compositeType.getMembers(),
                    compositeType.getName());
        } else {
            return compositeType;
        }
    }

    /** switch to edgeType and handle all expressions, that could be part of the edge. */
    private void handleEdge(CFAEdge edge) {
        switch (edge.getEdgeType()) {

        case AssumeEdge: {
            CExpression exp = ((CAssumeEdge) edge).getExpression();
            CFANode pre = edge.getPredecessor();

            VariablesCollectingVisitor dcv = new VariablesCollectingVisitor(pre);
            Set<String> vars = exp.accept(dcv);
            if (vars != null) {
                dependencies.addAll(vars, dcv.getValues(), edge, 0);
            }

            exp.accept(new BoolCollectingVisitor(pre));
            exp.accept(new IntEqualCollectingVisitor(pre));
            exp.accept(new IntAddCollectingVisitor(pre));

            exp.accept(new CollectingRHSVisitor(null));
            break;
        }

        case DeclarationEdge: {
            handleDeclarationEdge((CDeclarationEdge) edge);
            break;
        }

        case StatementEdge: {
            final CStatement statement = ((CStatementEdge) edge).getStatement();

            // normal assignment of variable, rightHandSide can be expression or (external) functioncall
            if (statement instanceof CAssignment) {
                handleAssignment(edge, (CAssignment) statement);

                // pure external functioncall
            } else if (statement instanceof CFunctionCallStatement) {
                handleExternalFunctionCall(edge,
                        ((CFunctionCallStatement) statement).getFunctionCallExpression().getParameterExpressions());

                ((CFunctionCallStatement) statement).getFunctionCallExpression()
                        .accept(new CollectingRHSVisitor(null));
            }

            break;
        }

        case FunctionCallEdge: {
            handleFunctionCallEdge((CFunctionCallEdge) edge);
            break;
        }

        case FunctionReturnEdge: {
            String scopedVarName = createFunctionReturnVariable(edge.getPredecessor().getFunctionName());
            dependencies.addVar(scopedVarName);
            Partition partition = getPartitionForVar(scopedVarName);
            partition.addEdge(edge, 0);
            break;
        }

        case ReturnStatementEdge: {
            // this is the 'x' from 'return (x);
            // adding a new temporary FUNCTION_RETURN_VARIABLE, that is not global (-> false)
            CReturnStatementEdge returnStatement = (CReturnStatementEdge) edge;
            if (returnStatement.getExpression().isPresent()) {
                String function = edge.getPredecessor().getFunctionName();
                handleExpression(edge, returnStatement.getExpression().get(),
                        scopeVar(function, FUNCTION_RETURN_VARIABLE),
                        VariableOrField.newVariable(createFunctionReturnVariable(function)));
            }
            break;
        }

        case MultiEdge:
            for (CFAEdge innerEdge : (MultiEdge) edge) {
                handleEdge(innerEdge);
            }
            break;

        case BlankEdge:
        case CallToReturnEdge:
            // other cases are not interesting
            break;

        default:
            throw new AssertionError("Unknoewn edgeType: " + edge.getEdgeType());
        }
    }

    /** This function handles a declaration with an optional initializer.
     * Only simple types are handled. */
    private void handleDeclarationEdge(final CDeclarationEdge edge) {
        CDeclaration declaration = edge.getDeclaration();
        if (!(declaration instanceof CVariableDeclaration)) {
            return;
        }

        CVariableDeclaration vdecl = (CVariableDeclaration) declaration;
        String varName = vdecl.getQualifiedName();

        // "connect" the edge with its partition
        Set<String> var = Sets.newHashSetWithExpectedSize(1);
        var.add(varName);
        dependencies.addAll(var, new HashSet<BigInteger>(), edge, 0);

        // only simple types (int, long) are allowed for booleans, ...
        if (!(vdecl.getType() instanceof CSimpleType)) {
            nonIntBoolVars.add(varName);
            nonIntEqVars.add(varName);
            nonIntAddVars.add(varName);
        }

        final CInitializer initializer = vdecl.getInitializer();
        List<CExpressionAssignmentStatement> l;

        try {
            l = CInitializers.convertToAssignments(vdecl, edge);
        } catch (UnrecognizedCCodeException should_not_happen) {
            throw new AssertionError(should_not_happen);
        }

        for (CExpressionAssignmentStatement init : l) {
            final CLeftHandSide lhsExpression = init.getLeftHandSide();
            final VariableOrField lhs = lhsExpression.accept(collectingLHSVisitor);

            final CExpression rhs = init.getRightHandSide();
            rhs.accept(new CollectingRHSVisitor(lhs));
        }

        if ((initializer == null) || !(initializer instanceof CInitializerExpression)) {
            return;
        }

        CExpression exp = ((CInitializerExpression) initializer).getExpression();
        if (exp == null) {
            return;
        }

        handleExpression(edge, exp, varName, VariableOrField.newVariable(varName));
    }

    /** This function handles normal assignments of vars. */
    private void handleAssignment(final CFAEdge edge, final CAssignment assignment) {
        CRightHandSide rhs = assignment.getRightHandSide();
        CExpression lhs = assignment.getLeftHandSide();
        String function = isGlobal(lhs) ? null : edge.getPredecessor().getFunctionName();
        String varName = scopeVar(function, lhs.toASTString());

        // only simple types (int, long) are allowed for booleans, ...
        if (!(lhs instanceof CIdExpression && lhs.getExpressionType() instanceof CSimpleType)) {
            nonIntBoolVars.add(varName);
            nonIntEqVars.add(varName);
            nonIntAddVars.add(varName);
        }

        dependencies.addVar(varName);

        final VariableOrField lhsVariableOrField = lhs.accept(collectingLHSVisitor);

        if (rhs instanceof CExpression) {
            handleExpression(edge, ((CExpression) rhs), varName, lhsVariableOrField);

        } else if (rhs instanceof CFunctionCallExpression) {
            // use FUNCTION_RETURN_VARIABLE for RIGHT SIDE
            CFunctionCallExpression func = (CFunctionCallExpression) rhs;
            String functionName = func.getFunctionNameExpression().toASTString(); // TODO correct?

            if (cfa.getAllFunctionNames().contains(functionName)) {
                // TODO is this case really appearing or is it always handled as "functionCallEdge"?
                dependencies.add(createFunctionReturnVariable(functionName), varName);

            } else {
                // external function
                Partition partition = getPartitionForVar(varName);
                partition.addEdge(edge, -1); // negative value, because all positives are used for params
            }

            rhs.accept(new CollectingRHSVisitor(lhsVariableOrField));

            handleExternalFunctionCall(edge, func.getParameterExpressions());

        } else {
            throw new AssertionError("unhandled assignment: " + edge.getRawStatement());
        }
    }

    /** This function handles the call of an external function
     * without an assignment of the result.
     * example: "printf("%d", output);" or "assert(exp);" */
    private void handleExternalFunctionCall(final CFAEdge edge, final List<CExpression> params) {
        for (int i = 0; i < params.size(); i++) {
            final CExpression param = params.get(i);

            /* special case: external functioncall with possible side-effect!
             * this is the only statement, where a pointer-operation is allowed
             * and the var can be boolean, intEqual or intAdd,
             * because we know, the variable can have a random (unknown) value after the functioncall.
             * example: "scanf("%d", &input);" */
            if (param instanceof CUnaryExpression && UnaryOperator.AMPER == ((CUnaryExpression) param).getOperator()
                    && ((CUnaryExpression) param).getOperand() instanceof CIdExpression) {
                final CIdExpression id = (CIdExpression) ((CUnaryExpression) param).getOperand();
                final String varName = id.getDeclaration().getQualifiedName();

                dependencies.addVar(varName);
                Partition partition = getPartitionForVar(varName);
                partition.addEdge(edge, i);

            } else {
                // "printf("%d", output);" or "assert(exp);"
                // TODO do we need the edge? ignore it?

                CFANode pre = edge.getPredecessor();
                VariablesCollectingVisitor dcv = new VariablesCollectingVisitor(pre);
                Set<String> vars = param.accept(dcv);
                if (vars != null) {
                    dependencies.addAll(vars, dcv.getValues(), edge, i);
                }

                param.accept(new BoolCollectingVisitor(pre));
                param.accept(new IntEqualCollectingVisitor(pre));
                param.accept(new IntAddCollectingVisitor(pre));
            }
        }
    }

    /** This function puts each param in same partition than its arg.
     * If there the functionresult is assigned, it is also handled. */
    private void handleFunctionCallEdge(CFunctionCallEdge edge) {

        // overtake arguments from last functioncall into function,
        // get args from functioncall and make them equal with params from functionstart
        final List<CExpression> args = edge.getArguments();
        final List<CParameterDeclaration> params = edge.getSuccessor().getFunctionParameters();
        final String innerFunctionName = edge.getSuccessor().getFunctionName();
        final String scopedRetVal = createFunctionReturnVariable(innerFunctionName);

        // functions can have more args than params used in the call
        assert args.size() >= params.size();

        for (int i = 0; i < params.size(); i++) {
            CParameterDeclaration param = params.get(i);
            String varName = param.getQualifiedName();

            // only simple types (int, long) are allowed for booleans, ...
            if (!(param.getType() instanceof CSimpleType)) {
                nonIntBoolVars.add(varName);
                nonIntEqVars.add(varName);
                nonIntAddVars.add(varName);
            }

            // build name for param and evaluate it
            // this variable is not global (->false)
            handleExpression(edge, args.get(i), varName, i, VariableOrField.newVariable(varName));
        }

        // create dependency for functionreturn
        CFunctionSummaryEdge func = edge.getSummaryEdge();
        CFunctionCall statement = func.getExpression();

        // a=f();
        if (statement instanceof CFunctionCallAssignmentStatement) {
            CFunctionCallAssignmentStatement call = (CFunctionCallAssignmentStatement) statement;
            CExpression lhs = call.getLeftHandSide();
            String function = isGlobal(lhs) ? null : edge.getPredecessor().getFunctionName();
            String varName = scopeVar(function, lhs.toASTString());
            dependencies.add(scopedRetVal, varName);

            final VariableOrField lhsVariableOrField = lhs.accept(collectingLHSVisitor);

            assignments.put(lhsVariableOrField, VariableOrField.newVariable(scopedRetVal));

            // f(); without assignment
        } else if (statement instanceof CFunctionCallStatement) {
            // next line is not necessary, but we do it for completeness, TODO correct?
            dependencies.addVar(scopedRetVal);
        }
    }

    /** evaluates an expression and adds containing vars to the sets. */
    private void handleExpression(CFAEdge edge, CExpression exp, String varName, final VariableOrField lhs) {
        handleExpression(edge, exp, varName, 0, lhs);
    }

    /** evaluates an expression and adds containing vars to the sets.
     * the id is the position of the expression in the edge,
     * it is 0 for all edges except a FuntionCallEdge. */
    private void handleExpression(CFAEdge edge, CExpression exp, String varName, int id,
            final VariableOrField lhs) {
        CFANode pre = edge.getPredecessor();

        VariablesCollectingVisitor dcv = new VariablesCollectingVisitor(pre);
        Set<String> vars = exp.accept(dcv);
        if (vars == null) {
            vars = Sets.newHashSetWithExpectedSize(1);
        }

        vars.add(varName);
        dependencies.addAll(vars, dcv.getValues(), edge, id);

        BoolCollectingVisitor bcv = new BoolCollectingVisitor(pre);
        Set<String> possibleBoolean = exp.accept(bcv);
        handleResult(varName, possibleBoolean, nonIntBoolVars);

        IntEqualCollectingVisitor ncv = new IntEqualCollectingVisitor(pre);
        Set<String> possibleIntEqualVars = exp.accept(ncv);
        handleResult(varName, possibleIntEqualVars, nonIntEqVars);

        IntAddCollectingVisitor icv = new IntAddCollectingVisitor(pre);
        Set<String> possibleIntAddVars = exp.accept(icv);
        handleResult(varName, possibleIntAddVars, nonIntAddVars);

        exp.accept(new CollectingRHSVisitor(lhs));
    }

    /** adds the variable to notPossibleVars, if possibleVars is null.  */
    private void handleResult(String varName, Collection<String> possibleVars, Collection<String> notPossibleVars) {
        if (possibleVars == null) {
            notPossibleVars.add(varName);
        }
    }

    /** Returns a scoped name for a given IdExpression. */
    static String scopeVar(final CIdExpression exp) {
        return exp.getDeclaration().getQualifiedName();
    }

    private static String scopeVar(@Nullable final String function, final String var) {
        return (function == null) ? (var) : (function + SCOPE_SEPARATOR + var);
    }

    private static boolean isGlobal(CExpression exp) {
        if (exp instanceof CIdExpression) {
            CSimpleDeclaration decl = ((CIdExpression) exp).getDeclaration();
            if (decl instanceof CDeclaration) {
                return ((CDeclaration) decl).isGlobal();
            }
        }
        return false;
    }

    public static String createFunctionReturnVariable(final String function) {
        return function + SCOPE_SEPARATOR + FUNCTION_RETURN_VARIABLE;
    }

    /** returns the value of a (nested) IntegerLiteralExpression
     * or null for everything else. */
    public static BigInteger getNumber(CExpression exp) {
        if (exp instanceof CIntegerLiteralExpression) {
            return ((CIntegerLiteralExpression) exp).getValue();

        } else if (exp instanceof CUnaryExpression) {
            CUnaryExpression unExp = (CUnaryExpression) exp;
            BigInteger value = getNumber(unExp.getOperand());
            if (value == null) {
                return null;
            }
            switch (unExp.getOperator()) {
            case MINUS:
                return value.negate();
            default:
                return null;
            }

        } else if (exp instanceof CCastExpression) {
            return getNumber(((CCastExpression) exp).getOperand());

        } else {
            return null;
        }
    }

    /** returns true, if the expression contains a casted binaryExpression. */
    private boolean isNestedBinaryExp(CExpression exp) {
        if (exp instanceof CBinaryExpression) {
            return true;

        } else if (exp instanceof CCastExpression) {
            return isNestedBinaryExp(((CCastExpression) exp).getOperand());

        } else {
            return false;
        }
    }

    @Override
    public String toString() {
        build();

        StringBuilder str = new StringBuilder();
        str.append("\nALL  " + allVars.size() + "\n    " + allVars);
        str.append("\nIntBool  " + intBoolVars.size() + "\n    " + intBoolVars);
        str.append("\nIntEq  " + intEqualVars.size() + "\n    " + intEqualVars);
        str.append("\nIntAdd  " + intAddVars.size() + "\n    " + intAddVars);
        return str.toString();
    }

    /** This Visitor evaluates an Expression. It collects all variables.
     * a visit of IdExpression or CFieldReference returns a collection containing the varName,
     * other visits return the inner visit-results.
    * The Visitor also collects all numbers used in the expression. */
    private class VariablesCollectingVisitor implements CExpressionVisitor<Set<String>, RuntimeException> {

        private CFANode predecessor;
        private Set<BigInteger> values = new TreeSet<>();

        public VariablesCollectingVisitor(CFANode pre) {
            this.predecessor = pre;
        }

        public Set<BigInteger> getValues() {
            return values;
        }

        @Override
        public Set<String> visit(CArraySubscriptExpression exp) {
            return null;
        }

        @Override
        public Set<String> visit(CBinaryExpression exp) {

            // for numeral values
            BigInteger val1 = getNumber(exp.getOperand1());
            Set<String> operand1;
            if (val1 == null) {
                operand1 = exp.getOperand1().accept(this);
            } else {
                values.add(val1);
                operand1 = null;
            }

            // for numeral values
            BigInteger val2 = getNumber(exp.getOperand2());
            Set<String> operand2;
            if (val2 == null) {
                operand2 = exp.getOperand2().accept(this);
            } else {
                values.add(val2);
                operand2 = null;
            }

            // handle vars from operands
            if (operand1 == null) {
                return operand2;
            } else if (operand2 == null) {
                return operand1;
            } else {
                operand1.addAll(operand2);
                return operand1;
            }
        }

        @Override
        public Set<String> visit(CCastExpression exp) {
            BigInteger val = getNumber(exp.getOperand());
            if (val == null) {
                return exp.getOperand().accept(this);
            } else {
                values.add(val);
                return null;
            }
        }

        @Override
        public Set<String> visit(CComplexCastExpression exp) {
            // TODO complex numbers are not supported for evaluation right now, this
            // way of handling the variables my be wrong

            BigInteger val = getNumber(exp.getOperand());
            if (val == null) {
                return exp.getOperand().accept(this);
            } else {
                values.add(val);
                return null;
            }
        }

        @Override
        public Set<String> visit(CFieldReference exp) {
            String varName = exp.toASTString(); // TODO "(*p).x" vs "p->x"
            String function = isGlobal(exp) ? "" : predecessor.getFunctionName();
            Set<String> ret = Sets.newHashSetWithExpectedSize(1);
            ret.add(scopeVar(function, varName));
            return ret;
        }

        @Override
        public Set<String> visit(CIdExpression exp) {
            Set<String> ret = Sets.newHashSetWithExpectedSize(1);
            ret.add(exp.getDeclaration().getQualifiedName());
            return ret;
        }

        @Override
        public Set<String> visit(CCharLiteralExpression exp) {
            return null;
        }

        @Override
        public Set<String> visit(CFloatLiteralExpression exp) {
            return null;
        }

        @Override
        public Set<String> visit(CImaginaryLiteralExpression exp) {
            return exp.getValue().accept(this);
        }

        @Override
        public Set<String> visit(CIntegerLiteralExpression exp) {
            values.add(exp.getValue());
            return null;
        }

        @Override
        public Set<String> visit(CStringLiteralExpression exp) {
            return null;
        }

        @Override
        public Set<String> visit(CTypeIdExpression exp) {
            return null;
        }

        @Override
        public Set<String> visit(CUnaryExpression exp) {
            BigInteger val = getNumber(exp);
            if (val == null) {
                return exp.getOperand().accept(this);
            } else {
                values.add(val);
                return null;
            }
        }

        @Override
        public Set<String> visit(CPointerExpression exp) {
            BigInteger val = getNumber(exp);
            if (val == null) {
                return exp.getOperand().accept(this);
            } else {
                values.add(val);
                return null;
            }
        }
    }

    /** This Visitor evaluates an Expression. It also collects all variables.
     * Each visit-function returns
     * - null, if the expression is not boolean
     * - a collection, if the expression is boolean.
     * The collection contains all boolean vars. */
    private class BoolCollectingVisitor extends VariablesCollectingVisitor {

        public BoolCollectingVisitor(CFANode pre) {
            super(pre);
        }

        @Override
        public Set<String> visit(CFieldReference exp) {
            nonIntBoolVars.addAll(super.visit(exp));
            return null;
        }

        @Override
        public Set<String> visit(CBinaryExpression exp) {
            Set<String> operand1 = exp.getOperand1().accept(this);
            Set<String> operand2 = exp.getOperand2().accept(this);

            if (operand1 == null || operand2 == null) { // a+123 --> a is not boolean
                if (operand1 != null) {
                    nonIntBoolVars.addAll(operand1);
                }
                if (operand2 != null) {
                    nonIntBoolVars.addAll(operand2);
                }
                return null;
            }

            switch (exp.getOperator()) {

            case EQUALS:
            case NOT_EQUALS: // ==, != work with boolean operands
                if (operand1.isEmpty() || operand2.isEmpty()) {
                    // one operand is Zero (or One, if allowed)
                    operand1.addAll(operand2);
                    return operand1;
                }
                // We compare 2 variables. There is no guarantee, that they are boolean!
                // Example: (a!=b) && (b!=c) && (c!=a)
                // -> FALSE for boolean, but TRUE for {1,2,3}

                //$FALL-THROUGH$

            default: // +-*/ --> no boolean operators, a+b --> a and b are not boolean
                nonIntBoolVars.addAll(operand1);
                nonIntBoolVars.addAll(operand2);
                return null;
            }
        }

        @Override
        public Set<String> visit(CIntegerLiteralExpression exp) {
            BigInteger value = exp.getValue();
            if (BigInteger.ZERO.equals(value) || (allowOneAsBooleanValue && BigInteger.ONE.equals(value))) {
                return new HashSet<>(0);
            } else {
                return null;
            }
        }

        @Override
        public Set<String> visit(CUnaryExpression exp) {
            Set<String> inner = exp.getOperand().accept(this);

            if (inner == null) {
                return null;
            } else { // PLUS, MINUS, etc --> not boolean
                nonIntBoolVars.addAll(inner);
                return null;
            }
        }

        @Override
        public Set<String> visit(CPointerExpression exp) {
            Set<String> inner = exp.getOperand().accept(this);

            if (inner == null) {
                return null;
            } else {
                nonIntBoolVars.addAll(inner);
                return null;
            }
        }
    }

    /** This Visitor evaluates an Expression.
     * Each visit-function returns
     * - null, if the expression contains calculations
     * - a collection, if the expression is a number, unaryExp, == or != */
    private class IntEqualCollectingVisitor extends VariablesCollectingVisitor {

        public IntEqualCollectingVisitor(CFANode pre) {
            super(pre);
        }

        @Override
        public Set<String> visit(CCastExpression exp) {
            BigInteger val = getNumber(exp.getOperand());
            if (val == null) {
                return exp.getOperand().accept(this);
            } else {
                return new HashSet<>(0);
            }
        }

        @Override
        public Set<String> visit(CFieldReference exp) {
            nonIntEqVars.addAll(super.visit(exp));
            return null;
        }

        @Override
        public Set<String> visit(CBinaryExpression exp) {

            // for numeral values
            BigInteger val1 = getNumber(exp.getOperand1());
            Set<String> operand1;
            if (val1 == null) {
                operand1 = exp.getOperand1().accept(this);
            } else {
                operand1 = new HashSet<>(0);
            }

            // for numeral values
            BigInteger val2 = getNumber(exp.getOperand2());
            Set<String> operand2;
            if (val2 == null) {
                operand2 = exp.getOperand2().accept(this);
            } else {
                operand2 = new HashSet<>(0);
            }

            // handle vars from operands
            if (operand1 == null || operand2 == null) { // a+0.2 --> no simple number
                if (operand1 != null) {
                    nonIntEqVars.addAll(operand1);
                }
                if (operand2 != null) {
                    nonIntEqVars.addAll(operand2);
                }
                return null;
            }

            switch (exp.getOperator()) {

            case EQUALS:
            case NOT_EQUALS: // ==, != work with numbers
                operand1.addAll(operand2);
                return operand1;

            default: // +-*/ --> no simple operators
                nonIntEqVars.addAll(operand1);
                nonIntEqVars.addAll(operand2);
                return null;
            }
        }

        @Override
        public Set<String> visit(CIntegerLiteralExpression exp) {
            return new HashSet<>(0);
        }

        @Override
        public Set<String> visit(CUnaryExpression exp) {

            // if exp is numeral
            BigInteger val = getNumber(exp);
            if (val != null) {
                return new HashSet<>(0);
            }

            // if exp is binary expression
            Set<String> inner = exp.getOperand().accept(this);
            if (isNestedBinaryExp(exp)) {
                return inner;
            }

            if (inner != null) {
                nonIntEqVars.addAll(inner);
            }
            return null;
        }

        @Override
        public Set<String> visit(CPointerExpression exp) {

            // if exp is numeral
            BigInteger val = getNumber(exp);
            if (val != null) {
                return new HashSet<>(0);
            }

            // if exp is binary expression
            Set<String> inner = exp.getOperand().accept(this);
            if (isNestedBinaryExp(exp)) {
                return inner;
            }

            // if exp is unknown
            if (inner == null) {
                return null;
            }

            nonIntEqVars.addAll(inner);
            return null;
        }
    }

    /** This Visitor evaluates an Expression.
     * Each visit-function returns
     * - a collection, if the expression is a var or a simple mathematical
     *   calculation (add, sub, <, >, <=, >=, ==, !=, !),
     * - else null */
    private class IntAddCollectingVisitor extends VariablesCollectingVisitor {

        public IntAddCollectingVisitor(CFANode pre) {
            super(pre);
        }

        @Override
        public Set<String> visit(CCastExpression exp) {
            return exp.getOperand().accept(this);
        }

        @Override
        public Set<String> visit(CFieldReference exp) {
            nonIntAddVars.addAll(super.visit(exp));
            return null;
        }

        @Override
        public Set<String> visit(CBinaryExpression exp) {
            Set<String> operand1 = exp.getOperand1().accept(this);
            Set<String> operand2 = exp.getOperand2().accept(this);

            if (operand1 == null || operand2 == null) { // a+0.2 --> no simple number
                if (operand1 != null) {
                    nonIntAddVars.addAll(operand1);
                }
                if (operand2 != null) {
                    nonIntAddVars.addAll(operand2);
                }
                return null;
            }

            switch (exp.getOperator()) {

            case PLUS:
            case MINUS:
            case LESS_THAN:
            case LESS_EQUAL:
            case GREATER_THAN:
            case GREATER_EQUAL:
            case EQUALS:
            case NOT_EQUALS:
            case BINARY_AND:
            case BINARY_XOR:
            case BINARY_OR:
                // this calculations work with all numbers
                operand1.addAll(operand2);
                return operand1;

            default: // *, /, %, shift --> no simple calculations
                nonIntAddVars.addAll(operand1);
                nonIntAddVars.addAll(operand2);
                return null;
            }
        }

        @Override
        public Set<String> visit(CIntegerLiteralExpression exp) {
            return new HashSet<>(0);
        }

        @Override
        public Set<String> visit(CUnaryExpression exp) {
            Set<String> inner = exp.getOperand().accept(this);
            if (inner == null) {
                return null;
            }
            if (exp.getOperator() == UnaryOperator.MINUS) {
                return inner;
            }

            // *, ~, etc --> not simple
            nonIntAddVars.addAll(inner);
            return null;
        }

        @Override
        public Set<String> visit(CPointerExpression exp) {
            Set<String> inner = exp.getOperand().accept(this);
            if (inner == null) {
                return null;
            }

            nonIntAddVars.addAll(inner);
            return null;
        }
    }

    private class CollectingLHSVisitor extends DefaultCExpressionVisitor<VariableOrField, RuntimeException> {

        @Override
        public VariableOrField visit(final CArraySubscriptExpression e) {
            final VariableOrField result = e.getArrayExpression().accept(this);
            e.getSubscriptExpression().accept(new CollectingRHSVisitor(result));
            return result;
        }

        @Override
        public VariableOrField visit(final CFieldReference e) {
            final CCompositeType compositeType = getCanonicalFieldOwnerType(e);
            assignedFields.put(compositeType, e.getFieldName());
            final VariableOrField result = VariableOrField.newField(compositeType, e.getFieldName());
            if (e.isPointerDereference()) {
                e.getFieldOwner().accept(new CollectingRHSVisitor(result));
            } else {
                e.getFieldOwner().accept(this);
            }
            return result;
        }

        @Override
        public VariableOrField visit(final CPointerExpression e) {
            e.getOperand().accept(new CollectingRHSVisitor(null));
            return null;
        }

        @Override
        public VariableOrField visit(final CComplexCastExpression e) {
            return e.getOperand().accept(this);
        }

        @Override
        public VariableOrField visit(final CCastExpression e) {
            return e.getOperand().accept(this);
        }

        @Override
        public VariableOrField visit(final CIdExpression e) {
            final VariableOrField.Variable result = VariableOrField
                    .newVariable(e.getDeclaration().getQualifiedName());
            assignedVariables.add(result.getScopedName());
            return result;
        }

        @Override
        protected VariableOrField visitDefault(final CExpression e) {
            throw new IllegalArgumentException("The expression should not occur in the left hand side");
        }
    }

    private void addVariableOrField(final @Nullable VariableOrField lhs, final VariableOrField rhs) {
        if (lhs != null) {
            assignments.put(lhs, rhs);
        } else {
            final VariableOrField.Variable variable = rhs.asVariable();
            final VariableOrField.Field field = rhs.asField();
            if (variable != null) {
                relevantVariables.add(variable.getScopedName());
            } else {
                relevantFields.put(field.getCompositeType(), field.getName());
            }
        }
    }

    private class CollectingRHSVisitor extends DefaultCExpressionVisitor<Void, RuntimeException>
            implements CRightHandSideVisitor<Void, RuntimeException> {

        private final @Nullable VariableOrField lhs;
        private boolean addressed = false;

        CollectingRHSVisitor(@Nullable VariableOrField pLhs) {
            lhs = pLhs;
        }

        @Override
        public Void visit(final CArraySubscriptExpression e) {
            CollectingRHSVisitor arrayExprVisitor = new CollectingRHSVisitor(null);
            arrayExprVisitor.addressed = true;
            e.getArrayExpression().accept(arrayExprVisitor);
            return e.getSubscriptExpression().accept(this);
        }

        @Override
        public Void visit(final CFieldReference e) {
            final CCompositeType compositeType = getCanonicalFieldOwnerType(e);
            addVariableOrField(lhs, VariableOrField.newField(compositeType, e.getFieldName()));
            return e.getFieldOwner().accept(this);
        }

        @Override
        public Void visit(final CBinaryExpression e) {
            e.getOperand1().accept(this);
            return e.getOperand2().accept(this);
        }

        @Override
        public Void visit(final CUnaryExpression e) {
            if (e.getOperator() != UnaryOperator.AMPER) {
                return e.getOperand().accept(this);
            } else {
                addressed = true;
                e.getOperand().accept(this);
                addressed = false;
                return null;
            }
        }

        @Override
        public Void visit(final CPointerExpression e) {
            return e.getOperand().accept(this);
        }

        @Override
        public Void visit(final CComplexCastExpression e) {
            return e.getOperand().accept(this);
        }

        @Override
        public Void visit(final CCastExpression e) {
            return e.getOperand().accept(this);
        }

        @Override
        public Void visit(final CIdExpression e) {
            final VariableOrField.Variable variable = VariableOrField
                    .newVariable(e.getDeclaration().getQualifiedName());
            addVariableOrField(lhs, variable);
            if (addressed) {
                addressedVariables.add(variable.getScopedName());
            }
            return null;
        }

        @Override
        public Void visit(CFunctionCallExpression e) throws RuntimeException {
            for (CExpression param : e.getParameterExpressions()) {
                param.accept(this);
            }
            return null;
        }

        @Override
        protected Void visitDefault(final CExpression e) {
            return null;
        }
    }

    /** A Partition is a Wrapper for a Collection of vars, values and edges.
    * The Partitions are disjunct, so no variable and no edge is in 2 Partitions. */
    public class Partition {

        private final Set<String> vars = new HashSet<>();
        private final Set<BigInteger> values = Sets.newTreeSet();
        private final Multimap<CFAEdge, Integer> edges = HashMultimap.create();

        private final Map<String, Partition> varToPartition;
        private final Map<Pair<CFAEdge, Integer>, Partition> edgeToPartition;

        public Partition(Map<String, Partition> varToPartition,
                Map<Pair<CFAEdge, Integer>, Partition> edgeToPartition) {
            this.varToPartition = varToPartition;
            this.edgeToPartition = edgeToPartition;
        }

        public Set<String> getVars() {
            return vars;
        }

        public Set<BigInteger> getValues() {
            return values;
        }

        public Multimap<CFAEdge, Integer> getEdges() {
            return edges;
        }

        /** adds the var to the partition and also to the global set of all vars. */
        public void add(String var) {
            vars.add(var);
            allVars.add(var);
            varToPartition.put(var, this);
        }

        public void addValues(Set<BigInteger> newValues) {
            values.addAll(newValues);
        }

        public void addEdge(CFAEdge edge, int index) {
            edges.put(edge, index);
            edgeToPartition.put(Pair.of(edge, index), this);
        }

        /** copies all data from other to current partition */
        public void merge(Partition other) {
            assert this.varToPartition == other.varToPartition;

            this.vars.addAll(other.vars);
            this.values.addAll(other.values);
            this.edges.putAll(other.edges);

            // update mapping of vars
            for (String var : other.vars) {
                varToPartition.put(var, this);
            }

            // update mapping of edges
            for (Entry<CFAEdge, Integer> edge : other.edges.entries()) {
                edgeToPartition.put(Pair.of(edge.getKey(), edge.getValue()), this);
            }
        }

        @Override
        public boolean equals(Object other) {
            if (other instanceof Partition) {
                Partition p = (Partition) other;
                return this.vars == p.vars;
            } else {
                return false;
            }
        }

        @Override
        public int hashCode() {
            return vars.hashCode();
        }

        @Override
        public String toString() {
            return vars.toString() + " --> " + Arrays.toString(values.toArray());
        }
    }

    /** This class stores dependencies between variables.
     * It sorts vars into partitions.
     * Dependent vars are in the same partition. Partitions are independent. */
    private class Dependencies {

        /** partitions, each of them contains vars */
        private final List<Partition> partitions = Lists.newArrayList();

        /** map to get partition of a var */
        private final Map<String, Partition> varToPartition = Maps.newHashMap();

        /** table to get a partition for a edge. */
        private final Map<Pair<CFAEdge, Integer>, Partition> edgeToPartition = Maps.newHashMap();

        public List<Partition> getPartitions() {
            return partitions;
        }

        public Partition getPartitionForVar(String var) {
            return varToPartition.get(var);
        }

        public Partition getPartitionForEdge(CFAEdge edge, int index) {
            return edgeToPartition.get(Pair.of(edge, index));
        }

        /** This function creates a dependency between function1::var1 and function2::var2. */
        public void add(String var1, String var2) {

            // if both vars exists in some dependencies,
            // either ignore them or merge their partitions
            Partition partition1 = varToPartition.get(var1);
            Partition partition2 = varToPartition.get(var2);
            if (partition1 != null && partition2 != null) {

                // swap partitions, we create partitions in the order they are used
                if (partitions.lastIndexOf(partition1) > partitions.lastIndexOf(partition2)) {
                    Partition tmp = partition2;
                    partition2 = partition1;
                    partition1 = tmp;
                }

                if (!partition1.equals(partition2)) {
                    partition1.merge(partition2);
                    partitions.remove(partition2);
                }

                // if only left side of dependency exists, add right side into same partition
            } else if (partition1 != null) {
                partition1.add(var2);

                // if only right side of dependency exists, add left side into same partition
            } else if (partition2 != null) {
                partition2.add(var1);

                // if none side is in any existing partition, create new partition
            } else {
                Partition partition = new Partition(varToPartition, edgeToPartition);
                partition.add(var1);
                partition.add(var2);
                partitions.add(partition);
            }
        }

        /** This function adds a group of vars to exactly one partition.
         * The values are stored in the partition.
         * The partition is "connected" with the expression.
         *
         * @param vars group of variables tobe added
         * @param values numbers, with are used in an expression together with the variables
         * @param edge where is the expression
         * @param index if an edge has several expressions, this index is the position ofthe expression
         *  */
        public void addAll(Collection<String> vars, Set<BigInteger> values, CFAEdge edge, int index) {
            if (vars == null || vars.isEmpty()) {
                return;
            }

            Iterator<String> iter = vars.iterator();

            // we use same varName for all other vars --> dependency
            String var = iter.next();

            // first add one single var
            addVar(var);

            // then add all other vars, they are dependent from the first var
            while (iter.hasNext()) {
                add(var, iter.next());
            }

            Partition partition = getPartitionForVar(var);
            partition.addValues(values);
            partition.addEdge(edge, index);
        }

        /** This function adds one single variable to the partitions.
         * This is the only method to create a partition with only one element. */
        public void addVar(String var) {

            // if var exists, we can ignore it, otherwise create new partition for var
            if (!varToPartition.containsKey(var)) {
                Partition partition = new Partition(varToPartition, edgeToPartition);
                partition.add(var);
                partitions.add(partition);
            }
        }

        /** This function adds all depending vars to the set, if necessary.
         * If A depends on B and A is part of the set, B is added to the set, and vice versa.
        * Example: If A is not boolean, B is not boolean. */
        public void solve(final Collection<String> vars) {
            for (Partition partition : partitions) {

                // is at least one var from the partition part of vars
                boolean isDependency = false;
                for (String var : partition.getVars()) {
                    if (vars.contains(var)) {
                        isDependency = true;
                        break;
                    }
                }

                // add all dependend vars to vars
                if (isDependency) {
                    vars.addAll(partition.getVars());
                }
            }
        }

        @Override
        public String toString() {
            StringBuilder str = new StringBuilder("[");
            for (Partition partition : partitions) {
                str.append(partition.toString() + ",\n");
            }
            str.append("]\n\n");

            //      for (Pair<CFAEdge, Integer> edge : edgeToPartition.keySet()) {
            //        str.append(edge.getFirst().getRawStatement() + " :: "
            //            + edge.getSecond() + " --> " + edgeToPartition.get(edge) + "\n");
            //      }
            return str.toString();
        }
    }

    private static class VariableOrField {
        private static class Variable extends VariableOrField {
            private Variable(final @Nonnull String scopedName) {
                this.scopedName = scopedName;
            }

            public @Nonnull String getScopedName() {
                return scopedName;
            }

            @Override
            public String toString() {
                return getScopedName();
            }

            @Override
            public boolean equals(final Object o) {
                if (o == this) {
                    return true;
                } else if (!(o instanceof Variable)) {
                    return false;
                } else {
                    final Variable other = (Variable) o;
                    return this.scopedName.equals(other.scopedName);
                }
            }

            @Override
            public int hashCode() {
                return scopedName.hashCode();
            }

            private final @Nonnull String scopedName;
        }

        private static class Field extends VariableOrField {
            private Field(final CCompositeType composite, final String name) {
                this.composite = composite;
                this.name = name;
            }

            public CCompositeType getCompositeType() {
                return composite;
            }

            public String getName() {
                return name;
            }

            @Override
            public String toString() {
                return composite + SCOPE_SEPARATOR + name;
            }

            @Override
            public boolean equals(final Object o) {
                if (o == this) {
                    return true;
                } else if (!(o instanceof Field)) {
                    return false;
                } else {
                    final Field other = (Field) o;
                    return this.composite.equals(other.composite) && this.name.equals(other.name);
                }
            }

            @Override
            public int hashCode() {
                final int prime = 67;
                return prime * composite.hashCode() + name.hashCode();
            }

            private @Nonnull CCompositeType composite;
            private @Nonnull String name;
        }

        public static Variable newVariable(final String scopedName) {
            return new Variable(scopedName);
        }

        public static Field newField(final @Nonnull CCompositeType composite, final @Nonnull String name) {
            return new Field(composite, name);
        }

        public @Nullable Variable asVariable() {
            if (this instanceof Variable) {
                return (Variable) this;
            } else {
                return null;
            }
        }

        public @Nullable Field asField() {
            if (this instanceof Field) {
                return (Field) this;
            } else {
                return null;
            }
        }
    }
}