blob: fa2e557f552dc4757ee62cd991c82c3bc8feeebb [file] [log] [blame]
/**************************************************************/
/* ********************************************************** */
/* * * */
/* * CONGRUENCE CLOSURE ALGORITHM * */
/* * * */
/* * $Module: CLOSURE * */
/* * * */
/* * Copyright (C) 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 * */
/* * * */
/* ********************************************************** */
/**************************************************************/
/**************************************************************/
/* Include */
/**************************************************************/
#include "closure.h"
/**************************************************************/
/* Global constants and variable */
/**************************************************************/
/* standard initial size of a closure's stacks */
static const int cc_RASSTDSIZE = 64;
/* cc_RASSTDSIZE * ld(cc_RASSTDSIZE) */
static const int cc_SIZELDSIZE = 384;
/* the virtual term "true" has number 0 */
static const ELEMENT cc_NOOFTRUE = 0;
static struct {
PARTITION partition;
TABLE table;
RAS car, cdr, size, pending, combine;
} cc_CLOSURE;
/* the closure consists of a partition, a signature table, stacks for */
/* (circularly linked) lists of predecessors of equivalence classes (i. e. */
/* terms with direct subterms from this class), for the sizes of these lists, */
/* for pending terms (the ones to be worked off) and for terms to be combined */
/* in the same equivalence class */
/**************************************************************/
/* Inline functions */
/**************************************************************/
static __inline__ PARTITION cc_GetPartition(void)
{
return cc_CLOSURE.partition;
}
static __inline__ void cc_SetPartition(PARTITION partition)
{
cc_CLOSURE.partition = partition;
}
static __inline__ TABLE cc_GetTable(void)
{
return cc_CLOSURE.table;
}
static __inline__ void cc_SetTable(TABLE table)
{
cc_CLOSURE.table = table;
}
static __inline__ RAS cc_GetCars(void)
{
return cc_CLOSURE.car;
}
static __inline__ TERM cc_GetCar(int index)
{
return (TERM) ras_Get(cc_GetCars(), index);
}
static __inline__ void cc_SetCars(RAS car)
{
cc_CLOSURE.car = car;
}
static __inline__ RAS cc_GetCdrs(void)
{
return cc_CLOSURE.cdr;
}
static __inline__ int cc_GetCdr(int index)
{
return (int) ras_Get(cc_GetCdrs(), index);
}
static __inline__ void cc_SetCdrs(RAS cdr)
{
cc_CLOSURE.cdr = cdr;
}
static __inline__ void cc_SetCdr(int index, int cdr)
{
ras_Set(cc_GetCdrs(), index, (POINTER) cdr);
}
static __inline__ RAS cc_GetSizes(void)
{
return cc_CLOSURE.size;
}
static __inline__ int cc_GetSize(int index)
{
return (int) ras_Get(cc_GetSizes(), index);
}
static __inline__ void cc_SetSizes(RAS size)
{
cc_CLOSURE.size = size;
}
static __inline__ void cc_SetSize(int index, int size)
{
ras_Set(cc_GetSizes(), index, (POINTER) size);
}
static __inline__ RAS cc_GetPending(void)
{
return cc_CLOSURE.pending;
}
static __inline__ void cc_SetPending(RAS pending)
{
cc_CLOSURE.pending = pending;
}
static __inline__ RAS cc_GetCombine(void)
{
return cc_CLOSURE.combine;
}
static __inline__ void cc_SetCombine(RAS combine)
{
cc_CLOSURE.combine = combine;
}
/**************************************************************/
/* Functions */
/**************************************************************/
static int cc_Number(int actno, TERM term, TERM pred)
/***************************************************************
INPUT: the actual number of terms, the term to be numbered
and its predecessor (may be the empty term
term_Null())
RETURNS: the new number of terms after recursively numbering
the term and its subterms
EFFECT: stores a term's number as its size, partially
initializes its predecessor list and pushes all
subterms to the pending stack
***************************************************************/
{
LIST terms;
#ifdef CHECK
if (actno < 0) {
misc_StartErrorReport();
misc_ErrorReport("\n In cc_Number: negative actual number of terms.");
misc_FinishErrorReport();
}
#endif
term_SetSize(term, actno++);
cc_SetCars(ras_Push(cc_GetCars(), pred));
cc_SetPending(ras_Push(cc_GetPending(), term));
for (terms = term_ArgumentList(term); !list_Empty(terms); terms =
list_Cdr(terms))
actno = cc_Number(actno, list_Car(terms), term);
return actno;
}
static void cc_Union(ECLASS c1, ECLASS c2)
/***************************************************************
EFFECT: unions c1 and c2, therefore the signatures of the
predecessors of one class change, so these
predecessors have to be deleted from the signature
table and become pending again; sets new class's
predecessor list and its size to the concatenation of
the old lists resp. the sum of the old sizes
***************************************************************/
{
int aux, size;
TERM term;
#ifdef CHECK
if (part_Find(cc_GetPartition(), c1) != c1) {
misc_StartErrorReport();
misc_ErrorReport("\n In cc_Union: first class corrupted, i. e. is not ");
misc_ErrorReport("the representative.");
misc_FinishErrorReport();
}
if (part_Find(cc_GetPartition(), c2) != c2) {
misc_StartErrorReport();
misc_ErrorReport("\n In cc_Union: second class corrupted, i. e. is not ");
misc_ErrorReport("the representative.");
misc_FinishErrorReport();
}
#endif
if (c1 != c2) {
/* make c1 the class with the bigger (or at least not smaller) list: */
if (cc_GetSize(c1) < cc_GetSize(c2)) {
aux = c1;
c1 = c2;
c2 = aux;
}
/* delete c2's predecessors from signature table and add them to pending: */
for (size = cc_GetSize(c2), aux = c2; size > 0; size--) {
term = cc_GetCar(aux);
aux = cc_GetCdr(aux);
table_Delete(cc_GetTable(), term);
cc_SetPending(ras_Push(cc_GetPending(), term));
}
if (cc_GetSize(c2) > 0) { /* then GetSize(c1) ( >= GetSize(c2) ) > 0 too */
/* union circularly linked lists by exchanging cdrs: */
aux = cc_GetCdr(c1);
cc_SetCdr(c1, cc_GetCdr(c2));
cc_SetCdr(c2, aux);
cc_SetSize(c1, cc_GetSize(c1) + cc_GetSize(c2));
}
part_Union(cc_GetPartition(), c1, c2);
}
}
static void cc_InitData(CLAUSE clause)
/***************************************************************
INPUT: the clause to investigate
EFFECT: pushes clause's atoms and their subterms on the
pending stack, initializes each predecessor list with
the list containing only a term's father, and unions
the equivalence classes of the terms of the same
antecedent equation
***************************************************************/
{
int last, actno, i, ld;
TERM atom;
RAS cdr, size;
cc_SetCars(ras_InitWithSize(cc_GetCars(), cc_RASSTDSIZE));
cc_SetPending(ras_InitWithSize(cc_GetPending(), cc_RASSTDSIZE));
ras_FastPush(cc_GetCars(), term_Null()); /* "true" has no predecessors */
actno = 1;
last = clause_LastLitIndex(clause);
for (i = clause_FirstLitIndex(); i <= last; i++) {
atom = clause_GetLiteralAtom(clause, i);
if (fol_IsEquality(atom)) {
actno = cc_Number(actno, term_FirstArgument(atom), term_Null());
actno = cc_Number(actno, term_SecondArgument(atom), term_Null());
}
else
actno = cc_Number(actno, atom, term_Null());
}
cc_SetPartition(part_Init(cc_GetPartition(), actno));
cc_SetTable(table_Init(cc_GetTable(), symbol_ActIndex() - 1,
clause_MaxVar(clause), actno - 1));
cdr = ras_InitWithSize(cc_GetCdrs(), actno);
size = ras_InitWithSize(cc_GetSizes(), actno);
for (i = 0; i < actno; i++) {
ras_FastPush(cdr, (POINTER) i); /* form a cycle */
ras_FastPush(size, (POINTER) (cc_GetCar(i) == term_Null()? 0 : 1));
}
cc_SetCdrs(cdr);
cc_SetSizes(size);
/* compute ceil(ld(actno)) avoiding mathbib-logarithm's rounding errors: */
for (ld = 0, i = actno - 1; i > 0; i >>= 1)
ld++;
cc_SetCombine(ras_InitWithSize(cc_GetCombine(), actno * ld + 1));
/* for every antecedent equation union equivalence classes of its terms */
/* (a non-equational atom is represented as the equation atom = "true"): */
last = clause_LastAntecedentLitIndex(clause);
for (i = clause_FirstLitIndex(); i <= last; i++) {
atom = clause_GetLiteralAtom(clause, i);
if (fol_IsEquality(atom))
cc_Union(term_Size(term_FirstArgument(atom)), /* clause not shared, therefore */
term_Size(term_SecondArgument(atom))); /* here no cc_Find needed */
else
cc_Union(term_Size(atom), part_Find(cc_GetPartition(), cc_NOOFTRUE));
}
}
static BOOL cc_Outit(CLAUSE clause)
/***************************************************************
RETURNS: the decision, if the clause is a tautology
***************************************************************/
{
int last, i;
BOOL result;
TERM atom;
#ifdef CHECK
if (!ras_Empty(cc_GetPending())) {
misc_StartErrorReport();
misc_ErrorReport("\n In cc_Outit: there are terms left to work off.");
misc_FinishErrorReport();
}
#endif
last = clause_LastLitIndex(clause);
for (i = clause_FirstSuccedentLitIndex(clause), result = FALSE;
i <= last && !result; i++) {
atom = clause_GetLiteralAtom(clause, i);
if (fol_IsEquality(atom))
result = part_Equivalent(cc_GetPartition(),
term_Size(term_FirstArgument(atom)),
term_Size(term_SecondArgument(atom)));
else
result = part_Equivalent(cc_GetPartition(), term_Size(atom), cc_NOOFTRUE);
}
return result;
}
/**************************************************************/
/* Main functions */
/**************************************************************/
void cc_Init(void)
{
cc_SetPartition(part_Create(cc_RASSTDSIZE));
cc_SetTable(table_Create(cc_RASSTDSIZE, cc_RASSTDSIZE, cc_RASSTDSIZE));
cc_SetCars(ras_CreateWithSize(cc_RASSTDSIZE));
cc_SetCdrs(ras_CreateWithSize(cc_RASSTDSIZE));
cc_SetSizes(ras_CreateWithSize(cc_RASSTDSIZE));
cc_SetPending(ras_CreateWithSize(cc_RASSTDSIZE));
cc_SetCombine(ras_CreateWithSize(cc_SIZELDSIZE));
}
void cc_Free(void)
{
part_Free(cc_GetPartition());
table_Free(cc_GetTable());
ras_Free(cc_GetCars());
ras_Free(cc_GetCdrs());
ras_Free(cc_GetSizes());
ras_Free(cc_GetPending());
ras_Free(cc_GetCombine());
}
BOOL cc_Tautology(CLAUSE clause)
/***************************************************************
INPUT: the clause to test
RETURNS: the decision, if the clause - where all variables are
regarded as skolem constants - is a tautology, using
the congruence closure algorithm of Downey, Sethi and
Tarjan
CAUTION: overrides the sizes of the clause's terms
***************************************************************/
{
TERM term, query;
cc_InitData(clause);
while (!ras_Empty(cc_GetPending())) {
/* propagate the closure: */
while (!ras_Empty(cc_GetPending())) {
term = ras_Pop(cc_GetPending());
query = table_QueryAndEnter(cc_GetTable(), cc_GetPartition(), term);
if (query != term_Null()) {
ras_FastPush(cc_GetCombine(), term);
ras_FastPush(cc_GetCombine(), query);
}
}
while (!ras_Empty(cc_GetCombine()))
cc_Union(part_Find(cc_GetPartition(), term_Size(ras_Pop(cc_GetCombine()))),
part_Find(cc_GetPartition(), term_Size(ras_Pop(cc_GetCombine()))));
}
return cc_Outit(clause);
}