/*

Copyright © 2023-26 Sean Holden. All rights reserved.

*/
/*

This file is part of Connect++.

Connect++ is free software: you can redistribute it and/or modify it 
under the terms of the GNU General Public License as published by the 
Free Software Foundation, either version 3 of the License, or (at your 
option) any later version.

Connect++ is distributed in the hope that it will be useful, but WITHOUT 
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 
FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for 
more details.

You should have received a copy of the GNU General Public License along 
with Connect++. If not, see <https://www.gnu.org/licenses/>. 

*/

#ifndef MATRIX_HPP
#define MATRIX_HPP

#include<iostream>
#include<fstream>
#include<filesystem>
#include<iomanip>
#include<string>
#include<vector>
#include<tuple>
#include<algorithm>
#include<random>

#include "InferenceItem.hpp"
#include "Clause.hpp"
#include "ClauseComparisons.hpp"
#include "Unifier.hpp"
#include "Reorder.hpp"

using std::pair;
using std::tuple;
using std::vector;
using std::ostream;
using std::endl;
using std::setw;
using std::cout;
using std::filesystem::path;

/**
* \brief We want to be able to quickly identify where in 
*        each clause a particular literal lives.
*        
* Store clause and position as pairs.
*/
using MatrixPairType = pair<ClauseNum, LitNum>;
/**
* \brief We also want - in the same way as leanCoP - to 
* store in advance pairs of Literal and Clause, where 
* the Clause is obtained by removing the Literal.
*/
using LiteralClausePairType = pair<Literal,Clause>;
/**
* \brief Representation of the Matrix within a proof.
*
* The Matrix itself never actually changes. However we do 
* want to be able to look things up quickly when finding 
* possible extension steps, so we build a pair of indexes 
* to help with that.
*/
class Matrix {
private:
    /**
    * \brief The Matrix itself is just a set of Clauses.
    */
    vector<Clause> clauses;
    /**
    * \brief Keep track of which clauses are positive. 
    *
    * This allows start clause heuristics to be applied.
    */
    vector<bool> positive_clauses;
     /**
    * \brief Keep track of which clauses are negative. 
    *
    * This allows start clause heuristics to be applied.
    */
    vector<bool> negative_clauses;
    /**
    * \brief We need to know this to control backtracking.
    */
    vector<bool> ground_clauses;
     /**
    * \brief Keep track of which clauses are conjectures etc. 
    *
    * This allows start clause heuristics to be applied.
    */
    vector<string> clause_roles;
    /**
    * \brief Keep track of the names of the clauses. 
    *
    * This allows us to keep track of everything when 
    * producing TPTP output.
    */
    vector<string> clause_names;
    /**
    * \brief You need to know how many equality axioms 
    *        there are in case you want to move them around.
    */
    uint32_t num_equals;
    /**
    * \brief It makes sense to keep a copy of the clauses 
    *        if your schedule reorders multiple times.
    */
    vector<Clause> clauses_copy;
     /**
    * \brief It makes sense to keep a copy of the roles
    *        if your schedule reorders multiple times.
    */
    vector<string> roles_copy;
    /**
    * \brief It makes sense to keep a copy of the names 
    *        if your schedule reorders multiple times.
    */
    vector<string> names_copy;
    /**
    * \brief Remember whether you've saved a copy.
    */
    bool copy_saved;
    /**
    * \brief We want to be able to quickly identify where in 
    *        each clause a particular literal lives
    *        
    * Store clause and position as pairs in a row corresponding 
    * to the literal.
    */
    vector<vector<MatrixPairType>> index;
    /**
    * \brief This corresponds closely to index, but has the 
    * actual Literal, and the Clause after the Literal's removal.
    */
    vector<vector<LiteralClausePairType>> literal_clause_index; 
    /**
    * \brief Randomness for random reordering.
    */
    static std::mt19937 d;
    /**
    * \brief Store a copy of the clauses.
    */
    void make_clauses_copy() {
      clauses_copy.clear();
      clauses_copy = clauses;
      roles_copy.clear();
      roles_copy = clause_roles;
      names_copy.clear();
      names_copy = clause_names;
    }
    /**
    * \brief Rebuild the index after, for example, reordering.
    *
    * @param cs Vector of clauses - matrix.
    * @param ss Vector of strings - roles.
    * @param ss Vector of strings - names.
    */
    void rebuild_index(vector<Clause>&, vector<string>&, vector<string>&);
    /**
    * \brief Template method for general sorting of clauses, which 
    *        sorts roles and names at the same time.
    *
    * ClauseComparisons.hpp contains function objects that can be used 
    * with this to implement different orderings.
    */
    template<class Comparison>
    void sort_clauses(Comparison comparison) {
      /*
      * Combine the clauses with their roles.
      */
      vector<ClauseRoleName> cr;
      size_t s = clauses.size();
      for (size_t i = 0; i < s; i++) {
        cr.push_back(ClauseRoleName(clauses[i], pair<string, string>(clause_roles[i], clause_names[i])));
      }
      /*
      * Sort them according to the relevant ordering.
      */
      std::sort(cr.begin(), cr.end(), comparison);
      /*
      * Split them again and re-build the index.
      */
      clauses.clear();
      positive_clauses.clear();
      negative_clauses.clear();
      clause_roles.clear();
      clause_names.clear();
      size_t s2 = index.size();
      index.clear();
      index = vector<vector<MatrixPairType> >(s2, vector<MatrixPairType>());
      for (size_t i = 0; i < s; i++) {
        add_clause(std::get<0>(cr[i]), std::get<1>(cr[i]).first, std::get<1>(cr[i]).second);
      }
    }
public:
    /**
    * \brief Use this constructor if you really want an empty 
    *        Matrix. 
    */
    Matrix()
    : clauses()
    , index()
    , literal_clause_index()
    , positive_clauses()
    , negative_clauses()
    , ground_clauses()
    , clause_roles()
    , clause_names()
    , num_equals(0)
    , clauses_copy()
    , roles_copy()
    , names_copy()
    , copy_saved(false)
    {}
    /**
    * \brief Use this constructor when you know how many 
    *        predicates there are.
    *
    * @param num_predicates Number of predicates.
    */
    Matrix(size_t);
    /**
    * \brief You're never going to need to copy a Matrix.
    *
    * Let the compiler be your friend.
    */
    Matrix(const Matrix&) = delete;
    Matrix(const Matrix&&) = delete;
    Matrix& operator=(const Matrix&) = delete;
    Matrix& operator=(const Matrix&&) = delete;
    /**
    * \brief Straightforward get method.
    */
    ClauseNum get_num_clauses() const { return clauses.size(); }
    /**
    * \brief The ExtensionEnumerator needs to know this.
    */
    inline size_t get_index_entry_size(size_t _i) const {
      return index[_i].size();
    }
    /**
    * \brief The ExtensionEnumerator needs to know this.
    */
    inline MatrixPairType get_index_entry(size_t _i, size_t _j) const {
      return (index[_i])[_j];
    }
    /**
    * \brief Straightforward get method.
    *
    * Beware - the parameter is not checked, so behave!
    *
    * @param i Index of item to get.
    */
    const Clause& operator[](size_t i) const { return clauses[i]; }
    /**
    * \brief Straightforward get method.
    *
    * Beware - the parameters are not checked, so behave!
    *
    * @param i Index of clause to get.
    * @param j Index of literal in clause.
    */
    inline const Literal& inspect_literal(size_t i, size_t j) { 
      return (clauses[i])[j]; 
    }
    /**
    * \brief Straightforward get method.
    *
    * Beware - the parameters are not checked, so behave!
    *
    * @param i Index in literal_clause_index;
    * @param j Index of required entry.
    */
    inline const Literal& inspect_index_literal(size_t i, size_t j) { 
      return (literal_clause_index[i])[j].first; 
    }
    /**
    * \brief Is a particular Clause positive?
    *
    * @param i Index of Clause to check.
    */
    bool is_positive(size_t i) const { return positive_clauses[i]; }
    /**
    * \brief Is a particular Clause negative?
    *
    * @param i Index of Clause to check.
    */
    bool is_negative(size_t i) const { return negative_clauses[i]; }
    /**
    * \brief Is a particular Clause ground?
    *
    * @param i Index of Clause to check.
    */
    bool is_ground(size_t i) const { return ground_clauses[i]; }
    /**
    * \brief Is a particular Clause a conjecture?
    *
    * @param i Index of Clause to check.
    */
    bool is_conjecture(size_t i) const;
    /**
    * \brief Straightforward set method.
    *
    * @param n Number of equality axioms.
    */
    inline void set_num_equals(uint32_t n) { num_equals = n; } 
    /**
    * \brief Use a simple heuristic to find a good start clause.
    *
    * Find a start clause that is positive/negative according to
    * the --negative flag, and if possible also a conjecture clause.
    * Also, indicate if there is no positive/negative clause, which 
    * means the problem is trivial.
    */
    pair<bool,size_t> find_start() const;
    /**
    * \brief Make an empty index.
    */
    void set_num_preds(size_t);
    /**
    * \brief Retrieve the name of one of the clauses in the matrix.
    */
    string get_name(size_t _i) const {
      return clause_names[_i];
    }
    /**
    * \brief Add a Clause to the Matrix and update the index accordingly.
    * 
    * Optionally add a clause role.
    *
    * @param clause Reference to the Clause.
    * @param role   A string denoting the role of the clause. 
    * @param name   The name of the clause.
    */
    void add_clause(Clause&, string = "", string="");
    /**
    * \brief Deterministic reorder of the clauses. 
    *
    * Call the reorder algorithm the specified number of times. This 
    * assumes you have stored a copy of the original clauses.
    *
    * @param n Number of times to call the reorder algorithm
    */
    void deterministic_reorder(size_t);
    /**
    * \brief Randomly reorder the matrix.
    */
    void random_reorder();
    /**
    * \brief Randomly reorder the literals in each clause 
    *        in the matrix.
    */
    void random_reorder_literals();
    /**
    * \brief Self-explanatory.
    *
    * BUT only do this to the matrix built at the start of the 
    * process, as it *assumes* the equality axioms are at the end of 
    * the matrix.
    */
    void move_equals_to_start();
    /*
    * Iteration on a Matrix is just iteration over the Clauses.
    */
    vector<Clause>::const_iterator cbegin() const { return clauses.cbegin(); }
    vector<Clause>::const_iterator cend() const { return clauses.cend(); }
    vector<Clause>::iterator begin() { return clauses.begin(); }
    vector<Clause>::iterator end() { return clauses.end(); }
    /**
    * \brief Make a string representation.
    */
    string to_string() const;
    /**
    * \brief Make a usable LaTeX representation.
    *
    * @param subbed Implement substitutions if true.
    */
    string make_LaTeX(bool = false) const;
    /**
    * \brief Write to a file that can be read by Prolog. 
    * 
    * This is for proof checking.
    *
    * @param path_to_file Reference to path object.
    */
    void write_to_prolog_file(const path&) const;
    /**
    * \brief Output in TPTP compatible format.
    */
    void show_tptp() const;
    /** 
    * \brief Self-explanatory. 
    *
    * Inspired by "Machine Learning 
    * Guidance for Connection Tableaux," Farber et al. 
    * 2020 although with a different measure of size and 
    * happening at a different place. 
    *
    * Makes use of the sort_clauses template.
    */
    void sort_clauses_by_increasing_size() {
      ClauseLengthCompare comparison;
      sort_clauses<ClauseLengthCompare>(comparison);
    }
    /**
    * \brief Get a literal and clause from the index.
    *
    * @param _l      Index for the literal of interest.
    * @param _i      Index for where the pair is in _lit's vector.
    * @param _lit    Result literal.
    * @param _clause Result clause.
    */
    void get_literal_clause_pair(LitNum, size_t, Literal&, Clause&) const;
    /**
    * \brief Make a new copy of a clause in the matrix.
    *
    * @param _index Clause to copy.
    * @param _c     Newly copied clause.
    * @param _vi    Reference to variable index.
    * @param _ti    Reference to term index.
    */
    void make_copy_with_new_unique_vars(size_t, Clause&, VariableIndex&, TermIndex&) const;

    friend ostream& operator<<(ostream&, const Matrix&);
};


#endif
