| /**************************************************************/ |
| /* ********************************************************** */ |
| /* * * */ |
| /* * TABLEAU PROOF TREES * */ |
| /* * * */ |
| /* * Copyright (C) 1998, 1999, 2000, 2001 * */ |
| /* * 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$ */ |
| |
| #include "tableau.h" |
| |
| /* for graph output */ |
| extern BOOL pcheck_ClauseCg; |
| extern GRAPHFORMAT pcheck_GraphFormat; |
| |
| TABPATH tab_PathCreate(int MaxLevel, TABLEAU Tab) |
| /************************************************************** |
| INPUT: A tableau, its maximum expected depth |
| RETURNS: A path consisting of the root node of the tableau |
| which can have a max length of <MaxLevel> |
| ***************************************************************/ |
| { |
| TABPATH TabPath; |
| |
| TabPath = (TABPATH)memory_Malloc(sizeof(TABPATH_NODE)); |
| TabPath->Path = (TABLEAU*)memory_Malloc(sizeof(TABLEAU)*(MaxLevel+1)); |
| TabPath->Path[0] = Tab; |
| TabPath->MaxLength = MaxLevel; |
| TabPath->Length = 0; |
| |
| return TabPath; |
| } |
| |
| void tab_PathDelete(TABPATH TabPath) |
| /************************************************************** |
| INPUT: A tableau path. |
| RETURNS: Nothing. |
| EFFECTS: The path is deleted. |
| ***************************************************************/ |
| { |
| memory_Free(TabPath->Path, (TabPath->MaxLength+1)*sizeof(TABLEAU)); |
| memory_Free(TabPath, sizeof(TABPATH_NODE)); |
| } |
| |
| |
| BOOL tab_PathContainsClause(TABPATH Path, CLAUSE Clause) |
| /************************************************************** |
| INPUT: A tableau path, a clause |
| RETURNS: TRUE iff the clause exists on its level (wrt. the |
| path) in the tableau |
| ***************************************************************/ |
| { |
| LIST Scan; |
| |
| if (clause_SplitLevel(Clause) > tab_PathLength(Path)) |
| return FALSE; |
| |
| for (Scan = tab_Clauses(tab_PathNthNode(Path, clause_SplitLevel(Clause))); |
| !list_Empty(Scan); Scan = list_Cdr(Scan)) { |
| if (list_Car(Scan) == Clause) |
| return TRUE; |
| } |
| return FALSE; |
| } |
| |
| static BOOL tab_PathContainsClauseSoft(TABPATH Path, CLAUSE Clause) |
| /************************************************************** |
| INPUT: A tableau path, a clause |
| RETURNS: TRUE iff the clause is on one of the levels |
| in the tableau traversed by the path. Different |
| from tab_PathContainsClauseSoft, since it does |
| not expect the clause on its split level. |
| ***************************************************************/ |
| { |
| LIST Scan; |
| int Level; |
| |
| if (clause_SplitLevel(Clause) > tab_PathLength(Path)) |
| return FALSE; |
| |
| for (Level = 0; Level <= tab_PathLength(Path); Level++) { |
| for (Scan = tab_Clauses(tab_PathNthNode(Path, Level)); |
| !list_Empty(Scan); Scan = list_Cdr(Scan)) { |
| if (list_Car(Scan) == Clause) |
| return TRUE; |
| } |
| } |
| return FALSE; |
| } |
| |
| /* unused */ |
| /*static*/ BOOL tab_PathContainsClauseRobust(TABPATH P, CLAUSE C) |
| /************************************************************** |
| INPUT: A tableau path, a clause |
| RETURNS: TRUE if clause can be find on the path (not |
| necessarily on its level) |
| EFFECTS: Prints a note if clause cannot be found on |
| its level. Intended for debugging. |
| ***************************************************************/ |
| { |
| if (tab_PathContainsClause(P,C)) |
| return TRUE; |
| |
| if (tab_PathContainsClauseSoft(P,C)) { |
| fputs("NOTE: Clause is found on path, but not indexed by level.\n", stderr); |
| clause_PParentsFPrint(stderr,C); |
| fflush(stderr); |
| return TRUE; |
| } |
| return FALSE; |
| } |
| |
| |
| void tab_AddSplitAtCursor(TABPATH Path, BOOL LeftSide) |
| /************************************************************** |
| INPUT: A tableau path, a flag |
| RETURNS: Nothing. |
| EFFECTS: Extends the tableau containing the path <Path> to |
| the left if <LeftSide> is TRUE, to the right |
| otherwise |
| ***************************************************************/ |
| { |
| TABLEAU Tab, NewBranch; |
| |
| Tab = tab_PathTop(Path); |
| NewBranch = tab_CreateNode(); |
| if (LeftSide) { |
| |
| #ifdef CHECK |
| if (!tab_LeftBranchIsEmpty(Tab)) { |
| misc_StartErrorReport(); |
| misc_ErrorReport("\n In tab_AddSplitAtCursor: Recreating existing"); |
| misc_ErrorReport(" left branch in tableau.\n"); |
| misc_FinishErrorReport(); |
| } |
| #endif |
| |
| tab_SetLeftBranch(Tab,NewBranch); |
| } else { |
| |
| #ifdef CHECK |
| if (!tab_RightBranchIsEmpty(Tab)) { |
| misc_StartErrorReport(); |
| misc_ErrorReport("\n In tab_AddSplitAtCursor: Recreating existing"); |
| misc_ErrorReport(" right branch in tableau.\n"); |
| misc_FinishErrorReport(); |
| } |
| #endif |
| tab_SetRightBranch(Tab, NewBranch); |
| } |
| tab_PathPush(NewBranch, Path); |
| } |
| |
| void tab_AddClauseOnItsLevel(CLAUSE C, TABPATH Path) |
| /************************************************************** |
| INPUT: A clause, a tableau path |
| RETURNS: Nothing |
| EFFECTS: Adds the clause on its split level which |
| must belong to <Path> |
| ***************************************************************/ |
| { |
| int Level; |
| |
| Level = clause_SplitLevel(C); |
| if (Level > tab_PathLength(Path)) { |
| misc_StartUserErrorReport(); |
| misc_UserErrorReport("\nError: Split level of some clause "); |
| misc_UserErrorReport("\nis higher than existing level."); |
| misc_UserErrorReport("\nThis may be a bug in the proof file."); |
| misc_FinishUserErrorReport(); |
| } |
| |
| tab_AddClause(C, tab_PathNthNode(Path, clause_SplitLevel(C))); |
| } |
| |
| |
| int tab_Depth(TABLEAU T) |
| /************************************************************** |
| INPUT: A tableau |
| RETURNS: The depth of the tableau |
| ***************************************************************/ |
| { |
| if (tab_IsEmpty(T)) |
| return 0; |
| if (tab_IsLeaf(T)) |
| return 0; |
| else |
| return (misc_Max(tab_Depth(tab_RightBranch(T))+1, tab_Depth(tab_LeftBranch(T)))+1); |
| } |
| |
| static BOOL tab_HasEmptyClause(TABLEAU T) |
| /************************************************************** |
| INPUT: A tableau |
| RETURNS: TRUE iff an empty clause is among the clauses |
| on this level |
| ***************************************************************/ |
| { |
| LIST Scan; |
| |
| for (Scan = tab_Clauses(T); !list_Empty(Scan); Scan = list_Cdr(Scan)) |
| if (clause_IsEmptyClause(list_Car(Scan))) |
| return TRUE; |
| |
| return FALSE; |
| } |
| |
| |
| BOOL tab_IsClosed(TABLEAU T) |
| /************************************************************** |
| INPUT: A Tableau |
| RETURNS: TRUE iff the tableau is closed. (NOTE: FALSE |
| if the tableau is empty) |
| ***************************************************************/ |
| { |
| if (tab_IsEmpty(T)) |
| return FALSE; |
| |
| if (tab_HasEmptyClause(T)) |
| return TRUE; |
| /* |
| * now tableau can only be closed |
| * if there has been a split, and |
| * both subtableaus are closed |
| */ |
| |
| if (tab_RightBranchIsEmpty(T) || tab_LeftBranchIsEmpty(T)) { |
| printf("\nopen node label: %d", T->Label); |
| fflush(stdout); |
| |
| return FALSE; |
| } |
| return tab_IsClosed(tab_RightBranch(T)) && tab_IsClosed(tab_LeftBranch(T)); |
| } |
| |
| static LIST tab_DeleteFlat(TABLEAU T) |
| /************************************************************** |
| INPUT: A tableau |
| RETURNS: The list of its clauses on the first level of |
| the tableau |
| EFFECTS: Frees the root tableau node. |
| ***************************************************************/ |
| { |
| LIST Clauses; |
| |
| Clauses = tab_Clauses(T); |
| memory_Free(T, sizeof(TABLEAU_NODE)); |
| |
| return Clauses; |
| } |
| |
| |
| static void tab_DeleteGen(TABLEAU T, LIST* Clauses, BOOL DeleteClauses) |
| /************************************************************** |
| INPUT: A tableau, a list of clauses by reference, a flag |
| RETURNS: Nothing |
| EFFECTS: Depending on <DeleteClauses>, all clauses in the |
| tableau are added to <Clauses> or just deleted. |
| The memory for the tableau and its clause lists is |
| freed. |
| ***************************************************************/ |
| { |
| if (tab_IsEmpty(T)) |
| return; |
| |
| tab_DeleteGen(tab_RightBranch(T), Clauses, DeleteClauses); |
| tab_DeleteGen(tab_LeftBranch(T), Clauses, DeleteClauses); |
| |
| list_Delete(tab_RightSplitClauses(T)); |
| if (DeleteClauses) |
| list_Delete(tab_Clauses(T)); |
| else |
| *Clauses = list_Nconc(tab_Clauses(T), *Clauses); |
| |
| tab_DeleteFlat(T); |
| |
| } |
| |
| static void tab_DeleteCollectClauses(TABLEAU T, LIST* Clauses) |
| /************************************************************** |
| INPUT: A tableau, a list of clauses by reference |
| RETURNS: Nothing |
| EFFECTS: Frees the memory of the tableau, but collects |
| its clauses |
| ***************************************************************/ |
| { |
| tab_DeleteGen(T, Clauses, FALSE); |
| } |
| |
| void tab_Delete(TABLEAU T) |
| /************************************************************** |
| INPUT: A tableau |
| RETURNS: Nothing |
| EFFECTS: Frees the memory of the tableau |
| ***************************************************************/ |
| { |
| LIST Redundant; |
| |
| Redundant = list_Nil(); |
| tab_DeleteGen(T, &Redundant, TRUE); |
| } |
| |
| static void tab_SetSplitLevelsRec(TABLEAU T, int Level) |
| /************************************************************** |
| INPUT: A tableau |
| RETURNS: Nothing |
| EFFECTS: The split levels of the clauses in the |
| tableau are set to the level of the |
| tableau level they are contained in. |
| ***************************************************************/ |
| { |
| LIST Scan; |
| |
| if (tab_IsEmpty(T)) |
| return; |
| |
| for (Scan = tab_Clauses(T); !list_Empty(Scan); Scan = list_Cdr(Scan)) { |
| clause_SetSplitLevel(list_Car(Scan), Level); |
| if (Level >0) { |
| clause_ClearSplitField(list_Car(Scan)); |
| clause_SetSplitFieldBit(list_Car(Scan), Level); |
| } |
| else |
| clause_SetSplitField(list_Car(Scan), (SPLITFIELD)NULL,0); |
| } |
| |
| tab_SetSplitLevelsRec(tab_RightBranch(T), Level+1); |
| tab_SetSplitLevelsRec(tab_LeftBranch(T), Level+1); |
| } |
| |
| void tab_SetSplitLevels(TABLEAU T) |
| /************************************************************** |
| INPUT: A tableau |
| RETURNS: Nothing |
| EFFECTS: The split levels of the clauses in the |
| tableau are set to the level of the |
| tableau they belong to. |
| ***************************************************************/ |
| { |
| tab_SetSplitLevelsRec(T,0); |
| } |
| |
| |
| TABLEAU tab_PruneClosedBranches(TABLEAU T, LIST* Clauses) |
| /************************************************************** |
| INPUT: A tableau, a list of clauses by reference. |
| RETURNS: The (destructively) reduced tableau: Descendants of |
| nodes that have an empty clause are deleted. |
| EFFECTS: The tableau is modified. |
| ***************************************************************/ |
| { |
| if (tab_IsEmpty(T)) |
| return T; |
| |
| /* if there is an empty clause on this level, delete subtrees */ |
| |
| if (tab_HasEmptyClause(T)) { |
| |
| tab_DeleteCollectClauses(tab_RightBranch(T), Clauses); |
| tab_DeleteCollectClauses(tab_LeftBranch(T), Clauses); |
| tab_SetRightBranch(T, tab_EmptyTableau()); |
| tab_SetLeftBranch(T, tab_EmptyTableau()); |
| list_Delete(tab_RightSplitClauses(T)); |
| tab_SetRightSplitClauses(T, list_Nil()); |
| tab_SetSplitClause(T,clause_Null()); |
| tab_SetLeftSplitClause(T, clause_Null()); |
| } |
| /* else recursively prune subtrees */ |
| else { |
| tab_SetRightBranch(T, tab_PruneClosedBranches(tab_RightBranch(T), Clauses)); |
| tab_SetLeftBranch(T, tab_PruneClosedBranches(tab_LeftBranch(T), Clauses)); |
| } |
| |
| return T; |
| } |
| |
| |
| TABLEAU tab_RemoveIncompleteSplits(TABLEAU T, LIST* Clauses) |
| /************************************************************** |
| INPUT: A Tableau, a list of clauses by reference |
| RETURNS: The reduced tableau: If a node has exactly one |
| successor (that is, the corresponding split was |
| not completed), delete the successor and move |
| its subtrees to <T>. |
| EFFECTS: The successor node is deleted, and its clauses added |
| to <Clauses> |
| ***************************************************************/ |
| { |
| LIST NewClauses; |
| TABLEAU Child; |
| |
| if (tab_IsEmpty(T)) |
| return T; |
| |
| if (tab_IsLeaf(T)) |
| return T; |
| |
| if (!tab_IsEmpty(tab_RightBranch(T)) && |
| !tab_IsEmpty(tab_LeftBranch(T))) { |
| tab_SetRightBranch(T, tab_RemoveIncompleteSplits(tab_RightBranch(T), Clauses)); |
| tab_SetLeftBranch(T, tab_RemoveIncompleteSplits(tab_LeftBranch(T), Clauses)); |
| return T; |
| } |
| if (tab_IsEmpty(tab_RightBranch(T))) |
| Child = tab_LeftBranch(T); |
| else |
| Child = tab_RightBranch(T); |
| |
| Child = tab_RemoveIncompleteSplits(Child, Clauses); |
| |
| tab_SetLeftBranch(T, tab_LeftBranch(Child)); |
| tab_SetRightBranch(T, tab_RightBranch(Child)); |
| |
| /* copy split data */ |
| |
| tab_SetSplitClause(T, tab_SplitClause(Child)); |
| tab_SetLeftSplitClause(T, tab_LeftSplitClause(Child)); |
| tab_SetRightSplitClauses(T, tab_RightSplitClauses(Child)); |
| |
| /* delete ancestors of deleted clauses and remember */ |
| |
| NewClauses = tab_DeleteFlat(Child); |
| (*Clauses) = list_Nconc(NewClauses, *Clauses); |
| |
| return T; |
| } |
| |
| |
| void tab_CheckEmpties(TABLEAU T) |
| /************************************************************** |
| INPUT: A tableau |
| RETURNS: Nothing. |
| EFFECTS: Prints warnings if non-leaf nodes contain |
| empty clauses (which should not be the case |
| after pruning any more), of if leaf nodes |
| contain more than one empty clause |
| ***************************************************************/ |
| { |
| LIST Scan, Empties; |
| BOOL Printem; |
| |
| if (tab_IsEmpty(T)) |
| return; |
| |
| /* get all empty clauses in this node */ |
| Empties = list_Nil(); |
| for (Scan = tab_Clauses(T); !list_Empty(Scan); Scan = list_Cdr(Scan)) { |
| if (clause_IsEmptyClause(list_Car(Scan))) |
| Empties = list_Cons(list_Car(Scan), Empties); |
| } |
| Printem = FALSE; |
| if (!list_Empty(Empties) && !tab_IsLeaf(T)) { |
| puts("\nNOTE: non-leaf node contains empty clauses."); |
| Printem = TRUE; |
| } |
| |
| if (tab_IsLeaf(T) && list_Length(Empties) > 1) { |
| puts("\nNOTE: Leaf contains more than one empty clauses."); |
| Printem = TRUE; |
| } |
| if (Printem) { |
| puts("Clauses:"); |
| clause_PParentsListPrint(tab_Clauses(T)); |
| } |
| list_Delete(Empties); |
| tab_CheckEmpties(tab_LeftBranch(T)); |
| tab_CheckEmpties(tab_RightBranch(T)); |
| } |
| |
| |
| void tab_GetAllEmptyClauses(TABLEAU T, LIST* L) |
| /************************************************************** |
| INPUT: A tableau, a list by reference |
| RETURNS: All empty clauses in the tableau prepended to <L>. |
| ***************************************************************/ |
| { |
| if (tab_IsEmpty(T)) |
| return; |
| |
| tab_GetAllEmptyClauses(tab_LeftBranch(T), L); |
| tab_GetAllEmptyClauses(tab_RightBranch(T), L); |
| } |
| |
| |
| void tab_GetEarliestEmptyClauses(TABLEAU T, LIST* L) |
| /************************************************************** |
| INPUT : A tableau, a list of clauses by reference |
| RETURNS: Nothing. |
| EFFECTS: For each leaf node, adds empty clauses in |
| leaf nodes to <L>. If the leaf node contains only one |
| empty clause, it is added to <L> anyway. |
| If the leaf node contains more than one empty clause, |
| the earliest derived empty clause is added to <L>. |
| ***************************************************************/ |
| { |
| CLAUSE FirstEmpty; |
| LIST Scan; |
| |
| if (tab_IsEmpty(T)) |
| return; |
| |
| if (tab_IsLeaf(T)) { |
| FirstEmpty = clause_Null(); |
| |
| for (Scan = tab_Clauses(T); !list_Empty(Scan); Scan = list_Cdr(Scan)) { |
| if (clause_IsEmptyClause(list_Car(Scan))) { |
| if (FirstEmpty == clause_Null()) |
| FirstEmpty = list_Car(Scan); |
| else if (clause_Number(FirstEmpty) > clause_Number(list_Car(Scan))) |
| FirstEmpty = list_Car(Scan); |
| } |
| } |
| |
| if (FirstEmpty != clause_Null()) |
| (*L) = list_Cons(FirstEmpty, *L); |
| } |
| tab_GetEarliestEmptyClauses(tab_LeftBranch(T), L); |
| tab_GetEarliestEmptyClauses(tab_RightBranch(T), L); |
| |
| } |
| |
| void tab_ToClauseList(TABLEAU T, LIST* Proof) |
| /************************************************************** |
| INPUT: A tableau <T>, a list of clauses |
| RETURNS: Nothing. |
| EFFECTS: All clauses in T are added to <Proof> |
| ***************************************************************/ |
| { |
| if (tab_IsEmpty(T)) |
| return; |
| |
| (*Proof) = list_Nconc(list_Copy(tab_Clauses(T)), *Proof); |
| |
| tab_ToClauseList(tab_LeftBranch(T),Proof); |
| tab_ToClauseList(tab_RightBranch(T),Proof); |
| } |
| |
| |
| static void tab_ToSeqProofOrdered(TABLEAU T, LIST* Proof) |
| /************************************************************** |
| INPUT: A tableau <T>, a list of clauses <Proof> representing a |
| proof by reference |
| RETURNS: The sequential proof corresponding to the tableau. |
| ***************************************************************/ |
| { |
| LIST Scan; |
| BOOL RightSplitRead, LeftSplitRead; |
| |
| if (tab_IsEmpty(T)) |
| return; |
| |
| Scan = tab_Clauses(T); |
| RightSplitRead = LeftSplitRead = FALSE; |
| |
| while (!list_Empty(Scan)) { |
| /* insert left and right splits and descendants controlled by clause number */ |
| |
| if (!RightSplitRead && !tab_RightBranchIsEmpty(T) && |
| clause_Number(list_Car(Scan)) < |
| clause_Number(list_Car(tab_RightSplitClauses(T)))) { |
| tab_ToSeqProofOrdered(tab_RightBranch(T), Proof); |
| RightSplitRead = TRUE; |
| } |
| if (!LeftSplitRead && !tab_LeftBranchIsEmpty(T) && |
| clause_Number(list_Car(Scan)) < |
| clause_Number(tab_LeftSplitClause(T))) { |
| tab_ToSeqProofOrdered(tab_LeftBranch(T), Proof); |
| LeftSplitRead = TRUE; |
| } |
| (*Proof) = list_Cons(list_Car(Scan), *Proof); |
| Scan = list_Cdr(Scan); |
| } |
| /* if a split clause with descendants has not been inserted yet, |
| it been generated after all other clauses */ |
| |
| if (!RightSplitRead) |
| tab_ToSeqProofOrdered(tab_RightBranch(T), Proof); |
| if (!LeftSplitRead) |
| tab_ToSeqProofOrdered(tab_LeftBranch(T), Proof); |
| } |
| |
| |
| /****************************************************************/ |
| /* SPECIALS FOR GRAPHS */ |
| /****************************************************************/ |
| |
| static void tab_LabelNodesRec(TABLEAU T, int* Num) |
| /************************************************************** |
| INPUT: A Tableau, a number by reference |
| RETURNS: Nothing. |
| EFFECTS: Labels the tableau nodes dflr, starting with <Num> |
| ***************************************************************/ |
| { |
| if (tab_IsEmpty(T)) |
| return; |
| T->Label = *Num; |
| (*Num)++; |
| tab_LabelNodesRec(tab_LeftBranch(T), Num); |
| tab_LabelNodesRec(tab_RightBranch(T), Num); |
| } |
| |
| |
| void tab_LabelNodes(TABLEAU T) |
| /************************************************************** |
| INPUT: A Tableau, a number by reference |
| RETURNS: Nothing. |
| EFFECTS: Labels the tableau nodes dflr, starting with 0 |
| ***************************************************************/ |
| { |
| int Num; |
| |
| Num = 0; |
| tab_LabelNodesRec(T, &Num); |
| } |
| |
| |
| static void tab_FPrintNodeLabel(FILE *File, TABLEAU T) |
| /************************************************************** |
| INPUT: A file handle, a tableau |
| RETURNS: Nothing. |
| EFFECTS: Prints the root node information to <File>: |
| clauses, split information |
| ***************************************************************/ |
| { |
| LIST Scan; |
| |
| /* start printing of node label string */ |
| |
| fprintf(File, "\"label: %d\\n", T->Label); |
| |
| /* print left and right parts of split */ |
| fputs("SplitClause : ", File); |
| clause_PParentsFPrint(File, tab_SplitClause(T)); |
| fputs("\\nLeft Clause : ", File); |
| clause_PParentsFPrint(File, tab_LeftSplitClause(T)); |
| fputs("\\nRightClauses: ", File); |
| if (list_Empty(tab_RightSplitClauses(T))) |
| fputs("[]\\n", File); |
| else { |
| clause_PParentsFPrint(File, list_Car(tab_RightSplitClauses(T))); |
| fputs("\\n", File); |
| for (Scan = list_Cdr(tab_RightSplitClauses(T)); !list_Empty(Scan); Scan = list_Cdr(Scan)) { |
| fputs(" ", File); |
| clause_PParentsFPrint(File, list_Car(Scan)); |
| fputs("\\n", File); |
| } |
| } |
| /* print clause at this level */ |
| if (pcheck_ClauseCg) { |
| if (list_Empty(tab_Clauses(T))) |
| fputs("[]", File); |
| else { |
| for (Scan = tab_Clauses(T); !list_Empty(Scan); Scan = list_Cdr(Scan)){ |
| clause_PParentsFPrint(File, list_Car(Scan)); |
| fputs("\\n", File); |
| } |
| } |
| } |
| putc('"', File); /* close string */ |
| } |
| |
| |
| static void tab_FPrintEdgeCgFormat(FILE* File, int Source, int Target, BOOL Left) |
| /************************************************************** |
| INPUT: A file handle, two node labels, a flag |
| RETURNS: Nothing. |
| EFFECTS: Prints the edge denoted by <Source> and <Target> |
| to <File>, with an edge label (0/1) according to <Left>. |
| The edge label is added since xvcg cannot handle |
| ordered edges. |
| ***************************************************************/ |
| { |
| fputs("\nedge: {", File); |
| fprintf(File, "\nsourcename: \"%d\"", Source); |
| fprintf(File, "\ntargetname: \"%d\"\n", Target); |
| fputs("\nlabel: \"", File); |
| if (Left) |
| putc('0', File); |
| else |
| putc('1', File); |
| fputs("\" }\n", File); |
| } |
| |
| |
| static void tab_FPrintEdgesCgFormat(FILE* File, TABLEAU T) |
| /************************************************************** |
| INPUT: A file handle, a tableau |
| RETURNS: Nothing. |
| EFFECTS: Prints edge information of <T> in xvcg graph format |
| to <File>. |
| ***************************************************************/ |
| { |
| if (tab_IsEmpty(T)) |
| return; |
| |
| if (!tab_LeftBranchIsEmpty(T)) |
| tab_FPrintEdgeCgFormat(File, T->Label, tab_LeftBranch(T)->Label, TRUE); |
| if (!tab_RightBranchIsEmpty(T)) |
| tab_FPrintEdgeCgFormat(File, T->Label, tab_RightBranch(T)->Label, FALSE); |
| |
| tab_FPrintEdgesCgFormat(File, tab_LeftBranch(T)); |
| tab_FPrintEdgesCgFormat(File, tab_RightBranch(T)); |
| } |
| |
| |
| static void tab_FPrintNodesCgFormat(FILE* File, TABLEAU T) |
| /************************************************************** |
| INPUT: A file handle, a tableau |
| RETURNS: Nothing |
| EFFECTS: Prints egde information of <T> in xvcg graph format |
| to <File>. |
| ***************************************************************/ |
| { |
| if (tab_IsEmpty(T)) |
| return; |
| |
| fputs("\nnode: {\n\nlabel: ", File); |
| tab_FPrintNodeLabel(File, T); |
| putc('\n', File); /* end label section */ |
| |
| fprintf(File, "title: \"%d\"\n", T->Label); |
| fputs(" }\n", File); |
| |
| /* recursion */ |
| tab_FPrintNodesCgFormat(File, tab_LeftBranch(T)); |
| tab_FPrintNodesCgFormat(File, tab_RightBranch(T)); |
| |
| } |
| |
| static void tab_FPrintCgFormat(FILE* File, TABLEAU T) |
| /************************************************************** |
| INPUT: A file handle, a tableau |
| RETURNS: Nothing. |
| EFFECTS: Prints tableau as a graph in xvcg format to <File> |
| ***************************************************************/ |
| { |
| fputs("graph: \n{\ndisplay_edge_labels: yes\n", File); |
| |
| tab_FPrintNodesCgFormat(File, T); |
| tab_FPrintEdgesCgFormat(File, T); |
| fputs("}\n", File); |
| } |
| |
| /* unused */ |
| /*static*/ void tab_PrintCgFormat(TABLEAU T) |
| /************************************************************** |
| INPUT: A tableau. |
| RETURNS: Nothing. |
| EFFECTS: Print tableau in xvcg format to stdout |
| ***************************************************************/ |
| { |
| tab_FPrintCgFormat(stdout, T); |
| } |
| |
| |
| /**************************************************************/ |
| /* procedures for printing graph in da Vinci format */ |
| /**************************************************************/ |
| |
| static void tab_FPrintDaVinciEdge(FILE* File, int L1, int L2) |
| /************************************************************** |
| INPUT: A file handle, two numbers |
| RETURNS: Nothing |
| EFFECTS: Print an edge in daVinci format |
| ***************************************************************/ |
| { |
| fprintf(File, "l(\"%d->%d\"," ,L1,L2); |
| fputs("e(\"\",[],\n", File); |
| /* print child node as reference */ |
| fprintf(File, "r(\"%d\")))\n", L2); |
| } |
| |
| |
| static void tab_FPrintDaVinciFormatRec(FILE* File, TABLEAU T) |
| /************************************************************** |
| INPUT: A file handle, a tableau |
| RETURNS: Nothing |
| EFFECTS: Prints tableau to <File> in daVinci format |
| ***************************************************************/ |
| { |
| /* print node label */ |
| fprintf(File, "l(\"%d\",", T->Label); |
| /* print node attributes */ |
| fputs("n(\"\", [a(\"OBJECT\",", File); |
| tab_FPrintNodeLabel(File, T); |
| fputs(")],\n", File); |
| |
| /* print egde list */ |
| putc('[', File); |
| if (!tab_LeftBranchIsEmpty(T)) |
| tab_FPrintDaVinciEdge(File, T->Label, tab_LeftBranch(T)->Label); |
| |
| if (!tab_RightBranchIsEmpty(T)) { |
| if (!tab_LeftBranchIsEmpty(T)) |
| putc(',', File); |
| tab_FPrintDaVinciEdge(File, T->Label, tab_RightBranch(T)->Label); |
| } |
| fputs("]))", File); /* this ends the node description */ |
| |
| if (!tab_LeftBranchIsEmpty(T)) { |
| putc(',', File); |
| tab_FPrintDaVinciFormatRec(File, tab_LeftBranch(T)); |
| } |
| if (!tab_RightBranchIsEmpty(T)) { |
| putc(',', File); |
| tab_FPrintDaVinciFormatRec(File, tab_RightBranch(T)); |
| } |
| } |
| |
| |
| static void tab_FPrintDaVinciFormat(FILE* File, TABLEAU T) |
| /************************************************************** |
| INPUT: A file handle <File>, a tableau |
| RETURNS: Nothing |
| EFFECTS: Print tableau in daVinci format to <File> |
| ***************************************************************/ |
| { |
| fputs("[\n", File); |
| tab_FPrintDaVinciFormatRec(File,T); |
| fputs("]\n", File); |
| } |
| |
| |
| void tab_WriteTableau(TABLEAU T, const char* Filename, GRAPHFORMAT Format) |
| /************************************************************** |
| INPUT: A tableau, a filename |
| RETURNS: Nothing. |
| ***************************************************************/ |
| { |
| FILE* File; |
| |
| if (Format != DAVINCI && Format != XVCG) { |
| misc_StartUserErrorReport(); |
| misc_UserErrorReport("\nError: unknown output format for tableau.\n"); |
| misc_FinishUserErrorReport(); |
| } |
| |
| File = misc_OpenFile(Filename, "w"); |
| |
| if (Format == DAVINCI) |
| tab_FPrintDaVinciFormat(File, T); |
| else |
| if (Format == XVCG) |
| tab_FPrintCgFormat(File, T); |
| |
| misc_CloseFile(File, Filename); |
| } |