blob: 59e037c5f78713e196ebf518bae4ddbbc53732ec [file] [log] [blame]
/**************************************************************/
/* ********************************************************** */
/* * * */
/* * 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);
}