/**************************************************************/
/* ********************************************************** */
/* *                                                        * */
/* *               SPLITTING OF CLAUSES                     * */
/* *                                                        * */
/* *  $Module:   SPLIT                                      * */ 
/* *                                                        * */
/* *  Copyright (C) 1996, 1997, 1998, 2000                  * */
/* *  MPI fuer Informatik                                   * */
/* *                                                        * */
/* *  This program 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 2 of the License, * */
/* *  or (at your option) any later version.                * */
/* *                                                        * */
/* *  This program 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 this program; if not, write * */
/* *  to the Free Software Foundation, Inc., 59 Temple      * */
/* *  Place, Suite 330, Boston, MA  02111-1307  USA         * */
/* *                                                        * */
/* *                                                        * */
/* $Revision$                                        * */
/* $State$                                            * */
/* $Date$                             * */
/* $Author$                                       * */
/* *                                                        * */
/* *             Contact:                                   * */
/* *             Christoph Weidenbach                       * */
/* *             MPI fuer Informatik                        * */
/* *             Stuhlsatzenhausweg 85                      * */
/* *             66123 Saarbruecken                         * */
/* *             Email: weidenb@mpi-sb.mpg.de               * */
/* *             Germany                                    * */
/* *                                                        * */
/* ********************************************************** */
/**************************************************************/


/* $RCSfile$ */

/**************************************************************/
/* Includes                                                   */
/**************************************************************/

#include "rules-split.h"

static LIST  split_DeleteClausesDependingOnLevelFromList(PROOFSEARCH,LIST, int, LIST*);
static LIST  split_DeleteInvalidClausesFromList(PROOFSEARCH, int, LIST);
static void  split_DeleteInvalidClausesFromStack(PROOFSEARCH);
static LIST  split_RemoveUnnecessarySplits(PROOFSEARCH, CLAUSE);


/**************************************************************/
/* Functions                                                  */
/**************************************************************/


LIST split_Backtrack(PROOFSEARCH PS, CLAUSE EmptyClause, CLAUSE* SplitClause) 
/**************************************************************
  INPUT:   A proofsearch object, an empty clause and a pointer to a clause
           used as return value.
  RETURNS: A list of clauses deleted in the backtracked split levels.
           <*SplitClause> is set to the split clause for the right branch
	   of the splitting step, or NULL, if the tableau is finished.
  EFFECT:  Backtracks the top of the split stack wrt the empty clause's level
***************************************************************/
{
  SPLIT ActBacktrackSplit;
  LIST  RecoverList, Scan;
  int   Backtracklevel;

  ActBacktrackSplit = (SPLIT)NULL;
  RecoverList       = split_RemoveUnnecessarySplits(PS, EmptyClause);
  Backtracklevel    = clause_SplitLevel(EmptyClause);
  *SplitClause      = NULL;

  /* Backtrack all split levels bigger than the level of the empty clause */
  while (!prfs_SplitStackEmpty(PS) && (prfs_ValidLevel(PS) > Backtracklevel)) {
    ActBacktrackSplit = prfs_SplitStackTop(PS);
    prfs_SplitStackPop(PS);
    if (prfs_SplitFatherClause(ActBacktrackSplit) != (CLAUSE)NULL) {
      RecoverList = list_Cons(prfs_SplitFatherClause(ActBacktrackSplit),
			      RecoverList);
      prfs_SplitSetFatherClause(ActBacktrackSplit, NULL);
    }
    RecoverList = list_Nconc(prfs_SplitDeletedClauses(ActBacktrackSplit),
			     RecoverList);
    clause_DeleteClauseList(prfs_SplitBlockedClauses(ActBacktrackSplit));
    prfs_SplitFree(ActBacktrackSplit);
    prfs_DecValidLevel(PS);
  }
  
  /* Backtrack further for all right branches on top of the stack */
  while (!prfs_SplitStackEmpty(PS) &&
	 list_Empty(prfs_SplitBlockedClauses(prfs_SplitStackTop(PS)))) {
    ActBacktrackSplit = prfs_SplitStackTop(PS);
    prfs_SplitStackPop(PS);
    if (prfs_SplitFatherClause(ActBacktrackSplit) != (CLAUSE)NULL)
      RecoverList = list_Cons(prfs_SplitFatherClause(ActBacktrackSplit),
			      RecoverList);
    RecoverList = list_Nconc(prfs_SplitDeletedClauses(ActBacktrackSplit),
			     RecoverList);
    prfs_SplitFree(ActBacktrackSplit);
    prfs_DecValidLevel(PS);
  }
  
  if (!prfs_SplitStackEmpty(PS)) {
    /* Enter the right branch of the splitting step */
    int SplitMinus1;
    LIST RightClauses;

    SplitMinus1       = prfs_ValidLevel(PS) - 1;
    ActBacktrackSplit = prfs_SplitStackTop(PS);

    RecoverList       = list_Nconc(prfs_SplitDeletedClauses(ActBacktrackSplit),
				   RecoverList);
    prfs_SplitSetDeletedClauses(ActBacktrackSplit, list_Nil());    
    RecoverList       = split_DeleteInvalidClausesFromList(PS, SplitMinus1,
							   RecoverList);

    RightClauses = prfs_SplitBlockedClauses(ActBacktrackSplit);
    prfs_SplitSetBlockedClauses(ActBacktrackSplit, list_Nil());    
    for (Scan = RightClauses; !list_Empty(Scan); Scan = list_Cdr(Scan)) {
      if (clause_Number(list_Car(Scan)) == 0) {
	/* Found the right clause, the negation clauses have number -1. */
#ifdef CHECK
	if (*SplitClause != NULL) {
	  misc_StartErrorReport();
	  misc_ErrorReport("\n In split_Backtrack:");
	  misc_ErrorReport(" Found two blocked clauses ");
	  misc_ErrorReport("\n with clause number 0 (this marks the clause ");
	  misc_ErrorReport("\n for the right branch of the tableau).");
	  misc_FinishErrorReport();
	}
#endif
	*SplitClause = list_Car(Scan);
      }
      
      clause_NewNumber((CLAUSE) list_Car(Scan));
      clause_AddParentClause((CLAUSE) list_Car(Scan), clause_Number(EmptyClause));
      clause_AddParentLiteral((CLAUSE) list_Car(Scan), 0);  /* dummy literal */
    }

#ifdef CHECK
    if (*SplitClause == NULL) {
      misc_StartErrorReport();
      misc_ErrorReport("\n In split_Backtrack: Didnīt find a blocked clause");
      misc_ErrorReport("\n with clause number 0. (this marks the clause ");
      misc_ErrorReport("\n for the right branch of the tableau).");
      misc_FinishErrorReport();
    }
#endif
    
    RecoverList = list_Nconc(RightClauses, RecoverList);

    /* Then, delete clauses from current level (Hack) */
    prfs_DecValidLevel(PS);
    prfs_MoveInvalidClausesDocProof(PS);
    split_DeleteInvalidClausesFromStack(PS);
    prfs_IncValidLevel(PS);
  } else {
    /* Don't delete clauses from current level (split is top level) */
    prfs_MoveInvalidClausesDocProof(PS);
    for (Scan = RecoverList; !list_Empty(Scan); Scan = list_Cdr(Scan))
      prfs_InsertDocProofClause(PS, list_Car(Scan));
    list_Delete(RecoverList);
    RecoverList = list_Nil();
  }
  prfs_SetLastBacktrackLevel(PS, prfs_ValidLevel(PS));

  return RecoverList;
}


static LIST split_DeleteClausesDependingOnLevelFromList(PROOFSEARCH Search,
							LIST ClauseList,
							int Level, LIST* New)
/**************************************************************
  INPUT:   A proof search object, a list of unshared clauses
           and a split level.
  EFFECT:  Deletes all clauses depending on split level from
           <ClauseList>.
           All split stored deleted clauses from the level of
           the deleted clauses from <ClauseList> are stored in
           <*New>.
  RETURNS: The updated list and the recover clauses in <*New>.
***************************************************************/
{
  LIST   Scan;
  CLAUSE Clause;
  SPLIT  Reinsert;

  for (Scan = ClauseList; !list_Empty(Scan); Scan = list_Cdr(Scan)) {
    Clause = list_Car(Scan);   
    if (clause_DependsOnSplitLevel(Clause, Level)) {
      Reinsert = prfs_GetSplitOfLevel(clause_SplitLevel(Clause), Search);
      if (prfs_SplitDeletedClauses(Reinsert) != list_Nil()) {
	*New = list_Nconc(prfs_SplitDeletedClauses(Reinsert), *New);
	prfs_SplitSetDeletedClauses(Reinsert, list_Nil());
      }
      prfs_InsertDocProofClause(Search,Clause);
      list_Rplaca(Scan, NULL);
    }
  }
  return list_PointerDeleteElement(ClauseList, NULL);
}
 

static LIST split_DeleteClausesDependingOnLevelFromSet(PROOFSEARCH PS,
						       LIST ClauseList,
						       int SplitLevel)
/**************************************************************
  INPUT:   A PROOFSEARCH object, a list of shared clauses
           and a split level.
  RETURNS: A list of clauses that have to be recovered possibly.
  EFFECT:  Clauses from the clause list depending on <SplitLevel>
           are moved to the doc proof index of <PS>.
           All formerly redundant clauses that were reduced by a clause
           of the same split level as a clause from the list depending
           on <SplitLevel> are returned.
***************************************************************/
{
  LIST   scan, delList, recover;
  CLAUSE clause;
  SPLIT  reinsert;

  delList = recover = list_Nil();

  for (scan = ClauseList; !list_Empty(scan); scan = list_Cdr(scan)){
    clause = list_Car(scan);
    if (clause_DependsOnSplitLevel(clause, SplitLevel)) {
      reinsert = prfs_GetSplitOfLevel(clause_SplitLevel(clause), PS);
      recover = list_Nconc(prfs_SplitDeletedClauses(reinsert), recover);
      prfs_SplitSetDeletedClauses(reinsert, list_Nil());
      delList = list_Cons(clause, delList);
    }
  }

  /* WARNING: The following move operations change the worked off */
  /* and usable sets of the proof search object destructively.    */
  /* So it's impossible to move those function calls into the     */
  /* loop above.                                                  */
  for ( ; !list_Empty(delList); delList = list_Pop(delList)) {
    clause = list_Car(delList);
    if (clause_GetFlag(clause, WORKEDOFF))
      prfs_MoveWorkedOffDocProof(PS, clause);
    else
      prfs_MoveUsableDocProof(PS, clause);
  }
  return recover;
}



static LIST split_DeleteInvalidClausesFromList(PROOFSEARCH Search, int Level,
					       LIST ClauseList)
/**************************************************************
  INPUT:   A proof search object, a split level and a list of clauses.
  RETURNS: The list where invalid clauses wrt 'Level' are deleted.
  EFFECT:  The invalid clauses are stored in the doc proof index
           of the proof search object if necessary.
***************************************************************/
{
  LIST   Scan;
  CLAUSE Clause;

  /*printf("\nDiese Liste soll von ungueltigen (Level > %d) "
    "befreit werden: \n",Level);fflush(stdout);
    clause_ListPrint(ClauseList);*/

  for (Scan = ClauseList; !list_Empty(Scan); Scan = list_Cdr(Scan)) {
    Clause = list_Car(Scan);
    if (!prfs_IsClauseValid(Clause,Level)) {
      prfs_InsertDocProofClause(Search,Clause);
      list_Rplaca(Scan, NULL);
    }
  }
  return list_PointerDeleteElement(ClauseList, NULL);
}

static void split_DeleteInvalidClausesFromStack(PROOFSEARCH Search)
/**************************************************************
  INPUT:   A proof search object.            
  EFFECT:  All clauses in the split stack of <Search> that have a higher
           split level than the current <Search> split level are deleted.
***************************************************************/
{
  LIST   Scan1,Scan2,ClauseList;
  int    Level;
  CLAUSE Clause;

  Level = prfs_ValidLevel(Search);

  for (Scan1=prfs_SplitStack(Search);!list_Empty(Scan1);Scan1=list_Cdr(Scan1)) {
    ClauseList = prfs_SplitDeletedClauses(list_Car(Scan1));
    for (Scan2 = ClauseList; !list_Empty(Scan2); Scan2 = list_Cdr(Scan2)) {
      Clause = (CLAUSE)list_Car(Scan2);
      if (!prfs_IsClauseValid(Clause,Level)) {
	prfs_InsertDocProofClause(Search,Clause);
	list_Rplaca(Scan2, NULL);
      }
    }
    prfs_SplitSetDeletedClauses(list_Car(Scan1),list_PointerDeleteElement(ClauseList, NULL));
  }
}


static LIST split_RemoveUnnecessarySplits(PROOFSEARCH PS, CLAUSE EmptyClause)
/**************************************************************
  INPUT:   An empty clause and a proof search object
  EFFECT:  Removes all splits up to the last backtrack level
           that were not necessary to derive the empty clause.
  RETURNS: A list of recovered clauses.
***************************************************************/
{
  LIST Scan;
  LIST Recover, New;
  LIST Deleted;
  LIST ScanStack;

  int SplitLevel;
  int LastBacktrackLevel;
  SPLIT Split,ScanSplit;

  Scan               = prfs_SplitStack(PS);
  SplitLevel         = prfs_ValidLevel(PS);
  LastBacktrackLevel = prfs_LastBacktrackLevel(PS);
  Recover            = list_Nil();

  while (SplitLevel > LastBacktrackLevel) {
    if (prfs_SplitIsUnused(list_Car(Scan)) &&
	!clause_DependsOnSplitLevel(EmptyClause, SplitLevel)) {
      New   = list_Nil();
      Split = list_Car(Scan);

      /*printf("\n\t Removed: %d",prfs_SplitSplitLevel(Split));*/
      
      clause_DeleteClauseList(prfs_SplitBlockedClauses(Split));
      prfs_SplitSetBlockedClauses(Split, list_Nil());
      
      Recover = list_Nconc(prfs_SplitDeletedClauses(Split), Recover);
      prfs_SplitSetDeletedClauses(Split, list_Nil());
      
      if (prfs_SplitFatherClause(Split) != (CLAUSE)NULL) {
	Recover = list_Cons(prfs_SplitFatherClause(Split),Recover);
	prfs_SplitSetFatherClause(Split,NULL);
      }
      Recover = split_DeleteClausesDependingOnLevelFromList(PS, Recover, SplitLevel, &New);
      
      ScanStack = prfs_SplitStack(PS);
      while (!list_StackEmpty(ScanStack) &&  
	     prfs_SplitSplitLevel((ScanSplit = (SPLIT)list_Car(ScanStack))) > LastBacktrackLevel) {
	Deleted = prfs_SplitDeletedClauses(ScanSplit);
	prfs_SplitSetDeletedClauses(ScanSplit, list_Nil()); /* IMPORTANT!, see next line */
	Deleted = split_DeleteClausesDependingOnLevelFromList(PS, Deleted, SplitLevel, &New);
	prfs_SplitSetDeletedClauses(ScanSplit, Deleted);
	ScanStack = list_Cdr(ScanStack);
      }
      
      while (!list_Empty(New)) {
	Deleted = list_Nil();
	Recover = list_Nconc(split_DeleteClausesDependingOnLevelFromList(PS, New, SplitLevel, &Deleted),
			     Recover);
	New     = Deleted;
      }
      Recover = list_Nconc(Recover, 
		    split_DeleteClausesDependingOnLevelFromSet(PS, prfs_UsableClauses(PS), SplitLevel));
      Recover = list_Nconc(Recover,
			   split_DeleteClausesDependingOnLevelFromSet(PS, prfs_WorkedOffClauses(PS), SplitLevel));
      
      prfs_SplitSetUsed(Split);
    }
    
    SplitLevel--;
    Scan = list_Cdr(Scan);
  }
  return Recover;
}


void split_DeleteClauseAtLevel(PROOFSEARCH PS, CLAUSE Clause, int Level)
/**************************************************************
  INPUT:   A clause, a level and a proofsearch object
  RETURNS: Nothing.
  EFFECT:  <Clause> is deleted from the usable or worked off set
           and made unshared.
***************************************************************/
{
  if (clause_GetFlag(Clause,WORKEDOFF)) 
    prfs_ExtractWorkedOff(PS, Clause);
  else 
    prfs_ExtractUsable(PS, Clause);

  split_KeepClauseAtLevel(PS, Clause, Level);
}


void split_KeepClauseAtLevel(PROOFSEARCH PS, CLAUSE Clause, int Level)
/**************************************************************
  INPUT:   A clause and a level as int.
  RETURNS: None.
  MEMORY:  A copy of clause is made and kept within the split stack.
***************************************************************/
{
  SPLIT  Split;

  Split = prfs_GetSplitOfLevel(Level, PS);
  prfs_SplitSetDeletedClauses(Split,list_Cons(Clause, prfs_SplitDeletedClauses(Split)));
}


LIST split_ExtractEmptyClauses(LIST Clauses, LIST* EmptyClauses)
/**************************************************************
  INPUT:   A list of clauses and a pointer to a list of empty clauses.
  RETURNS: <Clauses> without all empty clauses where the empty clauses
           are moved to <EmptyClauses>
  MEMORY:  Destructive on <Clauses>.
***************************************************************/
{
  LIST   Scan;
  CLAUSE Clause;

  for (Scan=Clauses;!list_Empty(Scan);Scan=list_Cdr(Scan)) {
    Clause = (CLAUSE)list_Car(Scan);
    if (clause_IsEmptyClause(Clause)) {
      *EmptyClauses = list_Cons(Clause,*EmptyClauses);
      list_Rplaca(Scan,NULL);
    }
  }
  Clauses = list_PointerDeleteElement(Clauses,NULL);

  return Clauses;
}

CLAUSE split_SmallestSplitLevelClause(LIST Clauses)
/**************************************************************
  INPUT:   A non-empty list of clauses.
  RETURNS: The clause with the smallest split level.
***************************************************************/
{
  CLAUSE Result;

  Result  = (CLAUSE)list_Car(Clauses);
  Clauses = list_Cdr(Clauses);
  
  while (!list_Empty(Clauses)) {
    if (clause_SplitLevel(Result) > clause_SplitLevel(list_Car(Clauses)))
      Result  = list_Car(Clauses);
    Clauses = list_Cdr(Clauses);
  }

  return Result;
}
