1735 lines
64 KiB
C
1735 lines
64 KiB
C
/*****************************************************************************/
|
|
/* Copyright (C) 2020 NORMAN MEGILL nm at alum.mit.edu */
|
|
/* License terms: GNU General Public License */
|
|
/*****************************************************************************/
|
|
/*34567890123456 (79-character line to adjust editor window) 2345678901234567*/
|
|
|
|
/* mmunif.c - Unifications for proof assistant (note: unifications for normal
|
|
proof verification is done in mmveri.c) */
|
|
|
|
/*
|
|
This module deals with an object called the stateVector, which is a pntrString
|
|
of 16 pointers (called entries 0 through 15 below) to either other pntrStrings
|
|
or to nmbrStrings. In the description, data in stateVector may be referred to
|
|
by the local C variable the data is typically assigned to, such as
|
|
"unkVarsLen". The word "variable" in the context of scheme content refers
|
|
to temporary (or "work" or "dummy") variables $1, $2, etc. The entries are not
|
|
organized in logical order for historical reasons, e.g. entry 11 logically
|
|
comes first.
|
|
|
|
Entry 11 is a nmbrString of length 4 holding individual parameters.
|
|
|
|
11[0] is the total number of variables ($1, $2, etc.) in schemeA and schemeB,
|
|
i.e. the two schemes being unified.
|
|
|
|
unkVarsLen = ((nmbrString *)((*stateVector)[11]))[0];
|
|
|
|
11[1] or stackTop is the number of variables (minus 1) that will require
|
|
substitutions in order to perform the unification. Warning: stackTop may be
|
|
-1, which could be confused with "end of nmbrString" by some nmbrString
|
|
functions.
|
|
|
|
stackTop = ((nmbrString *)((*stateVector)[11]))[1];
|
|
|
|
11[2] is the number of variables in schemeA, used by oneDirUnif() (only).
|
|
|
|
schemeAUnkVarsLen = ((nmbrString *)((*stateVector)[11]))[2];
|
|
|
|
11[3] is the number of entries in the "Henty filter", used by unifyH() (only).
|
|
|
|
g_hentyFilterSize = ((nmbrString *)((*stateVector)[11]))[3];
|
|
|
|
Entry 8 is the result of unifying schemeA and schemeB, which are the two
|
|
schemes being unified.
|
|
|
|
unifiedScheme = (nmbrString *)((*stateVector)[8]);
|
|
|
|
Entries 0 through 3 each have length unkVarsLen. Entry 1 is a list of token
|
|
numbers for the temporary variables substituted in the unification.
|
|
|
|
Entry 0 has all variables ($1, $2, etc.) in schemeA and schemeB.
|
|
|
|
unkVars = (nmbrString *)((*stateVector)[0]);
|
|
|
|
In entries 1 through 3, only variables 0 through stackTop (inclusive) have
|
|
meaning. These entries, along with unifiedScheme, determine what variables
|
|
were substituted and there substitutions.
|
|
|
|
Entry 1 is the list of variables that were substituted.
|
|
Entry 2 is the location of the substitution in unifiedScheme, for each variable
|
|
in entry 1.
|
|
Entry 3 is the length of the substitution for each variable in entry 1.
|
|
|
|
stackUnkVar = (nmbrString *)((*stateVector)[1]);
|
|
stackUnkVarStart = (nmbrString *)((*stateVector)[2]);
|
|
stackUnkVarLen = (nmbrString *)((*stateVector)[3]);
|
|
|
|
Entries 4 thru 7 each point to unkVarsLen nmbrString's. These entries save the
|
|
data needed to resume unification at any point. Entries 4 and 5 are
|
|
nmbrString's of length unkVarsLen. Entries 6 and 7 will have variable length.
|
|
Only the first stackTop+1 nmbrString's have meaning. Note that stackTop may be
|
|
-1.
|
|
|
|
stackSaveUnkVarStart = (pntrString *)((*stateVector)[4]);
|
|
stackSaveUnkVarLen = (pntrString *)((*stateVector)[5]);
|
|
stackSaveSchemeA = (pntrString *)((*stateVector)[6]);
|
|
stackSaveSchemeB = (pntrString *)((*stateVector)[7]);
|
|
|
|
Entries 9 and 10 save the contents of 2 and 3 in oneDirUnif (only)
|
|
|
|
nmbrLet((nmbrString **)(&(*stateVector)[9]),
|
|
(nmbrString *)((*stateVector)[2]));
|
|
nmbrLet((nmbrString **)(&(*stateVector)[10]),
|
|
(nmbrString *)((*stateVector)[3]));
|
|
|
|
Entries 12 through 15 hold the "Henty filter", i.e. a list of all "normalized"
|
|
unifications so far. Used by unifyH() (only). Each entry 12 through 15 is a
|
|
list of pointers of length g_hentyFilterSize, each pointing to g_hentyFilterSize
|
|
nmbrString's. The Henty filter eliminates redundant equivalent unifications.
|
|
|
|
Entry 12[i] is a list of variables substituted by the normalized unification.
|
|
Entry 13[i] is the start of each substitution in hentySubstList.
|
|
Entry 14[i] is the length of each substitution in hentySubstList.
|
|
Entry 15[i] is the unified scheme that resulted from the particular unification.
|
|
Note: i = 0 through g_hentyFilterSize-1 below.
|
|
|
|
hentyVars = (nmbrString *)(((pntrString *)((*stateVector)[12]))[i]);
|
|
hentyVarStart = (nmbrString *)(((pntrString *)((*stateVector)[13]))[i]);
|
|
hentyVarLen = (nmbrString *)(((pntrString *)((*stateVector)[14]))[i]);
|
|
hentySubstList = (nmbrString *)(((pntrString *)((*stateVector)[15]))[i]);
|
|
|
|
*/
|
|
|
|
#include <string.h>
|
|
#include <stdio.h>
|
|
#include <limits.h>
|
|
#include <stdlib.h>
|
|
#include <ctype.h>
|
|
#include <stdarg.h>
|
|
#include <setjmp.h>
|
|
#include <time.h>
|
|
#include "mmvstr.h"
|
|
#include "mmdata.h"
|
|
#include "mminou.h"
|
|
#include "mmpars.h"
|
|
#include "mmunif.h"
|
|
#include "mmpfas.h" /* 8/28/99 For proveStatement global variable */
|
|
|
|
/*long g_minSubstLen = 0;*/ /* User-settable value - 0 or 1 */
|
|
long g_minSubstLen = 1; /* It was decided to disallow empty subst. by default
|
|
since most formal systems don't need it */
|
|
long g_userMaxUnifTrials = 100000; /* Initial value */
|
|
/* User-defined upper limit (# backtracks) for unification trials */
|
|
/* 1-Jun-04 nm Changed g_userMaxUnifTrials from 1000 to 100000, which
|
|
is not a problem with today's faster computers. This results in
|
|
fewer annoying "Unification timed out" messages, but the drawback
|
|
is that (rarely) there may be hundreds of unification
|
|
choices for the user (which the user can quit from though). */
|
|
long g_unifTrialCount = 0;
|
|
/* 0 means don't time out; 1 means start counting trials */
|
|
long g_unifTimeouts = 0; /* Number of timeouts so far for this command */
|
|
flag g_hentyFilter = 1; /* Default to ON (turn OFF for debugging). */
|
|
flag g_bracketMatchInit = 0; /* Global so eraseSource() (mmcmds.c) can clr it */
|
|
|
|
/* Additional local prototypes */
|
|
void hentyNormalize(nmbrString **hentyVars, nmbrString **hentyVarStart,
|
|
nmbrString **hentyVarLen, nmbrString **hentySubstList,
|
|
pntrString **stateVector);
|
|
flag hentyMatch(nmbrString *hentyVars, nmbrString *hentyVarStart,
|
|
/*nmbrString *hentyVarLen,*/ nmbrString *hentySubstList,
|
|
pntrString **stateVector);
|
|
void hentyAdd(nmbrString *hentyVars, nmbrString *hentyVarStart,
|
|
nmbrString *hentyVarLen, nmbrString *hentySubstList,
|
|
pntrString **stateVector);
|
|
|
|
|
|
/* For heuristics */
|
|
int maxNestingLevel = -1;
|
|
int nestingLevel = 0;
|
|
|
|
/* 8/29/99 For improving rejection of impossible substitutions */
|
|
/* 1-Oct-2017 nm Made g_firstConst global so eraseSource() can clear it */
|
|
/* 2-Oct-2017 nm Made them all global so valgrind won't complain */
|
|
nmbrString *g_firstConst = NULL_NMBRSTRING;
|
|
nmbrString *g_lastConst = NULL_NMBRSTRING;
|
|
nmbrString *g_oneConst = NULL_NMBRSTRING;
|
|
|
|
|
|
|
|
/* Typical call:
|
|
nmbrStringXxx = makeSubstUnif(&newVarFlag,trialScheme,
|
|
stateVector);
|
|
Call this after calling unify().
|
|
trialScheme should have the same unknown variable names as were in
|
|
the schemes given to unify().
|
|
nmbrStringXxx will have these unknown variables substituted with
|
|
the result of the unification.
|
|
newVarFlag is 1 if there are new $nn variables in nmbrStringXxx.
|
|
The caller must deallocate the returned nmbrString.
|
|
*/
|
|
nmbrString *makeSubstUnif(flag *newVarFlag,
|
|
nmbrString *trialScheme, pntrString *stateVector)
|
|
{
|
|
long p,q,i,j,k,m,tokenNum;
|
|
long schemeLen;
|
|
nmbrString *result = NULL_NMBRSTRING;
|
|
nmbrString *stackUnkVar = NULL_NMBRSTRING;
|
|
nmbrString *unifiedScheme; /* Pointer only - not allocated */
|
|
nmbrString *stackUnkVarLen; /* Pointer only - not allocated */
|
|
nmbrString *stackUnkVarStart; /* Pointer only - not allocated */
|
|
long stackTop;
|
|
/*E*/long d;
|
|
/*E*/vstring tmpStr = "";
|
|
/*E*/let(&tmpStr,tmpStr);
|
|
|
|
stackTop = ((nmbrString *)(stateVector[11]))[1];
|
|
nmbrLet(&stackUnkVar,nmbrLeft((nmbrString *)(stateVector[1]), stackTop + 1));
|
|
stackUnkVarStart = (nmbrString *)(stateVector[2]); /* stackUnkVarStart */
|
|
stackUnkVarLen = (nmbrString *)(stateVector[3]); /* stackUnkVarLen */
|
|
unifiedScheme = (nmbrString *)(stateVector[8]);
|
|
|
|
/*E*/if(db7)print2("Entered makeSubstUnif.\n");
|
|
/*E*/if(db7)printLongLine(cat("unifiedScheme is ",
|
|
/*E*/ nmbrCvtMToVString(unifiedScheme), NULL), "", " ");
|
|
/*E*/if(db7)printLongLine(cat("trialScheme is ",
|
|
/*E*/ nmbrCvtMToVString(trialScheme), NULL), "", " ");
|
|
/*E*/if(db7)print2("stackTop is %ld.\n",stackTop);
|
|
/*E*/for (d = 0; d <= stackTop; d++) {
|
|
/*E*/ if(db7)print2("Unknown var %ld is %s.\n",d,
|
|
/*E*/ g_MathToken[stackUnkVar[d]].tokenName);
|
|
/*E*/ if(db7)print2(" Its start is %ld; its length is %ld.\n",
|
|
/*E*/ stackUnkVarStart[d],stackUnkVarLen[d]);
|
|
/*E*/}
|
|
schemeLen = nmbrLen(trialScheme);
|
|
/* Make the substitutions into trialScheme. */
|
|
/* First, calculate the length of the final result */
|
|
q = 0;
|
|
*newVarFlag = 0; /* Flag that there are new variables in the output string */
|
|
/*E*/if(db7)print2("schemeLen is %ld.\n",schemeLen);
|
|
for (p = 0; p < schemeLen; p++) {
|
|
/*E*/if(db7)print2("p is %ld.\n",p);
|
|
tokenNum = trialScheme[p];
|
|
/*E*/if(db7)print2("token is %s, tokenType is %ld\n",g_MathToken[tokenNum].tokenName,
|
|
/*E*/ (long)g_MathToken[tokenNum].tokenType);
|
|
if (g_MathToken[tokenNum].tokenType == (char)con_) {
|
|
q++;
|
|
} else {
|
|
if (tokenNum > g_mathTokens) {
|
|
/* It's a candidate for substitution */
|
|
m = nmbrElementIn(1,stackUnkVar,tokenNum);
|
|
/*E*/if(db7)print2("token is %s, m is %ld\n",g_MathToken[tokenNum].tokenName,m);
|
|
if (m) {
|
|
/* It will be substituted */
|
|
q = q + stackUnkVarLen[m - 1];
|
|
/* Flag the token position */
|
|
g_MathToken[tokenNum].tmp = m - 1;
|
|
} else {
|
|
/* It will not be substituted */
|
|
*newVarFlag = 1; /* The result will contain an "unknown" variable */
|
|
q++;
|
|
/* Flag the token position */
|
|
g_MathToken[tokenNum].tmp = -1;
|
|
}
|
|
} else {
|
|
/* It's not an "unknown" variable, so it won't be substituted */
|
|
q++;
|
|
}
|
|
}
|
|
}
|
|
/* Allocate space for the final result */
|
|
nmbrLet(&result, nmbrSpace(q));
|
|
/* Assign the final result */
|
|
q = 0;
|
|
for (p = 0; p < schemeLen; p++) {
|
|
tokenNum = trialScheme[p];
|
|
if (g_MathToken[tokenNum].tokenType == (char)con_) {
|
|
result[q] = tokenNum;
|
|
q++;
|
|
} else {
|
|
if (tokenNum > g_mathTokens) {
|
|
/* It's a candidate for substitution */
|
|
k = g_MathToken[tokenNum].tmp; /* Position in stackUnkVar */
|
|
if (k != -1) {
|
|
/* It will be substituted */
|
|
m = stackUnkVarStart[k]; /* Start of substitution */
|
|
j = stackUnkVarLen[k]; /* Length of substitition */
|
|
for (i = 0; i < j; i++) {
|
|
result[q + i] = unifiedScheme[m + i];
|
|
}
|
|
q = q + j;
|
|
} else {
|
|
/* It will not be substituted */
|
|
result[q] = tokenNum;
|
|
q++;
|
|
}
|
|
} else {
|
|
/* It's not an "unknown" variable, so it won't be substituted */
|
|
result[q] = tokenNum;
|
|
q++;
|
|
}
|
|
} /* end "if a constant" */
|
|
}
|
|
/*E*/if(db7)print2("after newVarFlag %d\n",(int)*newVarFlag);
|
|
/*E*/if(db7)print2("final len is %ld\n",q);
|
|
/*E*/if(db7)printLongLine(cat("result ",nmbrCvtMToVString(result),NULL),""," ");
|
|
nmbrLet(&stackUnkVar, NULL_NMBRSTRING); /* Deallocate */
|
|
return (result);
|
|
} /* makeSubstUnif */
|
|
|
|
|
|
|
|
|
|
|
|
char unify(
|
|
nmbrString *schemeA,
|
|
nmbrString *schemeB,
|
|
/* nmbrString **unifiedScheme, */ /* stateVector[8] holds this */
|
|
pntrString **stateVector,
|
|
long reEntryFlag)
|
|
{
|
|
|
|
|
|
/* This function unifies two math token strings, schemeA and
|
|
schemeB. The result is contained in unifiedScheme.
|
|
0 is returned if no assignment is possible, 1 if an assignment was
|
|
found, and 2 if the unification timed out.
|
|
If reEntryFlag is 1, the next possible set of assignments, if any,
|
|
is returned. (*stateVector) contains the state of the previous
|
|
call. It is the caller's responsibility to deallocate the
|
|
contents of (*stateVector) when done, UNLESS a 0 is returned.
|
|
The caller must assign (*stateVector) to a legal pntrString
|
|
(e.g. NULL_PNTRSTRING) before calling.
|
|
|
|
All variables with a tokenNum > g_mathTokens are assumed
|
|
to be "unknown" variables that can be assigned; all other
|
|
variables are treated like constants in the unification
|
|
algorithm.
|
|
|
|
The "unknown" variable assignments are contained in (*stateVector)
|
|
(which is a complex structure, described above). Some "unknown"
|
|
variables may have no assignment, in which case they will
|
|
remain "unknown", and others may have assignments which include
|
|
"unknown" variables. The (*stateVector) entries 9 and 10 are used
|
|
by oneDirUnif() only.
|
|
*/
|
|
|
|
long stackTop;
|
|
nmbrString *unkVars; /* List of all unknown vars */
|
|
long unkVarsLen;
|
|
long schemeAUnkVarsLen;
|
|
nmbrString *stackUnkVar; /* Location of stacked var in unkVars */
|
|
nmbrString *stackUnkVarStart; /* Start of stacked var in unifiedScheme*/
|
|
nmbrString *stackUnkVarLen; /* Length of stacked var assignment */
|
|
pntrString *stackSaveUnkVarStart; /* stackUnkVarStart at the time a
|
|
variable was first stacked */
|
|
pntrString *stackSaveUnkVarLen; /* stackUnkVarLen at the time a variable
|
|
was first stacked */
|
|
pntrString *stackSaveSchemeA; /* Pointer to saved schemeA at the time
|
|
the variable was first stacked */
|
|
pntrString *stackSaveSchemeB; /* Pointer to saved schemeB at the time
|
|
the variable was first stacked */
|
|
nmbrString *unifiedScheme; /* Final result */
|
|
long p; /* Current position in schemeA or schemeB */
|
|
long substToken; /* Token from schemeA or schemeB that will be substituted */
|
|
nmbrString *substitution = NULL_NMBRSTRING;
|
|
/* String to be subst. for substToken */
|
|
nmbrString *nmbrTmpPtr; /* temp pointer only */
|
|
pntrString *pntrTmpPtr; /* temp pointer only */
|
|
nmbrString *schA = NULL_NMBRSTRING; /* schemeA with dummy token at end */
|
|
nmbrString *schB = NULL_NMBRSTRING; /* schemeB with dummy token at end */
|
|
long i,j,k,m, pairingMismatches;
|
|
flag breakFlag;
|
|
flag schemeAFlag;
|
|
flag timeoutAbortFlag = 0;
|
|
vstring mToken; /* Pointer only; not allocated */
|
|
|
|
/* 8/28/99 For detection of simple impossible unifications */
|
|
flag impossible;
|
|
long stmt;
|
|
|
|
/* 26-Sep-2010 nm For bracket matching heuristic for set.mm */
|
|
static char bracketMatchOn; /* 26-Sep-2010 nm Default is 'on' */
|
|
/* static char g_bracketMatchInit = 0; */ /* Global so ERASE can init it */
|
|
long bracketScanStart, bracketScanStop; /* For one-time $a scan */
|
|
flag bracketMismatchFound;
|
|
|
|
|
|
/*E*/long d;
|
|
/*E*/vstring tmpStr = "";
|
|
/*E*/let(&tmpStr,tmpStr);
|
|
/*E*/if(db5)print2("Entering unify() with reEntryFlag = %ld.\n",
|
|
/*E*/ (long)reEntryFlag);
|
|
/*E*/if(db5)printLongLine(cat("schemeA is ",
|
|
/*E*/ nmbrCvtMToVString(schemeA),".",NULL)," "," ");
|
|
/*E*/if(db5)printLongLine(cat("schemeB is ",
|
|
/*E*/ nmbrCvtMToVString(schemeB),".",NULL)," "," ");
|
|
|
|
/* Initialization to avoid compiler warning (should not be theoretically
|
|
necessary) */
|
|
p = 0;
|
|
bracketMismatchFound = 0;
|
|
|
|
/* Fast early exit -- first or last constants of schemes don't match */
|
|
if (g_MathToken[schemeA[0]].tokenType == (char)con_) {
|
|
if (g_MathToken[schemeB[0]].tokenType == (char)con_) {
|
|
if (schemeA[0] != schemeB[0]) {
|
|
return (0);
|
|
}
|
|
}
|
|
}
|
|
/* (j and k are used below also) */
|
|
j = nmbrLen(schemeA);
|
|
k = nmbrLen(schemeB);
|
|
if (!j || !k) bug(1901);
|
|
if (g_MathToken[schemeA[j-1]].tokenType == (char)con_) {
|
|
if (g_MathToken[schemeB[k-1]].tokenType == (char)con_) {
|
|
if (schemeA[j-1] != schemeB[k-1]) {
|
|
return (0);
|
|
}
|
|
}
|
|
}
|
|
/* Add dummy token to end of schemeA and schemeB */
|
|
/* Use one beyond the last mathTokenArray entry for this */
|
|
nmbrLet(&schA, nmbrAddElement(schemeA, g_mathTokens));
|
|
nmbrLet(&schB, nmbrAddElement(schemeB, g_mathTokens));
|
|
|
|
/* 8/29/99 Initialize the usage of constants as the first, last,
|
|
only constant in a $a statement - for rejecting some simple impossible
|
|
substitutions - Speed-up: this is now done once and never deallocated*/
|
|
/* 1-Oct-2017 nm g_firstConst is now cleared in eraseSource.c() (mmcmds.c)
|
|
to trigger this initialization after "erase" */
|
|
if (!nmbrLen(g_firstConst)) {
|
|
/* nmbrSpace() sets all entries to 0, not 32 (ASCII space) */
|
|
nmbrLet(&g_firstConst, nmbrSpace(g_mathTokens));
|
|
nmbrLet(&g_lastConst, nmbrSpace(g_mathTokens));
|
|
nmbrLet(&g_oneConst, nmbrSpace(g_mathTokens));
|
|
/*for (stmt = 1; stmt < proveStatement; stmt++) {*/
|
|
/* Do it for all statements since we do it once permanently now */
|
|
for (stmt = 1; stmt <= g_statements; stmt++) {
|
|
if (g_Statement[stmt].type != (char)a_)
|
|
continue; /* Not $a */
|
|
if (g_Statement[stmt].mathStringLen < 2) continue;
|
|
/* Look at first symbol after variable type symbol */
|
|
if (g_MathToken[(g_Statement[stmt].mathString)[1]].tokenType == (char)con_) {
|
|
g_firstConst[(g_Statement[stmt].mathString)[1]] = 1; /* Set flag */
|
|
if (g_Statement[stmt].mathStringLen == 2) {
|
|
g_oneConst[(g_Statement[stmt].mathString)[1]] = 1; /* Set flag */
|
|
}
|
|
}
|
|
/* Look at last symbol */
|
|
if (g_MathToken[(g_Statement[stmt].mathString)[
|
|
g_Statement[stmt].mathStringLen - 1]].tokenType == (char)con_) {
|
|
g_lastConst[(g_Statement[stmt].mathString)[
|
|
g_Statement[stmt].mathStringLen - 1]] = 1; /* Set flag for const */
|
|
}
|
|
} /* Next stmt */
|
|
}
|
|
|
|
|
|
if (!reEntryFlag) {
|
|
/* First time called */
|
|
p = 0;
|
|
|
|
/* Collect the list of "unknown" variables */
|
|
/* (Pre-allocate max. length) */
|
|
/* (Note j and k assignment above) */
|
|
unkVars = NULL_NMBRSTRING;
|
|
nmbrLet(&unkVars, nmbrSpace(j + k));
|
|
unkVarsLen = 0;
|
|
for (i = 0; i < j; i++) {
|
|
if (schemeA[i] > g_mathTokens) {
|
|
/* It's an "unknown" variable */
|
|
breakFlag = 0;
|
|
for (m = 0; m < unkVarsLen; m++) {
|
|
if (unkVars[m] == schemeA[i]) {
|
|
/* It's already been added to the list */
|
|
breakFlag = 1;
|
|
}
|
|
}
|
|
if (!breakFlag) {
|
|
/* Add the new "unknown" var */
|
|
unkVars[unkVarsLen++] = schemeA[i];
|
|
}
|
|
}
|
|
}
|
|
/* Save the length of the list of unknown variables in schemeA */
|
|
schemeAUnkVarsLen = unkVarsLen;
|
|
for (i = 0; i < k; i++) {
|
|
if (schemeB[i] > g_mathTokens) {
|
|
/* It's an "unknown" variable */
|
|
breakFlag = 0;
|
|
for (m = 0; m < unkVarsLen; m++) {
|
|
if (unkVars[m] == schemeB[i]) {
|
|
/* It's already been added to the list */
|
|
breakFlag = 1;
|
|
}
|
|
}
|
|
if (!breakFlag) {
|
|
/* Add the new "unknown" var */
|
|
unkVars[unkVarsLen++] = schemeB[i];
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Deallocate old (*stateVector) assignments */
|
|
if (pntrLen(*stateVector)) {
|
|
/*if (((nmbrString *)((*stateVector)[11]))[0] != -1) { */ /*???Change to nmbrLen?*/
|
|
/* If (*stateVector) not an empty nmbrString */
|
|
for (i = 4; i <= 7; i++) {
|
|
pntrTmpPtr = (pntrString *)((*stateVector)[i]);
|
|
for (j = 0; j < ((nmbrString *)((*stateVector)[11]))[0]; j++) {
|
|
nmbrLet((nmbrString **)(&pntrTmpPtr[j]),
|
|
NULL_NMBRSTRING);
|
|
}
|
|
pntrLet((pntrString **)(&(*stateVector)[i]),
|
|
NULL_PNTRSTRING);
|
|
}
|
|
for (i = 0; i <= 3; i++) {
|
|
nmbrLet((nmbrString **)(&(*stateVector)[i]),
|
|
NULL_NMBRSTRING);
|
|
}
|
|
for (i = 8; i <= 10; i++) {
|
|
nmbrLet((nmbrString **)(&(*stateVector)[i]),
|
|
NULL_NMBRSTRING);
|
|
}
|
|
k = pntrLen((pntrString *)((*stateVector)[12]));
|
|
for (i = 12; i < 16; i++) {
|
|
pntrTmpPtr = (pntrString *)((*stateVector)[i]);
|
|
for (j = 0; j < k; j++) {
|
|
nmbrLet((nmbrString **)(&pntrTmpPtr[j]),
|
|
NULL_NMBRSTRING);
|
|
}
|
|
pntrLet((pntrString **)(&(*stateVector)[i]),
|
|
NULL_PNTRSTRING);
|
|
}
|
|
/* Leave [11] pre-allocated to length 4 */
|
|
} else {
|
|
/* It was never allocated before -- do it now */
|
|
/* Allocate stateVector - it will be assigned upon exiting */
|
|
pntrLet(&(*stateVector), pntrPSpace(16));
|
|
nmbrLet((nmbrString **)(&(*stateVector)[11]), nmbrSpace(4));
|
|
}
|
|
|
|
/* Pre-allocate the (*stateVector) structure */
|
|
stackTop = -1;
|
|
stackUnkVar = NULL_NMBRSTRING;
|
|
stackUnkVarStart = NULL_NMBRSTRING;
|
|
stackUnkVarLen = NULL_NMBRSTRING;
|
|
stackSaveUnkVarStart = NULL_PNTRSTRING;
|
|
stackSaveUnkVarLen = NULL_PNTRSTRING;
|
|
stackSaveSchemeA = NULL_PNTRSTRING;
|
|
stackSaveSchemeB = NULL_PNTRSTRING;
|
|
unifiedScheme = NULL_NMBRSTRING;
|
|
nmbrLet(&stackUnkVar, nmbrSpace(unkVarsLen));
|
|
nmbrLet(&stackUnkVarStart, stackUnkVar);
|
|
nmbrLet(&stackUnkVarLen, stackUnkVar);
|
|
|
|
/* These next 4 hold pointers to nmbrStrings */
|
|
pntrLet(&stackSaveUnkVarStart, pntrNSpace(unkVarsLen));
|
|
pntrLet(&stackSaveUnkVarLen, stackSaveUnkVarStart);
|
|
pntrLet(&stackSaveSchemeA, stackSaveUnkVarStart);
|
|
pntrLet(&stackSaveSchemeB, stackSaveUnkVarStart);
|
|
for (i = 0; i < unkVarsLen; i++) {
|
|
/* Preallocate the stack space for these */
|
|
nmbrLet((nmbrString **)(&stackSaveUnkVarStart[i]),
|
|
stackUnkVar);
|
|
nmbrLet((nmbrString **)(&stackSaveUnkVarLen[i]),
|
|
stackUnkVar);
|
|
}
|
|
|
|
/* Set a flag that the "unknown" variables are not on the stack yet */
|
|
/* (Otherwise this will be the position on the stack) */
|
|
for (i = 0; i < unkVarsLen; i++) {
|
|
g_MathToken[unkVars[i]].tmp = -1;
|
|
}
|
|
|
|
} else { /* reEntryFlag != 0 */
|
|
|
|
/* We are re-entering to get the next possible assignment. */
|
|
|
|
/* Restore the (*stateVector) variables */
|
|
unkVarsLen = ((nmbrString *)((*stateVector)[11]))[0];
|
|
unkVars = (nmbrString *)((*stateVector)[0]);
|
|
stackTop = ((nmbrString *)((*stateVector)[11]))[1];
|
|
stackUnkVar = (nmbrString *)((*stateVector)[1]);
|
|
stackUnkVarStart = (nmbrString *)((*stateVector)[2]);
|
|
stackUnkVarLen = (nmbrString *)((*stateVector)[3]);
|
|
stackSaveUnkVarStart = (pntrString *)((*stateVector)[4]);
|
|
stackSaveUnkVarLen = (pntrString *)((*stateVector)[5]);
|
|
stackSaveSchemeA = (pntrString *)((*stateVector)[6]);
|
|
stackSaveSchemeB = (pntrString *)((*stateVector)[7]);
|
|
unifiedScheme = (nmbrString *)((*stateVector)[8]);
|
|
schemeAUnkVarsLen = ((nmbrString *)((*stateVector)[11]))[2];
|
|
/* Used by oneDirUnif() */
|
|
|
|
/* Set the location of the "unknown" variables on the stack */
|
|
/* (This may have been corrupted outside this function) */
|
|
for (i = 0; i < unkVarsLen; i++) {
|
|
g_MathToken[unkVars[i]].tmp = -1; /* Not on the stack */
|
|
}
|
|
for (i = 0; i <= stackTop; i++) {
|
|
g_MathToken[stackUnkVar[i]].tmp = i;
|
|
}
|
|
|
|
/* Force a backtrack to the next assignment */
|
|
goto backtrack;
|
|
reEntry1: /* goto backtrack will come back here if reEntryFlag is set */
|
|
reEntryFlag = 0;
|
|
|
|
|
|
}
|
|
|
|
/* Perform the unification */
|
|
|
|
scan:
|
|
/*E*/if(db6)print2("Entered scan: p=%ld\n",p);
|
|
/*E*/if(db6)print2("Enter scn sbA %s\n",nmbrCvtMToVString(schA));
|
|
/*E*/if(db6)print2("Enter scn sbB %s\n",nmbrCvtMToVString(schB));
|
|
/*E*/if(db6)let(&tmpStr,tmpStr);
|
|
while (schA[p] == schB[p] &&
|
|
schA[p + 1] != -1) {
|
|
p++;
|
|
}
|
|
/*E*/if(db6)print2("First mismatch: p=%ld\n",p);
|
|
|
|
if (schA[p] == g_mathTokens
|
|
|| schB[p] == g_mathTokens) {
|
|
/* One of the strings is at the end. */
|
|
if (schA[p] != schB[p]) {
|
|
/* But one is longer than the other. */
|
|
if (schA[p] <= g_mathTokens &&
|
|
schB[p] <= g_mathTokens) {
|
|
/* Neither token is an unknown variable. (Otherwise we might be able
|
|
to assign the unknown variable to a null string, thus making
|
|
the schemes match, so we shouldn't backtrack.) */
|
|
/*E*/if(db6)print2("Backtracked because end-of-string\n");
|
|
goto backtrack;
|
|
}
|
|
} else {
|
|
if (schA[p + 1] == -1) {
|
|
/* End of schA; a successful unification occurred */
|
|
goto done;
|
|
}
|
|
/* Otherwise, we are in the middle of several schemes being unified
|
|
simultaneously, so just continue. */
|
|
/* (g_mathTokens should be used by the caller to separate
|
|
schemes that are joined together for simultaneous unification) */
|
|
}
|
|
}
|
|
|
|
/* This test, combined with switchover to schemeB in backtrack,
|
|
prevents variable lockup, for example where
|
|
schemeA = ?1 B C, schemeB = ?2 A B C. Without this test, schemeB
|
|
becomes ?1 A B C, and then it can never match schemeA. This should be
|
|
checked out further: what about more than 2 variables? This kind of
|
|
"variable lockup" may be a more serious problem. */
|
|
/* A test case:
|
|
schemeA is $$ |- ( ( ?463 -> -. -. ph ) -> ( -. ph -> ( ph -> -. -. ph ) ) ).
|
|
schemeB is $$ |- ( ?464 -> ( ?465 -> ?464 ) ).
|
|
*/
|
|
if (schB[p] > g_mathTokens && schA[p] > g_mathTokens) {
|
|
/* Both scheme A and scheme B have variables in the match position.
|
|
Which one to use? */
|
|
/* If neither A nor B is on the stack, use A. Backtrack will put B
|
|
on the stack when A's possibilities are exhausted. */
|
|
/* If A is on the stack, use A. */
|
|
/* If B is on the stack, use B. */
|
|
/* If A and B are on the stack, bug. */
|
|
/* In other words: if B is not on the stack, use A. */
|
|
if (g_MathToken[schB[p]].tmp == -1) {
|
|
/* B is not on the stack */
|
|
goto schAUnk;
|
|
} else {
|
|
if (g_MathToken[schA[p]].tmp != -1) bug(1902); /* Both are on the stack */
|
|
goto schBUnk;
|
|
}
|
|
}
|
|
|
|
schBUnk:
|
|
if (schB[p] > g_mathTokens) {
|
|
/*E*/if(db6)print2("schB has unknown variable\n");
|
|
/* An "unknown" variable is in scheme B */
|
|
schemeAFlag = 0;
|
|
substToken = schB[p];
|
|
|
|
if (g_MathToken[substToken].tmp == -1) {
|
|
/* The "unknown" variable is not on the stack; add it */
|
|
stackTop++;
|
|
stackUnkVar[stackTop] = substToken;
|
|
g_MathToken[substToken].tmp = stackTop;
|
|
stackUnkVarStart[stackTop] = p;
|
|
/* Start with a variable length of 0 or 1 */
|
|
stackUnkVarLen[stackTop] = g_minSubstLen;
|
|
/* Save the rest of the current state for backtracking */
|
|
nmbrTmpPtr = (nmbrString *)(stackSaveUnkVarStart[stackTop]);
|
|
for (i = 0; i <= stackTop; i++) {
|
|
nmbrTmpPtr[i] = stackUnkVarStart[i];
|
|
}
|
|
nmbrTmpPtr = (nmbrString *)(stackSaveUnkVarLen[stackTop]);
|
|
for (i = 0; i <= stackTop; i++) {
|
|
nmbrTmpPtr[i] = stackUnkVarLen[i];
|
|
}
|
|
nmbrLet((nmbrString **)(&stackSaveSchemeA[stackTop]),
|
|
schA);
|
|
nmbrLet((nmbrString **)(&stackSaveSchemeB[stackTop]),
|
|
schB);
|
|
}
|
|
|
|
if (substToken != stackUnkVar[stackTop]) {
|
|
print2("PROGRAM BUG #1903\n");
|
|
print2("substToken is %s\n", g_MathToken[substToken].tokenName);
|
|
print2("stackTop %ld\n", stackTop);
|
|
print2("p %ld stackUnkVar[stackTop] %s\n", p,
|
|
g_MathToken[stackUnkVar[stackTop]].tokenName);
|
|
print2("schA %s\nschB %s\n", nmbrCvtMToVString(schA),
|
|
nmbrCvtMToVString(schB));
|
|
bug(1903);
|
|
}
|
|
nmbrLet(&substitution, nmbrMid(schA, p + 1,
|
|
stackUnkVarLen[stackTop]));
|
|
goto substitute;
|
|
}
|
|
|
|
schAUnk:
|
|
if (schA[p] > g_mathTokens) {
|
|
/*E*/if(db6)print2("schA has unknown variable\n");
|
|
/* An "unknown" variable is in scheme A */
|
|
schemeAFlag = 1;
|
|
substToken = schA[p];
|
|
if (g_MathToken[substToken].tmp == -1) {
|
|
/* The "unknown" variable is not on the stack; add it */
|
|
stackTop++;
|
|
stackUnkVar[stackTop] = substToken;
|
|
g_MathToken[substToken].tmp = stackTop;
|
|
stackUnkVarStart[stackTop] = p;
|
|
/* Start with a variable length of 0 or 1 */
|
|
stackUnkVarLen[stackTop] = g_minSubstLen;
|
|
/* Save the rest of the current state for backtracking */
|
|
nmbrTmpPtr = (nmbrString *)(stackSaveUnkVarStart[stackTop]);
|
|
for (i = 0; i <= stackTop; i++) {
|
|
nmbrTmpPtr[i] = stackUnkVarStart[i];
|
|
}
|
|
nmbrTmpPtr = (nmbrString *)(stackSaveUnkVarLen[stackTop]);
|
|
for (i = 0; i <= stackTop; i++) {
|
|
nmbrTmpPtr[i] = stackUnkVarLen[i];
|
|
}
|
|
nmbrLet((nmbrString **)(&stackSaveSchemeA[stackTop]),
|
|
schA);
|
|
nmbrLet((nmbrString **)(&stackSaveSchemeB[stackTop]),
|
|
schB);
|
|
}
|
|
|
|
if (substToken != stackUnkVar[stackTop]) {
|
|
/*E*/print2("PROGRAM BUG #1904\n");
|
|
/*E*/print2("\nsubstToken is %s\n",g_MathToken[substToken].tokenName);
|
|
/*E*/print2("stack top %ld\n",stackTop);
|
|
/*E*/print2("p %ld stUnV[stakTop] %s\n",p,
|
|
/*E*/g_MathToken[stackUnkVar[stackTop]].tokenName);
|
|
/*E*/print2("schA %s\nschB %s\n",nmbrCvtMToVString(schA),nmbrCvtMToVString(schB));
|
|
bug(1904);
|
|
}
|
|
nmbrLet(&substitution, nmbrMid(schB, p + 1,
|
|
stackUnkVarLen[stackTop]));
|
|
goto substitute;
|
|
}
|
|
|
|
/* Neither scheme has an unknown variable; unification with current assignment
|
|
failed, so backtrack */
|
|
/*E*/if(db6)print2("Neither scheme has unknown variable\n");
|
|
goto backtrack;
|
|
|
|
|
|
substitute:
|
|
/*E*/if(db6)print2("Entering substitute...\n");
|
|
/*E*/for (d = 0; d <= stackTop; d++) {
|
|
/*E*/ if(db6)print2("Unknown var %ld is %s.\n",d,
|
|
/*E*/ g_MathToken[stackUnkVar[d]].tokenName);
|
|
/*E*/ if(db6)print2(" Its start is %ld; its length is %ld.\n",
|
|
/*E*/ stackUnkVarStart[d],stackUnkVarLen[d]);
|
|
/*E*/}
|
|
/* Subst. all occurrences of substToken with substitution in schA and schB */
|
|
/*???We could speed things up by making substitutions before the pointer to
|
|
???to the unifiedScheme only, and keeping track of 3 pointers (A, B, &
|
|
???unified schemes); unifiedScheme would hold only stuff before pointer */
|
|
|
|
/* First, we must make sure that the substToken doesn't occur in the
|
|
substutition. */
|
|
if (nmbrElementIn(1, substitution, substToken)) {
|
|
/*E*/if(db6)print2("Substituted token occurs in substitution string\n");
|
|
goto backtrack;
|
|
}
|
|
|
|
/* Next, we must make sure that the end of string doesn't occur in the
|
|
substutition. */
|
|
/* (This takes care of the case where the unknown variable aligns with
|
|
end of string character; in this case, only a null substitution is
|
|
permissible. If the substitution length is 1 or greater, this "if"
|
|
statement will detect it.) */
|
|
if (substitution[0] == g_mathTokens) {
|
|
/*E*/if(db6)print2("End of string token occurs in substitution string\n");
|
|
/* We must pop the stack here rather than in backtrack, because we
|
|
are already one token beyond the end of a scheme, and backtrack
|
|
would therefore test one token beyond that, missing the fact that
|
|
the substitution has overflowed beyond the end of a scheme. */
|
|
/* Set the flag that it's not on the stack and pop stack */
|
|
g_MathToken[stackUnkVar[stackTop]].tmp = -1;
|
|
stackTop--;
|
|
goto backtrack;
|
|
}
|
|
|
|
/************* Start of 26-Sep-2010 *************/
|
|
/* Bracket matching is customized to set.mm to result in fewer ambiguous
|
|
unifications. */
|
|
/* 26-Sep-2010 nm Automatically disable bracket matching if any $a has
|
|
unmatched brackets */
|
|
/* The static variable g_bracketMatchInit tells us to check all $a's
|
|
if it is 0; if 1, skip the $a checking. Make sure that the RESET
|
|
command sets g_bracketMatchInit=0. */
|
|
/* ??? To do: put individual bracket type checks into a loop or
|
|
function call for code efficiency (but don't slow down program); maybe
|
|
read the bracket types to check from a list; maybe refine so that only
|
|
the mismatched bracket types found in the $a scan are skipped, but
|
|
matched one are not */
|
|
for (i = g_bracketMatchInit; i <= 1; i++) {
|
|
/* This loop has 2 passes (0 and 1) if g_bracketMatchInit=0 to set
|
|
bracketMatchOn = 0 or 1, and 1 pass otherwise */
|
|
bracketMismatchFound = 0; /* Don't move down; needed for break below */
|
|
if (g_bracketMatchInit == 0) { /* Initialization pass */
|
|
if (i != 0) bug(1908);
|
|
/* Scan all ($a) statements */
|
|
bracketScanStart = 1;
|
|
bracketScanStop = g_statements;
|
|
} else { /* Normal pass */
|
|
if (i != 1) bug(1909);
|
|
if (!bracketMatchOn) break; /* Skip the whole bracket check because a
|
|
mismatched bracket was found in some $a in the initialization pass */
|
|
/* Set dummy parameters to force a single loop pass */
|
|
bracketScanStart = 0;
|
|
bracketScanStop = 0;
|
|
}
|
|
for (m = bracketScanStart; m <= bracketScanStop; m++) {
|
|
if (g_bracketMatchInit == 0) { /* Initialization pass */
|
|
if (g_Statement[m].type != a_) continue;
|
|
nmbrTmpPtr = g_Statement[m].mathString;
|
|
} else { /* Normal pass */
|
|
nmbrTmpPtr = substitution;
|
|
}
|
|
j = nmbrLen(nmbrTmpPtr);
|
|
|
|
/* Make sure left and right parentheses match */
|
|
pairingMismatches = 0; /* Counter of parens: + for "(" and - for ")" */
|
|
for (k = 0; k < j; k++) {
|
|
mToken = g_MathToken[nmbrTmpPtr[k]].tokenName;
|
|
if (mToken[0] == '(' && mToken[1] == 0 ) {
|
|
pairingMismatches++;
|
|
} else if (mToken[0] == ')' && mToken[1] == 0 ) {
|
|
pairingMismatches--;
|
|
if (pairingMismatches < 0) break; /* Detect wrong order */
|
|
}
|
|
} /* Next k */
|
|
if (pairingMismatches != 0) {
|
|
bracketMismatchFound = 1;
|
|
break;
|
|
}
|
|
|
|
/* Make sure left and right braces match */
|
|
pairingMismatches = 0; /* Counter of braces: + for "{" and - for "}" */
|
|
for (k = 0; k < j; k++) {
|
|
mToken = g_MathToken[nmbrTmpPtr[k]].tokenName;
|
|
if (mToken[0] == '{' && mToken[1] == 0 ) pairingMismatches++;
|
|
else
|
|
if (mToken[0] == '}' && mToken[1] == 0 ) {
|
|
pairingMismatches--;
|
|
if (pairingMismatches < 0) break; /* Detect wrong order */
|
|
}
|
|
} /* Next k */
|
|
if (pairingMismatches != 0) {
|
|
bracketMismatchFound = 1;
|
|
break;
|
|
}
|
|
|
|
/* Make sure left and right brackets match */ /* Added 12-Nov-05 nm */
|
|
pairingMismatches = 0; /* Counter of brackets: + for "[" and - for "]" */
|
|
for (k = 0; k < j; k++) {
|
|
mToken = g_MathToken[nmbrTmpPtr[k]].tokenName;
|
|
if (mToken[0] == '[' && mToken[1] == 0 )
|
|
pairingMismatches++;
|
|
else
|
|
if (mToken[0] == ']' && mToken[1] == 0 ) {
|
|
pairingMismatches--;
|
|
if (pairingMismatches < 0) break; /* Detect wrong order */
|
|
}
|
|
} /* Next k */
|
|
if (pairingMismatches != 0) {
|
|
bracketMismatchFound = 1;
|
|
break;
|
|
}
|
|
|
|
/* Make sure left and right triangle brackets match */
|
|
pairingMismatches = 0; /* Counter of brackets: + for "<.", - for ">." */
|
|
for (k = 0; k < j; k++) {
|
|
mToken = g_MathToken[nmbrTmpPtr[k]].tokenName;
|
|
if (mToken[1] == 0) continue;
|
|
if (mToken[0] == '<' && mToken[1] == '.' && mToken[2] == 0 )
|
|
pairingMismatches++;
|
|
else
|
|
if (mToken[0] == '>' && mToken[1] == '.' && mToken[2] == 0 ) {
|
|
pairingMismatches--;
|
|
if (pairingMismatches < 0) break; /* Detect wrong order */
|
|
}
|
|
} /* Next k */
|
|
if (pairingMismatches != 0) {
|
|
bracketMismatchFound = 1;
|
|
break;
|
|
}
|
|
|
|
/* Make sure underlined brackets match */ /* Added 12-Nov-05 nm */
|
|
pairingMismatches = 0; /* Counter of brackets: + for "[_", - for "]_" */
|
|
for (k = 0; k < j; k++) {
|
|
mToken = g_MathToken[nmbrTmpPtr[k]].tokenName;
|
|
if (mToken[1] == 0) continue;
|
|
if (mToken[0] == '[' && mToken[1] == '_' && mToken[2] == 0 )
|
|
pairingMismatches++;
|
|
else
|
|
if (mToken[0] == ']' && mToken[1] == '_' && mToken[2] == 0 ) {
|
|
pairingMismatches--;
|
|
if (pairingMismatches < 0) break; /* Detect wrong order */
|
|
}
|
|
} /* Next k */
|
|
if (pairingMismatches != 0) {
|
|
bracketMismatchFound = 1;
|
|
break;
|
|
}
|
|
} /* next m */
|
|
|
|
if (g_bracketMatchInit == 0) { /* Initialization pass */
|
|
/* We've finished the one-time $a scan. Set flags accordingly. */
|
|
if (bracketMismatchFound) { /* Some $a has a bracket mismatch */
|
|
if (m < 1 || m > g_statements) bug(1910);
|
|
printLongLine(cat("The bracket matching unification heuristic was",
|
|
" turned off for this database because of a bracket mismatch in",
|
|
" statement \"",
|
|
/* (m should be accurate due to break above) */
|
|
g_Statement[m].labelName,
|
|
"\".", NULL),
|
|
" ", " ");
|
|
/*
|
|
printLongLine(cat("The bracket matching unification heuristic was",
|
|
" turned off for this database.", NULL),
|
|
" ", " ");
|
|
*/
|
|
bracketMatchOn = 0; /* Turn off static flag for this database */
|
|
} else { /* Normal pass */
|
|
bracketMatchOn = 1; /* Turn it on */
|
|
}
|
|
/*E*/if(db6)print2("bracketMatchOn = %ld\n", (long)bracketMatchOn);
|
|
g_bracketMatchInit = 1; /* We're done with the one-time $a scan */
|
|
}
|
|
} /* next i */
|
|
|
|
if (bracketMismatchFound) goto backtrack;
|
|
/************* End of 26-Sep-2010 *************/
|
|
|
|
j = nmbrLen(substitution);
|
|
|
|
/* 8/29/99 - Quick scan to reject some impossible unifications: If the
|
|
first symbol in a substitution is a constant, it must match
|
|
the 2nd constant of some earlier $a statement (e.g. "<" matches
|
|
"class <", "Ord (/)" matches "class Ord A"). Same applies to
|
|
last symbol. */
|
|
/* 10/12/02 - This prefilter is too aggressive when empty substitutions
|
|
are allowed. Therefore added "g_minSubstLen > 0" to fix miu.mm theorem1
|
|
Proof Assistant failure reported by Josh Purinton. */
|
|
if (j/*subst len*/ > 0 && g_minSubstLen > 0) {
|
|
impossible = 0;
|
|
if (g_MathToken[substitution[0]].tokenType == (char)con_) {
|
|
if (!g_firstConst[substitution[0]]
|
|
|| (j == 1 && !g_oneConst[substitution[0]])) {
|
|
impossible = 1;
|
|
}
|
|
}
|
|
if (g_MathToken[substitution[j - 1]].tokenType == (char)con_) {
|
|
if (!g_lastConst[substitution[j - 1]]) {
|
|
impossible = 1;
|
|
}
|
|
}
|
|
if (impossible) {
|
|
/*E*/if(db6)print2("Impossible subst: %s\n", nmbrCvtMToVString(substitution));
|
|
goto backtrack;
|
|
}
|
|
}
|
|
|
|
/* Now perform the substitutions */
|
|
/*E*/if(db6)print2("Substitution is '%s'\n",nmbrCvtMToVString(substitution));
|
|
k = 1;
|
|
while (1) {
|
|
/* Perform the substitutions into scheme A */
|
|
k = nmbrElementIn(k, schA, substToken);
|
|
if (!k) break;
|
|
|
|
if (schemeAFlag) {
|
|
/* The token to be substituted was in scheme A */
|
|
/* Adjust position and earlier var. starts and lengths */
|
|
if (k - 1 <= p) {
|
|
if (k <= p) {
|
|
/* Adjust assignments in stack */
|
|
for (i = 0; i <= stackTop; i++) {
|
|
if (k - 1 < stackUnkVarStart[i]) {
|
|
stackUnkVarStart[i] = stackUnkVarStart[i] + j-1;
|
|
} else {
|
|
if (k <= stackUnkVarStart[i] +
|
|
stackUnkVarLen[i]) {
|
|
stackUnkVarLen[i] = stackUnkVarLen[i] + j - 1;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
p = p + j - 1; /* Adjust scan position */
|
|
/*E*/if(db6)print2("Scheme A adjusted p=%ld\n",p);
|
|
}
|
|
}
|
|
|
|
nmbrLet(&schA, nmbrCat(
|
|
nmbrLeft(schA, k - 1), substitution, nmbrRight(schA, k + 1), NULL));
|
|
k = k + j - 1;
|
|
}
|
|
k = 1;
|
|
while (1) {
|
|
/* Perform the substitutions into scheme B */
|
|
k = nmbrElementIn(k, schB, substToken);
|
|
if (!k) break;
|
|
|
|
if (!schemeAFlag) {
|
|
/* The token to be substituted was in scheme B */
|
|
/* Adjust scan position and earlier var. starts and lengths */
|
|
if (k - 1 <= p) {
|
|
if (k <= p) {
|
|
/* Adjust assignments in stack */
|
|
for (i = 0; i <= stackTop; i++) {
|
|
if (k - 1 < stackUnkVarStart[i]) {
|
|
stackUnkVarStart[i] = stackUnkVarStart[i] + j-1;
|
|
} else {
|
|
if (k <= stackUnkVarStart[i] +
|
|
stackUnkVarLen[i]) {
|
|
stackUnkVarLen[i] = stackUnkVarLen[i] + j - 1;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
p = p + j - 1; /* Adjust scan position */
|
|
}
|
|
/*E*/if(db6)print2("Scheme B adjusted p=%ld\n",p);
|
|
}
|
|
|
|
nmbrLet(&schB, nmbrCat(
|
|
nmbrLeft(schB, k - 1), substitution, nmbrRight(schB, k + 1), NULL));
|
|
k = k + j - 1;
|
|
}
|
|
p++;
|
|
/*E*/if(db6)print2("Scheme A or B final p=%ld\n",p);
|
|
/*E*/if(db6)print2("after sub sbA %s\n",nmbrCvtMToVString(schA));
|
|
/*E*/if(db6)print2("after sub sbB %s\n",nmbrCvtMToVString(schB));
|
|
/*E*/for (d = 0; d <= stackTop; d++) {
|
|
/*E*/ if(db6)print2("Unknown var %ld is %s.\n",d,
|
|
/*E*/ g_MathToken[stackUnkVar[d]].tokenName);
|
|
/*E*/ if(db6)print2(" Its start is %ld; its length is %ld.\n",
|
|
/*E*/ stackUnkVarStart[d],stackUnkVarLen[d]);
|
|
/*E*/}
|
|
goto scan;
|
|
|
|
backtrack:
|
|
/*E*/if(db6)print2("Entered backtrack with p=%ld stackTop=%ld\n",p,stackTop);
|
|
if (stackTop < 0) {
|
|
goto abort;
|
|
}
|
|
if (g_unifTrialCount > 0) { /* Flag that timeout is active */
|
|
g_unifTrialCount++;
|
|
if (g_unifTrialCount > g_userMaxUnifTrials) {
|
|
g_unifTimeouts++; /* Update number of timeouts found */
|
|
/*E*/if(db5)print2("Aborted due to timeout: %ld > %ld\n",
|
|
/*E*/ g_unifTrialCount, g_userMaxUnifTrials);
|
|
timeoutAbortFlag = 1;
|
|
goto abort;
|
|
}
|
|
}
|
|
/* Add 1 to stackTop variable length */
|
|
nmbrTmpPtr = (nmbrString *)(stackSaveUnkVarLen[stackTop]);
|
|
nmbrTmpPtr[stackTop]++;
|
|
/* Restore the state from the stack top */
|
|
nmbrLet(&stackUnkVarStart, (nmbrString *)(stackSaveUnkVarStart[stackTop]));
|
|
nmbrLet(&schA, (nmbrString *)(stackSaveSchemeA[stackTop]));
|
|
nmbrLet(&schB, (nmbrString *)(stackSaveSchemeB[stackTop]));
|
|
/* Restore the scan position */
|
|
p = stackUnkVarStart[stackTop];
|
|
switchVarToB:
|
|
/* Restore the state from the stack top */
|
|
nmbrLet(&stackUnkVarLen, (nmbrString *)(stackSaveUnkVarLen[stackTop]));
|
|
|
|
/* If the variable overflows the end of the scheme its assigned to,
|
|
pop the stack */
|
|
/*E*/if(db6)print2("Backtracked to token %s.\n",
|
|
/*E*/ g_MathToken[stackUnkVar[stackTop]].tokenName);
|
|
if (stackUnkVar[stackTop] == schA[p]) {
|
|
/* It was in scheme A; see if it overflows scheme B */
|
|
if (schB[p - 1 + stackUnkVarLen[stackTop]]
|
|
== g_mathTokens) {
|
|
/*E*/if(db6)print2("It was in scheme A; overflowed scheme B: p=%ld, len=%ld.\n",
|
|
/*E*/ p,stackUnkVarLen[stackTop]);
|
|
/* Set the flag that it's not on the stack */
|
|
g_MathToken[stackUnkVar[stackTop]].tmp = -1;
|
|
|
|
/* See if the token in scheme B at this position is also a variable */
|
|
/* If so, switch the stack top variable to the one in scheme B and
|
|
restart the scan on its length */
|
|
if (schB[p] > g_mathTokens) {
|
|
/*E*/if(db6)print2("Switched var-var match to scheme B token %s\n",
|
|
/*E*/ g_MathToken[stackUnkVar[stackTop]].tokenName);
|
|
/* The scheme B variable will not be on the stack. */
|
|
if (g_MathToken[schB[p]].tmp != -1) bug(1905);
|
|
/* Make the token in scheme B become the variable at the stack top */
|
|
stackUnkVar[stackTop] = schB[p];
|
|
g_MathToken[schB[p]].tmp = stackTop;
|
|
/* Start with a variable length of 0 or 1 */
|
|
stackUnkVarLen[stackTop] = g_minSubstLen;
|
|
/* Initialize stackTop variable length */
|
|
nmbrTmpPtr = (nmbrString *)(stackSaveUnkVarLen[stackTop]);
|
|
nmbrTmpPtr[stackTop] = g_minSubstLen;
|
|
/* Restart the backtrack with double variable switched to scheme B */
|
|
goto switchVarToB;
|
|
}
|
|
|
|
/* Pop stack */
|
|
stackTop--;
|
|
goto backtrack;
|
|
}
|
|
} else {
|
|
/* It was in scheme B; see if it overflows scheme A */
|
|
if (schA[p - 1 + stackUnkVarLen[stackTop]]
|
|
== g_mathTokens) {
|
|
/*E*/if(db6)print2("It was in scheme B; overflowed scheme A: p=%ld, len=%ld.\n",
|
|
/*E*/ p,stackUnkVarLen[stackTop]);
|
|
/* Set the flag that it's not on the stack and pop stack */
|
|
g_MathToken[stackUnkVar[stackTop]].tmp = -1;
|
|
stackTop--;
|
|
goto backtrack;
|
|
}
|
|
}
|
|
/*E*/if(db6)print2("Exited backtrack with p=%ld stackTop=%ld\n",p,stackTop);
|
|
if (reEntryFlag) goto reEntry1;
|
|
goto scan; /* Continue the scan */
|
|
|
|
done:
|
|
|
|
/* Assign the final result */
|
|
nmbrLet(&unifiedScheme, nmbrLeft(schA, nmbrLen(schA) - 1));
|
|
/*E*/if(db5)print2("Backtrack count was %ld\n",g_unifTrialCount);
|
|
/*E*/if(db5)printLongLine(cat("Unified scheme is ",
|
|
/*E*/ nmbrCvtMToVString(unifiedScheme),".",NULL)," "," ");
|
|
/* Assign the 12 components of (*stateVector). Some of the components hold
|
|
pointers to pointer strings. */
|
|
/* 0 holds the unkVars array and length */
|
|
((nmbrString *)((*stateVector)[11]))[0] = unkVarsLen;
|
|
(*stateVector)[0] = unkVars;
|
|
/* 1 holds the stack top and the "unknown" vars on the stack */
|
|
((nmbrString *)((*stateVector)[11]))[1] = stackTop;
|
|
(*stateVector)[1] = stackUnkVar;
|
|
(*stateVector)[2] = stackUnkVarStart;
|
|
(*stateVector)[3] = stackUnkVarLen;
|
|
(*stateVector)[4] = stackSaveUnkVarStart;
|
|
(*stateVector)[5] = stackSaveUnkVarLen;
|
|
(*stateVector)[6] = stackSaveSchemeA;
|
|
(*stateVector)[7] = stackSaveSchemeB;
|
|
/* Save the result */
|
|
(*stateVector)[8] = unifiedScheme;
|
|
/* Components 9 and 10 save the previous assignment.
|
|
This is handled by oneDirUnif() if oneDirUnif() is called. */
|
|
((nmbrString *)((*stateVector)[11]))[2] = schemeAUnkVarsLen; /* Used by oneDirUnif() */
|
|
|
|
/*E*/if(db5)printSubst(*stateVector);
|
|
/* Deallocate nmbrStrings */
|
|
nmbrLet(&schA, NULL_NMBRSTRING);
|
|
nmbrLet(&schB, NULL_NMBRSTRING);
|
|
nmbrLet(&substitution, NULL_NMBRSTRING);
|
|
return (1);
|
|
|
|
abort:
|
|
/*E*/if(db5)print2("Backtrack count was %ld\n",g_unifTrialCount);
|
|
/* Deallocate stateVector contents */
|
|
nmbrLet(&unkVars,NULL_NMBRSTRING);
|
|
nmbrLet(&stackUnkVar,NULL_NMBRSTRING);
|
|
nmbrLet(&stackUnkVarStart,NULL_NMBRSTRING);
|
|
nmbrLet(&stackUnkVarLen,NULL_NMBRSTRING);
|
|
for (i = 0; i < unkVarsLen; i++) {
|
|
/* Deallocate the stack space for these */
|
|
nmbrLet((nmbrString **)(&stackSaveUnkVarStart[i]),
|
|
NULL_NMBRSTRING);
|
|
nmbrLet((nmbrString **)(&stackSaveUnkVarLen[i]),
|
|
NULL_NMBRSTRING);
|
|
nmbrLet((nmbrString **)(&stackSaveSchemeA[i]),
|
|
NULL_NMBRSTRING);
|
|
nmbrLet((nmbrString **)(&stackSaveSchemeB[i]),
|
|
NULL_NMBRSTRING);
|
|
}
|
|
pntrLet(&stackSaveUnkVarStart,NULL_PNTRSTRING);
|
|
pntrLet(&stackSaveUnkVarLen,NULL_PNTRSTRING);
|
|
pntrLet(&stackSaveSchemeA,NULL_PNTRSTRING);
|
|
pntrLet(&stackSaveSchemeB,NULL_PNTRSTRING);
|
|
nmbrLet(&unifiedScheme,NULL_NMBRSTRING);
|
|
/* Deallocate entries used by oneDirUnif() */
|
|
nmbrLet((nmbrString **)(&(*stateVector)[9]),NULL_NMBRSTRING);
|
|
nmbrLet((nmbrString **)(&(*stateVector)[10]),NULL_NMBRSTRING);
|
|
/* Deallocate entries used by unifyH() */
|
|
k = pntrLen((pntrString *)((*stateVector)[12]));
|
|
for (i = 12; i < 16; i++) {
|
|
pntrTmpPtr = (pntrString *)((*stateVector)[i]);
|
|
for (j = 0; j < k; j++) {
|
|
nmbrLet((nmbrString **)(&pntrTmpPtr[j]),
|
|
NULL_NMBRSTRING);
|
|
}
|
|
pntrLet((pntrString **)(&(*stateVector)[i]),
|
|
NULL_PNTRSTRING);
|
|
}
|
|
/* Deallocate the stateVector itself */
|
|
((nmbrString *)((*stateVector)[11]))[1] = 0;
|
|
/* stackTop: Make sure it's not -1 before calling nmbrLet() */
|
|
nmbrLet((nmbrString **)(&(*stateVector)[11]),NULL_NMBRSTRING);
|
|
pntrLet(&(*stateVector),NULL_PNTRSTRING);
|
|
|
|
/* Deallocate nmbrStrings */
|
|
nmbrLet(&schA,NULL_NMBRSTRING);
|
|
nmbrLet(&schB,NULL_NMBRSTRING);
|
|
nmbrLet(&substitution,NULL_NMBRSTRING);
|
|
|
|
if (timeoutAbortFlag) {
|
|
return (2);
|
|
}
|
|
return (0);
|
|
} /* unify */
|
|
|
|
|
|
/* oneDirUnif() is like unify(), except that when reEntryFlag is 1,
|
|
a new unification is returned ONLY if the assignments to the
|
|
variables in schemeA have changed. This is used to speed up the
|
|
program. */
|
|
/*???This whole thing may be screwed up -- it seems to be based on
|
|
all unknown vars (including those without substitutions), not just
|
|
the ones on the stack. */
|
|
flag oneDirUnif(
|
|
nmbrString *schemeA,
|
|
nmbrString *schemeB,
|
|
pntrString **stateVector,
|
|
long reEntryFlag)
|
|
{
|
|
long i;
|
|
flag tmpFlag;
|
|
long schemeAUnkVarsLen;
|
|
nmbrString *stackUnkVarStart; /* Pointer only - not allocated */
|
|
nmbrString *stackUnkVarLen; /* Pointer only - not allocated */
|
|
nmbrString *oldStackUnkVarStart; /* Pointer only - not allocated */
|
|
nmbrString *oldStackUnkVarLen; /* Pointer only - not allocated */
|
|
|
|
if (!reEntryFlag) {
|
|
tmpFlag = unify(schemeA, schemeB, stateVector, 0);
|
|
if (tmpFlag) {
|
|
/* Save the initial variable assignments */
|
|
nmbrLet((nmbrString **)(&(*stateVector)[9]),
|
|
(nmbrString *)((*stateVector)[2]));
|
|
nmbrLet((nmbrString **)(&(*stateVector)[10]),
|
|
(nmbrString *)((*stateVector)[3]));
|
|
}
|
|
return (tmpFlag);
|
|
} else {
|
|
while (1) {
|
|
tmpFlag = unify(schemeA, schemeB, stateVector, 1);
|
|
if (!tmpFlag) return (0);
|
|
/* Check to see if the variables in schemeA changed */
|
|
schemeAUnkVarsLen = ((nmbrString *)((*stateVector)[11]))[2];
|
|
stackUnkVarStart = (nmbrString *)((*stateVector)[2]);
|
|
stackUnkVarLen = (nmbrString *)((*stateVector)[3]);
|
|
oldStackUnkVarStart = (nmbrString *)((*stateVector)[9]);
|
|
oldStackUnkVarLen = (nmbrString *)((*stateVector)[10]);
|
|
for (i = 0; i < schemeAUnkVarsLen; i++) {
|
|
if (stackUnkVarStart[i] != oldStackUnkVarStart[i]) {
|
|
/* The assignment changed */
|
|
/* Save the new assignment */
|
|
nmbrLet(&oldStackUnkVarStart, stackUnkVarStart);
|
|
nmbrLet(&oldStackUnkVarLen, stackUnkVarLen);
|
|
return (1);
|
|
}
|
|
if (stackUnkVarLen[i] != oldStackUnkVarLen[i]) {
|
|
/* The assignment changed */
|
|
/* Save the new assignment */
|
|
nmbrLet(&oldStackUnkVarStart, stackUnkVarStart);
|
|
nmbrLet(&oldStackUnkVarLen, stackUnkVarLen);
|
|
return (1);
|
|
}
|
|
}
|
|
} /* End while (1) */
|
|
}
|
|
return(0); /* Dummy return value - never happens */
|
|
} /* oneDirUnif */
|
|
|
|
|
|
|
|
/* uniqueUnif() is like unify(), but there is no reEntryFlag, and 3 possible
|
|
values are returned:
|
|
0: no unification was possible.
|
|
1: exactly one unification was possible, and stateVector is valid.
|
|
2: unification timed out
|
|
3: more than one unification was possible */
|
|
char uniqueUnif(
|
|
nmbrString *schemeA,
|
|
nmbrString *schemeB,
|
|
pntrString **stateVector)
|
|
{
|
|
pntrString *saveStateVector = NULL_PNTRSTRING;
|
|
pntrString *pntrTmpPtr1; /* Pointer only; not allocated */
|
|
pntrString *pntrTmpPtr2; /* Pointer only; not allocated */
|
|
long i, j, k;
|
|
char tmpFlag;
|
|
|
|
tmpFlag = unifyH(schemeA, schemeB, stateVector, 0);
|
|
if (!tmpFlag) {
|
|
return (0); /* No unification possible */
|
|
}
|
|
if (tmpFlag == 2) {
|
|
return (2); /* Unification timed out */
|
|
}
|
|
|
|
/* Save the state vector */
|
|
pntrLet(&saveStateVector,*stateVector);
|
|
if (pntrLen(*stateVector) != 16) bug(1906);
|
|
for (i = 0; i < 4; i++) {
|
|
/* Force new space to be allocated for each pointer */
|
|
saveStateVector[i] = NULL_NMBRSTRING;
|
|
nmbrLet((nmbrString **)(&saveStateVector[i]),
|
|
(nmbrString *)((*stateVector)[i]));
|
|
}
|
|
for (i = 4; i <= 7; i++) {
|
|
/* Force new space to be allocated for each pointer */
|
|
saveStateVector[i] = NULL_PNTRSTRING;
|
|
pntrLet((pntrString **)(&saveStateVector[i]),
|
|
(pntrString *)((*stateVector)[i]));
|
|
}
|
|
for (i = 8; i < 12; i++) {
|
|
/* Force new space to be allocated for each pointer */
|
|
saveStateVector[i] = NULL_NMBRSTRING;
|
|
nmbrLet((nmbrString **)(&saveStateVector[i]),
|
|
(nmbrString *)((*stateVector)[i]));
|
|
}
|
|
for (i = 12; i < 16; i++) {
|
|
/* Force new space to be allocated for each pointer */
|
|
saveStateVector[i] = NULL_PNTRSTRING;
|
|
pntrLet((pntrString **)(&saveStateVector[i]),
|
|
(pntrString *)((*stateVector)[i]));
|
|
}
|
|
k = ((nmbrString *)((*stateVector)[11]))[0]; /* unkVarsLen */
|
|
for (i = 4; i <= 7; i++) {
|
|
pntrTmpPtr1 = (pntrString *)(saveStateVector[i]);
|
|
pntrTmpPtr2 = (pntrString *)((*stateVector)[i]);
|
|
for (j = 0; j < k; j++) {
|
|
/* Force new space to be allocated for each pointer */
|
|
pntrTmpPtr1[j] = NULL_NMBRSTRING;
|
|
nmbrLet((nmbrString **)(&pntrTmpPtr1[j]),
|
|
(nmbrString *)(pntrTmpPtr2[j]));
|
|
}
|
|
}
|
|
k = pntrLen((pntrString *)((*stateVector)[12]));
|
|
for (i = 12; i < 16; i++) {
|
|
pntrTmpPtr1 = (pntrString *)(saveStateVector[i]);
|
|
pntrTmpPtr2 = (pntrString *)((*stateVector)[i]);
|
|
for (j = 0; j < k; j++) {
|
|
/* Force new space to be allocated for each pointer */
|
|
pntrTmpPtr1[j] = NULL_NMBRSTRING;
|
|
nmbrLet((nmbrString **)(&pntrTmpPtr1[j]),
|
|
(nmbrString *)(pntrTmpPtr2[j]));
|
|
}
|
|
}
|
|
|
|
/* See if there is a second unification */
|
|
tmpFlag = unifyH(schemeA, schemeB, stateVector, 1);
|
|
|
|
if (!tmpFlag) {
|
|
/* There is no 2nd unification. Unify cleared the original stateVector,
|
|
so return the saved version. */
|
|
*stateVector = saveStateVector;
|
|
return (1); /* Return flag that unification is unique. */
|
|
}
|
|
|
|
/* There are two or more unifications. Deallocate the stateVector
|
|
we just saved before returning, since we will not use it. */
|
|
/* stackTop: Make sure it's not -1 before calling nmbrLet() */
|
|
((nmbrString *)(saveStateVector[11]))[1] = 0;
|
|
for (i = 4; i <= 7; i++) {
|
|
pntrTmpPtr1 = (pntrString *)(saveStateVector[i]);
|
|
for (j = 0; j < ((nmbrString *)(saveStateVector[11]))[0]; j++) {
|
|
/* ((nmbrString *)(saveStateVector[11]))[0] is unkVarsLen */
|
|
nmbrLet((nmbrString **)(&pntrTmpPtr1[j]), NULL_NMBRSTRING);
|
|
}
|
|
}
|
|
for (i = 0; i <= 3; i++) {
|
|
nmbrLet((nmbrString **)(&saveStateVector[i]),
|
|
NULL_NMBRSTRING);
|
|
}
|
|
for (i = 4; i <= 7; i++) {
|
|
pntrLet((pntrString **)(&saveStateVector[i]),
|
|
NULL_PNTRSTRING);
|
|
}
|
|
for (i = 8; i <= 11; i++) {
|
|
nmbrLet((nmbrString **)(&saveStateVector[i]),
|
|
NULL_NMBRSTRING);
|
|
}
|
|
k = pntrLen((pntrString *)(saveStateVector[12]));
|
|
for (i = 12; i < 16; i++) {
|
|
pntrTmpPtr1 = (pntrString *)(saveStateVector[i]);
|
|
for (j = 0; j < k; j++) {
|
|
nmbrLet((nmbrString **)(&pntrTmpPtr1[j]),
|
|
NULL_NMBRSTRING);
|
|
}
|
|
pntrLet((pntrString **)(&saveStateVector[i]),
|
|
NULL_PNTRSTRING);
|
|
}
|
|
pntrLet(&saveStateVector, NULL_PNTRSTRING);
|
|
|
|
if (tmpFlag == 2) {
|
|
return (2); /* Unification timed out */
|
|
}
|
|
|
|
return (3); /* Return flag that unification is not unique */
|
|
|
|
} /* uniqueUnif */
|
|
|
|
|
|
/* Deallocates the contents of a stateVector */
|
|
/* Note: If unifyH() returns 0, there were no more unifications and
|
|
the stateVector is left empty, so we don't have to call
|
|
purgeStateVector. But no harm done if called anyway. */
|
|
void purgeStateVector(pntrString **stateVector) {
|
|
|
|
long i, j, k;
|
|
pntrString *pntrTmpPtr1;
|
|
|
|
if (!pntrLen(*stateVector)) return; /* It's already been purged */
|
|
|
|
/* stackTop: Make sure it's not -1 before calling nmbrLet() */
|
|
((nmbrString *)((*stateVector)[11]))[1] = 0;
|
|
for (i = 4; i <= 7; i++) {
|
|
pntrTmpPtr1 = (pntrString *)((*stateVector)[i]);
|
|
for (j = 0; j < ((nmbrString *)((*stateVector)[11]))[0]; j++) {
|
|
/* ((nmbrString *)((*stateVector)[11]))[0] is unkVarsLen */
|
|
nmbrLet((nmbrString **)(&pntrTmpPtr1[j]), NULL_NMBRSTRING);
|
|
}
|
|
}
|
|
for (i = 0; i <= 3; i++) {
|
|
nmbrLet((nmbrString **)(&(*stateVector)[i]),
|
|
NULL_NMBRSTRING);
|
|
}
|
|
for (i = 4; i <= 7; i++) {
|
|
pntrLet((pntrString **)(&(*stateVector)[i]),
|
|
NULL_PNTRSTRING);
|
|
}
|
|
for (i = 8; i <= 11; i++) {
|
|
nmbrLet((nmbrString **)(&(*stateVector)[i]),
|
|
NULL_NMBRSTRING);
|
|
}
|
|
k = pntrLen((pntrString *)((*stateVector)[12]));
|
|
for (i = 12; i < 16; i++) {
|
|
pntrTmpPtr1 = (pntrString *)((*stateVector)[i]);
|
|
for (j = 0; j < k; j++) {
|
|
nmbrLet((nmbrString **)(&pntrTmpPtr1[j]),
|
|
NULL_NMBRSTRING);
|
|
}
|
|
pntrLet((pntrString **)(&(*stateVector)[i]),
|
|
NULL_PNTRSTRING);
|
|
}
|
|
pntrLet(&(*stateVector), NULL_PNTRSTRING);
|
|
|
|
return;
|
|
|
|
} /* purgeStateVector */
|
|
|
|
|
|
/* Prints the substitutions determined by unify for debugging purposes */
|
|
void printSubst(pntrString *stateVector)
|
|
{
|
|
long d;
|
|
nmbrString *stackUnkVar; /* Pointer only - not allocated */
|
|
nmbrString *unifiedScheme; /* Pointer only - not allocated */
|
|
nmbrString *stackUnkVarLen; /* Pointer only - not allocated */
|
|
nmbrString *stackUnkVarStart; /* Pointer only - not allocated */
|
|
long stackTop;
|
|
vstring tmpStr = "";
|
|
nmbrString *nmbrTmp = NULL_NMBRSTRING;
|
|
|
|
stackTop = ((nmbrString *)(stateVector[11]))[1];
|
|
stackUnkVar = (nmbrString *)(stateVector[1]);
|
|
stackUnkVarStart = (nmbrString *)(stateVector[2]);
|
|
stackUnkVarLen = (nmbrString *)(stateVector[3]);
|
|
unifiedScheme = (nmbrString *)(stateVector[8]);
|
|
|
|
for (d = 0; d <= stackTop; d++) {
|
|
printLongLine(cat(" Variable '",
|
|
g_MathToken[stackUnkVar[d]].tokenName,"' was replaced with '",
|
|
nmbrCvtMToVString(
|
|
nmbrMid(unifiedScheme,stackUnkVarStart[d] + 1,
|
|
stackUnkVarLen[d])),"'.",NULL)," "," ");
|
|
/* Clear temporary string allocation */
|
|
let(&tmpStr,"");
|
|
nmbrLet(&nmbrTmp,NULL_NMBRSTRING);
|
|
}
|
|
} /* printSubst */
|
|
|
|
|
|
/* unifyH() is like unify(), except that when reEntryFlag is 1, a new
|
|
unification is returned ONLY if the normalized unification does not
|
|
previously exist in the "Henty filter" part of the stateVector. This
|
|
reduces ambiguous unifications. The values returned are the same as those
|
|
returned by unify(). (The redundancy of equivalent unifications was
|
|
a deficiency pointed out by Jeremy Henty.) */
|
|
char unifyH(
|
|
nmbrString *schemeA,
|
|
nmbrString *schemeB,
|
|
pntrString **stateVector,
|
|
long reEntryFlag)
|
|
{
|
|
char tmpFlag;
|
|
nmbrString *hentyVars = NULL_NMBRSTRING;
|
|
nmbrString *hentyVarStart = NULL_NMBRSTRING;
|
|
nmbrString *hentyVarLen = NULL_NMBRSTRING;
|
|
nmbrString *hentySubstList = NULL_NMBRSTRING;
|
|
|
|
/* Bypass this filter if SET HENTY_FILTER OFF is selected. */
|
|
if (!g_hentyFilter) return unify(schemeA, schemeB, stateVector, reEntryFlag);
|
|
|
|
if (!reEntryFlag) {
|
|
|
|
tmpFlag = unify(schemeA, schemeB, stateVector, 0);
|
|
if (tmpFlag == 1) { /* Unification OK */
|
|
|
|
/* Get the normalized equivalent substitutions */
|
|
hentyNormalize(&hentyVars, &hentyVarStart, &hentyVarLen,
|
|
&hentySubstList, stateVector);
|
|
|
|
/* This is the first unification so add it to the filter then return 1 */
|
|
hentyAdd(hentyVars, hentyVarStart, hentyVarLen,
|
|
hentySubstList, stateVector);
|
|
|
|
}
|
|
return (tmpFlag);
|
|
|
|
} else {
|
|
|
|
while (1) {
|
|
tmpFlag = unify(schemeA, schemeB, stateVector, 1);
|
|
if (tmpFlag == 1) { /* 0 = not possible, 1 == OK, 2 = timed out */
|
|
|
|
/* Get the normalized equivalent substitution */
|
|
hentyNormalize(&hentyVars, &hentyVarStart, &hentyVarLen,
|
|
&hentySubstList, stateVector);
|
|
|
|
/* Scan the Henty filter to see if this substitution is in it */
|
|
if (!hentyMatch(hentyVars, hentyVarStart, /*hentyVarLen,*/
|
|
hentySubstList, stateVector)) {
|
|
|
|
/* If it's not in there, this is a new unification so add it
|
|
to the filter then return 1 */
|
|
hentyAdd(hentyVars, hentyVarStart, hentyVarLen,
|
|
hentySubstList, stateVector);
|
|
return (1);
|
|
}
|
|
|
|
} else {
|
|
/* No unification is possible, or it timed out */
|
|
break;
|
|
}
|
|
|
|
/* If we get here this unification is in the Henty filter, so bypass it
|
|
and get the next unification. */
|
|
|
|
} /* End while (1) */
|
|
|
|
/* Deallocate memory (when reEntryFlag is 1 and (not possible or timeout)).
|
|
(In the other cases, hentyVars and hentySubsts pointers are assigned
|
|
directly to stateVector so they should not be deallocated) */
|
|
nmbrLet(&hentyVars, NULL_NMBRSTRING);
|
|
nmbrLet(&hentyVarStart, NULL_NMBRSTRING);
|
|
nmbrLet(&hentyVarLen, NULL_NMBRSTRING);
|
|
nmbrLet(&hentySubstList, NULL_NMBRSTRING);
|
|
return (tmpFlag);
|
|
|
|
}
|
|
} /* unifyH */
|
|
|
|
/* Extract and normalize the unification substitutions. */
|
|
void hentyNormalize(nmbrString **hentyVars, nmbrString **hentyVarStart,
|
|
nmbrString **hentyVarLen, nmbrString **hentySubstList,
|
|
pntrString **stateVector)
|
|
{
|
|
long vars, var1, var2, schLen;
|
|
long n, el, rra, rrb, rrc, ir, i, j; /* Variables for heap sort */
|
|
long totalSubstLen, pos;
|
|
nmbrString *substList = NULL_NMBRSTRING;
|
|
|
|
/* Extract the substitutions. */
|
|
vars = ((nmbrString *)((*stateVector)[11]))[1] + 1; /* stackTop + 1 */
|
|
nmbrLet((nmbrString **)(&(*hentyVars)), nmbrLeft(
|
|
(nmbrString *)((*stateVector)[1]), vars)); /* stackUnkVar */
|
|
nmbrLet((nmbrString **)(&(*hentyVarStart)), nmbrLeft(
|
|
(nmbrString *)((*stateVector)[2]), vars)); /* stackUnkVarStart */
|
|
nmbrLet((nmbrString **)(&(*hentyVarLen)), nmbrLeft(
|
|
(nmbrString *)((*stateVector)[3]), vars)); /* stackUnkVarLen */
|
|
nmbrLet((nmbrString **)(&(*hentySubstList)),
|
|
(nmbrString *)((*stateVector)[8])); /* unifiedScheme */
|
|
|
|
/* First, if a variable is substituted with another variable,
|
|
reverse the substitution if the substituted variable has a larger
|
|
tokenNum. */
|
|
for (i = 0; i < vars; i++) {
|
|
if ((*hentyVarLen)[i] == 1) {
|
|
var2 = (*hentySubstList)[(*hentyVarStart)[i]];
|
|
if (var2 > g_mathTokens) {
|
|
/* It's a variable-for-variable substitution */
|
|
var1 = (*hentyVars)[i];
|
|
if (var1 > var2) {
|
|
/* Swap the variables */
|
|
(*hentyVars)[i] = var2;
|
|
schLen = nmbrLen(*hentySubstList);
|
|
for (j = 0; j < schLen; j++) {
|
|
if ((*hentySubstList)[(*hentyVarStart)[i]] == var2) {
|
|
(*hentySubstList)[(*hentyVarStart)[i]] = var1;
|
|
}
|
|
} /* Next j */
|
|
} /* End if (var1 > var2) */
|
|
} /* End if (var2 > g_mathTokens) */
|
|
} /* End if ((*hentyVarLen)[i] == 1) */
|
|
} /* Next i */
|
|
|
|
/* Next, sort the variables to be substituted in tokenNum order */
|
|
/* Heap sort from "Numerical Recipes" (Press et. al.) p. 231 */
|
|
/* Note: The algorithm in the text has a bug; it does not work for n<2. */
|
|
n = vars;
|
|
if (n < 2) goto heapExit;
|
|
el = n / 2 + 1;
|
|
ir = n;
|
|
label10:
|
|
if (el > 1) {
|
|
el = el - 1;
|
|
rra = (*hentyVars)[el - 1];
|
|
rrb = (*hentyVarStart)[el - 1];
|
|
rrc = (*hentyVarLen)[el - 1];
|
|
} else {
|
|
rra = (*hentyVars)[ir - 1];
|
|
rrb = (*hentyVarStart)[ir - 1];
|
|
rrc = (*hentyVarLen)[ir - 1];
|
|
(*hentyVars)[ir - 1] = (*hentyVars)[0];
|
|
(*hentyVarStart)[ir - 1] = (*hentyVarStart)[0];
|
|
(*hentyVarLen)[ir - 1] = (*hentyVarLen)[0];
|
|
ir = ir - 1;
|
|
if (ir == 1) {
|
|
(*hentyVars)[0] = rra;
|
|
(*hentyVarStart)[0] = rrb;
|
|
(*hentyVarLen)[0] = rrc;
|
|
goto heapExit;
|
|
}
|
|
}
|
|
i = el;
|
|
j = el + el;
|
|
label20:
|
|
if (j <= ir) {
|
|
if (j < ir) {
|
|
if ((*hentyVars)[j - 1] < (*hentyVars)[j]) j = j + 1;
|
|
}
|
|
if (rra < (*hentyVars)[j - 1]) {
|
|
(*hentyVars)[i - 1] = (*hentyVars)[j - 1];
|
|
(*hentyVarStart)[i - 1] = (*hentyVarStart)[j - 1];
|
|
(*hentyVarLen)[i - 1] = (*hentyVarLen)[j - 1];
|
|
i = j;
|
|
j = j + j;
|
|
} else {
|
|
j = ir + 1;
|
|
}
|
|
goto label20;
|
|
}
|
|
(*hentyVars)[i - 1] = rra;
|
|
(*hentyVarStart)[i - 1] = rrb;
|
|
(*hentyVarLen)[i - 1] = rrc;
|
|
goto label10;
|
|
|
|
heapExit:
|
|
|
|
/* Finally, reconstruct the list of substitutions in
|
|
variable tokenNum order */
|
|
totalSubstLen = 0;
|
|
for (i = 0; i < vars; i++) {
|
|
totalSubstLen = totalSubstLen + (*hentyVarLen)[i];
|
|
}
|
|
/* For speedup, preallocate total string needed for the substitution list */
|
|
nmbrLet(&substList, nmbrSpace(totalSubstLen));
|
|
|
|
pos = 0; /* Position in list of substitutions */
|
|
for (i = 0; i < vars; i++) {
|
|
for (j = 0; j < (*hentyVarLen)[i]; j++) {
|
|
substList[pos + j] = (*hentySubstList)[(*hentyVarStart)[i] + j];
|
|
}
|
|
(*hentyVarStart)[i] = pos; /* Never used, but assign in case it ever does */
|
|
pos = pos + (*hentyVarLen)[i];
|
|
}
|
|
if (pos != totalSubstLen) bug(1907);
|
|
nmbrLet((nmbrString **)(&(*hentySubstList)), substList);
|
|
|
|
/* Deallocate memory */
|
|
nmbrLet(&substList, NULL_NMBRSTRING);
|
|
|
|
return;
|
|
|
|
} /* hentyNormalize */
|
|
|
|
/* Check to see if an equivalent unification exists in the Henty filter */
|
|
flag hentyMatch(nmbrString *hentyVars, nmbrString *hentyVarStart,
|
|
/*nmbrString *hentyVarLen,*/ nmbrString *hentySubstList,
|
|
pntrString **stateVector)
|
|
{
|
|
long i, size;
|
|
|
|
size = pntrLen((pntrString *)((*stateVector)[12]));
|
|
|
|
for (i = 0; i < size; i++) {
|
|
if (nmbrEq(hentyVars,
|
|
(nmbrString *)(((pntrString *)((*stateVector)[12]))[i]))) {
|
|
if (nmbrEq(hentyVarStart,
|
|
(nmbrString *)(((pntrString *)((*stateVector)[13]))[i]))) {
|
|
/* (We don't need to look at [14] because it is determined by [13]) */
|
|
if (nmbrEq(hentySubstList,
|
|
(nmbrString *)(((pntrString *)((*stateVector)[15]))[i]))) {
|
|
return(1); /* A previous equivalent unification was found */
|
|
}
|
|
}
|
|
}
|
|
} /* Next i */
|
|
|
|
return (0); /* There was no previous equivalent unification */
|
|
} /* hentyMatch */
|
|
|
|
/* Add an entry to the Henty filter */
|
|
void hentyAdd(nmbrString *hentyVars, nmbrString *hentyVarStart,
|
|
nmbrString *hentyVarLen, nmbrString *hentySubstList,
|
|
pntrString **stateVector)
|
|
{
|
|
long size;
|
|
size = pntrLen((pntrString *)((*stateVector)[12]));
|
|
|
|
pntrLet((pntrString **)(&(*stateVector)[12]), pntrAddGElement(
|
|
(pntrString *)((*stateVector)[12])));
|
|
((pntrString *)((*stateVector)[12]))[size] = hentyVars;
|
|
pntrLet((pntrString **)(&(*stateVector)[13]), pntrAddGElement(
|
|
(pntrString *)((*stateVector)[13])));
|
|
((pntrString *)((*stateVector)[13]))[size] = hentyVarStart;
|
|
pntrLet((pntrString **)(&(*stateVector)[14]), pntrAddGElement(
|
|
(pntrString *)((*stateVector)[14])));
|
|
((pntrString *)((*stateVector)[14]))[size] = hentyVarLen;
|
|
pntrLet((pntrString **)(&(*stateVector)[15]), pntrAddGElement(
|
|
(pntrString *)((*stateVector)[15])));
|
|
((pntrString *)((*stateVector)[15]))[size] =
|
|
hentySubstList;
|
|
} /* hentyAdd */
|