Sindbad~EG File Manager
/* maximal_sub()
M. Beeson for Mathpert
2.15.94 Original date
1.29.98 modified
1.8.00 modified pack to back small bignums as longs.
6.17.04 removed conditional 16-bit compilation
5.5.13 made termlength static.
4.28.24 at end of pack, if(max < 0), not if(count < 0)
*/
#include <assert.h>
#include <string.h>
#include <string.h>
#include <stdio.h>
#include "globals.h"
#include "cancel.h"
#include "autosub.h"
#include "order.h"
#include "algaux.h"
#include "prover.h"
#include "maxsub.h"
#include "psubst.h"
#include "pstring.h"
static int pack(term,int, unsigned short*);
static int unpack(unsigned short *, int, term *);
static int try_subterms(term temp, term t, term x, term *ans);
static unsigned termlength(term t);
/*_______________________________________________________________________*/
unsigned short * locate(unsigned short *buf, unsigned short *end, unsigned short c)
/* like strchr(buf,c), but works on 'strings' of unsigned shorts
containing null characters; searches buf for c up to but not including *end.
It is assumed that 'end' points into the string pointed to by buf,
and marks the end of the string to be searched.
Return NULL if c isn't found.
*/
{ unsigned short *marker;
for(marker = buf; marker < end; ++marker)
{ if(*marker == c)
return marker;
}
return NULL;
}
/*_______________________________________________________________________*/
static int pack(term t, int max, unsigned short *ans)
/* write the Polish form of t to *ans, returning the length of the ans array
IN BYTES, not in unsigned shorts, if it fits in at most max bytes.
Assumes *ans has room for at least max +1 bytes.
Return -1 if it won't fit. These strings CAN contain zeros, because numbers
can contain null bytes, and also because numbers are introduced by the zero
functor and atoms have zero arity byte.
An atom has functor and arity, 2 bytes each = 4 bytes.
An object has functor zero and type, 2 bytes each = 4 bytes,
followed by the data (4 bytes for an integer, 8 bytes for a double,
or 2 bytes for the bignum length plus the digits)
A compound term has functor and arity, followed by the args.
*/
{ unsigned short n;
unsigned nbytes;
int i,count,k;
unsigned short *marker;
long mm;
if(max <= 0)
return -1;
if(ISATOM(t))
{ ans[0] = FUNCTOR(t);
ans[1] = 0;
return 4; /* 4 bytes for an atom */
}
if(OBJECT(t))
{ ans[0] = 0;
switch(TYPE(t))
{ case INTEGER:
nbytes = sizeof(long);
break;
case DOUBLE:
nbytes = sizeof(double);
break;
case BIGNUM:
if(!bignum_long(BIGNUMDATA(t),&mm))
{ /* pack it as a long instead of as a bignum */
nbytes = sizeof(long);
t = make_int(mm);
}
else
nbytes = BIGNUMDATA(t).ln * sizeof(digit);
break;
default:
assert(0);
}
ans[1] = TYPE(t);
if(ans[1] == BIGNUM)
{ ans[2] = (unsigned short) BIGNUMDATA(t).ln;
if(max < (int)(6 + nbytes))
return -1;
memcpy(ans+3,BIGNUMDATA(t).val,nbytes);
return nbytes + 6;
}
memcpy(ans+2, ARGPTR(t), nbytes);
if(max < (int)( 4 + nbytes))
return 1;
return nbytes + 4;
}
/* Now t isn't atomic */
n = ARITY(t);
ans[0] = FUNCTOR(t);
ans[1] = n;
marker = ans + 2;
count = 4;
max -= 4;
for(i=0;i<n;i++)
{ k = pack(ARG(i,t),max,marker);
if(k < 0)
return -1;
count +=k;
marker += k/2;
/* divide by 2 since k is in bytes but marker is incremented 2 bytes at a time
because it is a pointer to unsigned short. */
max -= k;
}
if(max < 0)
return -1;
return count;
}
/*_______________________________________________________________________*/
static int unpack(unsigned short *in, int max, term *ans)
/* inverse of pack. Assumes *in has length at least max and reads a term off
the beginning of 'in', returning the number of bytes read, and
putting the term in *ans. Returns -1 for an error. Succeeds even if
the term read does not use up max 'characters', that is unsigned shorts, or
'entries', of the input, but does NOT ever read more than max entries;
fails if the string up to max entries doesn't form a term, or if there
is an illegal construction, or too few characters.
Any bignums that are small enough to convert to longs are packed as longs,
and then get unpacked as longs, so in that respect unpack is not quite the
inverse of pack.
*/
{ unsigned short f,n;
int i,nbytes,count;
unsigned short *marker;
term *varlist;
int nvariables;
if(in[0] && !in[1]) /* an atom */
{ *ans = MAKE_ATOM(*in);
/* Now set the .info field, which includes the type of the atom */
varlist = get_varlist();
nvariables = get_nvariables();
for(i=0;i<nvariables;i++)
{ if(FUNCTOR(varlist[i]) == FUNCTOR(*ans))
{ ans->info = varlist[i].info;
ans->args = varlist[i].args; /* the value pointer */
break;
}
}
if(i==nvariables)
SETTYPE(*ans,R);
return 4; /* 4 bytes for an atom = 2 unsigned shorts */
}
if(in[0] == 0) /* an object */
{ switch (in[1])
{ case INTEGER:
if(max < 8)
return -1;
*ans = make_int( * ((long *) (in+2)));
return 4 + sizeof(long);
case DOUBLE:
if(max < 12)
return -1;
*ans = make_double( *((double *) (in+2)));
return 4+sizeof(double);
case BIGNUM:
{ bignum b;
b.ln = in[2];
if(max < (int)(b.ln + 6))
return -1;
b.val = callocate(b.ln,sizeof(digit));
memcpy(b.val, in+3, b.ln *sizeof(digit));
*ans = make_bignum(b);
return 6 + b.ln * sizeof(digit);
}
}
}
f = in[0];
n = in[1];
*ans = make_term(f,n);
marker = in+2;
count = 4;
if(max < 2)
return -1;
max -= 2;
for(i=0;i<n;i++)
{ nbytes = unpack(marker,max,ARGPTR(*ans) + i);
if(nbytes < 0)
return -1;
max -= nbytes/2;
marker += nbytes/2;
count += nbytes;
}
return count;
}
/*________________________________________________________________*/
static unsigned termlength(term t)
/* return the number of bytes required for packing a term */
{ unsigned short i,n;
unsigned ans;
if(ISATOM(t))
return 4;
if(OBJECT(t))
{ switch(TYPE(t))
{ case INTEGER: return 4 + sizeof(long);
case DOUBLE: return 4 + sizeof(double);
case BIGNUM: return 6 + BIGNUMDATA(t).ln * sizeof(digit);
}
}
ans = 0;
n = ARITY(t);
for(i=0;i<n;i++)
ans += termlength(ARG(i,t));
return ans + 4;
}
/*_____________________________________________________________________ */
static unsigned short *string_sub_aux(unsigned short *s, int max, unsigned short *a, unsigned short *b, int *len)
/* supposing a and b point to occurrences of x in the packed term s of
length s, find the maximal substring which has a copy containing a
and a disjoint copy containing b. Return its length in *len. (This
length is the number of unsigned shorts involved, i.e. it is half the
byte length.)
Return a pointer to the copy around a. Return NULL if there is
no such substring (of length > 1). Adjust the left endpoint so it
begins on an even-numbered byte (from the start of s).
In general this maximal substring will not code a term; it will
code a term if maxsub is going to work though.
*/
{ unsigned short *aleft, *aright, *bleft, *bright;
aleft = aright = a;
bleft = bright = b;
while( aleft >= s && bleft > a)
{ if(*aleft == *bleft)
{ --aleft;
--bleft;
}
else
{ ++aleft;
++bleft;
break;
}
}
if(bleft == a)
{ aleft+=2;
bleft+=2; /* we overshot */
}
if(aleft == a)
return NULL; /* e.g. if the original string contained x^2 + x twice,
the first call would have a and b pointing to
the two x's within x^2 + x, so we will get here;
returning NULL now enables maximalsub to go on looking
and find a better second occurrence of x, rather
than failing completely. */
while(aright < bleft && bright <= s+max)
{ if(*aright == *bright)
{ ++aright;
++bright;
}
else
{ --aright; /* --bright is not needed as bright is never used */
break;
}
}
if(aright == bleft)
--aright;
/* the maximal string found so far may include an arity,
e.g. if one occurrence is sin x and the other is cos x, since
both occurrences are inside an arity 1 term, aleft will point to
the arity byte. Since a term always uses an even number of unsigned shorts,
we can test for this by checking whether (aleft-s) is even or odd. */
if( ((int) (aleft-s)) & 1)
++aleft; /* we've accidentally included an arity byte, so move right */
if(aleft == aright)
return NULL;
*len = (int) (aright - aleft + 1);
if(*len == 1)
return NULL;
if(*len == 2 && aleft[1] == '\0')
return NULL;
/* aleft points to an atom, followed by a zero arity byte */
return aleft;
}
/*_____________________________________________________________________ */
static unsigned short *string_sub(unsigned short *buffer, int k, int *length)
/* k is initially the length of the packed term in buffer in bytes*/
/* find a substitution as required in maximal_sub, but in string form */
/* Return a pointer to a substring of buffer giving the substitution's
right side, or NULL if it can't be found. Return in *length
the length of the string coding the substitution (in bytes) */
{ unsigned short x = FUNCTOR(get_eigenvariable());
unsigned short *a, *b;
unsigned short *ans = NULL;
unsigned short *end = buffer+k/2; /* k/2 because k is in bytes */
a = locate(buffer,end,x);
if(a==NULL)
return ans;
while(a)
{ if(a+1 == end)
return NULL;
b = locate(a+1,end,x);
while(b)
{ ans = string_sub_aux(buffer,k,a,b,length);
*length *= 2;
/* convert from number of unsigned shorts to number of bytes */
if(ans != NULL)
return ans;
if(b+1 == end)
break;
b = locate(b+1,end,x);
}
if(a+1 == end)
return NULL;
a = locate(a+1,end,x);
}
return NULL;
}
/*_______________________________________________________________________*/
int maximal_sub(term t, term *ans)
/* find a maximal subterm *ans of t such that
(1) two occurrences of the eigenvariable or derivative_subterm in t
are contained in occurrences of *ans
(2) *ans is not just the eigenvariable or derivative_subterm itself.
Return 0 for success, 1 for no such term, -1 for not enough space.
(3) substituting for *ans will get rid of the eigenvariable.
*/
/* Since the representation of terms does not have pointers UP the term
to the parent nodes, this is really difficult to program efficiently.
We solve this problem by using a string representation of terms, in which
case the problem reduces to finding substrings. For each pair of
occurrences of 'x' in the string, find the maximal substring which
occurs twice, once around each occurrence of x, without the two copies
intersecting. Then if there are other occurrences of x outside these
copies, see if that string, or some substring of it, contains ALL
occurrences of x. If so that's the answer (when converted back to a term). */
{ int k = termlength(t);
term x;
int len,err;
term temp;
unsigned short *s, *buffer;
int nbytes;
if(get_nvariables() == 0)
return 1; /* constant problem, e.g. in complex numbers;
without this get_nvariables returns a null term
and things go awry */
buffer = (unsigned short *) callocate(k+1, sizeof(char));
if(buffer==NULL)
return -1; /* no need to kill the entire program */
x = get_eigenvariable();
pack(t,k,buffer);
s = string_sub(buffer,k,&len);
if(s == NULL)
return 1;
nbytes = 2*(int)(s-buffer); // 2 because s and buffer point to unsigned shorts
if(nbytes > k)
assert(0);
assert(len <= k - nbytes);
if(unpack(s,len/2,&temp) == -1) //unpack needs the number of unsigned shorts, len/2
{ /* don't give up. Example: sqrt x - 2/(sqrt x + 1). Both sqrt x terms
are parts of a sum so what comes back is the string form of +(sqrt x, -2/...),
but it gets cut off because the second x is in the second summand of the
first sum, so we don't get into the situation described in the next
comment. */
while(1)
{ len -= 2;
s += 2;
if(*s == 0 || *s == FUNCTOR(x) || len <= 2)
return 1;
if(unpack(s,len/2,&temp) != -1)
break;
}
}
/* The string returned by string_sub can have spurious stuff on
the end, e.g. on sec^2 x tan^2 x the coded form is ^2s1x00120t1x00120
so the string that comes back is x00120, but it unpacks as just x.
Therefore we still have to check for atomic answer and fail in that case. */
/* Also, the string returned by string_sub can be too long, e.g. on
(root(3,x) -1)(root(3,x) + 1) the packed form is
*2+2r2003000x0-1001000+2^2+2r2003000x00...
and what comes back begins with +2r00 instead of with r00.
The unpacked term temp will then be root(3,x)-1. In general
the desired substitution is a leftmost subterm of temp.
These difficulties won't happen often, only when the desired
subterm is embedded in terms with similar beginnings that differ
in the other args. In that case we have to search subterms:
*/
err = try_subterms(temp,t,x,ans);
if(err)
return 1;
if(NEGATIVE(*ans) && equals(ARG(0,*ans),x))
return 1; /* don't ever make the substitution u = -x, it's useless. */
set_valuepointers(ans);
return 0;
}
/*__________________________________________________________________*/
static int try_subterms(term temp, term t, term x, term *ans)
/* try temp and all its subterms to see if substituting for
one of those subterms will eliminate atom x from t. If
so, instantiate *ans to that subterm and return 0.
But, don't return *ans as a VECTOR term, as otherwise
would happen in the system ax+y=1,x-y=1 after Cramer's
rule is used.
If not return 1. */
{ int err;
term test;
unsigned short i,n;
if(ATOMIC(temp))
return 1;
if(FUNCTOR(temp) != VECTOR)
{ subst(var0,temp,t,&test);
if(!contains(test,FUNCTOR(x)))
{ *ans = temp;
return 0;
}
}
n = ARITY(temp);
for(i=0;i<n;i++)
{ err = try_subterms(ARG(i,temp),t,x,ans);
if(!err)
return 0;
}
return 1;
}
/*______________________________________________________________________*/
static int parent(term t, term u, term *ans)
/* return in *ans the parent of the leftmost occurence of a non-negated
occurence of subterm u in term t.
Return 0 for success, 1 if u has no non-negated occurrence as a proper subterm of t.
*/
{ unsigned short f,n;
int i,err;
if(ATOMIC(t))
return 1;
n = ARITY(t);
f = FUNCTOR(t);
if(f == '-')
{ if(equals(u,ARG(0,t)))
return 1;
return parent(ARG(0,t),u,ans);
}
for(i=0;i<n;i++)
{ if(equals(u,ARG(i,t)))
{ *ans = t;
return 0;
}
err = parent(ARG(i,t),u,ans);
if(!err)
return 0;
}
return 1;
}
/*______________________________________________________________________*/
int enlarge_maxsub(term t, term u, term arg, term *ans)
/* u = arg will eliminate x in t; see if the parent of arg
in its leftmost occurrence will also work if psubst
is used to make the substitution. u must be an atom.
If the parent is a sum or a negation, fail, returning 1.
If the parent is a product, check whether new compound fractions
have been created, and if so, and if the parent contains 3 or more factors,
drop off the first one different from arg and try again. Similarly if
the old term had no fractions but enlarging the substitution creates them.
In case of success, return the larger term in *ans and return 0.
Example: t = e^2x + e^(-2x); arg = 2x,
return *ans = e^2x. Return 1 for failure.
*/
{ term *atomlist, *atomlist2;
int i,n,m,err;
term temp,v,newarg,q;
n = variablesin(t,&atomlist);
if(n == 0)
return 1;
psubst(u,arg,t,&temp);
m = variablesin(temp,&atomlist2);
if(m > n || !contains(temp,FUNCTOR(u)))
return 1; /* no variable eliminated (wrong input) */
err = parent(t,arg,&newarg);
if(err)
return 1;
if(FUNCTOR(newarg) == '-')
return 1;
if(FUNCTOR(newarg) == '+')
return 1;
err = psubst(u,newarg,t,&v);
if(err == 1)
return 1; /* fractional exponent created where one wasn't before */
if(FUNCTOR(newarg) == '*' &&
(
( contains_compound_fractions(v) && !contains_compound_fractions(t)) ||
( contains(v,'/') && !contains(t,'/'))
)
)
{ /* new compound fractions introduced */
if(ARITY(newarg) == 2)
return 1;
if(equals(ARG(0,newarg),arg))
return 1;
q = make_term('*',(unsigned short)( ARITY(newarg)-1));
for(i=0;i<ARITY(q);i++)
ARGREP(q,i,ARG(i+1,newarg));
psubst(u,q,t,&v);
if(contains_compound_fractions(v))
return 1;
newarg = q;
}
m = variablesin(v,&atomlist2);
if(m > n || !contains(v,FUNCTOR(u)))
return 1;
*ans = newarg;
return 0;
}
Sindbad File Manager Version 1.0, Coded By Sindbad EG ~ The Terrorists