public class Parameters
{

    public static String    PROPERTYFILE                             = "rockit.properties";

    // ================= PARAMETERS CONFIGURABLE IN PROPERTYFILE

    /**
     * Path to store temporary files, which are deleted again after program execution.
     */
    public static String    TEMP_PATH                                = "temp/";

    /**
     * Enables cutting-plane inference (do not add all constraints at once but add them step by step)
     * # Advantage: Problems are much smaller, thus much faster to solve
     * # Disadvantage: More than one problem has to be solved (but usually the overall time is still smaller)
     */
    public static boolean   USE_CUTTING_PLANE_INFERENCE              = true;

    /**
     * If activated, z variables will be aggregated.
     * 
     * This is usually more effective than the reduction of z variables (Z_VARIABLES_REDUCTION).
     * Please note that these two techniques can not be combined. If both are set to true, only
     * Z_VARIABLES_AGGREGATION will be applied.
     * 
     * Advantage: Less formulas in ILP, ILP runs faster.
     * Disadvantage: No known disadvantage. Maybe a slightly slower Java Code.
     * 
     */
    public static boolean   USE_CUTTING_PLANE_AGGREGATION            = true;

    /**
     * Time limit in seconds. Limits the total time expended for the gurobi solver (in seconds).
     * Note that this only limits the time of the gurobi solver. Since there can be multiple calls of the gurobi solver, the overall runtime
     * is usually larger.
     * If -1 is set, time limit is infinitive.
     */
    public static double    TIME_LIMIT                               = -1;

    /**
     * Sets the gap of the Gurobi solver. This gives exacter (value around 0.000001) but slower or
     * more approximative (value 0.01) but faster solutions.
     * 
     * If the gap is set to -1, the standard Gurobi gap is used.
     * 
     * HINT: If your problem does not terminate, then increase this value (take for instance 0.01)
     * 
     * In particular, the following Parameters are set.
     * 
     * model.getEnv().set(GRB.DoubleParam.FeasibilityTol, 1e-6);
     * model.getEnv().set(GRB.DoubleParam.IntFeasTol, 1e-6);
     * model.getEnv().set(GRB.DoubleParam.MIPGap, 1e-6);
     * model.getEnv().set(GRB.DoubleParam.OptimalityTol, 1e-6);
     */
    public static double    GAP                                      = -1;

    /**
     * If false, we compute the MAP State with the standard solver. If true, we perform learning.
     */
    public static boolean   USE_LEARNING                             = false;

    /**
     * Enables larger debug output.
     */
    public static boolean   DEBUG_OUTPUT                             = false;

    /**
     * Simplifies formulas with negatives weights like it is done in classic Markov Logic solvers like Tuffy or Alchemy.
     * 
     * 
     * //-2 student(a1) v !advisedBy(a2,a1) v !advisedBy(a1,a2)
     * //-1 student(a1) v !advisedBy(a1,a2)
     * //-1 student(a1) v !advisedBy(a2,a1)
     * 
     * //1 student(a1) v advisedBy(a1,a2)
     * //1 student(a1) v advisedBy(a2,a1)
     * 
     * The formulars with negative weights are changed so that:
     * - Their weight is multiplied with -1 (made positive).
     * - Each non-negated hidden predicate (in the restriction part) is set to its negation .
     * - Each negated hidden predicate (in the restriction part) is set to be not negated.
     * - Formular becomes a conjunction
     * 
     * The formulas with conjunction are changed in a way that:
     * - The weight is divided by the number of hidden predicates.
     * - For each predicate a new formula is created.
     * 
     */
    public static boolean   SIMPLIFY_NEGATIVE_WEIGHT_AND_CONJUNCTION = true;

    /**
     * If activated, the evidence occuring in ground formulas are deleted.
     * This results in a lower number of ground formulas and easier ground formulas due to omitted literals.
     * 
     * The disadvantage is higher runtime due to dublicate detection mechanisms required for each ground formula.
     * Furthermore, most of the evidence detection is later on done by the ILP in a very effective way.
     */
    public static boolean   LEVERAGE_EVIDENCE                        = true;

    /**
	 * 
	 */
    public static boolean   CONVERT_STRING_VALUES                    = true;

    /**
     * Database configurations: username
     */
    public static String    SQL_USERNAME                             = "root";

    /**
     * Database configurations: password
     */
    public static String    SQL_PASSWORD                             = "mannheim1234";
    /**
     * Database configurations: database name
     */
    public static String    SQL_DATABASE                             = "rockit";
    /**
     * Database configurations: url to database system
     */
    public static String    SQL_URL                                  = "jdbc:mysql://127.0.0.1/";

    public static int       THREAD_NUMBER                            = -1;

    // ================= PARAMETERS NOT CONFIGURABLE FOR USER

    // ================= SAMPLING PARAMETERS

    public static boolean   USE_SAMPLING                             = false;

    public static boolean   USE_SYMMETRIES_IN_MARGINAL_INFERENCE     = true;

    // ================= LEARNING PARAMETERS

    /**
     * In the Objective Evaluator instances of hard formulas will get a weight of 100
     */
    public static double    EVALUATION_WEIGHT_FOR_HARD_FORMULA       = 100000;

    /**
     * Learning rate
     */
    public static double    LEARNING_RATE                            = 1.0;

    /**
     * Algorithm used to solve continuous models or the root node of a MIP model
     * (-1=automatic, 0=primal simplex, 1=dual simplex, 2=barrier, 3=concurrent, 4=deterministic concurrent).
     * Concurrent optimizers run multiple solvers on multiple threads simultaneously, and choose the one that
     * finishes first. Deterministic concurrent (Method=4) gives the exact same result each time, while Method=3
     * is often faster but can produce different optimal bases when run multiple times. In the current release,
     * the default Automatic (-1) will typically choose non-deterministic concurrent (Method=3) for an LP, barrier
     * (Method=2) for a QP, and dual (Method=1) for the MIP root node. Only simplex and barrier algorithms are available
     * for continuous QP models. Only primal and dual simplex are available for solving the root of an MIQP model.
     * 
     */
    public static int       GUROBI_PARAMETER_METHOD                  = -1;

    public static Random    random                                   = new Random(1);

    public static ILPSolver ILP_SOLVER                               = ILPSolver.GUROBI;

    public static String    SCIP                                     = "";


    /**
     * Reads the parameters from the properties file and set the parameters in this class accordingly.
     * 
     * @throws ReadOrWriteToFileException
     */
    public static void readPropertyFile() throws ReadOrWriteToFileException
    {
        Properties properties = new Properties();
        try {
            properties.load(new FileInputStream(Parameters.PROPERTYFILE));
        } catch(IOException e) {
            throw new ReadOrWriteToFileException("Please define your properties data in a rockit.property file");
        }
        Parameters.SQL_URL = properties.getProperty("sql_url");
        Parameters.SQL_USERNAME = properties.getProperty("sql_username");
        Parameters.SQL_PASSWORD = properties.getProperty("sql_password");
        Parameters.SQL_DATABASE = properties.getProperty("sql_database");
        Parameters.TEMP_PATH = properties.getProperty("temp_path");
        File saveDir = new File(Parameters.TEMP_PATH);
        if(!saveDir.exists()) {
            saveDir.mkdirs();
        }
        if(properties.getProperty("use_cutting_plane_inference").equalsIgnoreCase("true")) {
            Parameters.USE_CUTTING_PLANE_INFERENCE = true;
        } else {
            Parameters.USE_CUTTING_PLANE_INFERENCE = false;
        }
        if(properties.getProperty("use_cutting_plane_aggregation").equalsIgnoreCase("true")) {
            Parameters.USE_CUTTING_PLANE_AGGREGATION = true;
        } else {
            Parameters.USE_CUTTING_PLANE_AGGREGATION = false;
        }
        Parameters.GAP = Double.parseDouble(properties.getProperty("gap"));
        Parameters.TIME_LIMIT = Long.parseLong(properties.getProperty("time_limit"));
        if(properties.getProperty("debug_output").equalsIgnoreCase("true")) {
            Parameters.DEBUG_OUTPUT = true;
        } else {
            Parameters.DEBUG_OUTPUT = false;
        }
        if(properties.getProperty("simplify_negative_weight_and_conjunction").equalsIgnoreCase("true")) {
            Parameters.SIMPLIFY_NEGATIVE_WEIGHT_AND_CONJUNCTION = true;
        } else {
            Parameters.SIMPLIFY_NEGATIVE_WEIGHT_AND_CONJUNCTION = false;
        }

        if(properties.getProperty("simplify_negative_weight_and_conjunction").equalsIgnoreCase("true")) {
            Parameters.SIMPLIFY_NEGATIVE_WEIGHT_AND_CONJUNCTION = true;
        } else {
            Parameters.SIMPLIFY_NEGATIVE_WEIGHT_AND_CONJUNCTION = false;
        }
        if(properties.getProperty("use_symmetries_in_marginal_inference").equalsIgnoreCase("true")) {
            Parameters.USE_SYMMETRIES_IN_MARGINAL_INFERENCE = true;
        } else {
            Parameters.USE_SYMMETRIES_IN_MARGINAL_INFERENCE = false;
        }
        if(properties.getProperty("convert_string_values").equalsIgnoreCase("true")) {
            Parameters.CONVERT_STRING_VALUES = true;
        } else {
            Parameters.CONVERT_STRING_VALUES = false;
        }
        try {
            Parameters.THREAD_NUMBER = Integer.parseInt(properties.getProperty("number_of_threads"));
            if(Parameters.THREAD_NUMBER <= 0) {
                int cores = Runtime.getRuntime().availableProcessors();
                Parameters.THREAD_NUMBER = cores;
            }
        } catch(NumberFormatException e) {
            throw new ReadOrWriteToFileException("Could not read number_of_threads from property file. Either the parameter is missing, or some non integer value is provided. ");
        }

        if(properties.containsKey("ilp_solver")) {
            try {
                Parameters.ILP_SOLVER = ILPSolver.valueOf(properties.getProperty("ilp_solver"));
            } catch(Exception e) {
                System.err.println("The selected ILP solver (" + properties.getProperty("ilp_solver") + ") does not exist");
                e.printStackTrace();
                System.exit(-1);
            }
        }
        
        if(properties.containsKey("scip")) {
            SCIP = properties.getProperty("scip").trim();
        }
    }
    // public static Random random = new Random(123456);

} public class MySQLConnector
{

    private Connection conn = null;


    public MySQLConnector() throws DatabaseException
    {
        this.connect();
    }


    /**
     * Returns the actual singelton object or creates a new one.
     * 
     * @return
     */
    /*
     * public static MySQLConnector getMySQLConnector()
     * {
     * if (ref == null)
     * // it's ok, we can call this constructor
     * ref = new MySQLConnector();
     * return ref;
     * }
     * public Object clone()
     * throws CloneNotSupportedException
     * {
     * throw new CloneNotSupportedException();
     * }
     */

    public boolean connect() throws DatabaseException
    {

        try {
            Class.forName("com.mysql.jdbc.Driver");
        } catch(ClassNotFoundException e) {
            e.printStackTrace();
            throw new DatabaseException("Class SQLConnector: ClassNotFoundException: Problems in initiating the SQL Connection.");
        }

        // load properties

        String url = Parameters.SQL_URL;
        String username = Parameters.SQL_USERNAME;
        String password = Parameters.SQL_PASSWORD;

        try {
            conn = DriverManager.getConnection(url, username, password);
        } catch(SQLException e) {
            e.printStackTrace();
            throw new DatabaseException("Class SQLConnector: SQLException: Problems connecting with database. Check username, url, and password.");
        }

        // give in memory tables more space
        this.executeQuery("SET max_heap_table_size = 1844674407370954752;");

        if(Parameters.DEBUG_OUTPUT)
            System.out.println("Database connection established");
        return true;
    }


    public void useDatabase() throws DatabaseException
    {
        String database = Parameters.SQL_DATABASE;
        this.executeQuery("USE `" + database + "`");
    }


    public void deleteAll() throws DatabaseException
    {
        String database = Parameters.SQL_DATABASE;
        // delete all tables
        try {
            this.executeQuery("CREATE Database `" + database + "`");
        } catch(DatabaseException e) {
            this.executeQuery("DROP Database IF EXISTS `" + database + "` ");
            this.executeQuery("CREATE Database `" + database + "`");
        }
        this.executeQuery("USE `" + database + "`");

    }


    /**
     * Executes an SQL query, which has no output. Examples are all sorts of
     * create, delete, and update Queries.
     * 
     * @param SQLQuery
     * @throws DatabaseException
     */

    public void executeQuery(String SQLQuery) throws DatabaseException
    {
        // long time = System.currentTimeMillis();
        Statement stmt;
        try {
            stmt = conn.createStatement();

            // System.out.println("without seeelect " +SQLQuery);

            stmt.executeUpdate(SQLQuery);
            stmt.close();
        } catch(SQLException e) {
            throw new DatabaseException(Messages.printDatabaseExceptionError(SQLQuery, e.getMessage()));
        }
        // this.executionTime = System.currentTimeMillis()-time;
    }


    /**
     * Returns a PreparedStatement for this connection.
     * 
     * @throws DatabaseException
     */
    /*
     * public PreparedStatement returnPreparedStatement(String query)
     * throws DatabaseException {
     * long time = System.currentTimeMillis();
     * PreparedStatement state = null;
     * state = conn.prepareStatement(query);
     * this.executionTime = System.currentTimeMillis()-time;
     * return state;
     * }
     */

    /**
     * Fills the prepared query insert into `similarity`.`individual` " +
     * "(name, position, isBigABox) values( ? , ?, ? )"
     * 
     * with the right values and executes the query afterwards.
     * 
     * @param preStatement
     * @param ind
     * @param bigABox
     * @throws DatabaseException
     */
    /*
     * private void insertIndividual(PreparedStatement preStatement,
     * Individual ind, boolean bigABox) throws DatabaseException {
     * preStatement.setString(1, ind.getName());
     * preStatement.setInt(2, ind.getPosition());
     * preStatement.setBoolean(3, bigABox);
     * preStatement.addBatch();
     * }
     */

    /**
     * Executes a SQL Query which has several rows and columns as output.
     * 
     * The output is given as a Result set.
     * 
     * @param sqlQuery
     * @return ResultSet
     * @throws DatabaseException
     * @throws DatabaseException
     */
    public ResultSet executeSelectQuery(String sqlQuery) throws DatabaseException
    {
        // long time = System.currentTimeMillis();
        ResultSet rs = null;
        Statement s;
        try {
            s = this.conn.createStatement();
            s.executeQuery(sqlQuery);
            rs = s.getResultSet();
        } catch(SQLException e) {
            throw new DatabaseException(Messages.printDatabaseExceptionError(sqlQuery, e.getMessage()));
        }
        // this.executionTime = System.currentTimeMillis()-time;

        return rs;
    }


    /**
     * Executes a query, which has a single String output.
     * 
     * If no data is returned, then the function returns "null".
     * 
     * @param sqlQuery
     * @return
     * @throws DatabaseException
     */
    public String executeStringQuery(String sqlQuery) throws DatabaseException
    {
        // long time = System.currentTimeMillis();

        ResultSet rs = executeSelectQuery(sqlQuery);

        // If some Data has been found, return the first entry as String.

        // this.executionTime = System.currentTimeMillis()-time;
        try {
            if(rs.next()) {
                String result = rs.getString(1);
                rs.getStatement().close();
                rs.close();
                return result;
            } else {
                return null;
            }
        } catch(SQLException e) {
            throw new DatabaseException("An SQL error occured at /n Query: " + sqlQuery + "/n position: " + e.getMessage());
        }
    }


    /**
     * Execute a query which has a single long output
     * 
     * @param sqlQuery
     * @return long output of the query.
     * @throws DatabaseException
     */
    public long executeLongQuery(String sqlQuery) throws DatabaseException
    {
        // long time = System.currentTimeMillis();
        ResultSet rs = executeSelectQuery(sqlQuery);
        long result;
        try {
            rs.next();
            result = rs.getLong(1);
            rs.getStatement().close();
            rs.close();
            // this.executionTime = System.currentTimeMillis()-time;
        } catch(SQLException e) {
            throw new DatabaseException("An SQL error occured at /n Query: " + sqlQuery + "/n position: " + e.getMessage());
        }
        return result;

    }


    /*
     * public void reconnect() throws DatabaseException{
     * this.close();
     * this.connect();
     * }
     */
    /**
     * Disconnects the database Connection and frees all allocated resources.
     * 
     * @return true, if disconnection was successful (ended without errors) and
     *         false otherwise.
     * @throws DatabaseException
     */
    public boolean close() throws DatabaseException
    {
        if(conn != null) {

            try {
                this.conn.close();
                System.out.println("Database connection terminated");
                return true;
            } catch(Exception e) {
                e.printStackTrace();
                throw new DatabaseException("Could not close the connection.");
            }
        }
        return false;
    }


    public void dropDatabase()
    {
        String database = Parameters.SQL_DATABASE;

        /*
         * for (String tableName: tableNames) {
         * StringBuilder delete = new StringBuilder();
         * delete.append("DROP table ").append(tableName);
         * //System.out.println(delete.toString());
         * this.executeQuery(delete.toString());
         * }
         * tableNames = new HashSet<String>();
         */
        try {
            this.executeQuery("DROP Database `" + database + "`");
            this.executeQuery("RESET QUERY CACHE");
        } catch(DatabaseException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }


    public void dropTable(String tableName) throws DatabaseException
    {
        // drop old table (eventually)
        StringBuilder deleteTable = new StringBuilder();
        deleteTable.append("DROP TABLE IF EXISTS `");
        deleteTable.append(tableName).append("`; ");
        this.executeQuery(deleteTable.toString());
    }


    /**
     * Creates an in-memory table.
     * 
     * @param tableName
     *            Name of the table
     * @param doubleName
     *            [optional] name of the double column. Will be inserted before the other columns
     * @param colNames
     *            name of the colums
     * @throws DatabaseException
     */
    public void createInMemoryTable(String tableName, String doubleColName, boolean temporaryTable, ArrayList<String> colNames, Integer colSize) throws DatabaseException
    {
        // this.tableNames.add(tableName);
        StringBuilder createTable = new StringBuilder();
        StringBuilder primaryKey = new StringBuilder();
        primaryKey.append(" PRIMARY KEY (");
        createTable.append("CREATE ");
        // if(tableName.contains("varString")){
        // temporaryTable=true;
        // }

        if(temporaryTable && !Parameters.DEBUG_OUTPUT)
            createTable.append("TEMPORARY ");
        createTable.append("TABLE  `").append(tableName).append("` ( ");
        if(doubleColName != null) {
            createTable.append(doubleColName).append(" double, ");
        }
        for(int i = 0; i < colNames.size(); i++) {
            createTable.append(" `").append(colNames.get(i));
            createTable.append("` char(").append(colSize).append(") NOT NULL, INDEX USING HASH (`");
            createTable.append(colNames.get(i)).append("`) , ");
            primaryKey.append("`").append(colNames.get(i)).append("`");
            if(i < colNames.size() - 1) {
                primaryKey.append(" , ");
            }
        }
        primaryKey.append(")   ");
        createTable.append(primaryKey);
        createTable.append(") ENGINE = MEMORY DEFAULT CHARSET=latin1;");
        try {
            this.executeQuery(createTable.toString());
        } catch(DatabaseException e) {
            System.out.println(createTable);
            System.out.println(e.getMessage());
            StringBuilder drop = new StringBuilder();
            drop.append("DROP TABLE ").append(tableName);
            this.executeQuery(drop.toString());
            this.executeQuery(createTable.toString());
        }
    }


    public void addData(String tableName, ArrayList<String[]> values, String filename) throws DatabaseException
    {
        this.addData(tableName, null, values, filename);
    }


    /**
     * Adds data efficiently with prepared statement.
     * 
     * TODO: possible futher optimizations:
     * - it is not neccessary any more to really store the ground values. In principle,
     * one could directly read the GroundValues file, efficiently create db files and read them in afterwards.
     * 
     * @param tableName
     *            Name of the table where the data should be added.
     * @param doubleValues
     *            [optional] double values to add, if any.
     * @param values
     *            normal string values to add.
     * @throws DatabaseException
     * @throws ReadOrWriteToFileException
     */
    public void addData(String tableName, ArrayList<Double> doubleValues, ArrayList<String[]> values, String filename) throws DatabaseException
    {
        if(values.size() > 0) {
            if(values.size() > 1000) {
                // faster
                MyFileWriter writer = null;
                try {
                    writer = new MyFileWriter(filename);
                } catch(ReadOrWriteToFileException e) {
                    e.printStackTrace();
                    throw new DatabaseException("Insert failed because temporary file " + filename + " could not been created.");
                }

                for(int i = 0; i < values.size(); i++) {
                    StringBuilder sb = new StringBuilder();
                    if(doubleValues != null) {
                        sb.append(doubleValues.get(i)).append("\t");
                    }
                    String[] groundings = values.get(i);
                    for(String grounding : groundings) {
                        sb.append(grounding).append("\t");
                    }
                    writer.writeln(sb.toString());
                }
                writer.closeFile();
                StringBuilder insertQuery = new StringBuilder();
                insertQuery.append("LOAD DATA LOCAL INFILE '").append(filename).append("' REPLACE INTO TABLE ").append(tableName);
                this.executeQuery(insertQuery.toString());
                writer.deleteFile();

            } else {
                StringBuilder addQuery = new StringBuilder();
                addQuery.append("INSERT IGNORE INTO `");
                addQuery.append(tableName);
                addQuery.append("`  VALUES");
                for(int i = 0; i < values.size(); i++) {
                    addQuery.append("(");
                    if(doubleValues != null) {
                        addQuery.append(doubleValues.get(i));
                        addQuery.append(",");
                    }
                    String[] value = values.get(i);
                    for(int j = 0; j < value.length; j++) {
                        addQuery.append("'");
                        addQuery.append(value[j]);
                        addQuery.append("'");
                        if(j < value.length - 1) {
                            addQuery.append(",");
                        }
                    }
                    addQuery.append(")");
                    if(i < values.size() - 1) {
                        addQuery.append(",");
                    }
                }
                this.executeQuery(addQuery.toString());
            }
            /*
             * // Faster for few insert values:
             * StringBuilder addQuery = new StringBuilder();
             * addQuery.append("INSERT IGNORE INTO `");
             * addQuery.append(tableName);
             * addQuery.append("`  VALUES(");
             * if (doubleValues != null) {
             * addQuery.append("?,");
             * }
             * for (int i = 0; i < values.get(0).length; i++) {
             * addQuery.append("?");
             * if (i < values.get(0).length - 1) {
             * addQuery.append(",");
             * }
             * }
             * addQuery.append(");");
             * // Lock table for faster writing
             * StringBuilder lockTable = new StringBuilder();
             * lockTable.append("LOCK TABLES ").append(tableName)
             * .append(" WRITE;");
             * this.executeQuery(lockTable.toString());
             * java.sql.PreparedStatement statement = conn.prepareStatement(addQuery.toString());
             * int posValues = 0;
             * if (doubleValues != null
             * && doubleValues.size() != values.size()) {
             * throw new DatabaseException(
             * "The ground value list and the list of double values do not have the same size in Predicate "
             * + tableName
             * + ". There is something wrong with your input.");
             * }
             * for (String[] groundValue : values) {
             * int posQuery = 1;
             * // if it is a Double value, the first row is affected
             * if (doubleValues != null) {
             * statement.setDouble(posQuery,
             * doubleValues.get(posValues));
             * posQuery++;
             * }
             * // for any normal groundvalue do
             * for (int i = 0; i < groundValue.length; i++) {
             * String value = groundValue[i];
             * statement.setString(posQuery, value);
             * posQuery++;
             * }
             * statement.execute();
             * posValues++;
             * }
             * this.executeQuery("UNLOCK TABLES;");
             * statement.close();
             * }
             */
        }
    }

    /*
     * public long getExecutionTime() {
     * return executionTime;
     * }
     */

} public class SQLQueryGenerator
{
    /**
     * This Function returns ready baked sql statements.
     * 
     * With these formulas it can be checked which variables violate restriction
     * formulas. The output is in a format that directly results in ILP
     * restrictions.
     * 
     * Basically, for every hidden predicate in the restriction formula an
     * expression
     * [+|-]||[PredicateName]|[value1]|...|[valueN]||[+|-]||[NextPredicate...
     * 
     * The first part identifies if this variable will be positive "+" or
     * negative "-" in the ILP. The second part perfectly fits with the naming
     * conventions of variables. Thus, the expression says something like:
     * "add/substract variableX" to ILP.
     * 
     * Example Query which is generated: SELECT CONCAT('-||subsumes|', xx.c1,
     * '|', xx.c2, '||','-||subsumes|', xx.c2, '|', xx.c3, '||','+||subsumes|',
     * xx.c1, '|', xx.c3) FROM ( SELECT x0.field0 as c1,x2.field0 as
     * c3,x1.field0 as c2 FROM class x0,class x1,class x2,subsumes y0,subsumes
     * y1 WHERE y0.field0 = x0.field0 AND y0.field1 = x1.field0 AND y1.field0 =
     * x1.field0 AND y1.field1 = x2.field0 ) as xx LEFT JOIN subsumes subsumes2
     * ON subsumes2.field0 = xx.c1 AND subsumes2.field1 = xx.c3 WHERE
     * subsumes2.field0 IS NULL
     * 
     * 
     * @param model
     * @return
     * @throws ParseException
     * @throws SolveException
     */
    public static HashSet<FormulaHard> getSQLQueriesForHardFormulas(Model model) throws ParseException, SolveException
    {
        HashSet<FormulaHard> sqlQueries = new HashSet<FormulaHard>();
        // if it is a Restriction Formula
        for(FormulaAbstract formulaAbstract : model.getFormulas()) {
            if(formulaAbstract.getClass().equals(FormulaHard.class)) {
                FormulaHard formula = (FormulaHard) formulaAbstract;

                // SELECT PART
                StringBuilder select = new StringBuilder();
                select.append("SELECT ");

                // sign if predicate is positive or negative
                select.append(getSQLQuerySelectSnippedVariables(formula, "xx"));

                StringBuilder sqlQuery = new StringBuilder();
                // Put select part together
                sqlQuery.append(select.toString());
                // get the sql query from the sqlstatementwithoutselect
                String sqlWithoutSelect = "";
                // only formulas cutting plane inference which do not produce
                // evidence (restriction size > 1).
                if(formula.isCuttingPlaneInferenceUsed() && formula.getRestrictions().size() > 1) {
                    sqlWithoutSelect = SQLQueryGenerator.getSQLStatementWithoutSelect(formula, true, true);
                } else {
                    formula.useCuttingPlaneInference(false);
                    sqlWithoutSelect = SQLQueryGenerator.getSQLStatementWithoutSelectWithoutCPI(formula, true, false);
                }
                sqlQuery.append(sqlWithoutSelect);

                if(Parameters.DEBUG_OUTPUT)
                    System.out.println("query hard: " + sqlQuery);
                formula.setSqlQuery(sqlQuery.toString());
                sqlQueries.add(formula);
            }
        }
        return sqlQueries;
    }


    public static HashSet<FormulaHard> getSQLQueriesForSoftFormulas(Model model) throws ParseException, SolveException
    {
        HashSet<FormulaHard> sqlQueries = new HashSet<FormulaHard>();
        // if it is a Restriction Formula
        for(FormulaAbstract formulaAbstract : model.getFormulas()) {
            if(formulaAbstract.getClass().equals(FormulaSoft.class)) {
                FormulaSoft formula = (FormulaSoft) formulaAbstract;
                if(formula.isCuttingPlaneInferenceUsed() && (formula.getRestrictions().size() > 1 || formula.getWeight() < 0)) {
                    Double weight = formula.getWeight();
                    // if we have individual weights for each grounding, we do
                    // not know if they are positive or negative.
                    // Thus, we just include them all.
                    if(weight == null) {
                        formula.useCuttingPlaneInference(false);
                        formula.setSqlQuery(getSQLQueryForSoftFormulaAll(formula));
                    }
                    // if the weight is greater than 0 than we use the JOINs
                    else if(
                    // formula.getRestrictions().size()<=1 ||
                    (weight > 0 && formula.isDisjunction()) || (weight < 0 && formula.isConjunction())) {
                        formula.setSqlQuery(getSQLQueryForSoftFormulaJoin(formula));
                    } else {
                        formula.setSqlQuery(getSQLQueryForSoftFormulaUnion(formula));
                    }
                } else {
                    Double weight = formula.getWeight();
                    if(weight == null || weight != 0) {
                        formula.useCuttingPlaneInference(false);
                        formula.setSqlQuery(getSQLQueryForSoftFormulaAll(formula));
                    }
                }
                sqlQueries.add(formula);
            }
        }
        return sqlQueries;
    }


    private static String getSQLQueryForSoftFormulaUnion(FormulaSoft formula) throws ParseException, SolveException
    {
        // SELECT PART
        StringBuilder surroundQuery = new StringBuilder();
        surroundQuery.append("SELECT  ");

        surroundQuery.append(getSQLQuerySelectSnippedWeight(formula, "yy")).append(", ");

        // sign if predicate is positive or negative
        surroundQuery.append(getSQLQuerySelectSnippedVariables(formula, "yy"));

        surroundQuery.append(" FROM ( ");
        // System.out.println(select.toString());

        // SMALL INNER QUERIES CONNECTED WITH UNION ALL
        StringBuilder innerSelect = new StringBuilder();
        innerSelect.append("SELECT ");
        int i = 0;
        for(VariableType var : formula.getForVariables()) {
            innerSelect.append("xx.").append(var.getName()).append(" as ").append("`").append(var.getName()).append("`");
            if(i < formula.getForVariables().size() - 1) {
                innerSelect.append(",");
            }
            innerSelect.append(" ");
            i++;
        }

        ArrayList<String> innerRestQueries = new ArrayList<String>();
        // copy actual formula
        FormulaSoft copiedFormula = new FormulaSoft(formula.getName(), formula.getForVariables(), formula.getIfExpressions(), formula.getDoubleVariable(), new ArrayList<PredicateExpression>(), formula.isConjunction());
        copiedFormula.setWeight(formula.getWeight());
        for(int j = 0; j < formula.getRestrictions().size(); j++) {
            if(j >= 1) {
                PredicateExpression expr = copiedFormula.getRestrictions().get(j - 1);
                expr.setPositive(!expr.isPositive());
            }
            copiedFormula.addRestriction(formula.getRestrictions().get(j));
            String innerQu = SQLQueryGenerator.getSQLStatementWithoutSelect(copiedFormula, false, true);
            innerRestQueries.add(innerQu);
        }
        // undo positive negative changes
        for(int j = 0; j < formula.getRestrictions().size() - 1; j++) {
            PredicateExpression a = formula.getRestrictions().get(j);
            a.setPositive(!a.isPositive());
        }

        StringBuilder sqlQuery = new StringBuilder();
        // Encompassing part
        sqlQuery.append(surroundQuery.toString());

        for(int j = 0; j < innerRestQueries.size(); j++) {
            sqlQuery.append(innerSelect);
            sqlQuery.append(innerRestQueries.get(j));
            if(j < innerRestQueries.size() - 1) {
                sqlQuery.append(" UNION ALL ");
            }
        }

        sqlQuery.append(") as yy");

        if(Parameters.DEBUG_OUTPUT)
            System.out.println("query soft union: " + sqlQuery);
        return sqlQuery.toString();

    }


    private static String getSQLQueryForSoftFormulaJoin(FormulaSoft formula) throws SolveException
    {
        // SELECT PART
        StringBuilder select = new StringBuilder();
        select.append("SELECT  ");

        select.append(getSQLQuerySelectSnippedWeight(formula, "xx")).append(", ");

        // sign if predicate is positive or negative
        select.append(getSQLQuerySelectSnippedVariables(formula, "xx"));// .append(", ");
        // System.out.println(select.toString());

        StringBuilder sqlQuery = new StringBuilder();
        // Put select part together
        sqlQuery.append(select.toString());
        // get the sql query from the sqlstatementwithoutselect
        String sqlWithoutSelect = SQLQueryGenerator.getSQLStatementWithoutSelect(formula, true, true);
        sqlQuery.append(sqlWithoutSelect);

        if(Parameters.DEBUG_OUTPUT)
            System.out.println("query soft join: " + sqlQuery);
        return sqlQuery.toString();
    }


    private static String getSQLQueryForSoftFormulaAll(FormulaSoft formula) throws ParseException, SolveException
    {
        // SELECT PART
        StringBuilder select = new StringBuilder();
        select.append("SELECT ");

        select.append(getSQLQuerySelectSnippedWeight(formula, "xx")).append(", ");
        // sign if predicate is positive or negative
        select.append(getSQLQuerySelectSnippedVariables(formula, "xx"));
        // System.out.println(select.toString());

        StringBuilder sqlQuery = new StringBuilder();
        // Put select part together
        sqlQuery.append(select.toString());
        // get the sql query from the sqlstatementwithoutselect
        String sqlWithoutSelect = SQLQueryGenerator.getSQLStatementWithoutSelectWithoutCPI(formula, true, false);
        sqlQuery.append(sqlWithoutSelect);

        if(Parameters.DEBUG_OUTPUT)
            System.out.println("query soft all: " + sqlQuery);
        return sqlQuery.toString();
    }


    private static String getSQLQuerySelectSnippedWeight(FormulaSoft formula, String prefix)
    {
        StringBuilder selectSnipped = new StringBuilder();
        // double variable if exists
        if(formula.getDoubleVariable() != null) {
            selectSnipped.append("(").append(prefix).append(".").append(formula.getDoubleVariable().getName());
        } else {
            selectSnipped.append("(1 ");
        }
        // ...multiplied with the weights of the formula
        if(formula.getWeight() != null) {
            double weightValue = formula.getWeight();
            selectSnipped.append(" * ");
            selectSnipped.append(weightValue);
        } else {
            selectSnipped.append(" * 1");
        }
        selectSnipped.append(") as weight");
        return selectSnipped.toString();
    }


    /*
     * private String getSQLQuerySelectSnippedRestrictionILP(FormulaHard
     * formula, String prefix){ StringBuilder selectSnipped = new
     * StringBuilder(); int preCount = 0; selectSnipped.append("CONCAT(");
     * for(PredicateExpression predicateExpr : formula.getRestrictions()){
     * if(predicateExpr.isPositive()){ selectSnipped.append("'+||"); }else{
     * selectSnipped.append("'-||"); }
     * selectSnipped.append(predicateExpr.getPredicate().getName());
     * selectSnipped.append("|', "); int varCount = 0; for(VariableAbstract
     * variable : predicateExpr.getVariables()){ if(variable instanceof
     * VariableString){
     * selectSnipped.append("'").append(variable.getName()).append("'"); }else{
     * selectSnipped.append(prefix).append(".");
     * selectSnipped.append(variable.getName()); }
     * if(varCount<predicateExpr.getVariables().size()-1){
     * selectSnipped.append(", '|', "); }else{ // do nothing } varCount++; }
     * if(preCount<formula.getRestrictions().size()-1){
     * selectSnipped.append(", '||',"); } preCount++; }
     * selectSnipped.append(") as restriction"); return
     * selectSnipped.toString(); }
     */

    private static String getSQLQuerySelectSnippedVariables(FormulaAbstract formula, String prefix)
    {
        StringBuilder selectSnipped = new StringBuilder();
        // selectSnipped.append("CONCAT(");
        int i = 0;
        for(VariableType v : formula.getForVariables()) {
            selectSnipped.append(prefix).append(".").append(v.toString());
            if(i < formula.getForVariables().size() - 1) {
                selectSnipped.append(", ");
            }
            i++;
        }
        // selectSnipped.append(") as dublicateDetection");
        return selectSnipped.toString();
    }


    /**
     * 
     * Build Query for FormulaCardinality. The idea is to print every
     * possibility and to execute the less equal or greater equal later in the
     * java program code.
     * 
     * Therefore, the output does not only need the String with the SQL Query,
     * but also the corresponding FormulaCardinality.
     * 
     * The first row gives the key of the cardinality:
     * [formulaName][[FixValue1]...[FixValueN] where FixValueI is the value of
     * the variables which are not in the over part.
     * 
     * The following rows (number equals number of PredicateExpressions in
     * formula) each stand for one variable to add. The variables are already in
     * the correct format: [HiddenPredName]|[Value1]|...|[ValueN]
     * 
     * Example:
     * 
     * Select Part: - FROM Part: Similar to Restriction Formula WHERE Part:
     * Similar to Restriction Formula GROUP BY: take those variables which are
     * in card part. HAVING: count > variableName
     * 
     * formula card1 for Property2 p2,Concept1 c1,Property1 p1,Concept2 c2 if
     * prop1(p1) & prop2(p2) & concept1(c1) & concept2(c2) card c1, p1:
     * hiddenCon(c1, c2) & hiddenProp(p1, p2) <= 3;
     * 
     * -->
     * 
     * 
     * SELECT CONCAT(card1.c2, card1.p2) as count, CONCAT('hiddenCon|',
     * card1.c1, '|', card1.c2), CONCAT('hiddenProp|', card1.p1, '|', card1.p2)
     * FROM hiddenCon hiddenCon0, hiddenProp hiddenProp1, card1 WHERE
     * hiddenCon0.field1 = card1.c2 AND hiddenProp1.field1 = card1.p2 AND
     * hiddenCon0.field0 = card1.c1 AND hiddenProp1.field0=card1.p1 ORDER BY
     * count
     * 
     * @param model
     * @return
     * @throws ParseException
     * @throws SolveException
     */
    public static HashSet<FormulaCardinality> getSQLQueriesForCardinalityFormula(Model model) throws ParseException, SolveException
    {

        HashSet<FormulaCardinality> sqlQueries = new HashSet<FormulaCardinality>();
        for(FormulaAbstract formulaAbstract : model.getFormulas()) {
            if(formulaAbstract.getClass().equals(FormulaCardinality.class)) {

                FormulaCardinality formula = (FormulaCardinality) formulaAbstract;

                // SELECT statement
                // =================
                StringBuilder select = new StringBuilder();
                select.append("SELECT ");
                // sign if predicate is positive or negative
                select.append("CONCAT('").append(formula.getName()).append("',");

                for(VariableType var : formula.getForVariables()) {
                    if(!formula.getOverVariables().contains(var)) {
                        select.append("xx.");
                        select.append(var.getName());
                        select.append(",");
                    }
                }
                select.deleteCharAt(select.length() - 1);
                select.append(") as count, ");
                int preCount = 0;
                for(PredicateExpression predicateExpr : formula.getRestrictions()) {
                    select.append("CONCAT('");
                    select.append(predicateExpr.getPredicate().getName());
                    select.append("|', ");
                    int varCount = 0;
                    for(VariableAbstract variable : predicateExpr.getVariables()) {
                        if(variable instanceof VariableString) {
                            select.append("'").append(variable.getName()).append("'");
                        } else {
                            select.append("xx.");
                            select.append(variable.getName());
                        }

                        if(varCount < predicateExpr.getVariables().size() - 1) {
                            select.append(", '|', ");
                        } else {
                            // do nothing
                        }
                        varCount++;
                    }
                    if(preCount < formula.getRestrictions().size() - 1) {
                        select.append("), ");
                    }
                    preCount++;
                }
                select.append(")");

                StringBuilder sqlQuery = new StringBuilder();
                sqlQuery.append(select);

                // get sql query without select
                String sqlQueryWithoutSelect = "";
                if(formula.isCuttingPlaneInferenceUsed()) {
                    sqlQueryWithoutSelect = SQLQueryGenerator.getSQLStatementWithoutSelect(formula, false, true);
                } else {
                    sqlQueryWithoutSelect = SQLQueryGenerator.getSQLStatementWithoutSelectWithoutCPI(formula, false, false);
                }
                sqlQuery.append(sqlQueryWithoutSelect);

                // ORDER BY part
                // important to make counting possible later on.
                sqlQuery.append(" ORDER BY count");
                formula.setSqlQuery(sqlQuery.toString());
                sqlQueries.add(formula);
            }
        }
        return sqlQueries;

    }


    public static String getSQLStatementWithoutSelectWithoutCPI(FormulaHard formula, boolean reversePositiveAndNegative, boolean dublicateDetection) throws SolveException, ParseException
    {
        ArrayList<PredicateExpression> restrictions = formula.getRestrictions();
        formula.setRestrictions(new ArrayList<PredicateExpression>());
        String result = SQLQueryGenerator.getSQLStatementWithoutSelect(formula, reversePositiveAndNegative, dublicateDetection);
        formula.setRestrictions(restrictions);
        return result;
    }


    /**
     * This function returns the from and where parts of the select statement
     * for all kinds of formulas, Soft, Hard, Cardinality and Restriction
     * Formulas. Every query will have a complete inner query sourrounded by a
     * very simple outer query.
     * 
     * Example:
     * 
     * formula [cpi=yes] f1 for person a1,paper a3,paper a2,cat a4 if wrote(a1,
     * a3) & wrote(a1, a2) & cat_typePred(a4) soft category(a3, a4) |
     * !category(a2, a4) * w1;
     * 
     * !wrote(a1,a3) v !wrote(a1,a2) v category(a3,a4) v !category(a2,a4)
     * 
     * will be:
     * 
     * 
     * FROM (SELECT x0.field0 as a1,x0.field1 as a3,x1.field1 as a2,x2.field0 as
     * a4 FROM wrote as x0 INNER JOIN wrote x1 ON x0.field0 = x1.field0 INNER
     * JOIN cat_typePred x2 LEFT JOIN category y0 ON y0.field0 = x0.field1 AND
     * y0.field1 = x2.field0 INNER JOIN category y1 ON y1.field0 = x1.field1 AND
     * y1.field1 = x2.field0 AND y1.field1 = y0.field1 WHERE y0.field0 IS NULL)
     * as xx
     * 
     * 
     * @param formula
     * @param reversePositiveAndNegative
     *            in case of Hard formulas it has to be true; in case of Soft /
     *            Cardinality formulas false.
     * @return
     */
    public static String getSQLStatementWithoutSelect(FormulaHard formula, boolean reversePositiveAndNegative, boolean dublicateDetection) throws SolveException
    {
        // SELECT, FROM and WHERE Statements from inner query
        // here we only need to cover the positive ones --> they will be
        // negative...
        StringBuilder selectStatements = new StringBuilder();
        // from statements includes inner / outer join predicates
        StringBuilder fromStatements = new StringBuilder();
        ArrayList<String> whereStatements = new ArrayList<String>();

        HashMap<VariableAbstract, ArrayList<String>> databaseFieldsForVariable = new HashMap<VariableAbstract, ArrayList<String>>();

        // 1. PRECATEGORIZE EXPRESSIONS in positive and negative ones
        ArrayList<PredicateExpression> normalJoinExpressions = new ArrayList<PredicateExpression>();
        ArrayList<PredicateExpression> outerJoinExpressions = new ArrayList<PredicateExpression>();
        // If part of formula
        for(IfExpression ifExpression : formula.getIfExpressions()) {
            if(ifExpression instanceof PredicateExpression) {
                PredicateExpression predicateExpr = (PredicateExpression) ifExpression;
                if(predicateExpr.isPositive()) {
                    // normal join
                    normalJoinExpressions.add(predicateExpr);
                } else {
                    // outer join
                    outerJoinExpressions.add(predicateExpr);
                }
            }
        }
        // Restrictions part of formula
        for(PredicateExpression predicateExpr : formula.getRestrictions()) {
            if((predicateExpr.isPositive() && !reversePositiveAndNegative) || (!predicateExpr.isPositive() && reversePositiveAndNegative)) {
                // normal join
                normalJoinExpressions.add(predicateExpr);
            } else {
                // outer join
                outerJoinExpressions.add(predicateExpr);
            }
        }

        // 2. BUILD FROM part
        for(int i = 0; i < normalJoinExpressions.size(); i++) {
            StringBuilder fromPart = new StringBuilder();
            PredicateExpression expr = normalJoinExpressions.get(i);

            // FROM wrote as x0
            // INNER JOIN wrote x1
            if(i == 0) {
                fromPart.append(" FROM ");
            } else {
                fromPart.append(" INNER JOIN ");
            }
            fromPart.append("`").append(expr.getPredicate().getName()).append("`");
            fromPart.append(" x").append(i).append(" ");

            StringBuilder onPart = new StringBuilder();
            for(int j = 0; j < expr.getVariables().size(); j++) {
                VariableAbstract var = expr.getVariables().get(j);
                if(!(var instanceof VariableString)) {
                    StringBuilder varString = new StringBuilder();
                    varString.append("x").append(i).append(".");
                    if(var instanceof VariableType)
                        varString.append("field").append(j);
                    else if(var instanceof VariableDouble)
                        varString.append("value");
                    ArrayList<String> databaseFields = databaseFieldsForVariable.get(var);
                    if(databaseFields == null) {
                        databaseFields = new ArrayList<String>();
                        databaseFields.add(varString.toString());
                        databaseFieldsForVariable.put(var, databaseFields);
                    } else {
                        for(String databaseField : databaseFields) {
                            // if we are at position 1, we have to do the join
                            // predicates in the where clause
                            if(i == 0) {
                                StringBuilder newWhere = new StringBuilder();
                                newWhere.append(varString).append(" = ").append(databaseField);
                                whereStatements.add(newWhere.toString());
                            } else {
                                // else we have them as 'on's
                                if(onPart.length() == 0)
                                    onPart.append("ON ");
                                else
                                    onPart.append("AND ");
                                onPart.append(varString).append(" = ").append(databaseField).append(" ");
                            }
                        }
                        databaseFields.add(varString.toString());
                    }
                } else {
                    // Extra: If we have a string variable, we have to add it to
                    // sql
                    // code dublication from above
                    if(var instanceof VariableString) {
                        StringBuilder sb = new StringBuilder();
                        sb.append(" x").append(i).append(".");
                        sb.append("field").append(j);
                        sb.append(" = ");
                        sb.append("'").append(var.getName()).append("'");
                        whereStatements.add(sb.toString());
                    }
                }
            }
            fromPart.append(onPart);
            fromStatements.append(fromPart);
        }

        for(int i = 0; i < outerJoinExpressions.size(); i++) {
            StringBuilder fromPart = new StringBuilder();
            PredicateExpression expr = outerJoinExpressions.get(i);

            // LEFT JOIN category y0

            // code dublication from normalJoinExpressions
            // ON y0.field0 = x0.field1 AND y0.field1 = x2.field0
            StringBuilder onPart = new StringBuilder();
            String oneNormalVar = null;
            for(int j = 0; j < expr.getVariables().size(); j++) {
                VariableAbstract var = expr.getVariables().get(j);
                if(!(var instanceof VariableString || var instanceof VariableArithmeticExpression)) {
                    StringBuilder varString = new StringBuilder();
                    varString.append("y").append(i).append(".");
                    if(var instanceof VariableType) {
                        varString.append("field").append(j);
                        // TODO check...
                        if(var.addToWhere())
                            oneNormalVar = varString.toString();

                    } else if(var instanceof VariableDouble)
                        varString.append("value");
                    ArrayList<String> databaseFields = databaseFieldsForVariable.get(var);
                    if(databaseFields == null) {
                        // System.err.println("Warning: variable " + var +
                        // " is not bound. Formula: " + formula);
                        throw new SolveException("Could not find a positive binding for variable " + var + ". Formula: " + formula);
                    } else {
                        for(String databaseField : databaseFields) {
                            // String databaseField=databaseFields.get(0);
                            if(onPart.length() == 0)
                                onPart.append("ON ");
                            else
                                onPart.append("AND ");
                            onPart.append(varString).append(" = ").append(databaseField).append(" ");
                        }
                    }
                }
            }
            for(int j = 0; j < expr.getVariables().size(); j++) {
                VariableAbstract var = expr.getVariables().get(j);
                if(var instanceof VariableString) {
                    // Extra: If we have a string variable, we have to add it to
                    // sql
                    // code dublication from above
                    if(var instanceof VariableString) {
                        StringBuilder sb = new StringBuilder();
                        sb.append(" y").append(i).append(".");
                        sb.append("field").append(j);
                        // if(oneNormalVar!=null) {
                        sb.append(" = ");
                        // }else{
                        // sb.append(" <> ");
                        // }
                        sb.append("'").append(var.getName()).append("'");
                        whereStatements.add(sb.toString());
                    }
                }
            }
            if(onPart.length() == 0) {
                fromPart.append(" JOIN ");
            } else {
                fromPart.append(" LEFT JOIN ");
            }
            fromPart.append("`").append(expr.getPredicate().getName()).append("`");
            fromPart.append(" y").append(i).append(" ");

            fromPart.append(onPart);
            fromStatements.append(fromPart);

            // where y0.field0 IS NULL
            // + StringExpressions like y0.field1 = "SomeString"
            if(oneNormalVar != null)
                whereStatements.add(new StringBuilder().append(oneNormalVar).append(" IS NULL").toString());
        }

        // 3. DUBLICATE DETECTION JOIN (Join with database called like the
        // table)
        // only for formulas with more than one restriction.
        // only if dublicate detection is enabled
        if(dublicateDetection) {
            int lastY = outerJoinExpressions.size(); // one larger than before
            fromStatements.append(" LEFT JOIN `");
            fromStatements.append(formula.getName());
            fromStatements.append("` y").append(lastY);
            ArrayList<StringBuilder> onParts = new ArrayList<StringBuilder>();
            // VariableAbstract someVariable = null;
            int i = 0;
            for(Entry<VariableAbstract, ArrayList<String>> entry : databaseFieldsForVariable.entrySet()) {
                VariableAbstract var = entry.getKey();
                if(var instanceof VariableType) {
                    if(i == 0) {
                        // where y3.v1 is null
                        StringBuilder whereNull = new StringBuilder();
                        whereNull.append("y").append(lastY).append(".").append(var.getName()).append(" IS NULL");
                        whereStatements.add(whereNull.toString());
                    }
                    i++;
                    // someVariable=var;
                    for(String col : entry.getValue()) {
                        StringBuilder onPart = new StringBuilder();
                        onPart.append("y").append(lastY).append(".").append(var.getName());
                        onPart.append(" = ");
                        onPart.append(col);
                        onParts.add(onPart);
                    }
                }
            }
            i = 0;
            for(StringBuilder onPart : onParts) {
                if(i == 0) {
                    fromStatements.append(" ON ");
                } else {
                    fromStatements.append(" AND ");
                }
                fromStatements.append(onPart);
                i++;
            }
        }

        // 4. ENHANCE WHERE PART with equalVariable-, and ThresholdExpressions
        for(IfExpression ifExpression : formula.getIfExpressions()) {
            if(ifExpression instanceof EqualVariableExpression) {
                EqualVariableExpression varExpr = (EqualVariableExpression) ifExpression;
                VariableType var1 = varExpr.getVariable1();
                VariableType var2 = varExpr.getVariable2();
                ArrayList<String> databaseFields1 = databaseFieldsForVariable.get(var1);
                ArrayList<String> databaseFields2 = databaseFieldsForVariable.get(var2);
                if(databaseFields1 != null && databaseFields2 != null) {
                    for(String databaseField1 : databaseFields1) {
                        // String databaseField1 = databaseFields1.get(0);
                        for(String databaseField2 : databaseFields2) {
                            // String databaseField2 = databaseFields2.get(0);
                            StringBuilder sb = new StringBuilder();
                            sb.append(databaseField1);
                            if(varExpr.isNegative())
                                sb.append(" != ");
                            else
                                sb.append(" = ");
                            sb.append(databaseField2);
                            whereStatements.add(sb.toString());
                        }
                    }
                }
            } else if(ifExpression instanceof ThresholdExpression) {
                ThresholdExpression thresholdExpr = (ThresholdExpression) ifExpression;
                VariableDouble var = thresholdExpr.getVariable1();
                ArrayList<String> databaseFields = databaseFieldsForVariable.get(var);
                if(databaseFields != null) {
                    for(String databaseField : databaseFields) {
                        StringBuilder sb = new StringBuilder();
                        sb.append(databaseField);
                        if(thresholdExpr.isGreaterEqual())
                            sb.append(" >= ");
                        else
                            sb.append(" <= ");
                        sb.append(thresholdExpr.getValue());
                        whereStatements.add(sb.toString());
                    }
                }
            }
        }

        // 4. BUILD SELECT with variables of databaseFieldsForVariable
        // SELECT x0.field0 as a1,x0.field1 as a3,x1.field1 as a2,x2.field0 as
        // a4
        int i = 0;
        for(VariableAbstract var : databaseFieldsForVariable.keySet()) {
            if(!(var instanceof VariableString)) {
                String databaseField = databaseFieldsForVariable.get(var).get(0);
                if(i == 0)
                    selectStatements.append("SELECT ");
                else
                    selectStatements.append(", ");
                selectStatements.append(databaseField).append(" as `").append(var.getName()).append("`");
                i++;
            }
        }

        // 5. CONCAT WHOLE QUERY
        StringBuilder queryWithoutSelect = new StringBuilder();
        queryWithoutSelect.append(" FROM (");
        queryWithoutSelect.append(selectStatements);
        queryWithoutSelect.append(fromStatements);
        for(i = 0; i < whereStatements.size(); i++) {
            if(i == 0)
                queryWithoutSelect.append(" WHERE ");
            else
                queryWithoutSelect.append(" AND ");
            queryWithoutSelect.append(whereStatements.get(i));
        }
        queryWithoutSelect.append(") as xx");

        return queryWithoutSelect.toString();

    }

} public class ParseException extends Exception
{

    /**
     * Exceptions which can occur in the Parse Phase of the application.
     */
    private static final long serialVersionUID = -4390051337485101364L;


    public ParseException()
    {
    }


    public ParseException(String msg)
    {
        super(Messages.printParseExceptionError(msg));
    }

} public class SolveException extends Exception
{

    /**
     * Exceptions which can occur in the Parse Phase of the application.
     */
    private static final long serialVersionUID = -4390221337485101364L;


    public SolveException()
    {
    }


    public SolveException(String msg)
    {
        super(msg);
    }

} public class HerbrandUniverse
{

    private static HerbrandUniverse instance = new HerbrandUniverse();


    /**
     * Singelton. Private constructor.
     */
    private HerbrandUniverse()
    {
    }


    /**
     * Gets the one and only reference to this class (singelton).
     */
    public static HerbrandUniverse getInstance()
    {
        return instance;
    }

    private final char[]            chars           = { 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9' };

    private final int               size            = chars.length;

    private long                    constantNr      = 0;

    private int                     currentKeySize  = 0;
    // private int maxNumberKeySize = 0;

    private HashMap<String, String> constantKeyPair = new HashMap<String, String>();

    private HashMap<String, String> keyConstantPair = new HashMap<String, String>();


    public void reset()
    {
        constantNr = 0;
        currentKeySize = 0;
        // maxNumberKeySize = 0;
        constantKeyPair = new HashMap<String, String>();
        keyConstantPair = new HashMap<String, String>();
    }


    /**
     * Returns key of constant. Creates a new Key, if no key exists yet for this constant.
     * 
     * @return
     */
    public String getKey(String constant)
    {
        String key = constantKeyPair.get(constant);
        if(key == null) {
            if(constant.matches("^[-+]?[0-9]*\\.?[0-9]+([eE][-+]?[0-9]+)?$")) { // ^-?\\d+$
                constant = String.valueOf(Double.valueOf(constant));
                // maxNumberKeySize = Math.max(constantNr.length(), maxNumberKeySize);
            }
            StringBuilder sb = new StringBuilder();
            long cNr = constantNr;
            do {
                int rest = (int) (cNr % size);
                sb.append(chars[rest]);
                cNr = cNr / size;
            } while(cNr != 0);
            key = "c" + sb.toString();
            constantNr++;
            currentKeySize = Math.max(key.length(), currentKeySize);
        }

        constantKeyPair.put(constant, key);
        keyConstantPair.put(key, constant);

        return key;
    }


    /**
     * Returns the constant given a key.
     * 
     * @param key
     * @return
     */
    public String getConstant(String key)
    {
        return keyConstantPair.get(key);
    }


    /**
     * Transforms internally used data structure of axioms back to the input format structure.
     * Especially, it transforms the short constants back to its original long names.
     * 
     * @param compromizedAxiom
     * @return
     */
    public String transformKeysToConstants(String compromizedAxiom)
    {
        String[] resArray = compromizedAxiom.split("\\|");
        StringBuilder output = new StringBuilder();
        output.append(resArray[0]).append("(");
        if(resArray.length > 2) {
            for(int i = 1; i < resArray.length - 1; i++) {
                output.append("\"").append(this.getConstant(resArray[i])).append("\"").append(", ");
            }
        }
        output.append("\"").append(this.getConstant(resArray[resArray.length - 1])).append("\"").append(")");
        return output.toString();
    }


    /**
     * Returns the maximal key size s of all current strings. This is used as length in the sql tables (char(s)).
     * 
     * @return
     */
    public int getMaximalKeySize()
    {
        // return Math.max(Math.max(currentKeySize, maxNumberKeySize), 100);
        return currentKeySize + 3;
    }


    @Override
    public String toString()
    {
        StringBuilder sb = new StringBuilder();
        for(String c : constantKeyPair.keySet()) {
            sb.append(c + "\t" + constantKeyPair.get(c) + System.lineSeparator());
        }
        return sb.toString();
    }


    public static void main(String[] args)
    {
        HerbrandUniverse t = HerbrandUniverse.getInstance();
        System.out.println(t.getKey("1"));
        System.out.println(t.getKey("2"));
        System.out.println(t.getKey("3"));
        System.out.println(t.getKey("4"));
        System.out.println(t.getKey("5"));
        System.out.println(t.getKey("6"));
        System.out.println(t.getKey("7"));
        System.out.println(t.getKey("8"));
        System.out.println(t.getKey("9"));
        System.out.println(t.getKey("10"));
        System.out.println(t.getKey("12"));
        System.out.println(t.getKey("13"));
        System.out.println(t.getKey("11"));
        System.out.println(t.getKey("112"));
        System.out.println(t.getKey("113"));
        System.out.println(t.getKey("111"));
        System.out.println(t.getKey("1112"));
        System.out.println(t.getKey("1113"));
        System.out.println(t.getKey("11111"));
        System.out.println(t.getKey("11112"));
        System.out.println(t.getKey("a1"));
        System.out.println(t.getKey("a2"));
        System.out.println(t.getKey("a3"));
        System.out.println(t.getKey("a4"));
        System.out.println(t.getKey("a5"));
        System.out.println(t.getKey("a6"));
        System.out.println(t.getKey("a7"));
        System.out.println(t.getKey("a8"));
        System.out.println(t.getKey("a9"));
        System.out.println(t.getKey("a10"));
        System.out.println(t.getKey("a12"));
        System.out.println(t.getKey("a13"));
        System.out.println(t.getKey("a11"));
        System.out.println(t.getKey("a112"));
        System.out.println(t.getKey("a113"));
        System.out.println(t.getKey("a111"));
        System.out.println(t.getKey("a1112"));
        System.out.println(t.getKey("a1113"));
        System.out.println(t.getKey("a11111"));
        System.out.println(t.getKey("a11112"));
        System.out.println(t.getKey("ba1"));
        System.out.println(t.getKey("ba2"));
        System.out.println(t.getKey("ba3"));
        System.out.println(t.getKey("ba4"));
        System.out.println(t.getKey("ba5"));
        System.out.println(t.getKey("ba6"));
        System.out.println(t.getKey("ba7"));
        System.out.println(t.getKey("ba8"));
        System.out.println(t.getKey("ba9"));
        System.out.println(t.getKey("ba10"));
        System.out.println(t.getKey("ba12"));
        System.out.println(t.getKey("ba13"));
        System.out.println(t.getKey("ba11"));
        System.out.println(t.getKey("ba112"));
        System.out.println(t.getKey("ba113"));
        System.out.println(t.getKey("ba111"));
        System.out.println(t.getKey("ba1112"));
        System.out.println(t.getKey("ba1113"));
        System.out.println(t.getKey("ba11111"));
        System.out.println(t.getKey("ba11112"));
        System.out.println(t.constantKeyPair);
        System.out.println(t.keyConstantPair);
        System.out.println(t.getMaximalKeySize());
    }

} public class Model
{
    private ArrayList<FormulaAbstract> formulars          = new ArrayList<FormulaAbstract>();

    private HashSet<Type>              types              = new HashSet<Type>();

    private HashSet<PredicateAbstract> observedPredicates = new HashSet<PredicateAbstract>();

    private HashSet<PredicateAbstract> hiddenPredicates   = new HashSet<PredicateAbstract>();

    private HashSet<PredicateAbstract> initialSolution    = new HashSet<PredicateAbstract>();


    public Model()
    {
    }


    public Model(ArrayList<FormulaAbstract> formulars)
    {
        this.addFormulas(formulars);
    }


    public Model(FormulaAbstract... formulars)
    {
        this.addFormulas(formulars);
    }


    public void addPredicate(PredicateAbstract predicate)
    {
        if(predicate.isHidden()) {
            hiddenPredicates.add(predicate);
        } else {
            observedPredicates.add(predicate);
        }
        for(Type t : predicate.getTypes()) {
            types.add(t);
        }
    }


    public void setInitialSolution(HashSet<PredicateAbstract> initialSolution)
    {
        this.initialSolution = initialSolution;
    }


    public HashSet<PredicateAbstract> getInitialSolution()
    {
        return initialSolution;
    }


    public void addType(Type type)
    {
        types.add(type);
    }


    public void addFormulas(FormulaAbstract... formulars)
    {
        for(int i = 0; i < formulars.length; i++) {
            this.addFormula(formulars[i]);
        }
    }


    public void removeFormula(FormulaAbstract formula)
    {
        formulars.remove(formula);
    }


    public void addFormulas(ArrayList<FormulaAbstract> formulars)
    {
        for(FormulaAbstract f : formulars) {
            this.addFormula(f);
        }
    }


    public void addFormula(FormulaAbstract formular)
    {
        this.hiddenPredicates.addAll(formular.getAllHiddenPredicatesSet());
        this.observedPredicates.addAll(formular.getAllObservedPredicates());
        this.types.addAll(formular.getAllTypes());

        this.formulars.add(formular);
    }


    public ArrayList<FormulaAbstract> getFormulas()
    {
        return formulars;
    }


    public HashSet<Type> getAllTypes()
    {
        return types;

    }


    public HashSet<PredicateAbstract> getAllObservedPredicates()
    {
        for(Type t : this.types) {
            if(t.getGroundValuesPredicate() != null) {
                this.observedPredicates.add(t.getGroundValuesPredicate());
            }
        }

        return this.observedPredicates;
    }


    public HashSet<PredicateAbstract> getAllHiddenPredicates()
    {
        return this.hiddenPredicates;
    }


    public HashSet<PredicateNumerical> getAllNumericalPredicates()
    {
        HashSet<PredicateNumerical> preds = new HashSet<>();
        for(PredicateAbstract p : hiddenPredicates) {
            if(p instanceof PredicateNumerical) {
                preds.add((PredicateNumerical) p);
            }
        }
        return preds;
    }


    public String toLongString()
    {
        StringBuilder sb = new StringBuilder();
        sb.append(this.toString());

        sb.append(this.toStringGroundValues());

        return sb.toString();
    }


    public String toStringGroundValues()
    {
        StringBuilder sb = new StringBuilder();
        sb.append("\n\n// ground axioms\n");
        // getAllPredicates & their values
        HashSet<PredicateAbstract> observedPredicates = new HashSet<PredicateAbstract>();
        HashSet<PredicateAbstract> hiddenPredicates = new HashSet<PredicateAbstract>();

        observedPredicates = this.getAllObservedPredicates();
        hiddenPredicates = this.getAllHiddenPredicates();

        for(PredicateAbstract p : observedPredicates) {
            sb.append(p.toLongString()).append("\n");
        }
        for(PredicateAbstract p : hiddenPredicates) {
            sb.append(p.toLongString()).append("\n");
        }
        return sb.toString();
    }


    public String toTheBeastWeightString()
    {
        StringBuilder sb = new StringBuilder();
        for(FormulaAbstract f : this.getFormulas()) {
            if(f instanceof FormulaSoft) {
                // >wf1
                // -0.5
                sb.append("\n>w").append(f.getName());
                sb.append("\n").append(((FormulaSoft) f).getWeight());
                sb.append("\n");
            }
        }
        return sb.toString();
    }


    private String toTheBestAtomsString(HashSet<PredicateAbstract> predicates)
    {
        StringBuilder sb = new StringBuilder();
        for(PredicateAbstract p : predicates) {
            sb.append(">").append(p.getName().toLowerCase()).append("\n");
            for(String[] gv : p.getGroundValues()) {
                for(String g : gv) {
                    try {
                        Double.parseDouble(g);
                        sb.append(g);
                    } catch(NumberFormatException e) {
                        // not a double
                        sb.append("\"").append(g).append("\"");
                    }
                    sb.append(" ");
                }
                sb.append("\n");
            }
            sb.append("\n");
        }
        return sb.toString();
    }


    public String toTheBeastAtomsString()
    {
        StringBuilder sb = new StringBuilder();
        // >cmap
        // "hallo" "hallo2" 0.5
        sb.append(">>\n\n");

        sb.append(this.toTheBestAtomsString(this.getAllObservedPredicates()));
        sb.append(this.toTheBestAtomsString(this.getAllHiddenPredicates()));

        return sb.toString();
    }


    public String toTheBeastMLNString()
    {
        StringBuilder sb = new StringBuilder();
        sb.append("\n//types");
        for(Type t : this.getAllTypes()) {
            sb.append("\ntype ");
            sb.append(t.getName()).append(": ...;");
        }
        sb.append("\n\n//predicates\n");
        for(PredicateAbstract p : this.getAllHiddenPredicates()) {
            sb.append(p.toTheBeastString());
            sb.append("\n");
        }
        for(PredicateAbstract p : this.getAllObservedPredicates()) {
            sb.append(p.toTheBeastString());
            sb.append("\n");
        }
        sb.append("\n\n//hidden vs. observed");
        sb.append("\nhidden: ");
        boolean first = true;
        for(PredicateAbstract p : this.getAllHiddenPredicates()) {
            if(!first)
                sb.append(", ");
            first = false;
            sb.append(p.toString().toLowerCase());
        }
        sb.append(";");
        sb.append("\nobserved: ");
        first = true;
        for(PredicateAbstract p : this.getAllObservedPredicates()) {
            if(!first)
                sb.append(", ");
            first = false;
            sb.append(p.toString().toLowerCase());
        }
        sb.append(";");
        sb.append("\n\n//weights");
        for(FormulaAbstract f : this.getFormulas()) {
            if(f instanceof FormulaSoft) {
                sb.append("\nweight w").append(f.getName()).append(": Double;");
            }
        }
        sb.append("\n\n//formulas: ");

        for(FormulaAbstract f : this.getFormulas()) {
            // for part
            sb.append("\nfactor: for ");
            first = true;
            for(VariableType v : f.getForVariables()) {
                if(!first)
                    sb.append(", ");
                first = false;
                sb.append(v.getType().getName()).append(" ");
                sb.append(v.getName());
            }
            if(f instanceof FormulaSoft) {
                VariableDouble v = ((FormulaSoft) f).getDoubleVariable();
                if(v == null) {
                    // do nothing
                } else {
                    sb.append(", Double ").append(v.getName());
                }
            } else if(f instanceof FormulaObjective) {
                VariableDouble v = ((FormulaObjective) f).getDoubleVariable();
                sb.append(", Double ").append(v.getName());
            }
            // if part
            StringBuilder ifString = new StringBuilder();
            ifString.append("\nif ");
            first = true;
            for(IfExpression e : f.getIfExpressions()) {
                if(e instanceof ThresholdExpression) {
                    System.err.println("Threshold Expressions are not supported in TheBeast " + e + ". Rewrite formula f " + f);
                } else if(e instanceof PredicateExpression) {
                    if(!first)
                        ifString.append(" & ");
                    first = false;
                    PredicateExpression pe = (PredicateExpression) e;
                    ifString.append(pe.toTheBeastString(false));
                    // extend for part with equalString Variables
                    for(String forHelper : pe.getTheBeastForExtension()) {
                        sb.append(", ").append(forHelper);
                    }
                } else if(e instanceof EqualVariableExpression) {
                    System.err.println("Equal Variable Expressions are not supported in TheBeast " + e + ". Rewrite formula f " + f);
                }
            }

            // append if part from above
            sb.append(ifString.toString());

            // restriction part
            if(f instanceof FormulaSoft) {
                FormulaSoft fs = (FormulaSoft) f;
                sb.append("\nadd [");
                if(fs.getRestrictions().size() > 1) {
                    for(int i = 0; i < fs.getRestrictions().size(); i++) {
                        PredicateExpression p = fs.getRestrictions().get(i);
                        if(i > 0 && i < fs.getRestrictions().size() - 1) {
                            sb.append(" & ");
                            sb.append(p.toTheBeastString(true));
                        } else if(i > 0 && i == fs.getRestrictions().size() - 1) {
                            sb.append(" => ");
                            sb.append(p.toTheBeastString(false));
                        } else {
                            sb.append(p.toTheBeastString(true));
                        }
                    }
                } else {
                    PredicateExpression p = fs.getRestrictions().get(0);
                    sb.append(p.toTheBeastString(false));
                }
                sb.append("] * ");
                VariableDouble v = fs.getDoubleVariable();
                if(v == null) {
                    sb.append("w").append(f.getName());
                } else {
                    sb.append(v.getName());
                }

            } else if(f instanceof FormulaCardinality) {
                sb.append("\n");
                FormulaCardinality fc = (FormulaCardinality) f;
                if(fc.getOverVariables().size() == 1) {
                    for(VariableType v : fc.getOverVariables()) {
                        sb.append("|").append(v.getType().getName()).append(" ").append(v.getName()).append(": ");
                    }

                } else {
                    System.err.println("More than one over variable is not supported in TheBeast . Rewrite formula f " + f);
                }
                if(fc.getRestrictions().size() == 1) {
                    for(PredicateExpression p : fc.getRestrictions()) {
                        sb.append(p.toTheBeastString(false));
                    }
                    sb.append("|");
                } else {
                    System.err.println("More than one restriction is not supported in card constraints in TheBeast . Rewrite formula f " + f);
                }
            } else if(f instanceof FormulaHard) {
                sb.append(":\n");
                FormulaHard fh = (FormulaHard) f;
                if(fh.getRestrictions().size() > 1) {
                    for(int i = 0; i < fh.getRestrictions().size(); i++) {
                        PredicateExpression p = fh.getRestrictions().get(i);
                        if(i > 0 && i < fh.getRestrictions().size() - 1) {
                            sb.append(" & ");
                            sb.append(p.toTheBeastString(true));
                        } else if(i > 0 && i == fh.getRestrictions().size() - 1) {
                            sb.append(" => ");
                            sb.append(p.toTheBeastString(false));
                        } else {
                            sb.append(p.toTheBeastString(true));
                        }
                    }
                } else {
                    PredicateExpression p = fh.getRestrictions().get(0);
                    sb.append(p.toTheBeastString(false));
                }
            } else if(f instanceof FormulaObjective) {
                sb.append("\n");
                FormulaObjective fo = (FormulaObjective) f;
                sb.append("add [");
                PredicateExpression p = fo.getObjectiveExpression();
                sb.append(p.toTheBeastString(false));
                sb.append("] * ").append(fo.getDoubleVariable().getName());
            }
            sb.append(";\n\n");
        }

        return sb.toString();
    }


    public String toString()
    {
        StringBuilder sb = new StringBuilder();

        // getAllTypes
        /*
         * sb.append("//types\n");
         * for(Type t: this.getAllTypes()){
         * sb.append(t.toMediumString()).append("\n");
         * }
         */

        sb.append("\n//predicates\n");
        // getAllPredicates
        HashSet<PredicateAbstract> observedPredicates = new HashSet<PredicateAbstract>();
        HashSet<PredicateAbstract> hiddenPredicates = new HashSet<PredicateAbstract>();
        observedPredicates = this.getAllObservedPredicates();
        hiddenPredicates = this.getAllHiddenPredicates();
        for(PredicateAbstract p : observedPredicates) {
            sb.append(p.toMediumString()).append("\n");
        }
        for(PredicateAbstract p : hiddenPredicates) {
            sb.append(p.toMediumString()).append("\n");
        }
        /*
         * HashSet<Weight> weights = this.getAllWeights();
         * if(weights.size()>0){
         * sb.append("\n//weights\n");
         * for(Weight weight:weights){
         * sb.append(weight.toString()).append("\n");
         * }
         * }
         */

        sb.append("\n//formulars\n");
        for(FormulaAbstract formular : this.formulars) {
            sb.append(formular.toString());
        }
        return sb.toString();
    }
} public abstract class FormulaAbstract implements Comparable<FormulaAbstract>
{
    private HashSet<VariableType>   forVariables          = new HashSet<VariableType>();
    private ArrayList<IfExpression> ifExpressions         = new ArrayList<IfExpression>();
    private String                  name;
    private boolean                 cuttingPlaneInference = true;
    private String                  sqlQuery;


    public abstract HashSet<PredicateAbstract> getAllHiddenPredicatesSet();


    @Override
    public int hashCode()
    {
        final int prime = 31;
        int result = 1;
        result = prime * result + ((name == null) ? 0 : name.hashCode());
        return result;
    }


    @Override
    public boolean equals(Object obj)
    {
        if(this == obj)
            return true;
        if(obj == null)
            return false;
        if(getClass() != obj.getClass())
            return false;
        FormulaAbstract other = (FormulaAbstract) obj;
        if(name == null) {
            if(other.name != null)
                return false;
        } else if(!name.equals(other.name))
            return false;
        return true;
    }


    public HashSet<PredicateAbstract> getAllObservedPredicates()
    {
        HashSet<PredicateAbstract> result = new HashSet<PredicateAbstract>();

        for(IfExpression ifE : ifExpressions) {
            if(ifE instanceof PredicateExpression) {
                result.add(((PredicateExpression) ifE).getPredicate());
            }
        }
        return result;
    }


    public HashSet<Type> getAllTypes()
    {
        HashSet<Type> result = new HashSet<Type>();
        for(VariableType var : this.forVariables) {
            if(var instanceof VariableType) {
                result.add(((VariableType) var).getType());
            }
        }
        return result;
    }


    public void setForVariables(VariableType... forVariables)
    {
        for(int i = 0; i < forVariables.length; i++) {
            this.forVariables.add(forVariables[i]);
        }
    }


    public void setForVariables(HashSet<VariableType> forVariables)
    {
        this.forVariables = forVariables;
    }


    public HashSet<VariableType> getForVariables()
    {
        return forVariables;
    }


    public void setIfExpressions(ArrayList<IfExpression> ifExpressions) throws ParseException
    {
        for(IfExpression ex : ifExpressions) {
            this.checkIfExpression(ex);
        }
        this.ifExpressions = ifExpressions;
    }


    public void addIfExpression(IfExpression ifExpression) throws ParseException
    {
        this.checkIfExpression(ifExpression);

        this.ifExpressions.add(ifExpression);
    }


    public void setIfExpressions(IfExpression... ifExpressions) throws ParseException
    {
        for(int i = 0; i < ifExpressions.length; i++) {
            this.checkIfExpression(ifExpressions[i]);
            this.ifExpressions.add(ifExpressions[i]);
        }
    }


    public ArrayList<IfExpression> getIfExpressions()
    {
        return ifExpressions;
    }


    private void checkIfExpression(IfExpression ex) throws ParseException
    {
        if(ex.getClass().equals(PredicateExpression.class)) {
            PredicateExpression exPred = (PredicateExpression) ex;
            if(exPred.getPredicate().isHidden()) { throw new ParseException("Error in the if part of the formular. The predicate " + exPred.getPredicate().getName() + " is set to hidden. But only observed predicates are allowed here."); }
        }
    }


    public String toString()
    {
        StringBuilder sb = new StringBuilder();
        /*
         * sb.append("//formular ");
         * if(!(this instanceof FormularObjective)){
         * sb.append("[cpi=");
         * if(this.cuttingPlaneInference){
         * sb.append("yes");
         * }else{
         * sb.append("no");
         * }
         * sb.append("] ");
         * }
         * sb.append(this.getName());
         * sb.append("\n  ");
         */
        /*
         * int i=0;
         * for(VariableType fv: this.getVariableTypes()){
         * sb.append(fv.toMediumString());
         * if(i==this.getVariableTypes().size()-1){
         * // nothing
         * } else{
         * sb.append(",");
         * }
         * i++;
         * }
         */
        // sb.append("\n  if ");
        // int i=0;
        for(IfExpression ie : this.getIfExpressions()) {
            if(ie instanceof PredicateExpression) {
                PredicateExpression p = (PredicateExpression) ie;
                sb.append(p.toString(true));
                sb.append(" v ");
            }
            // i++;
        }

        // sb.append("");
        return sb.toString();
    }


    public void setName(String name)
    {
        this.name = name;
    }


    public String getName()
    {
        return name;
    }


    @Override
    public int compareTo(FormulaAbstract o)
    {
        return this.getName().compareTo(o.getName());
    }


    public void useCuttingPlaneInference(boolean cuttingPlaneInference)
    {
        this.cuttingPlaneInference = cuttingPlaneInference;
    }


    public boolean isCuttingPlaneInferenceUsed()
    {
        return cuttingPlaneInference;
    }


    public String getSqlQuery()
    {
        return sqlQuery;
    }


    public void setSqlQuery(String sqlQuery)
    {
        this.sqlQuery = sqlQuery;
    }
} public class FormulaHard extends FormulaAbstract
{

    private ArrayList<PredicateExpression> restrictions       = new ArrayList<PredicateExpression>();
    private boolean                        conjunction        = false;
    private RestrictionBuilder             restrictionBuilder = null;


    public void setAllAsForVariables()
    {
        TreeSet<VariableType> forVar = new TreeSet<VariableType>();
        for(PredicateExpression expr : this.restrictions) {
            for(VariableAbstract var : expr.getVariables()) {
                if(var instanceof VariableType) {
                    forVar.add((VariableType) var);
                }
            }
        }
        for(IfExpression ifexpr : this.getIfExpressions()) {
            for(VariableAbstract var : ifexpr.getAllVariables()) {
                if(var instanceof VariableType) {
                    forVar.add((VariableType) var);
                }
            }
        }
        HashSet<VariableType> setset = new HashSet<VariableType>();
        for(VariableType fv : forVar) {
            setset.add(fv);
        }
        this.setForVariables(setset);
    }


    public FormulaHard(String name, HashSet<VariableType> forVariables, ArrayList<IfExpression> ifExpressions, ArrayList<PredicateExpression> restrictions, boolean usesConjunctions) throws ParseException
    {
        this.setForVariables(forVariables);
        this.setName(name);
        this.setIfExpressions(ifExpressions);
        this.setRestrictions(restrictions);
        this.conjunction = usesConjunctions;
    }


    public FormulaHard()
    {

    }


    public void setRestrictions(ArrayList<PredicateExpression> restrictions) throws ParseException
    {
        for(PredicateExpression res : restrictions) {
            this.checkRestrictionExpression(res);
        }
        this.restrictions = restrictions;
    }


    public void setRestrictions(PredicateExpression... restrictions) throws ParseException
    {
        for(int i = 0; i < restrictions.length; i++) {
            this.checkRestrictionExpression(restrictions[i]);
            this.restrictions.add(restrictions[i]);
        }
    }


    public ArrayList<PredicateExpression> getRestrictions()
    {
        return restrictions;
    }


    private void checkRestrictionExpression(PredicateExpression ex) throws ParseException
    {
        if(ex.getClass().equals(PredicateExpression.class)) {
            PredicateExpression exPred = (PredicateExpression) ex;
            if(exPred.getPredicate().isObserved()) { throw new ParseException("Error in the restriction part of the formular. The predicate " + exPred.getPredicate().getName() + " is set to observed. But only hidden predicates are allowed in the restriction part."); }
        }
    }


    public void addRestriction(PredicateExpression expression) throws ParseException
    {
        this.checkRestrictionExpression(expression);
        this.restrictions.add(expression);
    }


    public String toString()
    {
        StringBuilder sb = new StringBuilder();
        sb.append(super.toString());
        // sb.append("  hard ");
        if(this.conjunction)
            sb.append("(");
        int i = 0;
        for(PredicateExpression pe : this.restrictions) {
            sb.append(pe.toString());
            if(i < this.restrictions.size() - 1) {
                if(this.conjunction) {
                    sb.append(" n ");
                } else {
                    sb.append(" v ");
                }
            }
            i++;
        }
        if(this.conjunction)
            sb.append(")");
        sb.append(".\n");

        return sb.toString();
    }


    protected String toSuperString()
    {
        return super.toString();
    }


    /**
     * Does the formula consists only of conjunction operators (and).
     */
    public void setConjunction()
    {
        this.conjunction = true;
    }


    /**
     * Does the formula consists only of disjunction operators (or).
     */
    public void setDisjunction()
    {
        this.conjunction = false;
    }


    /**
     * Does the formula consists only of conjunction operators (and).
     */
    public boolean isConjunction()
    {
        return conjunction;
    }


    /**
     * Does the formula consists only of disjunction operators (or).
     */

    public boolean isDisjunction()
    {
        return !conjunction;
    }


    @Override
    public HashSet<PredicateAbstract> getAllHiddenPredicatesSet()
    {
        HashSet<PredicateAbstract> result = new HashSet<PredicateAbstract>();

        for(PredicateExpression expr : this.restrictions) {
            result.add(expr.getPredicate());
        }

        return result;
    }


    public RestrictionBuilder getRestrictionBuilder()
    {
        return restrictionBuilder;
    }


    public void setRestrictionBuilder(RestrictionBuilder restrictionBuilder)
    {
        this.restrictionBuilder = restrictionBuilder;
    }
} public class FormulaObjective extends FormulaAbstract
{
    private VariableDouble      doubleVariable;
    private PredicateExpression objectiveExpression;
    private Double              weight = null;


    public FormulaObjective()
    {
    }


    public FormulaObjective(String name, HashSet<VariableType> forVariables, ArrayList<IfExpression> ifExpressions, VariableDouble doubleVariable, PredicateExpression objectiveExpression) throws ParseException
    {
        this.setForVariables(forVariables);
        this.setName(name);
        this.setIfExpressions(ifExpressions);
        this.setDoubleVariable(doubleVariable);
        this.setObjectiveExpression(objectiveExpression);
    }


    public void setObjectiveExpression(PredicateExpression objectiveExpression) throws ParseException
    {
        this.checkObjectiveExpression(objectiveExpression);
        this.objectiveExpression = objectiveExpression;
    }


    public PredicateExpression getObjectiveExpression()
    {
        return objectiveExpression;
    }


    public void setDoubleVariable(VariableDouble doubleVariable)
    {
        this.doubleVariable = doubleVariable;
    }


    public VariableDouble getDoubleVariable()
    {
        return doubleVariable;
    }


    private void checkObjectiveExpression(PredicateExpression ex) throws ParseException
    {
        if(ex.getClass().equals(PredicateExpression.class)) {
            PredicateExpression exPred = (PredicateExpression) ex;
            if(exPred.getPredicate().isObserved()) { throw new ParseException("Error in the objective part of the formular. The predicate " + exPred.getPredicate().getName() + " is set to observed. But only hidden predicates are allowed in the objective part."); }
            if(exPred.isNegative()) { throw new ParseException("Error in the objective part of the formular. The predicate " + exPred.getPredicate().getName() + " is set to negative. But only positive predicates are allowed in the objective part."); }
        }
    }


    public void setWeights(Double weight)
    {
        this.weight = weight;
    }


    public Double getWeight()
    {
        return weight;
    }


    public String toString()
    {
        StringBuilder sb = new StringBuilder();
        if(this.weight != null) {
            sb.append(this.getWeight()).append("  ");
        } else if(this.doubleVariable != null) {
            sb.append(this.doubleVariable.getName()).append(":  ");
        }
        sb.append(super.toString());
        sb.append(" ");
        sb.append(this.objectiveExpression.toString());
        sb.append("\n");
        return sb.toString();
    }


    @Override
    public HashSet<PredicateAbstract> getAllHiddenPredicatesSet()
    {
        HashSet<PredicateAbstract> result = new HashSet<PredicateAbstract>();
        result.add(objectiveExpression.getPredicate());
        return result;
    }
} public class VariableType extends VariableAbstract
{
    private Type type;


    public VariableType()
    {
    }


    public VariableType(String name, Type type)
    {
        this.setName(name);
        this.type = type;
    }


    public VariableType(String name)
    {
        this.setName(name);
    }


    public void setType(Type type)
    {
        this.type = type;

    }


    public Type getType()
    {
        return type;
    }


    public String toString()
    {
        return getName();
    }

} public class PredicateDouble extends PredicateAbstract
{
    private ArrayList<Double> doubleValues = new ArrayList<Double>();


    public ArrayList<Double> getDoubleValues()
    {
        return doubleValues;
    }


    public void setDoubleValues(ArrayList<Double> doubleValues)
    {
        this.doubleValues = doubleValues;
    }


    public PredicateDouble()
    {
    }


    public PredicateDouble(String name, boolean hidden)
    {
        this.setHidden(hidden);
        this.setName(name);
    }


    /**
     * In the groundvalues, the last position of the Array contains the double value (encoded as string).
     * 
     */
    public void setGroundAndDoubleValues(ArrayList<String[]> groundValues) throws ParseException
    {
        if(groundValues == null || groundValues.size() == 0)
            return;
        if((this.getTypes().size() > 0) && groundValues.get(0).length != this.getTypes().size() + 1) {
            for(String[] s : groundValues) {
                for(String ss : s) {
                    System.out.print(ss);
                    System.out.print(",");
                }
                System.out.println();
            }
            throw new ParseException("PredicateDouble: " + this.getName() + " - The size of the line input has to be equal to the size of the types attribute.");
        }
        this.doubleValues = new ArrayList<Double>();
        this.setGroundValues(new ArrayList<String[]>());
        for(String[] sArray : groundValues) {
            int lastPos = sArray.length - 1;
            double value = 0;
            try {
                value = Double.parseDouble(sArray[lastPos]);
            } catch(NumberFormatException e) {
                throw new ParseException("In PrdicateDouble " + this.getName() + " is a Double value which can not be parsed to a double number: " + sArray[lastPos]);
            }
            String[] groundV = new String[lastPos];
            for(int i = 0; i < lastPos; i++) {
                groundV[i] = sArray[i];
            }
            this.addGroundValueLine(value, groundV);
        }
    }


    public PredicateDouble(String name, boolean hidden, Type... types) throws ParseException
    {
        this.setName(name);
        this.setHidden(hidden);
        for(int i = 0; i < types.length; i++) {
            this.addType(types[i]);
        }
    }


    /**
     * Add one line to the ground atoms.
     * 
     * Note that the size of the line attribut has to be equal to the
     * types attribute.
     * 
     * @param line
     * @param value
     *            The value of the predicate. has to be between 0 and 1.
     * @throws ParseException
     */
    public void addGroundValueLine(double value, String... values) throws ParseException
    {
        /*
         * if(value <0){
         * throw new ParseException("Predicate: " + this.getName() + " - Value has to be greater than 0.");
         * }
         */
        if(values.length != getTypes().size()) { throw new ParseException("Predicate: " + this.getName() + " - The size of the line input has to be equal the size of the types attribute. Add types first." + " actual size line: " + values.length + " actual size types " + getTypes().size()); }

        this.getGroundValues().add(values);

        this.doubleValues.add(value);

    }


    public void addGroundValueLine(double value, ArrayList<String> line) throws ParseException
    {
        if(this.getTypes().size() > 0 && line.size() != this.getTypes().size()) { throw new ParseException("Predicate: " + this.getName() + " - The size of the line input has to be equal to the size of the types attribute. "); }
        String[] e = new String[line.size()];
        for(int i = 0; i < line.size(); i++) {
            e[i] = line.get(i);
        }

        this.doubleValues.add(value);

        this.getGroundValues().add(e);
    }


    public PredicateDouble(String name, boolean hidden, ArrayList<Type> types, ArrayList<String[]> groundValues) throws ParseException
    {
        this.setName(name);
        this.setHidden(hidden);
        if(groundValues == null) { throw new ParseException("Predicate: " + this.getName() + " - Ground value axiom must not be zero."); }
        if(types == null) { throw new ParseException("Predicate: " + this.getName() + " - Types axiom must not be zero."); }
        if(groundValues.size() != types.size()) { throw new ParseException("Predicate: " + this.getName() + " - The size of the line input has to be equal to the size of the types attribute."); }
        this.setGroundValues(groundValues);
        this.setTypes(types);

    }


    @Override
    public String getId()
    {
        return this.getClass().getName();
    }


    public String toTheBeastString()
    {
        String superString = super.toMediumString();
        String result = superString.substring(0, superString.length() - 1);
        result = result.replace(";", "");
        StringBuilder sb = new StringBuilder();
        sb.append(result).append(" x Double;");
        return sb.toString();
    }


    public String toMediumString()
    {
        String superString = super.toMediumString();
        String result = superString.substring(0, superString.length() - 1);
        result = result.replace(")", ",");
        StringBuilder sb = new StringBuilder();
        sb.append(result).append(", float_ )");
        return sb.toString();
    }


    public String toLongString()
    {
        StringBuffer sb = new StringBuffer();
        sb.append("// ").append(this.getName()).append("\n");
        // print axioms
        if(this.getGroundValues().size() > 0) {
            for(int i = 0; i < this.getGroundValues().size(); i++) {
                sb.append(this.getName()).append("(");
                String[] groundValue = this.getGroundValues().get(i);
                for(int j = 0; j < groundValue.length; j++) {
                    sb.append("\"").append(u.getConstant(groundValue[j])).append("\"").append(", ");
                }
                sb.append(this.doubleValues.get(i));
                sb.append(")\n");
            }
        } else {
            sb.append("// no values");
        }
        return sb.toString();
    }


    public String output()
    {
        StringBuilder sb = new StringBuilder();
        ArrayList<Type> types = this.getTypes();

        sb.append("PredicateDouble " + this.getName() + "P = new PredicateDouble(\"" + this.getName() + "\"," + this.isHidden() + ",");
        for(int i = 0; i < types.size() - 1; i++) {
            sb.append(types.get(i).getName() + "T, ");
        }
        sb.append(types.get(types.size() - 1).getName() + "T");
        sb.append(");\n");

        return sb.toString();
    }
}
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashSet;
import com.googlecode.rockit.app.Parameters;
import com.googlecode.rockit.conn.sql.MySQLConnector;
import com.googlecode.rockit.conn.sql.SQLQueryGenerator;
import com.googlecode.rockit.exception.DatabaseException;
import com.googlecode.rockit.exception.ParseException;
import com.googlecode.rockit.exception.SolveException;
import com.googlecode.rockit.javaAPI.HerbrandUniverse;
import com.googlecode.rockit.javaAPI.Model;
import com.googlecode.rockit.javaAPI.formulas.FormulaAbstract;
import com.googlecode.rockit.javaAPI.formulas.FormulaHard;
import com.googlecode.rockit.javaAPI.formulas.FormulaObjective;
import com.googlecode.rockit.javaAPI.formulas.expressions.impl.PredicateExpression;
import com.googlecode.rockit.javaAPI.formulas.variables.impl.VariableType;
import com.googlecode.rockit.javaAPI.predicates.PredicateAbstract;
import com.googlecode.rockit.javaAPI.predicates.PredicateDouble;
package com.googlecode.rockit.app.grounder;




public class StandardGrounder
{
    private boolean          groundHiddenPredicates = false;

    private Model            model                  = null;

    private MySQLConnector   connector;

    private HerbrandUniverse u                      = HerbrandUniverse.getInstance();


    public StandardGrounder(Model model, MySQLConnector connector)
    {
        this.connector = connector;
        this.model = model;
    }


    /**
     * This function generates a table for every observed predicate value.
     * 
     * The number of cols equals the number of Types of the respective predicate.
     * For Example:
     * predicate [observed] subsumesConfidence: Concept X Concept X Double;
     * 
     * results in:
     * CREATE TABLE `subsumesConfidence` ( value double, field0 varchar(50) NOT NULL, INDEX USING HASH (field0) , field1 varchar(50) NOT NULL, INDEX USING HASH (field1) , PRIMARY KEY (field0 , field1) ) ENGINE = MEMORY DEFAULT CHARSET=latin1;
     * 
     * Naming Conventions:
     * 1. The types are named "fieldX", where "X" is a number starting by 0 and counting the number of types.
     * 2. If it is a DoubleVariable, the field containing the double values is called "value"
     *
     * @param model
     * @param connector
     * @throws DatabaseException
     * @throws SQLException
     */
    private void generateObservedPredicateTables() throws DatabaseException
    {
        HashSet<PredicateAbstract> predicates = model.getAllObservedPredicates();
        if(this.groundHiddenPredicates) {
            predicates.addAll(model.getAllHiddenPredicates());
        }
        for(PredicateAbstract predicateAbstract : predicates) {

            // Create new one & Create prepared add statement
            ArrayList<String> fieldNames = new ArrayList<String>();
            for(int counter = 0; counter < predicateAbstract.getTypes().size(); counter++) {
                StringBuilder sb = new StringBuilder();
                fieldNames.add(sb.append("field").append(counter).toString());
            }

            Integer size = u.getMaximalKeySize();

            if(predicateAbstract.getClass().equals(PredicateDouble.class)) {
                connector.createInMemoryTable(predicateAbstract.getName(), "value", false, fieldNames, size);
            } else {
                connector.createInMemoryTable(predicateAbstract.getName(), null, false, fieldNames, size);
            }
        }
    }


    /**
     * This function puts the data into the tables. Thereby it uses the data stored in the
     * groundValue attributes of the observed predicates.
     * 
     * @param model
     * @param connector
     * @throws DatabaseException
     * @throws SQLException
     */
    private void putDataIntoTables() throws DatabaseException
    {
        long start = System.currentTimeMillis();
        HashSet<PredicateAbstract> predicates = model.getAllObservedPredicates();
        if(this.groundHiddenPredicates) {
            predicates.addAll(model.getAllHiddenPredicates());
        }
        for(PredicateAbstract predicateAbstract : predicates) {

            // Prepare add statement and fill with values
            ArrayList<String[]> groundValues = predicateAbstract.getGroundValues();
            StringBuilder filename = new StringBuilder();
            filename.append(predicateAbstract.getName()).append("_temp.db");
            if(PredicateDouble.class.equals(predicateAbstract.getClass())) {
                PredicateDouble predicateDouble = (PredicateDouble) predicateAbstract;
                if(Parameters.DEBUG_OUTPUT)
                    System.out.println("INSERT Data into " + predicateAbstract.getName());
                else
                    System.out.print(".");
                connector.addData(predicateAbstract.getName(), predicateDouble.getDoubleValues(), groundValues, filename.toString());
            } else {
                if(Parameters.DEBUG_OUTPUT)
                    System.out.println("INSERT Data into " + predicateAbstract.getName());
                else
                    System.out.print(".");
                connector.addData(predicateAbstract.getName(), groundValues, filename.toString());
            }
        }
        System.out.println("PutDataIntoTables duration: " + (System.currentTimeMillis() - start));
        System.out.println();
    }


    /**
     * Generates tables for every objective formular.
     * 
     * The cols of the tables are the variable names from the for part of the formular.
     * 
     * The concrete grounding then takes place with the if part of a formular.
     * Thereby, the cols are filled with the values comming from the given observed predicate values.
     * More precisely, the observed predicate tables are always joined iff two variables of the predicate expression are the same.
     * 
     * For example:
     * formular: objF
     * for Double cconf,Concept c2,Concept c1
     * if class(c1) & class(c2) & subsumesConfidence(c1, c2, cconf)
     * 
     * results in table "objF":
     * cconf | c2 | c1
     * 
     * The values are filled with an Insert-Select statement where
     * "class.c1 == subsumesConfidence.c1" and "class.c2 == subsumesConfidence.c2"
     * 
     * Note: due to the generalization this looks a little bit more ugly in reality:
     * INSERT INTO objF (cconf,c2,c1)
     * SELECT x2.value,x1.field0,x0.field0
     * FROM class x0,class x1,subsumesConfidence x2
     * WHERE x0.field0 = x2.field0 AND x1.field0 = x2.field1
     * 
     * @param model
     * @param connector
     * @throws SQLException
     * @throws ParseException
     * @throws SolveException
     */
    private void groundObjectiveFormulas() throws ParseException, SolveException
    {
        for(FormulaAbstract formularAbstract : model.getFormulas()) {
            if(formularAbstract.getClass().equals(FormulaObjective.class)) {